Skip to content

Commit ed87a96

Browse files
authored
Add XML-representation for ds:Transforms element (#12)
* Add XML-representation for ds:Transforms element * Reduce marshall-tests to minimum * Fix ReferenceList/DataReference/KeyReference * Simplify ReferenceListTest
1 parent 1f1f7b1 commit ed87a96

14 files changed

Lines changed: 294 additions & 124 deletions

src/XML/ds/Transforms.php

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\XMLSecurity\XML\ds;
6+
7+
use DOMElement;
8+
use SimpleSAML\Assert\Assert;
9+
use SimpleSAML\XML\Exception\InvalidDOMElementException;
10+
11+
/**
12+
* Class representing a ds:Transforms element.
13+
*
14+
* @package simplesamlphp/xml-security
15+
*/
16+
final class Transforms extends AbstractDsElement
17+
{
18+
/** @var \SimpleSAML\XMLSecurity\Transform[] */
19+
protected array $transform;
20+
21+
22+
/**
23+
* Initialize a ds:Transforms
24+
*
25+
* @param \SimpleSAML\XMLSecurity\Transform[] $transform
26+
*/
27+
public function __construct(array $transform)
28+
{
29+
$this->setTransform($transform);
30+
}
31+
32+
33+
/**
34+
* @return \SimpleSAML\XMLSecurity\Transform[]
35+
*/
36+
public function getTransform(): array
37+
{
38+
return $this->transform;
39+
}
40+
41+
42+
/**
43+
* @param \SimpleSAML\XMLSecurity\Transform[] $transform
44+
*/
45+
protected function setTransform(array $transform): void
46+
{
47+
Assert::allIsInstanceOf($transform, Transform::class);
48+
$this->transform = $transform;
49+
}
50+
51+
52+
/**
53+
* Test if an object, at the state it's in, would produce an empty XML-element
54+
*
55+
* @return bool
56+
*/
57+
public function isEmptyElement(): bool
58+
{
59+
return empty($this->transform);
60+
}
61+
62+
63+
/**
64+
* Convert XML into a Transforms element
65+
*
66+
* @param \DOMElement $xml The XML element we should load
67+
* @return self
68+
*
69+
* @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
70+
* If the qualified name of the supplied element is wrong
71+
*/
72+
public static function fromXML(DOMElement $xml): object
73+
{
74+
Assert::same($xml->localName, 'Transforms', InvalidDOMElementException::class);
75+
Assert::same($xml->namespaceURI, Transforms::NS, InvalidDOMElementException::class);
76+
77+
$transform = Transform::getChildrenOfClass($xml);
78+
79+
return new self($transform);
80+
}
81+
82+
83+
/**
84+
* Convert this Transforms element to XML.
85+
*
86+
* @param \DOMElement|null $parent The element we should append this Transforms element to.
87+
* @return \DOMElement
88+
*/
89+
public function toXML(DOMElement $parent = null): DOMElement
90+
{
91+
$e = $this->instantiateParentElement($parent);
92+
93+
foreach ($this->transform as $t) {
94+
if (!$t->isEmptyElement()) {
95+
$t->toXML($e);
96+
}
97+
}
98+
99+
return $e;
100+
}
101+
}

src/XML/xenc/AbstractReference.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public static function fromXML(DOMElement $xml): object
104104
if (!($element instanceof DOMElement)) {
105105
continue;
106106
} elseif ($element->namespaceURI === Transforms::NS && $element->localName === 'Transforms') {
107-
$elements[] = new Transforms($element);
107+
$elements[] = Transforms::fromXML($element);
108108
} else {
109109
$elements[] = new Chunk($element);
110110
}

tests/XML/ds/TransformTest.php

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ public function testMarshalling(): void
4545
$transform = new Transform(
4646
'http://www.w3.org/TR/1999/REC-xpath-19991116',
4747
[
48-
new Chunk(DOMDocumentFactory::fromString('<some:Chunk>Random</some:Chunk>')->documentElement),
4948
new Chunk(DOMDocumentFactory::fromString('<ds:XPath>count(//. | //@* | //namespace::*)</ds:XPath>')->documentElement)
5049
],
5150
);
@@ -55,12 +54,10 @@ public function testMarshalling(): void
5554
$transformElement = $transform->toXML($document->firstChild);
5655

5756
$this->assertEquals('http://www.w3.org/TR/1999/REC-xpath-19991116', $transformElement->getAttribute('Algorithm'));
58-
$this->assertCount(2, $transformElement->childNodes);
57+
$this->assertCount(1, $transformElement->childNodes);
5958

60-
$this->assertEquals('some:Chunk', $transformElement->childNodes[0]->localName);
61-
$this->assertEquals('Random', $transformElement->childNodes[0]->textContent);
62-
$this->assertEquals('ds:XPath', $transformElement->childNodes[1]->localName);
63-
$this->assertEquals('count(//. | //@* | //namespace::*)', $transformElement->childNodes[1]->textContent);
59+
$this->assertEquals('ds:XPath', $transformElement->childNodes[0]->localName);
60+
$this->assertEquals('count(//. | //@* | //namespace::*)', $transformElement->childNodes[0]->textContent);
6461

6562
$this->assertEquals(
6663
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
@@ -77,10 +74,9 @@ public function testUnmarshalling(): void
7774
$this->assertEquals('http://www.w3.org/TR/1999/REC-xpath-19991116', $transform->getAlgorithm());
7875

7976
$elements = $transform->getElements();
80-
$this->assertCount(2, $elements);
77+
$this->assertCount(1, $elements);
8178

8279
$this->assertInstanceOf(Chunk::class, $elements[0]);
83-
$this->assertInstanceOf(Chunk::class, $elements[1]);
8480

8581
$this->assertEquals(
8682
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
@@ -90,7 +86,7 @@ public function testUnmarshalling(): void
9086

9187

9288
/**
93-
* Adding an empty StatusDetail element should yield an empty element.
89+
* Adding an empty Transform element should yield an empty element.
9490
*/
9591
public function testMarshallingEmptyElement(): void
9692
{

tests/XML/ds/TransformsTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\Test\SAML2\XML\ds;
6+
7+
use DOMDocument;
8+
use PHPUnit\Framework\TestCase;
9+
use SimpleSAML\SAML2\Constants;
10+
use SimpleSAML\Test\XML\SerializableXMLTestTrait;
11+
use SimpleSAML\XML\DOMDocumentFactory;
12+
use SimpleSAML\XML\Chunk;
13+
use SimpleSAML\XML\Utils as XMLUtils;
14+
use SimpleSAML\XMLSecurity\XML\ds\Transform;
15+
use SimpleSAML\XMLSecurity\XML\ds\Transforms;
16+
17+
/**
18+
* Class \SimpleSAML\XMLSecurity\Test\XML\ds\TransformsTest
19+
*
20+
* @covers \SimpleSAML\XMLSecurity\XML\ds\Transforms
21+
* @covers \SimpleSAML\XMLSecurity\XML\ds\AbstractDsElement
22+
*
23+
* @package simplesamlphp/xml-security
24+
*/
25+
final class TransformsTest extends TestCase
26+
{
27+
use SerializableXMLTestTrait;
28+
29+
30+
/**
31+
*/
32+
public function setUp(): void
33+
{
34+
$this->testedClass = Transforms::class;
35+
36+
$this->xmlRepresentation = DOMDocumentFactory::fromFile(
37+
dirname(dirname(dirname(__FILE__))) . '/resources/xml/ds_Transforms.xml'
38+
);
39+
}
40+
41+
42+
/**
43+
*/
44+
public function testMarshalling(): void
45+
{
46+
$transforms = new Transforms(
47+
[
48+
new Transform(
49+
'http://www.w3.org/TR/1999/REC-xpath-19991116',
50+
[
51+
new Chunk(
52+
DOMDocumentFactory::fromString(
53+
'<ds:XPath xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">self::xenc:CipherValue[@Id="example1"]</ds:XPath>'
54+
)->documentElement
55+
)
56+
]
57+
)
58+
]
59+
);
60+
61+
$this->assertEquals(
62+
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
63+
strval($transforms)
64+
);
65+
}
66+
67+
68+
/**
69+
*/
70+
public function testUnmarshalling(): void
71+
{
72+
$transforms = Transforms::fromXML($this->xmlRepresentation->documentElement);
73+
$transform = $transforms->getTransform();
74+
$this->assertCount(1, $transform);
75+
76+
$transform = array_pop($transform);
77+
$this->assertEquals('http://www.w3.org/TR/1999/REC-xpath-19991116', $transform->getAlgorithm());
78+
79+
$elements = $transform->getElements();
80+
$this->assertCount(1, $elements);
81+
82+
$this->assertInstanceOf(Chunk::class, $elements[0]);
83+
84+
$this->assertEquals(
85+
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
86+
strval($transforms)
87+
);
88+
}
89+
90+
91+
/**
92+
* Adding an empty Transforms element should yield an empty element.
93+
*/
94+
public function testMarshallingEmptyElement(): void
95+
{
96+
$ds_ns = Transforms::NS;
97+
$transforms = new Transforms([]);
98+
$this->assertEquals(
99+
"<ds:Transforms xmlns:ds=\"$ds_ns\"/>",
100+
strval($transforms)
101+
);
102+
$this->assertTrue($transforms->isEmptyElement());
103+
}
104+
}

tests/XML/xenc/CipherReferenceTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPUnit\Framework\TestCase;
99
use SimpleSAML\Test\XML\SerializableXMLTestTrait;
1010
use SimpleSAML\XML\DOMDocumentFactory;
11+
use SimpleSAML\XML\Utils as XMLUtils;
1112
use SimpleSAML\XMLSecurity\XML\ds\Transforms;
1213
use SimpleSAML\XMLSecurity\XML\xenc\CipherReference;
1314
use SimpleSAML\XMLSecurity\XMLSecurityDSig;
@@ -54,16 +55,16 @@ public function setup(): void
5455
*/
5556
public function testMarshalling(): void
5657
{
57-
$cipherReference = new CipherReference('#Cipher_VALUE_ID', [Transforms::fromXML($this->transforms)]);
58+
$cipherReference = new CipherReference('#Cipher_VALUE_ID', [Transforms::fromXML($this->transforms->documentElement)]);
5859

5960
$cipherReferenceElement = $cipherReference->toXML();
6061
$this->assertEquals('#Cipher_VALUE_ID', $cipherReferenceElement->getAttribute('URI'));
6162

6263
$transformsElement = XMLUtils::xpQuery($cipherReferenceElement, './ds:Transforms');
6364
$this->assertCount(1, $transformsElement);
6465

65-
$transformElement = XMLUtils::xpQuery($transformsElement, './ds:Transform');
66-
$this->assertCount(1, $transformsElement);
66+
$transformElement = XMLUtils::xpQuery($transformsElement[0], './ds:Transform');
67+
$this->assertCount(1, $transformElement);
6768

6869
$this->assertEquals(
6970
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),

tests/XML/xenc/DataReferenceTest.php

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use SimpleSAML\Test\XML\SerializableXMLTestTrait;
1010
use SimpleSAML\XML\Chunk;
1111
use SimpleSAML\XML\DOMDocumentFactory;
12+
use SimpleSAML\XMLSecurity\XML\ds\Transform;
13+
use SimpleSAML\XMLSecurity\XML\ds\Transforms;
1214
use SimpleSAML\XMLSecurity\XML\xenc\DataReference;
1315
use SimpleSAML\XMLSecurity\XMLSecurityDSig;
1416

@@ -25,9 +27,6 @@ final class DataReferenceTest extends TestCase
2527
{
2628
use SerializableXMLTestTrait;
2729

28-
/** @var \SimpleSAML\XML\Chunk $reference */
29-
private Chunk $reference;
30-
3130

3231
/**
3332
*/
@@ -38,19 +37,6 @@ public function setup(): void
3837
$this->xmlRepresentation = DOMDocumentFactory::fromFile(
3938
dirname(dirname(dirname(dirname(__FILE__)))) . '/tests/resources/xml/xenc_DataReference.xml'
4039
);
41-
42-
$dsNamespace = XMLSecurityDSig::XMLDSIGNS;
43-
44-
$this->reference = new Chunk(DOMDocumentFactory::fromString(<<<XML
45-
<ds:Transforms xmlns:ds="{$dsNamespace}">
46-
<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
47-
<ds:XPath xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
48-
self::xenc:EncryptedData[@Id="example1"]
49-
</ds:XPath>
50-
</ds:Transform>
51-
</ds:Transforms>
52-
XML
53-
)->documentElement);
5440
}
5541

5642
// marshalling
@@ -60,13 +46,23 @@ public function setup(): void
6046
*/
6147
public function testMarshalling(): void
6248
{
63-
$dataReference = new DataReference('#Encrypted_DATA_ID', [$this->reference]);
64-
65-
$this->assertEquals('#Encrypted_DATA_ID', $dataReference->getURI());
66-
67-
$references = $dataReference->getElements();
68-
$this->assertCount(1, $references);
69-
$this->assertEquals($this->reference, $references[0]);
49+
$dataReference = new DataReference(
50+
'#Encrypted_DATA_ID',
51+
[
52+
new Transforms([
53+
new Transform(
54+
'http://www.w3.org/TR/1999/REC-xpath-19991116',
55+
[
56+
new Chunk(
57+
DOMDocumentFactory::fromString(
58+
'<ds:XPath xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">self::xenc:EncryptedData[@Id="example1"]</ds:XPath>'
59+
)->documentElement
60+
)
61+
]
62+
)
63+
])
64+
]
65+
);
7066

7167
$this->assertEquals(
7268
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
@@ -88,7 +84,6 @@ public function testUnmarshalling(): void
8884

8985
$references = $dataReference->getElements();
9086
$this->assertCount(1, $references);
91-
$this->assertEquals($this->reference, $references[0]);
9287

9388
$this->assertEquals(
9489
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),

0 commit comments

Comments
 (0)