Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
291 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace SimpleSAML\SAML2\XML\samlp; | ||
|
||
use DOMElement; | ||
use SimpleSAML\Assert\Assert; | ||
use SimpleSAML\SAML2\Exception\Protocol\RequestVersionTooHighException; | ||
use SimpleSAML\SAML2\Exception\Protocol\RequestVersionTooLowException; | ||
use SimpleSAML\SAML2\Exception\ProtocolViolationException; | ||
use SimpleSAML\SAML2\XML\saml\Issuer; | ||
use SimpleSAML\SAML2\XML\saml\Subject; | ||
use SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext; | ||
use SimpleSAML\XML\Exception\InvalidDOMElementException; | ||
use SimpleSAML\XML\Exception\MissingElementException; | ||
use SimpleSAML\XML\Exception\TooManyElementsException; | ||
use SimpleSAML\XML\Utils as XMLUtils; | ||
use SimpleSAML\XMLSecurity\XML\ds\Signature; | ||
|
||
use function array_pop; | ||
use function in_array; | ||
|
||
/** | ||
* Class for SAML 2 AuthnQuery query messages. | ||
* | ||
* @package simplesamlphp/saml2 | ||
*/ | ||
final class AuthnQuery extends AbstractSubjectQuery | ||
{ | ||
/** | ||
* Constructor for SAML 2 AuthnQuery. | ||
* | ||
* @param \SimpleSAML\SAML2\XML\saml\Subject $subject | ||
* @param \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext|null $requestedAuthnContext | ||
* @param string|null $sessionIndex | ||
* @param \SimpleSAML\SAML2\XML\saml\Issuer $issuer | ||
* @param string|null $id | ||
* @param string $version | ||
* @param int $issueInstant | ||
* @param string|null $destination | ||
* @param string|null $consent | ||
* @param \SimpleSAML\SAML2\XML\samlp\Extensions $extensions | ||
*/ | ||
public function __construct( | ||
Subject $subject, | ||
protected ?RequestedAuthnContext $requestedAuthnContext = null, | ||
protected ?string $sessionIndex = null, | ||
?Issuer $issuer = null, | ||
?string $id = null, | ||
string $version = '2.0', | ||
?int $issueInstant = null, | ||
?string $destination = null, | ||
?string $consent = null, | ||
?Extensions $extensions = null, | ||
) { | ||
parent::__construct($subject, $issuer, $id, $version, $issueInstant, $destination, $consent, $extensions); | ||
} | ||
|
||
|
||
/** | ||
* Retrieve RequestedAuthnContext. | ||
* | ||
* @return \SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext|null | ||
*/ | ||
public function getRequestedAuthnContext(): ?RequestedAuthnContext | ||
{ | ||
return $this->requestedAuthnContext; | ||
} | ||
|
||
|
||
/** | ||
* Retrieve session index. | ||
* | ||
* @return string|null | ||
*/ | ||
public function getSessionIndex(): ?string | ||
{ | ||
return $this->sessionIndex; | ||
} | ||
|
||
|
||
/** | ||
* Create a class from XML | ||
* | ||
* @param \DOMElement $xml | ||
* @return static | ||
* | ||
* @throws \SimpleSAML\XML\Exception\InvalidDOMElementException | ||
* if the qualified name of the supplied element is wrong | ||
* @throws \SimpleSAML\XML\Exception\MissingAttributeException | ||
* if the supplied element is missing one of the mandatory attributes | ||
* @throws \SimpleSAML\XML\Exception\MissingElementException | ||
* if one of the mandatory child-elements is missing | ||
* @throws \SimpleSAML\XML\Exception\TooManyElementsException | ||
* if too many child-elements of a type are specified | ||
*/ | ||
public static function fromXML(DOMElement $xml): static | ||
{ | ||
Assert::same($xml->localName, 'AuthnQuery', InvalidDOMElementException::class); | ||
Assert::same($xml->namespaceURI, AuthnQuery::NS, InvalidDOMElementException::class); | ||
|
||
/** @psalm-var string $version */ | ||
$version = self::getAttribute($xml, 'Version'); | ||
Assert::true(version_compare('2.0', $version, '<='), RequestVersionTooLowException::class); | ||
Assert::true(version_compare('2.0', $version, '>='), RequestVersionTooHighException::class); | ||
|
||
$id = self::getAttribute($xml, 'ID'); | ||
$destination = self::getAttribute($xml, 'Destination', null); | ||
$consent = self::getAttribute($xml, 'Consent', null); | ||
$sessionIndex = self::getAttribute($xml, 'SessionIndex', null); | ||
|
||
/** @psalm-var string $issueInstant */ | ||
$issueInstant = self::getAttribute($xml, 'IssueInstant'); | ||
// Strip sub-seconds - See paragraph 1.3.3 of SAML core specifications | ||
$issueInstant = preg_replace('/([.][0-9]+Z)$/', 'Z', $issueInstant, 1); | ||
|
||
Assert::validDateTimeZulu($issueInstant, ProtocolViolationException::class); | ||
$issueInstant = XMLUtils::xsDateTimeToTimestamp($issueInstant); | ||
|
||
$requestedAuthnContext = RequestedAuthnContext::getChildrenOfClass($xml); | ||
|
||
$issuer = Issuer::getChildrenOfClass($xml); | ||
Assert::countBetween($issuer, 0, 1); | ||
|
||
$extensions = Extensions::getChildrenOfClass($xml); | ||
Assert::maxCount( | ||
$extensions, | ||
1, | ||
'Only one saml:Extensions element is allowed.', | ||
TooManyElementsException::class, | ||
); | ||
|
||
$subject = Subject::getChildrenOfClass($xml); | ||
Assert::notEmpty($subject, 'Missing subject in subject query.', MissingElementException::class); | ||
Assert::maxCount( | ||
$subject, | ||
1, | ||
'More than one <saml:Subject> in AttributeQuery', | ||
TooManyElementsException::class, | ||
); | ||
|
||
$signature = Signature::getChildrenOfClass($xml); | ||
Assert::maxCount($signature, 1, 'Only one ds:Signature element is allowed.', TooManyElementsException::class); | ||
|
||
$request = new static( | ||
array_pop($subject), | ||
array_pop($requestedAuthnContext), | ||
$sessionIndex, | ||
array_pop($issuer), | ||
$id, | ||
$version, | ||
$issueInstant, | ||
$destination, | ||
$consent, | ||
array_pop($extensions), | ||
); | ||
|
||
if (!empty($signature)) { | ||
$request->setSignature($signature[0]); | ||
$request->setXML($xml); | ||
} | ||
|
||
return $request; | ||
} | ||
|
||
|
||
/** | ||
* Convert this message to an unsigned XML document. | ||
* This method does not sign the resulting XML document. | ||
* | ||
* @return \DOMElement The root element of the DOM tree | ||
*/ | ||
protected function toUnsignedXML(?DOMElement $parent = null): DOMElement | ||
{ | ||
$e = parent::toUnsignedXML($parent); | ||
|
||
$sessionIndex = $this->getSessionIndex(); | ||
if ($sessionIndex !== null) { | ||
$e->setAttribute('SessionIndex', $sessionIndex); | ||
} | ||
|
||
$this->getRequestedAuthnContext()?->toXML($e); | ||
|
||
return $e; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace SimpleSAML\Test\SAML2\XML\samlp; | ||
|
||
use DOMDocument; | ||
use PHPUnit\Framework\TestCase; | ||
use SimpleSAML\SAML2\Constants as C; | ||
use SimpleSAML\SAML2\Utils\XPath; | ||
use SimpleSAML\SAML2\XML\saml\AuthnContextDeclRef; | ||
use SimpleSAML\SAML2\XML\saml\Issuer; | ||
use SimpleSAML\SAML2\XML\saml\NameID; | ||
use SimpleSAML\SAML2\XML\saml\Subject; | ||
use SimpleSAML\SAML2\XML\samlp\AuthnQuery; | ||
use SimpleSAML\SAML2\XML\samlp\RequestedAuthnContext; | ||
use SimpleSAML\Test\XML\SchemaValidationTestTrait; | ||
use SimpleSAML\Test\XML\SerializableElementTestTrait; | ||
use SimpleSAML\XML\DOMDocumentFactory; | ||
use SimpleSAML\XML\Exception\MissingElementException; | ||
use SimpleSAML\XML\Exception\MissingAttributeException; | ||
use SimpleSAML\XML\Exception\TooManyElementsException; | ||
use SimpleSAML\XMLSecurity\TestUtils\SignedElementTestTrait; | ||
|
||
use function dirname; | ||
use function strval; | ||
|
||
/** | ||
* Class \SimpleSAML\SAML2\XML\samlp\AuthnQueryTest | ||
* | ||
* @covers \SimpleSAML\SAML2\XML\samlp\AuthnQuery | ||
* @covers \SimpleSAML\SAML2\XML\samlp\AbstractSubjectQuery | ||
* @covers \SimpleSAML\SAML2\XML\samlp\AbstractRequest | ||
* @covers \SimpleSAML\SAML2\XML\samlp\AbstractMessage | ||
* @covers \SimpleSAML\SAML2\XML\samlp\AbstractSamlpElement | ||
* @package simplesamlphp/saml2 | ||
*/ | ||
final class AuthnQueryTest extends TestCase | ||
{ | ||
use SchemaValidationTestTrait; | ||
use SerializableElementTestTrait; | ||
use SignedElementTestTrait; | ||
|
||
|
||
/** | ||
*/ | ||
public function setup(): void | ||
{ | ||
$this->schema = dirname(__FILE__, 5) . '/schemas/saml-schema-protocol-2.0.xsd'; | ||
|
||
$this->testedClass = AuthnQuery::class; | ||
|
||
$this->xmlRepresentation = DOMDocumentFactory::fromFile( | ||
dirname(__FILE__, 4) . '/resources/xml/samlp_AuthnQuery.xml', | ||
); | ||
} | ||
|
||
|
||
/** | ||
*/ | ||
public function testMarshalling(): void | ||
{ | ||
$nameId = new NameID('urn:example:subject', null, null, C::NAMEID_UNSPECIFIED); | ||
$authnContextDeclRef = new AuthnContextDeclRef('https://example.org/relative/path/to/document.xml'); | ||
$requestedAuthnContext = new RequestedAuthnContext([$authnContextDeclRef], 'exact'); | ||
|
||
$authnQuery = new AuthnQuery( | ||
subject: new Subject($nameId), | ||
requestedAuthnContext: $requestedAuthnContext, | ||
issuer: new Issuer( | ||
value: 'https://example.org/', | ||
Format: C::NAMEID_ENTITY, | ||
), | ||
id: 'aaf23196-1773-2113-474a-fe114412ab72', | ||
issueInstant: 1504698567, | ||
sessionIndex: 'phpunit', | ||
); | ||
|
||
$this->assertEquals( | ||
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement), | ||
strval($authnQuery), | ||
); | ||
} | ||
|
||
|
||
public function testUnmarshalling(): void | ||
{ | ||
$authnQuery = AuthnQuery::fromXML($this->xmlRepresentation->documentElement); | ||
|
||
$this->assertEquals( | ||
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement), | ||
strval($authnQuery), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<samlp:AuthnQuery xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="aaf23196-1773-2113-474a-fe114412ab72" IssueInstant="2017-09-06T11:49:27Z" SessionIndex="phpunit"> | ||
<saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://example.org/</saml:Issuer> | ||
<saml:Subject> | ||
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">urn:example:subject</saml:NameID> | ||
</saml:Subject> | ||
<samlp:RequestedAuthnContext xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Comparison="exact"> | ||
<saml:AuthnContextDeclRef xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://example.org/relative/path/to/document.xml</saml:AuthnContextDeclRef> | ||
</samlp:RequestedAuthnContext> | ||
</samlp:AuthnQuery> |