diff --git a/src/SAML2/XML/saml/Advice.php b/src/SAML2/XML/saml/Advice.php new file mode 100644 index 000000000..9e951deb2 --- /dev/null +++ b/src/SAML2/XML/saml/Advice.php @@ -0,0 +1,182 @@ +setElements($elements); + } + + + /** + * Test if an object, at the state it's in, would produce an empty XML-element + * + * @return bool + */ + public function isEmptyElement(): bool + { + return ( + empty($this->assertionIDRef) + && empty($this->assertionURIRef) + && empty($this->assertion) + && empty($this->encryptedAssertion) + && empty($this->getElements()) + ); + } + + + /** + * @return \SimpleSAML\SAML2\XML\saml\AssertionIDRef[] + */ + public function getAssertionIDRef(): array + { + return $this->assertionIDRef; + } + + + /** + * @return \SimpleSAML\SAML2\XML\saml\AssertionURIRef[] + */ + public function getAssertionURIRef(): array + { + return $this->assertionURIRef; + } + + + /** + * @return \SimpleSAML\SAML2\XML\saml\Assertion[] + */ + public function getAssertion(): array + { + return $this->assertion; + } + + + /** + * @return \SimpleSAML\SAML2\XML\saml\EncryptedAssertion[] + */ + public function getEncryptedAssertion(): array + { + return $this->encryptedAssertion; + } + + + /** + * Convert XML into an Advice + * + * @param \DOMElement $xml The XML element we should load + * @return static + * + * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException + * If the qualified name of the supplied element is wrong + */ + public static function fromXML(DOMElement $xml): static + { + $qualifiedName = static::getClassName(static::class); + Assert::eq( + $xml->localName, + $qualifiedName, + 'Unexpected name for endpoint: ' . $xml->localName . '. Expected: ' . $qualifiedName . '.', + InvalidDOMElementException::class, + ); + + $assertionIDRef = AssertionIDRef::getChildrenOfClass($xml); + $assertionURIRef = AssertionURIRef::getChildrenOfClass($xml); + $assertion = Assertion::getChildrenOfClass($xml); + $encryptedAssertion = EncryptedAssertion::getChildrenOfClass($xml); + + $elements = []; + foreach ($xml->childNodes as $element) { + if ($element->namespaceURI === C::NS_SAML) { + continue; + } elseif (!($element instanceof DOMElement)) { + continue; + } + + $elements[] = new Chunk($element); + } + + return new static( + $assertionIDRef, + $assertionURIRef, + $assertion, + $encryptedAssertion, + $elements, + ); + } + + + /** + * Convert this Advince to XML. + * + * @param \DOMElement $parent The element we are converting to XML. + * @return \DOMElement The XML element after adding the data corresponding to this Condition. + */ + public function toXML(DOMElement $parent = null): DOMElement + { + $e = $this->instantiateParentElement($parent); + + foreach ($this->getAssertionIDRef() as $assertionIDRef) { + $assertionIDRef->toXML($e); + } + + foreach ($this->getAssertionURIRef() as $assertionURIRef) { + $assertionURIRef->toXML($e); + } + + foreach ($this->getAssertion() as $assertion) { + $assertion->toXML($e); + } + + foreach ($this->getEncryptedAssertion() as $encryptedAssertion) { + $encryptedAssertion->toXML($e); + } + + foreach ($this->getElements() as $element) { + /** @psalm-var \SimpleSAML\XML\SerializableElementInterface $element */ + $element->toXML($e); + } + + return $e; + } +} diff --git a/tests/SAML2/XML/saml/AdviceTest.php b/tests/SAML2/XML/saml/AdviceTest.php new file mode 100644 index 000000000..eaecd58d0 --- /dev/null +++ b/tests/SAML2/XML/saml/AdviceTest.php @@ -0,0 +1,128 @@ +schema = dirname(__FILE__, 5) . '/schemas/saml-schema-assertion-2.0.xsd'; + + $this->testedClass = Advice::class; + + $this->xmlRepresentation = DOMDocumentFactory::fromFile( + dirname(__FILE__, 4) . '/resources/xml/saml_Advice.xml', + ); + + $this->assertionIDRef = DOMDocumentFactory::fromFile( + dirname(__FILE__, 4) . '/resources/xml/saml_AssertionIDRef.xml', + ); + + $this->assertionURIRef = DOMDocumentFactory::fromFile( + dirname(__FILE__, 4) . '/resources/xml/saml_AssertionURIRef.xml', + ); + + $this->assertion = DOMDocumentFactory::fromFile( + dirname(__FILE__, 4) . '/resources/xml/saml_Assertion.xml', + ); + + $this->encryptedAssertion = DOMDocumentFactory::fromFile( + dirname(__FILE__, 4) . '/resources/xml/saml_EncryptedAssertion.xml', + ); + } + + + /** + */ + public function testMarshalling(): void + { + $chunkXml = DOMDocumentFactory::fromString( + 'Value' + ); + $chunk = Chunk::fromXML($chunkXml->documentElement); + + $advice = new Advice( + [AssertionIDRef::fromXML($this->assertionIDRef->documentElement)], + [AssertionURIRef::fromXML($this->assertionURIRef->documentElement)], + [Assertion::fromXML($this->assertion->documentElement)], + [EncryptedAssertion::fromXML($this->encryptedAssertion->documentElement)], + [$chunk], + ); + + $this->assertFalse($advice->isEmptyElement()); + + $this->assertEquals( + $this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement), + strval($advice), + ); + } + + + /** + */ + public function testMarshallingWithNoContent(): void + { + $advice = new Advice(); + $this->assertEquals( + '', + strval($advice) + ); + $this->assertTrue($advice->isEmptyElement()); + } + + + /** + */ + public function testUnmarshalling(): void + { + $advice = Advice::fromXML($this->xmlRepresentation->documentElement); + + $this->assertEquals( + $this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement), + strval($advice), + ); + } +} diff --git a/tests/resources/xml/saml_Advice.xml b/tests/resources/xml/saml_Advice.xml new file mode 100644 index 000000000..7fb43a958 --- /dev/null +++ b/tests/resources/xml/saml_Advice.xml @@ -0,0 +1,60 @@ + + _Test + urn:x-simplesamlphp:reference + + Provider + + SomeNameIDValue + + SomeOtherNameIDValue + + + + + + https://simplesamlphp.org/sp/metadata + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + + + + + 1 + + + 1 + + + 1 + + + + + + + + + + + sNLWjwyj/R0oPwSgNnqowahiOwM0YU3YaH3jsH0t2YUDcHkcgouvW5x6YbNdgvGq0ImsNrkjI//0hrL4HvrOX33+DkhCo2FX5+a7UCdftfBfSjvt0houF8z3Zq/XOm6HxBUbWt5MULYpMKMZ9iAY6+raydxk2tFWgnAyHaBfzvU= + + + + + + self::xenc:CipherValue[@Id="example1"] + + + + + + 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 + + + + Value +