Skip to content

Commit

Permalink
Implement saml:Evidence element (#320)
Browse files Browse the repository at this point in the history
* Implement saml:Evidence element

* Some fixes
  • Loading branch information
tvdijen committed Jan 31, 2023
1 parent 0225645 commit 7d0c9c2
Show file tree
Hide file tree
Showing 4 changed files with 346 additions and 0 deletions.
151 changes: 151 additions & 0 deletions src/SAML2/XML/saml/AbstractEvidenceType.php
@@ -0,0 +1,151 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\XML\saml;

use DOMElement;
use SimpleSAML\Assert\Assert;
use SimpleSAML\XML\Exception\InvalidDOMElementException;
use SimpleSAML\XML\Exception\SchemaViolationException;

/**
* Class representing a saml:AbstractEvidenceType element.
*
* @package simplesaml/saml2
*/
abstract class AbstractEvidenceType extends AbstractSamlElement
{
/**
* @param \SimpleSAML\SAML2\XML\saml\AssertionIDRef[] $assertionIDRef
* @param \SimpleSAML\SAML2\XML\saml\AssertionURIRef[] $assertionURIRef
* @param \SimpleSAML\SAML2\XML\saml\Assertion[] $assertion
* @param \SimpleSAML\SAML2\XML\saml\EncryptedAssertion[] $encryptedAssertion
*/
final public function __construct(
protected array $assertionIDRef = [],
protected array $assertionURIRef = [],
protected array $assertion = [],
protected array $encryptedAssertion = [],
) {
Assert::allIsInstanceOf($assertionIDRef, AssertionIDRef::class, SchemaViolationException::class);
Assert::allIsInstanceOf($assertionURIRef, AssertionURIRef::class, SchemaViolationException::class);
Assert::allIsInstanceOf($assertion, Assertion::class, SchemaViolationException::class);
Assert::allIsInstanceOf($encryptedAssertion, EncryptedAssertion::class, SchemaViolationException::class);
}


/**
* Test if an object, at the state it's in, would produce an empty XML-element
*
* @return bool
*/
public function isEmptyElement(): bool
{
return (
empty($this->assertionIDRef)
&& empty($this->assertionURIRef)
&& empty($this->assertion)
&& empty($this->encryptedAssertion)
);
}


/**
* @return \SimpleSAML\SAML2\XML\saml\AssertionIDRef[]
*/
public function getAssertionIDRef(): array
{
return $this->assertionIDRef;
}


/**
* @return \SimpleSAML\SAML2\XML\saml\AssertionURIRef[]
*/
public function getAssertionURIRef(): array
{
return $this->assertionURIRef;
}


/**
* @return \SimpleSAML\SAML2\XML\saml\Assertion[]
*/
public function getAssertion(): array
{
return $this->assertion;
}


/**
* @return \SimpleSAML\SAML2\XML\saml\EncryptedAssertion[]
*/
public function getEncryptedAssertion(): array
{
return $this->encryptedAssertion;
}


/**
* Convert XML into an AbstractEvidenceType
*
* @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
*/
public static function fromXML(DOMElement $xml): static
{
$qualifiedName = static::getClassName(static::class);
Assert::eq(
$xml->localName,
$qualifiedName,
'Unexpected name for endpoint: ' . $xml->localName . '. Expected: ' . $qualifiedName . '.',
InvalidDOMElementException::class,
);

$assertionIDRef = AssertionIDRef::getChildrenOfClass($xml);
$assertionURIRef = AssertionURIRef::getChildrenOfClass($xml);
$assertion = Assertion::getChildrenOfClass($xml);
$encryptedAssertion = EncryptedAssertion::getChildrenOfClass($xml);

return new static(
$assertionIDRef,
$assertionURIRef,
$assertion,
$encryptedAssertion,
);
}


/**
* Convert this AbstractEvidenceType to XML.
*
* @param \DOMElement $parent The element we are converting to XML.
* @return \DOMElement The XML element after adding the data corresponding to this Condition.
*/
public function toXML(DOMElement $parent = null): DOMElement
{
$e = $this->instantiateParentElement($parent);

foreach ($this->getAssertionIDRef() as $assertionIDRef) {
$assertionIDRef->toXML($e);
}

foreach ($this->getAssertionURIRef() as $assertionURIRef) {
$assertionURIRef->toXML($e);
}

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

foreach ($this->getEncryptedAssertion() as $encryptedAssertion) {
$encryptedAssertion->toXML($e);
}

return $e;
}
}
14 changes: 14 additions & 0 deletions src/SAML2/XML/saml/Evidence.php
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\XML\saml;

/**
* Class representing a saml:Evidence element.
*
* @package simplesaml/saml2
*/
final class Evidence extends AbstractEvidenceType
{
}
122 changes: 122 additions & 0 deletions tests/SAML2/XML/saml/EvidenceTest.php
@@ -0,0 +1,122 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML2\Test\SAML2\XML\saml;

use DOMDocument;
use PHPUnit\Framework\TestCase;
use SimpleSAML\SAML2\XML\saml\Assertion;
use SimpleSAML\SAML2\XML\saml\AssertionIDRef;
use SimpleSAML\SAML2\XML\saml\AssertionURIRef;
use SimpleSAML\SAML2\XML\saml\EncryptedAssertion;
use SimpleSAML\SAML2\XML\saml\Evidence;
use SimpleSAML\Test\XML\SchemaValidationTestTrait;
use SimpleSAML\Test\XML\SerializableElementTestTrait;
use SimpleSAML\XML\DOMDocumentFactory;

use function dirname;
use function strval;

/**
* Class \SimpleSAML\SAML2\XML\saml\EvidenceTest
*
* @covers \SimpleSAML\SAML2\XML\saml\Evidence
* @covers \SimpleSAML\SAML2\XML\saml\AbstractEvidenceType
* @covers \SimpleSAML\SAML2\XML\saml\AbstractSamlElement
*
* @package simplesamlphp/saml2
*/
final class EvidenceTest extends TestCase
{
use SchemaValidationTestTrait;
use SerializableElementTestTrait;

/** @var \DOMDocument $assertionIDRef */
private DOMDocument $assertionIDRef;

/** @var \DOMDocument $assertionURIRef */
private DOMDocument $assertionURIRef;

/** @var \DOMDocument $assertion */
private DOMDocument $assertion;

/** @var \DOMDocument $encryptedAssertion */
private DOMDocument $encryptedAssertion;


/**
*/
protected function setUp(): void
{
$this->schema = dirname(__FILE__, 5) . '/schemas/saml-schema-assertion-2.0.xsd';

$this->testedClass = Evidence::class;

$this->xmlRepresentation = DOMDocumentFactory::fromFile(
dirname(__FILE__, 4) . '/resources/xml/saml_Evidence.xml',
);

$this->assertionIDRef = DOMDocumentFactory::fromFile(
dirname(__FILE__, 4) . '/resources/xml/saml_AssertionIDRef.xml',
);

$this->assertionURIRef = DOMDocumentFactory::fromFile(
dirname(__FILE__, 4) . '/resources/xml/saml_AssertionURIRef.xml',
);

$this->assertion = DOMDocumentFactory::fromFile(
dirname(__FILE__, 4) . '/resources/xml/saml_Assertion.xml',
);

$this->encryptedAssertion = DOMDocumentFactory::fromFile(
dirname(__FILE__, 4) . '/resources/xml/saml_EncryptedAssertion.xml',
);
}


/**
*/
public function testMarshalling(): void
{
$evidence = new Evidence(
[AssertionIDRef::fromXML($this->assertionIDRef->documentElement)],
[AssertionURIRef::fromXML($this->assertionURIRef->documentElement)],
[Assertion::fromXML($this->assertion->documentElement)],
[EncryptedAssertion::fromXML($this->encryptedAssertion->documentElement)],
);

$this->assertFalse($evidence->isEmptyElement());

$this->assertEquals(
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
strval($evidence),
);
}


/**
*/
public function testMarshallingWithNoContent(): void
{
$evidence = new Evidence();
$this->assertEquals(
'<saml:Evidence xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"/>',
strval($evidence)
);
$this->assertTrue($evidence->isEmptyElement());
}


/**
*/
public function testUnmarshalling(): void
{
$evidence = Evidence::fromXML($this->xmlRepresentation->documentElement);

$this->assertEquals(
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
strval($evidence),
);
}
}
59 changes: 59 additions & 0 deletions tests/resources/xml/saml_Evidence.xml
@@ -0,0 +1,59 @@
<saml:Evidence xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
<saml:AssertionIDRef>_Test</saml:AssertionIDRef>
<saml:AssertionURIRef>urn:x-simplesamlphp:reference</saml:AssertionURIRef>
<saml:Assertion Version="2.0" ID="_93af655219464fb403b34436cfb0c5cb1d9a5502" IssueInstant="1970-01-01T01:33:31Z">
<saml:Issuer>Provider</saml:Issuer>
<saml:Subject>
<saml:NameID SPNameQualifier="https://sp.example.org/authentication/sp/metadata" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">SomeNameIDValue</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:NameID SPNameQualifier="https://sp.example.org/authentication/sp/metadata" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">SomeOtherNameIDValue</saml:NameID>
<saml:SubjectConfirmationData NotOnOrAfter="2011-08-31T08:51:05Z" Recipient="https://sp.example.org/authentication/sp/consume-assertion" InResponseTo="_13603a6565a69297e9809175b052d115965121c8" />
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="2011-08-31T08:51:05Z" NotOnOrAfter="2011-08-31T08:51:05Z">
<saml:AudienceRestriction>
<saml:Audience>https://simplesamlphp.org/sp/metadata</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="2011-08-31T08:51:05Z" SessionIndex="_93af655219464fb403b34436cfb0c5cb1d9a5502">
<saml:SubjectLocality Address="127.0.0.1"/>
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="urn:test:ServiceID">
<saml:AttributeValue>1</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="urn:test:EntityConcernedID">
<saml:AttributeValue>1</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="urn:test:EntityConcernedSubID">
<saml:AttributeValue>1</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
<saml:EncryptedAssertion>
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2009xmlenc11#aes256-gcm"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<xenc:EncryptedKey>
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
<xenc:CipherData>
<xenc:CipherValue>sNLWjwyj/R0oPwSgNnqowahiOwM0YU3YaH3jsH0t2YUDcHkcgouvW5x6YbNdgvGq0ImsNrkjI//0hrL4HvrOX33+DkhCo2FX5+a7UCdftfBfSjvt0houF8z3Zq/XOm6HxBUbWt5MULYpMKMZ9iAY6+raydxk2tFWgnAyHaBfzvU=</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedKey>
<ds:RetrievalMethod URI="#Encrypted_KEY_ID" Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
<ds:XPath xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">self::xenc:CipherValue[@Id="example1"]</ds:XPath>
</ds:Transform>
</ds:Transforms>
</ds:RetrievalMethod>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>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</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</saml:EncryptedAssertion>
</saml:Evidence>

0 comments on commit 7d0c9c2

Please sign in to comment.