Skip to content

Commit 2a67536

Browse files
authored
Add XML-representation for ds:Transform element (#11)
* Add XML-representation for ds:Transform element * Improve coverage
1 parent 2a00453 commit 2a67536

3 files changed

Lines changed: 249 additions & 0 deletions

File tree

src/XML/ds/Transform.php

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
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\Chunk;
10+
use SimpleSAML\XML\Exception\InvalidDOMElementException;
11+
12+
/**
13+
* Class representing a ds:Transform element.
14+
*
15+
* @package simplesamlphp/xml-security
16+
*/
17+
final class Transform extends AbstractDsElement
18+
{
19+
/** @var \SimpleSAML\XML\Chunk[] */
20+
protected array $elements = [];
21+
22+
/** @var string */
23+
protected string $Algorithm;
24+
25+
26+
/**
27+
* Initialize a ds:Transform
28+
*
29+
* @param \SimpleSAML\XML\Chunk[] $elements
30+
* @param string $Algorithm
31+
*/
32+
public function __construct(array $elements = [], string $Algorithm = 'http://www.w3.org/TR/1999/REC-xpath-19991116')
33+
{
34+
$this->setElements($elements);
35+
$this->setAlgorithm($Algorithm);
36+
}
37+
38+
39+
/**
40+
* @return string
41+
*/
42+
public function getAlgorithm(): string
43+
{
44+
return $this->Algorithm;
45+
}
46+
47+
48+
/**
49+
* @param string $Algorithm
50+
* @throws \SimpleSAML\Assert\AssertionFailedException
51+
*/
52+
protected function setAlgorithm(string $Algorithm): void
53+
{
54+
Assert::notEmpty($Algorithm, 'Cannot set an empty algorithm in ' . static::NS_PREFIX . ':Transform.');
55+
$this->Algorithm = $Algorithm;
56+
}
57+
58+
59+
/**
60+
* Collect the elements
61+
*
62+
* @return \SimpleSAML\XML\Chunk[]
63+
*/
64+
public function getElements(): array
65+
{
66+
return $this->elements;
67+
}
68+
69+
70+
/**
71+
* Set the value of the elements-property
72+
*
73+
* @param \SimpleSAML\XML\Chunk[] $elements
74+
* @throws \SimpleSAML\Assert\AssertionFailedException if the supplied array contains anything other than Chunk objects
75+
*/
76+
private function setElements(array $elements): void
77+
{
78+
Assert::allIsInstanceOf($elements, Chunk::class);
79+
80+
$this->elements = $elements;
81+
}
82+
83+
84+
/**
85+
* Test if an object, at the state it's in, would produce an empty XML-element
86+
*
87+
* @return bool
88+
*/
89+
public function isEmptyElement(): bool
90+
{
91+
return empty($this->elements);
92+
}
93+
94+
95+
/**
96+
* Convert XML into a Transform element
97+
*
98+
* @param \DOMElement $xml The XML element we should load
99+
* @return self
100+
*
101+
* @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
102+
* If the qualified name of the supplied element is wrong
103+
*/
104+
public static function fromXML(DOMElement $xml): object
105+
{
106+
Assert::same($xml->localName, 'Transform', InvalidDOMElementException::class);
107+
Assert::same($xml->namespaceURI, Transform::NS, InvalidDOMElementException::class);
108+
109+
$Algorithm = self::getAttribute($xml, 'Algorithm');
110+
111+
$elements = [];
112+
foreach ($xml->childNodes as $element) {
113+
if (!($element instanceof DOMElement)) {
114+
continue;
115+
}
116+
117+
$elements[] = new Chunk($element);
118+
}
119+
120+
return new self($elements, $Algorithm);
121+
}
122+
123+
124+
/**
125+
* Convert this Transform element to XML.
126+
*
127+
* @param \DOMElement|null $parent The element we should append this Transform element to.
128+
* @return \DOMElement
129+
*/
130+
public function toXML(DOMElement $parent = null): DOMElement
131+
{
132+
$e = $this->instantiateParentElement($parent);
133+
$e->setAttribute('Algorithm', $this->Algorithm);
134+
135+
foreach ($this->elements as $element) {
136+
$e->appendChild($e->ownerDocument->importNode($element->getXML(), true));
137+
}
138+
139+
return $e;
140+
}
141+
}

tests/XML/ds/TransformTest.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+
16+
/**
17+
* Class \SAML2\XML\ds\TransformTest
18+
*
19+
* @covers \SimpleSAML\SAML2\XML\ds\Transform
20+
* @covers \SimpleSAML\SAML2\XML\ds\AbstractDsElement
21+
*
22+
* @package simplesamlphp/saml2
23+
*/
24+
final class TransformTest extends TestCase
25+
{
26+
use SerializableXMLTestTrait;
27+
28+
29+
/**
30+
*/
31+
public function setUp(): void
32+
{
33+
$this->testedClass = Transform::class;
34+
35+
$this->xmlRepresentation = DOMDocumentFactory::fromFile(
36+
dirname(dirname(dirname(__FILE__))) . '/resources/xml/ds_Transform.xml'
37+
);
38+
}
39+
40+
41+
/**
42+
*/
43+
public function testMarshalling(): void
44+
{
45+
$transform = new Transform(
46+
[
47+
new Chunk(DOMDocumentFactory::fromString('<some:Chunk>Random</some:Chunk>')->documentElement),
48+
new Chunk(DOMDocumentFactory::fromString('<ds:XPath>count(//. | //@* | //namespace::*)</ds:XPath>')->documentElement)
49+
],
50+
);
51+
52+
$document = DOMDocumentFactory::fromString('<root />');
53+
/** @psalm-var \DOMElement $document->firstChild */
54+
$transformElement = $transform->toXML($document->firstChild);
55+
56+
$this->assertEquals('http://www.w3.org/TR/1999/REC-xpath-19991116', $transformElement->getAttribute('Algorithm'));
57+
$this->assertCount(2, $transformElement->childNodes);
58+
59+
$this->assertEquals('some:Chunk', $transformElement->childNodes[0]->localName);
60+
$this->assertEquals('Random', $transformElement->childNodes[0]->textContent);
61+
$this->assertEquals('ds:XPath', $transformElement->childNodes[1]->localName);
62+
$this->assertEquals('count(//. | //@* | //namespace::*)', $transformElement->childNodes[1]->textContent);
63+
64+
$this->assertEquals(
65+
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
66+
strval($transform)
67+
);
68+
}
69+
70+
71+
/**
72+
*/
73+
public function testUnmarshalling(): void
74+
{
75+
$transform = Transform::fromXML($this->xmlRepresentation->documentElement);
76+
$this->assertEquals('http://www.w3.org/TR/1999/REC-xpath-19991116', $transform->getAlgorithm());
77+
78+
$elements = $transform->getElements();
79+
$this->assertCount(2, $elements);
80+
81+
$this->assertInstanceOf(Chunk::class, $elements[0]);
82+
$this->assertInstanceOf(Chunk::class, $elements[1]);
83+
84+
$this->assertEquals(
85+
$this->xmlRepresentation->saveXML($this->xmlRepresentation->documentElement),
86+
strval($transform)
87+
);
88+
}
89+
90+
91+
/**
92+
* Adding an empty StatusDetail element should yield an empty element.
93+
*/
94+
public function testMarshallingEmptyElement(): void
95+
{
96+
$ds_ns = Transform::NS;
97+
$transform = new Transform([]);
98+
$this->assertEquals(
99+
"<ds:Transform xmlns:ds=\"$ds_ns\" Algorithm=\"http://www.w3.org/TR/1999/REC-xpath-19991116\"/>",
100+
strval($transform)
101+
);
102+
$this->assertTrue($transform->isEmptyElement());
103+
}
104+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<ds:Transform xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116">
2+
<some:Chunk>Random</some:Chunk>
3+
<ds:XPath>count(//. | //@* | //namespace::*)</ds:XPath>
4+
</ds:Transform>

0 commit comments

Comments
 (0)