From acb5f18616a4012a865c7543f83aca4daf31aaab Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 2 Sep 2024 11:06:52 +0200 Subject: [PATCH] Add support for xs:dateTime AttributeValue and improve tests --- src/SAML2/XML/saml/AttributeValue.php | 32 +++++++++++++++++++-- tests/SAML2/XML/saml/AttributeTest.php | 4 +++ tests/SAML2/XML/saml/AttributeValueTest.php | 25 ++++++++++++++++ tests/resources/xml/saml_Attribute.xml | 3 ++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/SAML2/XML/saml/AttributeValue.php b/src/SAML2/XML/saml/AttributeValue.php index 9a0b1b431..cba71f90b 100644 --- a/src/SAML2/XML/saml/AttributeValue.php +++ b/src/SAML2/XML/saml/AttributeValue.php @@ -4,6 +4,8 @@ namespace SimpleSAML\SAML2\XML\saml; +use DateTimeImmutable; +use DateTimeInterface; use DOMElement; use SimpleSAML\Assert\Assert; use SimpleSAML\SAML2\Constants as C; @@ -27,16 +29,18 @@ class AttributeValue extends AbstractSamlElement /** * Create an AttributeValue. * - * @param string|int|null|\SimpleSAML\XML\AbstractElement $value The value of this element. Can be one of: + * The value of this element. Can be one of: * - string * - int * - null + * - \DateTimeInterface * - \SimpleSAML\XML\AbstractElement * + * @param string|int|null|\DateTimeInterface|\SimpleSAML\XML\AbstractElement $value * @throws \SimpleSAML\Assert\AssertionFailedException if the supplied value is neither a string or a DOMElement */ final public function __construct( - protected string|int|null|AbstractElement $value, + protected string|int|null|DateTimeInterface|AbstractElement $value, ) { } @@ -56,6 +60,10 @@ public function getXsiType(): string case "NULL": return "xs:nil"; case "object": + if ($this->value instanceof DateTimeInterface) { + return 'xs:dateTime'; + } + return sprintf( '%s:%s', $this->value::getNamespacePrefix(), @@ -111,14 +119,25 @@ public static function fromXML(DOMElement $xml): static $xml->hasAttributeNS(C::NS_XSI, "type") && $xml->getAttributeNS(C::NS_XSI, "type") === "xs:integer" ) { + Assert::numeric($xml->textContent); + // we have an integer as value $value = intval($xml->textContent); + } elseif ( + $xml->hasAttributeNS(C::NS_XSI, "type") && + $xml->getAttributeNS(C::NS_XSI, "type") === "xs:dateTime" + ) { + Assert::validDateTime($xml->textContent); + + // we have a dateTime as value + $value = new DateTimeImmutable($xml->textContent); } elseif ( // null value $xml->hasAttributeNS(C::NS_XSI, "nil") && ($xml->getAttributeNS(C::NS_XSI, "nil") === "1" || $xml->getAttributeNS(C::NS_XSI, "nil") === "true") ) { + Assert::isEmpty($xml->textContent); $value = null; } else { $value = $xml->textContent; @@ -155,7 +174,14 @@ public function toXML(DOMElement $parent = null): DOMElement $e->textContent = ''; break; case "object": - $this->getValue()->toXML($e); + if ($this->value instanceof DateTimeInterface) { + $e->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', C::NS_XSI); + $e->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xs', C::NS_XS); + $e->setAttributeNS(C::NS_XSI, 'xsi:type', 'xs:dateTime'); + $e->textContent = $this->value->format(C::DATETIME_FORMAT); + } else { + $this->getValue()->toXML($e); + } break; default: // string $e->textContent = $this->getValue(); diff --git a/tests/SAML2/XML/saml/AttributeTest.php b/tests/SAML2/XML/saml/AttributeTest.php index af4902b7d..3cf5eecf4 100644 --- a/tests/SAML2/XML/saml/AttributeTest.php +++ b/tests/SAML2/XML/saml/AttributeTest.php @@ -4,6 +4,7 @@ namespace SimpleSAML\Test\SAML2\XML\saml; +use DateTimeImmutable; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; @@ -88,6 +89,9 @@ public function testMarshalling(): void [ new AttributeValue('FirstValue'), new AttributeValue('SecondValue'), + new AttributeValue(3), + new AttributeValue(new DateTimeImmutable('2024-04-04T04:44:44Z')), + new AttributeValue(null), ], [$attr1, $attr2], ); diff --git a/tests/SAML2/XML/saml/AttributeValueTest.php b/tests/SAML2/XML/saml/AttributeValueTest.php index 90313eba6..3a7f626e6 100644 --- a/tests/SAML2/XML/saml/AttributeValueTest.php +++ b/tests/SAML2/XML/saml/AttributeValueTest.php @@ -4,6 +4,7 @@ namespace SimpleSAML\Test\SAML2\XML\saml; +use DateTimeImmutable; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; @@ -77,6 +78,30 @@ public function testMarshallingString(): void } + /** + * Test creating an AttributeValue from scratch using an integer. + */ + public function testMarshallingInteger(): void + { + $av = new AttributeValue(3); + + $this->assertEquals(3, $av->getValue()); + $this->assertEquals('xs:integer', $av->getXsiType()); + } + + + /** + * Test creating an AttributeValue from scratch using an dateTime. + */ + public function testMarshallingDateTime(): void + { + $av = new AttributeValue(new DateTimeImmutable("2024-04-04T04:44:44Z")); + + $this->assertEquals('2024-04-04T04:44:44Z', $av->getValue()->format(C::DATETIME_FORMAT)); + $this->assertEquals('xs:dateTime', $av->getXsiType()); + } + + /** */ public function testMarshallingNull(): void diff --git a/tests/resources/xml/saml_Attribute.xml b/tests/resources/xml/saml_Attribute.xml index 19e919f3c..0731d136a 100644 --- a/tests/resources/xml/saml_Attribute.xml +++ b/tests/resources/xml/saml_Attribute.xml @@ -1,4 +1,7 @@ FirstValue SecondValue + 3 + 2024-04-04T04:44:44Z +