Skip to content

Commit 691bbc8

Browse files
committed
Add NameIDMappingResponse-element
1 parent 4f8fd3c commit 691bbc8

File tree

3 files changed

+259
-0
lines changed

3 files changed

+259
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\SAML2\XML\samlp;
6+
7+
use DOMElement;
8+
use SimpleSAML\SAML2\Assert\Assert;
9+
use SimpleSAML\SAML2\Constants as C;
10+
use SimpleSAML\SAML2\Exception\Protocol\RequestVersionTooHighException;
11+
use SimpleSAML\SAML2\Exception\Protocol\RequestVersionTooLowException;
12+
use SimpleSAML\SAML2\Type\SAMLAnyURIValue;
13+
use SimpleSAML\SAML2\Type\SAMLDateTimeValue;
14+
use SimpleSAML\SAML2\Type\SAMLStringValue;
15+
use SimpleSAML\SAML2\XML\IdentifierTrait;
16+
use SimpleSAML\SAML2\XML\saml\Assertion;
17+
use SimpleSAML\SAML2\XML\saml\EncryptedAssertion;
18+
use SimpleSAML\SAML2\XML\saml\EncryptedID;
19+
use SimpleSAML\SAML2\XML\saml\Issuer;
20+
use SimpleSAML\SAML2\XML\saml\NameID;
21+
use SimpleSAML\XML\SchemaValidatableElementInterface;
22+
use SimpleSAML\XML\SchemaValidatableElementTrait;
23+
use SimpleSAML\XMLSchema\Exception\InvalidDOMElementException;
24+
use SimpleSAML\XMLSchema\Exception\MissingElementException;
25+
use SimpleSAML\XMLSchema\Exception\TooManyElementsException;
26+
use SimpleSAML\XMLSchema\Type\IDValue;
27+
use SimpleSAML\XMLSchema\Type\NCNameValue;
28+
use SimpleSAML\XMLSecurity\XML\ds\Signature;
29+
30+
use function array_merge;
31+
use function array_pop;
32+
use function strval;
33+
34+
/**
35+
* Class for SAML 2 NameIDMappingResponse messages.
36+
*
37+
* @package simplesamlphp/saml2
38+
*/
39+
class NameIDMappingResponse extends AbstractStatusResponse implements SchemaValidatableElementInterface
40+
{
41+
use IdentifierTrait;
42+
use SchemaValidatableElementTrait;
43+
44+
45+
/**
46+
* Constructor for SAML 2 response messages.
47+
*
48+
* @param \SimpleSAML\SAML2\XML\saml\NameID|\SimpleSAML\SAML2\XML\saml\EncryptedID $identifier
49+
* @param \SimpleSAML\XMLSchema\Type\IDValue $id
50+
* @param \SimpleSAML\SAML2\XML\samlp\Status $status
51+
* @param \SimpleSAML\SAML2\Type\SAMLDateTimeValue $issueInstant
52+
* @param \SimpleSAML\SAML2\XML\saml\Issuer|null $issuer
53+
* @param \SimpleSAML\XMLSchema\Type\NCNameValue|null $inResponseTo
54+
* @param \SimpleSAML\SAML2\Type\SAMLAnyURIValue|null $destination
55+
* @param \SimpleSAML\SAML2\Type\SAMLAnyURIValue|null $consent
56+
* @param \SimpleSAML\SAML2\XML\samlp\Extensions $extensions
57+
*/
58+
final public function __construct(
59+
NameID|EncryptedID $identifier,
60+
IDValue $id,
61+
Status $status,
62+
SAMLDateTimeValue $issueInstant,
63+
?Issuer $issuer = null,
64+
?NCNameValue $inResponseTo = null,
65+
?SAMLAnyURIValue $destination = null,
66+
?SAMLAnyURIValue $consent = null,
67+
?Extensions $extensions = null,
68+
) {
69+
$this->setIdentifier($identifier);
70+
71+
parent::__construct(
72+
$id,
73+
$status,
74+
$issueInstant,
75+
$issuer,
76+
$inResponseTo,
77+
$destination,
78+
$consent,
79+
$extensions,
80+
);
81+
}
82+
83+
84+
/**
85+
* Convert XML into a Response element.
86+
*
87+
* @throws \SimpleSAML\XMLSchema\Exception\InvalidDOMElementException
88+
* if the qualified name of the supplied element is wrong
89+
* @throws \SimpleSAML\XMLSchema\Exception\MissingAttributeException
90+
* if the supplied element is missing one of the mandatory attributes
91+
* @throws \SimpleSAML\XMLSchema\Exception\MissingElementException
92+
* if one of the mandatory child-elements is missing
93+
*/
94+
public static function fromXML(DOMElement $xml): static
95+
{
96+
Assert::same($xml->localName, static::getLocalName(), InvalidDOMElementException::class);
97+
Assert::same($xml->namespaceURI, static::NS, InvalidDOMElementException::class);
98+
99+
$version = self::getAttribute($xml, 'Version', SAMLStringValue::class);
100+
Assert::true(version_compare('2.0', strval($version), '<='), RequestVersionTooLowException::class);
101+
Assert::true(version_compare('2.0', strval($version), '>='), RequestVersionTooHighException::class);
102+
103+
$signature = Signature::getChildrenOfClass($xml);
104+
Assert::maxCount($signature, 1, 'Only one ds:Signature element is allowed.', TooManyElementsException::class);
105+
106+
$issuer = Issuer::getChildrenOfClass($xml);
107+
Assert::countBetween($issuer, 0, 1);
108+
109+
$status = Status::getChildrenOfClass($xml);
110+
Assert::minCount($status, 1, MissingElementException::class);
111+
Assert::maxCount($status, 1, TooManyElementsException::class);
112+
113+
$extensions = Extensions::getChildrenOfClass($xml);
114+
Assert::maxCount(
115+
$extensions,
116+
1,
117+
'Only one saml:Extensions element is allowed.',
118+
TooManyElementsException::class,
119+
);
120+
121+
$response = new static(
122+
self::getIdentifierFromXML($xml),
123+
self::getAttribute($xml, 'ID', IDValue::class),
124+
array_pop($status),
125+
self::getAttribute($xml, 'IssueInstant', SAMLDateTimeValue::class),
126+
empty($issuer) ? null : array_pop($issuer),
127+
self::getOptionalAttribute($xml, 'InResponseTo', NCNameValue::class, null),
128+
self::getOptionalAttribute($xml, 'Destination', SAMLAnyURIValue::class, null),
129+
self::getOptionalAttribute($xml, 'Consent', SAMLAnyURIValue::class, null),
130+
empty($extensions) ? null : array_pop($extensions),
131+
);
132+
133+
if (!empty($signature)) {
134+
$response->setSignature($signature[0]);
135+
$response->messageContainedSignatureUponConstruction = true;
136+
$response->setXML($xml);
137+
}
138+
139+
return $response;
140+
}
141+
142+
143+
/**
144+
* Convert the response message to an XML element.
145+
*/
146+
protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
147+
{
148+
$e = parent::toUnsignedXML($parent);
149+
150+
$this->getIdentifier()->toXML($e);
151+
152+
return $e;
153+
}
154+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\Test\SAML2\XML\samlp;
6+
7+
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\Group;
9+
use PHPUnit\Framework\TestCase;
10+
use SimpleSAML\SAML2\Constants as C;
11+
use SimpleSAML\SAML2\Type\SAMLAnyURIValue;
12+
use SimpleSAML\SAML2\Type\SAMLDateTimeValue;
13+
use SimpleSAML\SAML2\Type\SAMLStringValue;
14+
use SimpleSAML\SAML2\XML\saml\Issuer;
15+
use SimpleSAML\SAML2\XML\saml\NameID;
16+
use SimpleSAML\SAML2\XML\samlp\AbstractMessage;
17+
use SimpleSAML\SAML2\XML\samlp\AbstractSamlpElement;
18+
use SimpleSAML\SAML2\XML\samlp\AbstractStatusResponse;
19+
use SimpleSAML\SAML2\XML\samlp\NameIDMappingResponse;
20+
use SimpleSAML\SAML2\XML\samlp\Status;
21+
use SimpleSAML\SAML2\XML\samlp\StatusCode;
22+
use SimpleSAML\XML\DOMDocumentFactory;
23+
use SimpleSAML\XML\TestUtils\SchemaValidationTestTrait;
24+
use SimpleSAML\XML\TestUtils\SerializableElementTestTrait;
25+
use SimpleSAML\XMLSchema\Type\IDValue;
26+
use SimpleSAML\XMLSchema\Type\NCNameValue;
27+
use SimpleSAML\XMLSecurity\TestUtils\SignedElementTestTrait;
28+
29+
use function dirname;
30+
use function strval;
31+
32+
/**
33+
* Class \SimpleSAML\SAML2\XML\samlp\NameIDMappingResponseTest
34+
*
35+
* @package simplesamlphp/saml2
36+
*/
37+
#[Group('samlp')]
38+
#[CoversClass(NameIDMappingResponse::class)]
39+
#[CoversClass(AbstractStatusResponse::class)]
40+
#[CoversClass(AbstractMessage::class)]
41+
#[CoversClass(AbstractSamlpElement::class)]
42+
final class NameIDMappingResponseTest extends TestCase
43+
{
44+
use SchemaValidationTestTrait;
45+
use SerializableElementTestTrait;
46+
use SignedElementTestTrait;
47+
48+
49+
/**
50+
*/
51+
public static function setUpBeforeClass(): void
52+
{
53+
self::$testedClass = NameIDMappingResponse::class;
54+
55+
self::$xmlRepresentation = DOMDocumentFactory::fromFile(
56+
dirname(__FILE__, 4) . '/resources/xml/samlp_NameIDMappingResponse.xml',
57+
);
58+
}
59+
60+
61+
/**
62+
*/
63+
public function testMarshalling(): void
64+
{
65+
$status = new Status(
66+
new StatusCode(
67+
SAMLAnyURIValue::fromString(C::STATUS_SUCCESS),
68+
),
69+
);
70+
$issuer = new Issuer(
71+
SAMLStringValue::fromString('https://IdentityProvider.com'),
72+
);
73+
74+
$nameId = new NameID(
75+
SAMLStringValue::fromString('TheNameIDValue'),
76+
SAMLStringValue::fromString('urn:x-simplesamlphp:namequalifier'),
77+
SAMLStringValue::fromString('urn:x-simplesamlphp:spnamequalifier'),
78+
SAMLAnyURIValue::fromString('urn:the:format'),
79+
SAMLStringValue::fromString('TheSPProvidedID'),
80+
);
81+
82+
$nameIdMappingResponse = new NameIDMappingResponse(
83+
id: IDValue::fromString('abc123'),
84+
status: $status,
85+
issuer: $issuer,
86+
destination: SAMLAnyURIValue::fromString('https://example.org/metadata'),
87+
consent: SAMLAnyURIValue::fromString(C::CONSENT_EXPLICIT),
88+
inResponseTo: NCNameValue::fromString('PHPUnit'),
89+
issueInstant: SAMLDateTimeValue::fromString('2021-03-25T16:53:26Z'),
90+
identifier: $nameId,
91+
);
92+
93+
$this->assertEquals(
94+
self::$xmlRepresentation->saveXML(self::$xmlRepresentation->documentElement),
95+
strval($nameIdMappingResponse),
96+
);
97+
}
98+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<samlp:NameIDMappingResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="abc123" IssueInstant="2021-03-25T16:53:26Z" Destination="https://example.org/metadata" Consent="urn:oasis:names:tc:SAML:2.0:consent:current-explicit" InResponseTo="PHPUnit">
2+
<saml:Issuer>https://IdentityProvider.com</saml:Issuer>
3+
<samlp:Status>
4+
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
5+
</samlp:Status>
6+
<saml:NameID NameQualifier="urn:x-simplesamlphp:namequalifier" SPNameQualifier="urn:x-simplesamlphp:spnamequalifier" Format="urn:the:format" SPProvidedID="TheSPProvidedID">TheNameIDValue</saml:NameID>
7+
</samlp:NameIDMappingResponse>

0 commit comments

Comments
 (0)