From 2d5482a3a185a42e29a3a3e33a6c7aa2f6d0fb7c Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 1 Feb 2023 23:28:02 +0100 Subject: [PATCH] Implement saml:AuthzDecisionStatement element --- src/SAML2/Constants.php | 13 ++ src/SAML2/XML/saml/AuthzDecisionStatement.php | 156 ++++++++++++++++++ .../XML/saml/AuthzDecisionStatementTest.php | 87 ++++++++++ .../xml/saml_AuthzDecisionStatement.xml | 63 +++++++ 4 files changed, 319 insertions(+) create mode 100644 src/SAML2/XML/saml/AuthzDecisionStatement.php create mode 100644 tests/SAML2/XML/saml/AuthzDecisionStatementTest.php create mode 100644 tests/resources/xml/saml_AuthzDecisionStatement.xml diff --git a/src/SAML2/Constants.php b/src/SAML2/Constants.php index e964b48fd..5b4b62bb4 100644 --- a/src/SAML2/Constants.php +++ b/src/SAML2/Constants.php @@ -453,4 +453,17 @@ class Constants extends \SimpleSAML\XMLSecurity\Constants * The maximum size for any entityid as per SAML2INT-specification */ public const SAML2INT_ENTITYID_MAX_LENGTH = 256; + + /** + * Valid values for saml:DecisionType + */ + public const AUTHZ_DECISION_PERMIT = 'Permit'; + public const AUTHZ_DECISION_DENY = 'Deny'; + public const AUTHZ_DECISION_INDETERMINATE = 'Indeterminate'; + + public const AUTHZ_DECISIONS = [ + self::AUTHZ_DECISION_PERMIT, + self::AUTHZ_DECISION_DENY, + self::AUTHZ_DECISION_INDETERMINATE, + ]; } diff --git a/src/SAML2/XML/saml/AuthzDecisionStatement.php b/src/SAML2/XML/saml/AuthzDecisionStatement.php new file mode 100644 index 000000000..f1daff341 --- /dev/null +++ b/src/SAML2/XML/saml/AuthzDecisionStatement.php @@ -0,0 +1,156 @@ +resource; + } + + + /** + * Collect the value of the decision-property + * + * @return string + */ + public function getDecision(): string + { + return $this->decision; + } + + + /** + * Collect the value of the action-property + * + * @return array + */ + public function getAction(): array + { + return $this->action; + } + + + /** + * Collect the value of the evidence-property + * + * @return \SimpleSAML\SAML2\XML\saml\Evidence|null + */ + public function getEvidence(): ?Evidence + { + return $this->evidence; + } + + + /** + * Convert XML into an AuthzDecisionStatement + * + * @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\MissingElementException + * if one of the mandatory child-elements is missing + * @throws \Exception if the authentication instant is not a valid timestamp. + */ + public static function fromXML(DOMElement $xml): static + { + Assert::same($xml->localName, 'AuthzDecisionStatement', InvalidDOMElementException::class); + Assert::same($xml->namespaceURI, AuthzDecisionStatement::NS, InvalidDOMElementException::class); + + $action = Action::getChildrenOfClass($xml); + Assert::minCount( + $action, + 1, + 'Missing in ', + MissingElementException::class, + ); + + $evidence = Evidence::getChildrenOfClass($xml); + Assert::maxCount( + $evidence, + 1, + 'Too many in ', + TooManyElementException::class, + ); + + return new static( + self::getAttribute($xml, 'Resource'), + self::getAttribute($xml, 'Decision'), + $action, + array_pop($evidence), + ); + } + + + /** + * Convert this AuthzDecisionStatement to XML. + * + * @param \DOMElement|null $parent The element we should append this AuthzDecisionStatement to. + * @return \DOMElement + */ + public function toXML(DOMElement $parent = null): DOMElement + { + $e = $this->instantiateParentElement($parent); + + $e->setAttribute('Resource', $this->getResource()); + $e->setAttribute('Decision', $this->getDecision()); + + foreach ($this->getAction() as $action) { + $action->toXML($e); + } + + if ($this->getEvidence() !== null && !$this->getEvidence()->isEmptyElement()) { + $this->getEvidence()->toXML($e); + } + + return $e; + } +} diff --git a/tests/SAML2/XML/saml/AuthzDecisionStatementTest.php b/tests/SAML2/XML/saml/AuthzDecisionStatementTest.php new file mode 100644 index 000000000..9a1589a23 --- /dev/null +++ b/tests/SAML2/XML/saml/AuthzDecisionStatementTest.php @@ -0,0 +1,87 @@ +schema = dirname(__FILE__, 5) . '/schemas/saml-schema-assertion-2.0.xsd'; + + $this->testedClass = AuthzDecisionStatement::class; + + $this->xmlRepresentation = DOMDocumentFactory::fromFile( + dirname(__FILE__, 4) . '/resources/xml/saml_AuthzDecisionStatement.xml', + ); + + $this->evidence = DOMDocumentFactory::fromFile( + dirname(__FILE__, 4) . '/resources/xml/saml_Evidence.xml', + ); + } + + + /** + */ + public function testMarshalling(): void + { + $authzDecisionStatement = new AuthzDecisionStatement( + 'urn:x-simplesamlphp:resource', + 'Permit', + [ + new Action('urn:x-simplesamlphp:namespace', 'SomeAction'), + new Action('urn:x-simplesamlphp:namespace', 'OtherAction'), + ], + Evidence::fromXML($this->evidence->documentElement), + ); + + $this->assertEquals( + $this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement), + strval($authzDecisionStatement), + ); + } + + + /** + */ + public function testUnmarshalling(): void + { + $authzDecisionStatement = AuthzDecisionStatement::fromXML($this->xmlRepresentation->documentElement); + + $this->assertEquals( + $this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement), + strval($authzDecisionStatement), + ); + } +} diff --git a/tests/resources/xml/saml_AuthzDecisionStatement.xml b/tests/resources/xml/saml_AuthzDecisionStatement.xml new file mode 100644 index 000000000..2946c0ea7 --- /dev/null +++ b/tests/resources/xml/saml_AuthzDecisionStatement.xml @@ -0,0 +1,63 @@ + + SomeAction + OtherAction + + _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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 + + + + +