diff --git a/composer.json b/composer.json
index 6b5567f21..4ab7c484e 100644
--- a/composer.json
+++ b/composer.json
@@ -47,7 +47,8 @@
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true,
- "dealerdirect/phpcodesniffer-composer-installer": true
+ "dealerdirect/phpcodesniffer-composer-installer": true,
+ "phpstan/extension-installer": true
}
}
}
diff --git a/src/SAML2/Constants.php b/src/SAML2/Constants.php
index 0cc5cee71..fe12984db 100644
--- a/src/SAML2/Constants.php
+++ b/src/SAML2/Constants.php
@@ -231,6 +231,11 @@ class Constants
*/
const NS_ECP = 'urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp';
+ /**
+ * The namespace for the IDP Discovery protocol.
+ */
+ const NS_IDPDISC = 'urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol';
+
/**
* The namespace for the SOAP protocol.
*/
diff --git a/src/SAML2/Utils.php b/src/SAML2/Utils.php
index 3d55dd0c8..450009c39 100644
--- a/src/SAML2/Utils.php
+++ b/src/SAML2/Utils.php
@@ -172,7 +172,7 @@ public static function validateSignature(array $info, XMLSecurityKey $key) : voi
/** @var XMLSecurityDSig $objXMLSecDSig */
$objXMLSecDSig = $info['Signature'];
-
+
/**
* @var \DOMElement[] $sigMethod
* @var \DOMElement $objXMLSecDSig->sigNode
@@ -221,6 +221,7 @@ public static function xpQuery(DOMNode $node, string $query) : array
$xpCache->registerNamespace('saml_protocol', Constants::NS_SAMLP);
$xpCache->registerNamespace('saml_assertion', Constants::NS_SAML);
$xpCache->registerNamespace('saml_metadata', Constants::NS_MD);
+ $xpCache->registerNamespace('saml_idpdisc', Constants::NS_IDPDISC);
$xpCache->registerNamespace('ds', XMLSecurityDSig::XMLDSIGNS);
$xpCache->registerNamespace('xenc', XMLSecEnc::XMLENCNS);
}
diff --git a/src/SAML2/XML/idpdisc/DiscoveryResponse.php b/src/SAML2/XML/idpdisc/DiscoveryResponse.php
new file mode 100644
index 000000000..9ac0a2019
--- /dev/null
+++ b/src/SAML2/XML/idpdisc/DiscoveryResponse.php
@@ -0,0 +1,57 @@
+toXMLInternal($parent, Constants::NS_IDPDISC, 'idpdisc:DiscoveryResponse');
+ }
+}
diff --git a/src/SAML2/XML/md/EndpointType.php b/src/SAML2/XML/md/EndpointType.php
index ab4e986d2..3747355cd 100644
--- a/src/SAML2/XML/md/EndpointType.php
+++ b/src/SAML2/XML/md/EndpointType.php
@@ -59,7 +59,7 @@ public function __construct(?DOMElement $xml = null)
if (!$xml->hasAttribute('Binding')) {
throw new \Exception('Missing Binding on '.$xml->tagName);
}
- $this->Binding = $xml->getAttribute('Binding');
+ $this->setBinding($xml->getAttribute('Binding'));
if (!$xml->hasAttribute('Location')) {
throw new \Exception('Missing Location on '.$xml->tagName);
@@ -230,11 +230,12 @@ public function setResponseLocation(?string $responseLocation = null) : void
*
* @param \DOMElement $parent The element we should append this endpoint to.
* @param string $name The name of the element we should create.
+ * @param string $namespace The namespace of the element we should create
* @return \DOMElement
*/
- public function toXML(DOMElement $parent, string $name) : DOMElement
+ protected function toXMLInternal(DOMElement $parent, string $namespace, string $name) : DOMElement
{
- $e = $parent->ownerDocument->createElementNS(Constants::NS_MD, $name);
+ $e = $parent->ownerDocument->createElementNS($namespace, $name);
$parent->appendChild($e);
if (empty($this->Binding)) {
@@ -257,4 +258,17 @@ public function toXML(DOMElement $parent, string $name) : DOMElement
return $e;
}
+
+
+ /**
+ * Convert this Attribute to XML.
+ *
+ * @param \DOMElement $parent The element we should append this Attribute to.
+ * @param string $name
+ * @return \DOMElement
+ */
+ public function toXML(DOMElement $parent, string $name) : \DOMElement
+ {
+ return $this->toXMLInternal($parent, Constants::NS_MD, $name);
+ }
}
diff --git a/src/SAML2/XML/md/Extensions.php b/src/SAML2/XML/md/Extensions.php
index fda7a4a3c..14a9f6a9f 100644
--- a/src/SAML2/XML/md/Extensions.php
+++ b/src/SAML2/XML/md/Extensions.php
@@ -12,6 +12,7 @@
use SAML2\XML\alg\DigestMethod;
use SAML2\XML\alg\SigningMethod;
use SAML2\XML\Chunk;
+use SAML2\XML\idpdisc\DiscoveryResponse;
use SAML2\XML\mdattr\EntityAttributes;
use SAML2\XML\mdrpi\Common as MDRPI;
use SAML2\XML\mdrpi\PublicationInfo;
@@ -32,6 +33,7 @@ class Extensions
*
* @param \DOMElement $parent The element that may contain the md:Extensions element.
* @return (\SAML2\XML\shibmd\Scope|
+ * \SAML2\XML\idpdisc\DiscoveryResponse|
* \SAML2\XML\mdattr\EntityAttributes|
* \SAML2\XML\mdrpi\RegistrationInfo|
* \SAML2\XML\mdrpi\PublicationInfo|
@@ -45,6 +47,9 @@ public static function getList(DOMElement $parent) : array
{
$ret = [];
$supported = [
+ Constants::NS_IDPDISC => [
+ 'DiscoveryResponse' => DiscoveryResponse::class,
+ ],
Scope::NS => [
'Scope' => Scope::class,
],
diff --git a/src/SAML2/XML/md/IndexedEndpointType.php b/src/SAML2/XML/md/IndexedEndpointType.php
index 162c137a2..341994ca6 100644
--- a/src/SAML2/XML/md/IndexedEndpointType.php
+++ b/src/SAML2/XML/md/IndexedEndpointType.php
@@ -104,11 +104,12 @@ public function setIsDefault(?bool $flag = null) : void
*
* @param \DOMElement $parent The element we should append this endpoint to.
* @param string $name The name of the element we should create.
+ * @param string $namespace The namesapce of the element we should create.
* @return \DOMElement
*/
- public function toXML(DOMElement $parent, string $name) : DOMElement
+ protected function toXMLInternal(DOMElement $parent, string $namespace, string $name) : DOMElement
{
- $e = parent::toXML($parent, $name);
+ $e = parent::toXMLInternal($parent, $namespace, $name);
$e->setAttribute('index', strval($this->index));
if (is_bool($this->isDefault)) {
diff --git a/tests/SAML2/XML/md/EndpointTypeTest.php b/tests/SAML2/XML/md/EndpointTypeTest.php
index 0763e660d..fb49abafe 100644
--- a/tests/SAML2/XML/md/EndpointTypeTest.php
+++ b/tests/SAML2/XML/md/EndpointTypeTest.php
@@ -25,8 +25,8 @@ public function testMarshalling() : void
$document = DOMDocumentFactory::fromString('');
$endpointTypeElement = $endpointType->toXML($document->firstChild, 'md:Test');
-
$endpointTypeElements = Utils::xpQuery($endpointTypeElement, '/root/saml_metadata:Test');
+
$this->assertCount(1, $endpointTypeElements);
$endpointTypeElement = $endpointTypeElements[0];
diff --git a/tests/SAML2/XML/md/IndexedEndpointTypeTest.php b/tests/SAML2/XML/md/IndexedEndpointTypeTest.php
index 4698dda14..d349b2bee 100644
--- a/tests/SAML2/XML/md/IndexedEndpointTypeTest.php
+++ b/tests/SAML2/XML/md/IndexedEndpointTypeTest.php
@@ -4,7 +4,10 @@
namespace SAML2\XML\md;
+use InvalidArgumentException;
+use SAML2\Constants;
use SAML2\DOMDocumentFactory;
+use SAML2\XML\idpdisc\DiscoveryResponse;
use SAML2\XML\md\IndexedEndpointType;
use SAML2\Utils;
@@ -16,7 +19,7 @@ class IndexedEndpointTypeTest extends \PHPUnit\Framework\TestCase
/**
* @return void
*/
- public function testMarshalling() : void
+ public function testMarshalling(): void
{
$indexedEndpointType = new IndexedEndpointType();
$indexedEndpointType->setBinding('TestBinding');
@@ -50,4 +53,41 @@ public function testMarshalling() : void
$this->assertCount(1, $indexedEndpointTypeElement);
$this->assertTrue(!$indexedEndpointTypeElement[0]->hasAttribute('isDefault'));
}
+
+
+ /**
+ * @return void
+ */
+ public function testMarshallingDiscoveryResponse(): void
+ {
+ $discoResponse = new DiscoveryResponse();
+ $discoResponse->setBinding(Constants::NS_IDPDISC);
+ $discoResponse->setLocation('TestLocation');
+ $discoResponse->setIndex(42);
+ $discoResponse->setIsDefault(false);
+
+ $document = DOMDocumentFactory::fromString('');
+ $discoResponseElement = $discoResponse->toXML($document->firstChild, 'idpdisc:DiscoverResponse');
+
+ $discoResponseElements = Utils::xpQuery($discoResponseElement, '/root/saml_idpdisc:DiscoveryResponse');
+ $this->assertCount(1, $discoResponseElements);
+ $discoResponseElement = $discoResponseElements[0];
+
+ $this->assertEquals(Constants::NS_IDPDISC, $discoResponseElement->getAttribute('Binding'));
+ $this->assertEquals('TestLocation', $discoResponseElement->getAttribute('Location'));
+ $this->assertEquals('42', $discoResponseElement->getAttribute('index'));
+ $this->assertEquals('false', $discoResponseElement->getAttribute('isDefault'));
+ }
+
+
+ /**
+ * @return void
+ */
+ public function testMarshallingDiscoveryResponseWrongBindingFails(): void
+ {
+ $discoResponse = new DiscoveryResponse();
+
+ $this->expectException(InvalidArgumentException::class);
+ $discoResponse->setBinding('This is not OK.');
+ }
}