From 86830a63e2e5eb9adc5799f4a489107156afa01c Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 19 Nov 2024 22:23:35 +0100 Subject: [PATCH] Add samlp:Status --- src/SAML11/XML/samlp/AbstractStatusType.php | 136 ++++++++++++++++++++ src/SAML11/XML/samlp/Status.php | 14 ++ tests/resources/xml/samlp_Status.xml | 9 ++ tests/src/SAML11/XML/samlp/StatusTest.php | 120 +++++++++++++++++ 4 files changed, 279 insertions(+) create mode 100644 src/SAML11/XML/samlp/AbstractStatusType.php create mode 100644 src/SAML11/XML/samlp/Status.php create mode 100644 tests/resources/xml/samlp_Status.xml create mode 100644 tests/src/SAML11/XML/samlp/StatusTest.php diff --git a/src/SAML11/XML/samlp/AbstractStatusType.php b/src/SAML11/XML/samlp/AbstractStatusType.php new file mode 100644 index 0000000..8a10d60 --- /dev/null +++ b/src/SAML11/XML/samlp/AbstractStatusType.php @@ -0,0 +1,136 @@ +getValue(), + [ + C::STATUS_SUCCESS, + C::STATUS_REQUESTER, + C::STATUS_RESPONDER, + C::STATUS_VERSION_MISMATCH, + ], + 'Invalid top-level status code: %s', + ProtocolViolationException::class, + ); + } + + + /** + * Collect the StatusCode + * + * @return \SimpleSAML\SAML11\XML\samlp\StatusCode + */ + public function getStatusCode(): StatusCode + { + return $this->statusCode; + } + + + /** + * Collect the value of the statusMessage + * + * @return \SimpleSAML\SAML11\XML\samlp\StatusMessage|null + */ + public function getStatusMessage(): ?StatusMessage + { + return $this->statusMessage; + } + + + /** + * Collect the value of the statusDetails property + * + * @return \SimpleSAML\SAML11\XML\samlp\StatusDetail|null + */ + public function getStatusDetail(): ?StatusDetail + { + return $this->statusDetail; + } + + + /** + * Convert XML into a Status + * + * @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 + * @throws \SimpleSAML\XML\Exception\TooManyElementsException + * if too many child-elements of a type are specified + * @throws \SimpleSAML\XML\Exception\MissingElementException + * if one of the mandatory child-elements is missing + */ + public static function fromXML(DOMElement $xml): static + { + Assert::same($xml->localName, 'Status', InvalidDOMElementException::class); + Assert::same($xml->namespaceURI, Status::NS, InvalidDOMElementException::class); + + $statusCode = StatusCode::getChildrenOfClass($xml); + Assert::minCount($statusCode, 1, MissingElementException::class); + Assert::count($statusCode, 1, TooManyElementsException::class); + + $statusMessage = StatusMessage::getChildrenOfClass($xml); + Assert::maxCount($statusMessage, 1, TooManyElementsException::class); + + $statusDetail = StatusDetail::getChildrenOfClass($xml); + + return new static( + array_pop($statusCode), + array_pop($statusMessage), + array_pop($statusDetail), + ); + } + + + /** + * Convert this Status to XML. + * + * @param \DOMElement|null $parent The element we are converting to XML. + * @return \DOMElement The XML element after adding the data corresponding to this Status. + */ + public function toXML(DOMElement $parent = null): DOMElement + { + $e = $this->instantiateParentElement($parent); + + $this->getStatusCode()->toXML($e); + + $this->getStatusMessage()?->toXML($e); + + $this->getStatusDetail()?->toXML($e); + + return $e; + } +} diff --git a/src/SAML11/XML/samlp/Status.php b/src/SAML11/XML/samlp/Status.php new file mode 100644 index 0000000..9bff9ca --- /dev/null +++ b/src/SAML11/XML/samlp/Status.php @@ -0,0 +1,14 @@ + + + + + Something went wrong + + org.sourceid.websso.profiles.idp.FailedAuthnSsoException + + diff --git a/tests/src/SAML11/XML/samlp/StatusTest.php b/tests/src/SAML11/XML/samlp/StatusTest.php new file mode 100644 index 0000000..a649639 --- /dev/null +++ b/tests/src/SAML11/XML/samlp/StatusTest.php @@ -0,0 +1,120 @@ +documentElement, + ), + ); + + $this->assertEquals( + self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement), + strval($status), + ); + } + + + /** + */ + public function testMarshallingElementOrdering(): void + { + $status = new Status( + new StatusCode( + C::STATUS_RESPONDER, + [ + new StatusCode( + C::STATUS_REQUEST_DENIED, + ), + ], + ), + new StatusMessage('Something went wrong'), + new StatusDetail([new Chunk(self::$detail->documentElement)]), + ); + + $statusElement = $status->toXML(); + + // Test for a StatusCode + $xpCache = XPath::getXPath($statusElement); + $statusElements = XPath::xpQuery($statusElement, './saml_protocol:StatusCode', $xpCache); + $this->assertCount(1, $statusElements); + + // Test ordering of Status contents + /** @psalm-var \DOMElement[] $statusElements */ + $statusElements = XPath::xpQuery($statusElement, './saml_protocol:StatusCode/following-sibling::*', $xpCache); + $this->assertCount(2, $statusElements); + $this->assertEquals('samlp:StatusMessage', $statusElements[0]->tagName); + $this->assertEquals('samlp:StatusDetail', $statusElements[1]->tagName); + } +}