Skip to content

Commit 7d0c9c2

Browse files
authored
Implement saml:Evidence element (#320)
* Implement saml:Evidence element * Some fixes
1 parent 0225645 commit 7d0c9c2

4 files changed

Lines changed: 346 additions & 0 deletions

File tree

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\SAML2\XML\saml;
6+
7+
use DOMElement;
8+
use SimpleSAML\Assert\Assert;
9+
use SimpleSAML\XML\Exception\InvalidDOMElementException;
10+
use SimpleSAML\XML\Exception\SchemaViolationException;
11+
12+
/**
13+
* Class representing a saml:AbstractEvidenceType element.
14+
*
15+
* @package simplesaml/saml2
16+
*/
17+
abstract class AbstractEvidenceType extends AbstractSamlElement
18+
{
19+
/**
20+
* @param \SimpleSAML\SAML2\XML\saml\AssertionIDRef[] $assertionIDRef
21+
* @param \SimpleSAML\SAML2\XML\saml\AssertionURIRef[] $assertionURIRef
22+
* @param \SimpleSAML\SAML2\XML\saml\Assertion[] $assertion
23+
* @param \SimpleSAML\SAML2\XML\saml\EncryptedAssertion[] $encryptedAssertion
24+
*/
25+
final public function __construct(
26+
protected array $assertionIDRef = [],
27+
protected array $assertionURIRef = [],
28+
protected array $assertion = [],
29+
protected array $encryptedAssertion = [],
30+
) {
31+
Assert::allIsInstanceOf($assertionIDRef, AssertionIDRef::class, SchemaViolationException::class);
32+
Assert::allIsInstanceOf($assertionURIRef, AssertionURIRef::class, SchemaViolationException::class);
33+
Assert::allIsInstanceOf($assertion, Assertion::class, SchemaViolationException::class);
34+
Assert::allIsInstanceOf($encryptedAssertion, EncryptedAssertion::class, SchemaViolationException::class);
35+
}
36+
37+
38+
/**
39+
* Test if an object, at the state it's in, would produce an empty XML-element
40+
*
41+
* @return bool
42+
*/
43+
public function isEmptyElement(): bool
44+
{
45+
return (
46+
empty($this->assertionIDRef)
47+
&& empty($this->assertionURIRef)
48+
&& empty($this->assertion)
49+
&& empty($this->encryptedAssertion)
50+
);
51+
}
52+
53+
54+
/**
55+
* @return \SimpleSAML\SAML2\XML\saml\AssertionIDRef[]
56+
*/
57+
public function getAssertionIDRef(): array
58+
{
59+
return $this->assertionIDRef;
60+
}
61+
62+
63+
/**
64+
* @return \SimpleSAML\SAML2\XML\saml\AssertionURIRef[]
65+
*/
66+
public function getAssertionURIRef(): array
67+
{
68+
return $this->assertionURIRef;
69+
}
70+
71+
72+
/**
73+
* @return \SimpleSAML\SAML2\XML\saml\Assertion[]
74+
*/
75+
public function getAssertion(): array
76+
{
77+
return $this->assertion;
78+
}
79+
80+
81+
/**
82+
* @return \SimpleSAML\SAML2\XML\saml\EncryptedAssertion[]
83+
*/
84+
public function getEncryptedAssertion(): array
85+
{
86+
return $this->encryptedAssertion;
87+
}
88+
89+
90+
/**
91+
* Convert XML into an AbstractEvidenceType
92+
*
93+
* @param \DOMElement $xml The XML element we should load
94+
* @return static
95+
*
96+
* @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
97+
* If the qualified name of the supplied element is wrong
98+
*/
99+
public static function fromXML(DOMElement $xml): static
100+
{
101+
$qualifiedName = static::getClassName(static::class);
102+
Assert::eq(
103+
$xml->localName,
104+
$qualifiedName,
105+
'Unexpected name for endpoint: ' . $xml->localName . '. Expected: ' . $qualifiedName . '.',
106+
InvalidDOMElementException::class,
107+
);
108+
109+
$assertionIDRef = AssertionIDRef::getChildrenOfClass($xml);
110+
$assertionURIRef = AssertionURIRef::getChildrenOfClass($xml);
111+
$assertion = Assertion::getChildrenOfClass($xml);
112+
$encryptedAssertion = EncryptedAssertion::getChildrenOfClass($xml);
113+
114+
return new static(
115+
$assertionIDRef,
116+
$assertionURIRef,
117+
$assertion,
118+
$encryptedAssertion,
119+
);
120+
}
121+
122+
123+
/**
124+
* Convert this AbstractEvidenceType to XML.
125+
*
126+
* @param \DOMElement $parent The element we are converting to XML.
127+
* @return \DOMElement The XML element after adding the data corresponding to this Condition.
128+
*/
129+
public function toXML(DOMElement $parent = null): DOMElement
130+
{
131+
$e = $this->instantiateParentElement($parent);
132+
133+
foreach ($this->getAssertionIDRef() as $assertionIDRef) {
134+
$assertionIDRef->toXML($e);
135+
}
136+
137+
foreach ($this->getAssertionURIRef() as $assertionURIRef) {
138+
$assertionURIRef->toXML($e);
139+
}
140+
141+
foreach ($this->getAssertion() as $assertion) {
142+
$assertion->toXML($e);
143+
}
144+
145+
foreach ($this->getEncryptedAssertion() as $encryptedAssertion) {
146+
$encryptedAssertion->toXML($e);
147+
}
148+
149+
return $e;
150+
}
151+
}

src/SAML2/XML/saml/Evidence.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\SAML2\XML\saml;
6+
7+
/**
8+
* Class representing a saml:Evidence element.
9+
*
10+
* @package simplesaml/saml2
11+
*/
12+
final class Evidence extends AbstractEvidenceType
13+
{
14+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\SAML2\Test\SAML2\XML\saml;
6+
7+
use DOMDocument;
8+
use PHPUnit\Framework\TestCase;
9+
use SimpleSAML\SAML2\XML\saml\Assertion;
10+
use SimpleSAML\SAML2\XML\saml\AssertionIDRef;
11+
use SimpleSAML\SAML2\XML\saml\AssertionURIRef;
12+
use SimpleSAML\SAML2\XML\saml\EncryptedAssertion;
13+
use SimpleSAML\SAML2\XML\saml\Evidence;
14+
use SimpleSAML\Test\XML\SchemaValidationTestTrait;
15+
use SimpleSAML\Test\XML\SerializableElementTestTrait;
16+
use SimpleSAML\XML\DOMDocumentFactory;
17+
18+
use function dirname;
19+
use function strval;
20+
21+
/**
22+
* Class \SimpleSAML\SAML2\XML\saml\EvidenceTest
23+
*
24+
* @covers \SimpleSAML\SAML2\XML\saml\Evidence
25+
* @covers \SimpleSAML\SAML2\XML\saml\AbstractEvidenceType
26+
* @covers \SimpleSAML\SAML2\XML\saml\AbstractSamlElement
27+
*
28+
* @package simplesamlphp/saml2
29+
*/
30+
final class EvidenceTest extends TestCase
31+
{
32+
use SchemaValidationTestTrait;
33+
use SerializableElementTestTrait;
34+
35+
/** @var \DOMDocument $assertionIDRef */
36+
private DOMDocument $assertionIDRef;
37+
38+
/** @var \DOMDocument $assertionURIRef */
39+
private DOMDocument $assertionURIRef;
40+
41+
/** @var \DOMDocument $assertion */
42+
private DOMDocument $assertion;
43+
44+
/** @var \DOMDocument $encryptedAssertion */
45+
private DOMDocument $encryptedAssertion;
46+
47+
48+
/**
49+
*/
50+
protected function setUp(): void
51+
{
52+
$this->schema = dirname(__FILE__, 5) . '/schemas/saml-schema-assertion-2.0.xsd';
53+
54+
$this->testedClass = Evidence::class;
55+
56+
$this->xmlRepresentation = DOMDocumentFactory::fromFile(
57+
dirname(__FILE__, 4) . '/resources/xml/saml_Evidence.xml',
58+
);
59+
60+
$this->assertionIDRef = DOMDocumentFactory::fromFile(
61+
dirname(__FILE__, 4) . '/resources/xml/saml_AssertionIDRef.xml',
62+
);
63+
64+
$this->assertionURIRef = DOMDocumentFactory::fromFile(
65+
dirname(__FILE__, 4) . '/resources/xml/saml_AssertionURIRef.xml',
66+
);
67+
68+
$this->assertion = DOMDocumentFactory::fromFile(
69+
dirname(__FILE__, 4) . '/resources/xml/saml_Assertion.xml',
70+
);
71+
72+
$this->encryptedAssertion = DOMDocumentFactory::fromFile(
73+
dirname(__FILE__, 4) . '/resources/xml/saml_EncryptedAssertion.xml',
74+
);
75+
}
76+
77+
78+
/**
79+
*/
80+
public function testMarshalling(): void
81+
{
82+
$evidence = new Evidence(
83+
[AssertionIDRef::fromXML($this->assertionIDRef->documentElement)],
84+
[AssertionURIRef::fromXML($this->assertionURIRef->documentElement)],
85+
[Assertion::fromXML($this->assertion->documentElement)],
86+
[EncryptedAssertion::fromXML($this->encryptedAssertion->documentElement)],
87+
);
88+
89+
$this->assertFalse($evidence->isEmptyElement());
90+
91+
$this->assertEquals(
92+
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
93+
strval($evidence),
94+
);
95+
}
96+
97+
98+
/**
99+
*/
100+
public function testMarshallingWithNoContent(): void
101+
{
102+
$evidence = new Evidence();
103+
$this->assertEquals(
104+
'<saml:Evidence xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"/>',
105+
strval($evidence)
106+
);
107+
$this->assertTrue($evidence->isEmptyElement());
108+
}
109+
110+
111+
/**
112+
*/
113+
public function testUnmarshalling(): void
114+
{
115+
$evidence = Evidence::fromXML($this->xmlRepresentation->documentElement);
116+
117+
$this->assertEquals(
118+
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
119+
strval($evidence),
120+
);
121+
}
122+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<saml:Evidence xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">
2+
<saml:AssertionIDRef>_Test</saml:AssertionIDRef>
3+
<saml:AssertionURIRef>urn:x-simplesamlphp:reference</saml:AssertionURIRef>
4+
<saml:Assertion Version="2.0" ID="_93af655219464fb403b34436cfb0c5cb1d9a5502" IssueInstant="1970-01-01T01:33:31Z">
5+
<saml:Issuer>Provider</saml:Issuer>
6+
<saml:Subject>
7+
<saml:NameID SPNameQualifier="https://sp.example.org/authentication/sp/metadata" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">SomeNameIDValue</saml:NameID>
8+
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
9+
<saml:NameID SPNameQualifier="https://sp.example.org/authentication/sp/metadata" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">SomeOtherNameIDValue</saml:NameID>
10+
<saml:SubjectConfirmationData NotOnOrAfter="2011-08-31T08:51:05Z" Recipient="https://sp.example.org/authentication/sp/consume-assertion" InResponseTo="_13603a6565a69297e9809175b052d115965121c8" />
11+
</saml:SubjectConfirmation>
12+
</saml:Subject>
13+
<saml:Conditions NotBefore="2011-08-31T08:51:05Z" NotOnOrAfter="2011-08-31T08:51:05Z">
14+
<saml:AudienceRestriction>
15+
<saml:Audience>https://simplesamlphp.org/sp/metadata</saml:Audience>
16+
</saml:AudienceRestriction>
17+
</saml:Conditions>
18+
<saml:AuthnStatement AuthnInstant="2011-08-31T08:51:05Z" SessionIndex="_93af655219464fb403b34436cfb0c5cb1d9a5502">
19+
<saml:SubjectLocality Address="127.0.0.1"/>
20+
<saml:AuthnContext>
21+
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
22+
</saml:AuthnContext>
23+
</saml:AuthnStatement>
24+
<saml:AttributeStatement>
25+
<saml:Attribute Name="urn:test:ServiceID">
26+
<saml:AttributeValue>1</saml:AttributeValue>
27+
</saml:Attribute>
28+
<saml:Attribute Name="urn:test:EntityConcernedID">
29+
<saml:AttributeValue>1</saml:AttributeValue>
30+
</saml:Attribute>
31+
<saml:Attribute Name="urn:test:EntityConcernedSubID">
32+
<saml:AttributeValue>1</saml:AttributeValue>
33+
</saml:Attribute>
34+
</saml:AttributeStatement>
35+
</saml:Assertion>
36+
<saml:EncryptedAssertion>
37+
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Type="http://www.w3.org/2001/04/xmlenc#Element">
38+
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2009xmlenc11#aes256-gcm"/>
39+
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
40+
<xenc:EncryptedKey>
41+
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
42+
<xenc:CipherData>
43+
<xenc:CipherValue>sNLWjwyj/R0oPwSgNnqowahiOwM0YU3YaH3jsH0t2YUDcHkcgouvW5x6YbNdgvGq0ImsNrkjI//0hrL4HvrOX33+DkhCo2FX5+a7UCdftfBfSjvt0houF8z3Zq/XOm6HxBUbWt5MULYpMKMZ9iAY6+raydxk2tFWgnAyHaBfzvU=</xenc:CipherValue>
44+
</xenc:CipherData>
45+
</xenc:EncryptedKey>
46+
<ds:RetrievalMethod URI="#Encrypted_KEY_ID" Type="http://www.w3.org/2001/04/xmlenc#EncryptedKey">
47+
<ds:Transforms>
48+
<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
49+
<ds:XPath xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">self::xenc:CipherValue[@Id="example1"]</ds:XPath>
50+
</ds:Transform>
51+
</ds:Transforms>
52+
</ds:RetrievalMethod>
53+
</ds:KeyInfo>
54+
<xenc:CipherData>
55+
<xenc:CipherValue>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</xenc:CipherValue>
56+
</xenc:CipherData>
57+
</xenc:EncryptedData>
58+
</saml:EncryptedAssertion>
59+
</saml:Evidence>

0 commit comments

Comments
 (0)