Skip to content

Commit

Permalink
Add structure for samlp responses
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdijen committed Dec 31, 2024
1 parent ba5c955 commit 2abde7c
Show file tree
Hide file tree
Showing 5 changed files with 422 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/SAML11/XML/samlp/AbstractResponseAbstractType.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function getID(): string
protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
{
$e = parent::toUnsignedXML($parent);
$e->setAttribute($e, 'ResponseID', $this->getId());
$e->setAttribute('ResponseID', $this->getId());

return $e;
}
Expand Down
123 changes: 123 additions & 0 deletions src/SAML11/XML/samlp/AbstractResponseType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\XML\samlp;

use DateTimeImmutable;
use DOMElement;
use SimpleSAML\Assert\Assert;
use SimpleSAML\SAML11\XML\saml\Assertion;
use SimpleSAML\SAML11\XML\samlp\Status;
use SimpleSAML\XML\Exception\SchemaViolationException;

/**
* Base class for all SAML 1.1 samlp:AbstractResponseType.
*
* @package simplesamlphp/saml11
*/
abstract class AbstractResponsetype extends AbstractResponseAbstractType
{
/**
* Initialize a response.
*
* @param string $id
* @param \SimpleSAML\SAML11\XML\samlp\Status $status
* @param array<\SimpleSAML\SAML11\XML\samlp\Assertion> $assertion
* @param int $majorVersion
* @param int $minorVersion
* @param \DateTimeImmutable|null $issueInstant
* @param string|null $inResponseTo
* @param string|null $recipient
*
* @throws \Exception
*/
public function __construct(
string $id,
protected Status $status,
protected array $assertion = [],
int $majorVersion = 1,
int $minorVersion = 1,
?DateTimeImmutable $issueInstant = null,
protected ?string $inResponseTo = null,
protected ?string $recipient = null,
) {
Assert::nullOrValidNCName($inResponseTo, SchemaViolationException::class);
Assert::nullOrValidURI($recipient, SchemaViolationException::class);
Assert::allIsInstanceOf($assertion, Assertion::class, SchemaViolationException::class);

parent::__construct($id, $majorVersion, $minorVersion, $issueInstant);
}


/**
* Retrieve the inResponseTo of this message.
*
* @return string|null The inResponseTo of this message
*/
public function getInResponseTo(): ?string
{
return $this->inResponseTo;
}


/**
* Retrieve the recipient of this message.
*
* @return string|null The recipient of this message
*/
public function getRecipient(): ?string
{
return $this->recipient;
}


/**
* Retrieve the assertion of this message.
*
* @return array<\SimpleSAML\SAML11\XML\saml\Assertion> The assertion of this message
*/
public function getAssertion(): array
{
return $this->assertion;
}


/**
* Retrieve the status of this message.
*
* @return \SimpleSAML\SAML11\XML\samlp\Status The status of this message
*/
public function getStatus(): Status
{
return $this->status;
}


/**
* 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);

if ($this->getRecipient() !== null) {
$e->setAttribute('Recipient', $this->getRecipient());
}

if ($this->getInResponseTo() !== null) {
$e->setAttribute('InResponseTo', $this->getResponseTo());
}

$this->getStatus()->toXML($e);

foreach ($this->getAssertion() as $assertion) {
$assertion->toXML($e);
}

return $e;
}
}
74 changes: 74 additions & 0 deletions src/SAML11/XML/samlp/Response.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\XML\samlp;

use DateTimeImmutable;
use DOMElement;
use SimpleSAML\Assert\Assert;
use SimpleSAML\SAML11\Assert\Assert as SAMLAssert;
use SimpleSAML\SAML11\Exception\ProtocolViolationException;
use SimpleSAML\SAML11\Exception\VersionMismatchException;
use SimpleSAML\SAML11\XML\saml\Assertion;
use SimpleSAML\SAML11\XML\samlp\Status;
use SimpleSAML\XML\Exception\InvalidDOMElementException;
use SimpleSAML\XML\Exception\MissingElementException;
use SimpleSAML\XML\Exception\TooManyElementsException;

use function array_pop;

/**
* Class representing a samlp:Response element.
*
* @package simplesaml/xml-saml11
*/
final class Response extends AbstractResponseType
{
/**
* Convert XML into Response
*
* @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, 'Response', InvalidDOMElementException::class);
Assert::same($xml->namespaceURI, Response::NS, InvalidDOMElementException::class);

$majorVersion = self::getIntegerAttribute($xml, 'MajorVersion');
Assert::same($majorVersion, 1, VersionMismatchException::class);

$minorVersion = self::getIntegerAttribute($xml, 'MinorVersion');
Assert::same($minorVersion, 1, VersionMismatchException::class);

$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);

SAMLAssert::validDateTime($issueInstant, ProtocolViolationException::class);
$issueInstant = new DateTimeImmutable($issueInstant);

$status = Status::getChildrenOfClass($xml);
Assert::minCount($status, 1, MissingElementException::class);
Assert::maxCount($status, 1, TooManyElementsException::class);

return new static(
self::getAttribute($xml, 'ResponseID'),
array_pop($status),
Assertion::getChildrenOfClass($xml),
$majorVersion,
$minorVersion,
$issueInstant,
$inResponseTo = self::getOptionalAttribute($xml, 'InResponseTo', null),
$recipient = self::getOptionalAttribute($xml, 'Recipient', null),
);
}
}
36 changes: 36 additions & 0 deletions tests/resources/xml/samlp_Response.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" MajorVersion="1" MinorVersion="1" IssueInstant="2023-01-24T09:42:26Z" ResponseID="def456">
<samlp:Status>
<samlp:StatusCode Value="samlp:Responder">
<samlp:StatusCode Value="samlp:RequestDenied"/>
</samlp:StatusCode>
<samlp:StatusMessage>Something went wrong</samlp:StatusMessage>
</samlp:Status>
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" MajorVersion="1" MinorVersion="1" AssertionID="_abc123" Issuer="urn:x-simplesamlphp:phpunit" IssueInstant="2023-01-24T09:42:26Z">
<saml:Conditions NotBefore="2023-01-24T09:42:26Z" NotOnOrAfter="2023-01-24T09:47:26Z">
<saml:AudienceRestrictionCondition>
<saml:Audience>urn:x-simplesamlphp:audience</saml:Audience>
</saml:AudienceRestrictionCondition>
<saml:DoNotCacheCondition />
</saml:Conditions>
<saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password" AuthenticationInstant="2023-01-24T09:42:26Z">
<saml:Subject>
<saml:NameIdentifier NameQualifier="TheNameQualifier" Format="urn:the:format">TheNameIDValue</saml:NameIdentifier>
<saml:SubjectConfirmation>
<saml:ConfirmationMethod>_Test1</saml:ConfirmationMethod>
<saml:ConfirmationMethod>_Test2</saml:ConfirmationMethod>
<saml:SubjectConfirmationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:integer">2</saml:SubjectConfirmationData>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="fed654">
<ds:KeyName>testkey</ds:KeyName>
<ds:X509Data>
<ds:X509Certificate>MIICxDCCAi2gAwIBAgIUZ9QDx+SBFHednUWDFGm9tyVKrgQwDQYJKoZIhvcNAQELBQAwczElMCMGA1UEAwwcc2VsZnNpZ25lZC5zaW1wbGVzYW1scGhwLm9yZzEZMBcGA1UECgwQU2ltcGxlU0FNTHBocCBIUTERMA8GA1UEBwwISG9ub2x1bHUxDzANBgNVBAgMBkhhd2FpaTELMAkGA1UEBhMCVVMwIBcNMjIxMjAzMTAzNTQwWhgPMjEyMjExMDkxMDM1NDBaMHMxJTAjBgNVBAMMHHNlbGZzaWduZWQuc2ltcGxlc2FtbHBocC5vcmcxGTAXBgNVBAoMEFNpbXBsZVNBTUxwaHAgSFExETAPBgNVBAcMCEhvbm9sdWx1MQ8wDQYDVQQIDAZIYXdhaWkxCzAJBgNVBAYTAlVTMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDessdFRVDTMQQW3Na81B1CjJV1tmY3nopoIhZrkbDxLa+pv7jGDRcYreyu1DoQxEs06V2nHLoyOPhqJXSFivqtUwVYhR6NYgbNI6RRSsIJCweH0YOdlHna7gULPcLX0Bfbi4odStaFwG9yzDySwSEPtsKxm5pENPjNVGh+jJ+H/QIDAQABo1MwUTAdBgNVHQ4EFgQUvV75t8EoQo2fVa0E9otdtIGK5X0wHwYDVR0jBBgwFoAUvV75t8EoQo2fVa0E9otdtIGK5X0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQANQUeiwPJXkWMXuaDHToEBKcezYGqGEYnGUi9LMjeb+Kln7X8nn5iknlz4k77rWCbSwLPC/WDr0ySYQA+HagaeUaFpoiYFJKS6uFlK1HYWnM3W4PUiGHg1/xeZlMO44wTwybXVo0y9KMhchfB5XNbDdoJcqWYvi6xtmZZNRbxUyw==</ds:X509Certificate>
<ds:X509SubjectName>/CN=selfsigned.simplesamlphp.org/O=SimpleSAMLphp HQ/L=Honolulu/ST=Hawaii/C=US</ds:X509SubjectName>
</ds:X509Data>
<ssp:Chunk xmlns:ssp="urn:x-simplesamlphp:namespace">some</ssp:Chunk>
</ds:KeyInfo>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:SubjectLocality IPAddress="127.0.0.1" DNSAddress="simplesamlphp.org" />
<saml:AuthorityBinding xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" AuthorityKind="samlp:AttributeQuery" Location="urn:x-simplesamlphp:location" Binding="urn:x-simplesamlphp:binding" />
</saml:AuthenticationStatement>
</saml:Assertion>
</samlp:Response>
Loading

0 comments on commit 2abde7c

Please sign in to comment.