Passed
Pull Request — master (#317)
by Tim
12:30
created

EntityAttributes::toXML()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\mdattr;
6
7
use DOMElement;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\SAML2\Exception\ProtocolViolationException;
10
use SimpleSAML\SAML2\Constants as C;
11
use SimpleSAML\SAML2\Utils\XPath;
12
use SimpleSAML\SAML2\XML\saml\Assertion;
13
use SimpleSAML\SAML2\XML\saml\Attribute;
14
use SimpleSAML\SAML2\XML\saml\AttributeStatement;
15
use SimpleSAML\SAML2\XML\saml\NameID;
16
use SimpleSAML\SAML2\XML\saml\Subject;
17
use SimpleSAML\SAML2\XML\saml\SubjectConfirmation;
18
use SimpleSAML\XML\Exception\InvalidDOMElementException;
19
20
use function array_filter;
21
use function array_merge;
22
use function sprintf;
23
24
/**
25
 * Class for handling the EntityAttributes metadata extension.
26
 *
27
 * @link: http://docs.oasis-open.org/security/saml/Post2.0/sstc-metadata-attr-cs-01.pdf
28
 * @package simplesamlphp/saml2
29
 */
30
final class EntityAttributes extends AbstractMdattrElement
31
{
32
    /**
33
     * Create a EntityAttributes element.
34
     *
35
     * @param (\SimpleSAML\SAML2\XML\saml\Assertion|\SimpleSAML\SAML2\XML\saml\Attribute)[] $children
36
     */
37
    public function __construct(
38
        protected array $children,
39
    ) {
40
        Assert::allIsInstanceOfAny($children, [Assertion::class, Attribute::class]);
41
42
        $assertions = array_filter($children, function ($child) {
43
            return $child instanceof Assertion;
44
        });
45
46
        foreach ($assertions as $assertion) {
47
            $statements = array_merge(
48
                $assertion->getAttributeStatements(),
49
                $assertion->getAuthnStatements(),
50
                $assertion->getStatements(),
51
            );
52
53
            Assert::allIsInstanceOf(
54
                $statements,
55
                AttributeStatement::class,
56
                '<saml:Asssertion> elements in an <mdattr:EntityAttributes> may only contain AttributeStatements',
57
                ProtocolViolationException::class,
58
            );
59
            Assert::count(
60
                $statements,
61
                1,
62
                'One (and only one) <saml:AttributeStatement> MUST be included '
63
                . 'in a <saml:Assertion> inside a <mdattr:EntityAttribute>',
64
                ProtocolViolationException::class,
65
            );
66
            Assert::notNull(
67
                Assertion::fromXML($assertion->toXML())->getSignature(),
68
                'Every <saml:Assertion> inside a <mdattr:EntityAttributes> must be individually signed',
69
                ProtocolViolationException::class,
70
            );
71
72
            $subject = $assertion->getSubject();
73
            Assert::notNull(
74
                $subject,
75
                'Every <saml:Assertion> inside a <mdattr:EntityAttributes> must contain a Subject',
76
                ProtocolViolationException::class,
77
            );
78
79
            Assert::isEmpty(
80
                $subject?->getSubjectConfirmation(),
81
                'Every <saml:Assertion> inside a <mdattr:EntityAttributes> must NOT contain any SubjectConfirmation',
82
                ProtocolViolationException::class,
83
            );
84
85
            $nameId = $subject?->getIdentifier();
86
            Assert::isInstanceOf(
87
                $nameId,
88
                NameID::class,
89
                'Every <saml:Assertion> inside a <mdattr:EntityAttributes> must contain a NameID',
90
                ProtocolViolationException::class,
91
            );
92
            Assert::same(
93
                $nameId?->getFormat(),
94
                C::NAMEID_ENTITY,
95
                sprintf('The NameID format must be %s', C::NAMEID_ENTITY),
96
                ProtocolViolationException::class,
97
            );
98
        }
99
    }
100
101
102
    /**
103
     * Collect the value of the children-property
104
     *
105
     * @return (\SimpleSAML\SAML2\XML\saml\Assertion|\SimpleSAML\SAML2\XML\saml\Attribute)[]
106
     */
107
    public function getChildren(): array
108
    {
109
        return $this->children;
110
    }
111
112
113
    /**
114
     * Add the value to the children-property
115
     *
116
     * @param \SimpleSAML\SAML2\XML\saml\Assertion|\SimpleSAML\SAML2\XML\saml\Attribute $child
117
     * @return void
118
     * @throws \SimpleSAML\Assert\AssertionFailedException
119
     */
120
    public function addChild($child): void
121
    {
122
        $this->children = array_merge($this->children, [$child]);
123
    }
124
125
126
    /**
127
     * Convert XML into a EntityAttributes
128
     *
129
     * @param \DOMElement $xml The XML element we should load
130
     * @return self
131
     *
132
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
133
     *   if the qualified name of the supplied element is wrong
134
     */
135
    public static function fromXML(DOMElement $xml): static
136
    {
137
        Assert::same($xml->localName, 'EntityAttributes', InvalidDOMElementException::class);
138
        Assert::same($xml->namespaceURI, EntityAttributes::NS, InvalidDOMElementException::class);
139
140
        $children = [];
141
        foreach ($xml->childNodes as $node) {
142
            if ($node instanceof DOMElement && $node->namespaceURI === C::NS_SAML) {
143
                switch ($node->localName) {
144
                    case 'Assertion':
145
                        $children[] = Assertion::fromXML($node);
146
                        break;
147
                    case 'Attribute':
148
                        $children[] = Attribute::fromXML($node);
149
                        break;
150
                    default:
151
                        continue 2;
152
                }
153
            }
154
        }
155
156
        return new static($children);
157
    }
158
159
160
    /**
161
     * Convert this EntityAttributes to XML.
162
     *
163
     * @param \DOMElement|null $parent The element we should append to.
164
     * @return \DOMElement
165
     */
166
    public function toXML(DOMElement $parent = null): DOMElement
167
    {
168
        $e = $this->instantiateParentElement($parent);
169
170
        foreach ($this->getChildren() as $child) {
171
            $child->toXML($e);
172
        }
173
174
        return $e;
175
    }
176
}
177