Passed
Branch feature/php83 (cb5cde)
by Tim
14:20
created

EntityDescriptor   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 283
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 119
dl 0
loc 283
rs 9.76
c 0
b 0
f 0
wmc 33
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\md;
6
7
use DOMElement;
8
use SimpleSAML\SAML2\Assert\Assert;
9
use SimpleSAML\SAML2\Constants as C;
10
use SimpleSAML\SAML2\Exception\ProtocolViolationException;
11
use SimpleSAML\SAML2\Type\EntityIDValue;
12
use SimpleSAML\SAML2\Type\SAMLDateTimeValue;
13
use SimpleSAML\XML\ExtendableAttributesTrait;
14
use SimpleSAML\XML\SchemaValidatableElementInterface;
15
use SimpleSAML\XML\SchemaValidatableElementTrait;
16
use SimpleSAML\XMLSchema\Exception\InvalidDOMElementException;
17
use SimpleSAML\XMLSchema\Exception\TooManyElementsException;
18
use SimpleSAML\XMLSchema\Type\DurationValue;
19
use SimpleSAML\XMLSchema\Type\IDValue;
20
use SimpleSAML\XMLSchema\XML\Constants\NS;
21
use SimpleSAML\XMLSecurity\XML\ds\Signature;
22
23
use function is_null;
24
25
/**
26
 * Class representing SAML 2 EntityDescriptor element.
27
 *
28
 * @package simplesamlphp/saml2
29
 */
30
final class EntityDescriptor extends AbstractMetadataDocument implements SchemaValidatableElementInterface
31
{
32
    use ExtendableAttributesTrait;
33
    use SchemaValidatableElementTrait;
34
35
36
    /** The namespace-attribute for the xs:anyAttribute element */
37
    public const string XS_ANY_ATTR_NAMESPACE = NS::OTHER;
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 37 at column 24
Loading history...
38
39
40
    /**
41
     * Initialize an EntitiyDescriptor.
42
     *
43
     * @param \SimpleSAML\SAML2\Type\EntityIDValue $entityId The entityID of the entity described by this descriptor.
44
     * @param \SimpleSAML\XMLSchema\Type\IDValue|null $id The ID for this document. Defaults to null.
45
     * @param \SimpleSAML\SAML2\Type\SAMLDateTimeValue|null $validUntil Unix time of validify for this document.
46
     *   Defaults to null.
47
     * @param \SimpleSAML\XMLSchema\Type\DurationValue|null $cacheDuration Maximum time this document can be cached.
48
     *   Defaults to null.
49
     * @param \SimpleSAML\SAML2\XML\md\Extensions|null $extensions An array of extensions.
50
     * @param \SimpleSAML\SAML2\XML\md\AbstractRoleDescriptorType[] $roleDescriptor An array of role descriptors.
51
     * @param \SimpleSAML\SAML2\XML\md\AffiliationDescriptor|null $affiliationDescriptor An affiliation descriptor to
52
     *   use instead of role descriptors.
53
     * @param \SimpleSAML\SAML2\XML\md\Organization|null $organization The organization responsible for the SAML entity.
54
     * @param \SimpleSAML\SAML2\XML\md\ContactPerson[] $contactPerson A list of contact persons for this SAML entity.
55
     * @param \SimpleSAML\SAML2\XML\md\AdditionalMetadataLocation[] $additionalMetadataLocation A list of
56
     *   additional metadata locations.
57
     * @param list<\SimpleSAML\XML\Attribute> $namespacedAttribute
58
     *
59
     * @throws \Exception
60
     */
61
    public function __construct(
62
        protected EntityIDValue $entityId,
63
        ?IDValue $id = null,
64
        ?SAMLDateTimeValue $validUntil = null,
65
        ?DurationValue $cacheDuration = null,
66
        ?Extensions $extensions = null,
67
        protected array $roleDescriptor = [],
68
        protected ?AffiliationDescriptor $affiliationDescriptor = null,
69
        protected ?Organization $organization = null,
70
        protected array $contactPerson = [],
71
        protected array $additionalMetadataLocation = [],
72
        array $namespacedAttribute = [],
73
    ) {
74
        Assert::false(
75
            (empty($roleDescriptor) && $affiliationDescriptor === null),
76
            'Must have either one of the RoleDescriptors or an AffiliationDescriptor in EntityDescriptor.',
77
            ProtocolViolationException::class,
78
        );
79
        Assert::maxCount($roleDescriptor, C::UNBOUNDED_LIMIT);
80
        Assert::allIsInstanceOf(
81
            $roleDescriptor,
82
            AbstractRoleDescriptorType::class,
83
            'All role descriptors must extend AbstractRoleDescriptor.',
84
        );
85
        Assert::maxCount($contactPerson, C::UNBOUNDED_LIMIT);
86
        Assert::allIsInstanceOf(
87
            $contactPerson,
88
            ContactPerson::class,
89
            'All md:ContactPerson elements must be an instance of ContactPerson.',
90
        );
91
        Assert::maxCount($additionalMetadataLocation, C::UNBOUNDED_LIMIT);
92
        Assert::allIsInstanceOf(
93
            $additionalMetadataLocation,
94
            AdditionalMetadataLocation::class,
95
            'All md:AdditionalMetadataLocation elements must be an instance of AdditionalMetadataLocation',
96
        );
97
98
        parent::__construct($id, $validUntil, $cacheDuration, $extensions);
99
100
        $this->setAttributesNS($namespacedAttribute);
101
    }
102
103
104
    /**
105
     * Convert an existing XML into an EntityDescriptor object
106
     *
107
     * @throws \SimpleSAML\XMLSchema\Exception\InvalidDOMElementException
108
     *   if the qualified name of the supplied element is wrong
109
     * @throws \SimpleSAML\XMLSchema\Exception\MissingAttributeException
110
     *   if the supplied element is missing one of the mandatory attributes
111
     * @throws \SimpleSAML\XMLSchema\Exception\TooManyElementsException
112
     *   if too many child-elements of a type are specified
113
     */
114
    public static function fromXML(DOMElement $xml): static
115
    {
116
        Assert::same($xml->localName, 'EntityDescriptor', InvalidDOMElementException::class);
117
        Assert::same($xml->namespaceURI, EntityDescriptor::NS, InvalidDOMElementException::class);
118
119
        $extensions = Extensions::getChildrenOfClass($xml);
120
        Assert::maxCount($extensions, 1, 'Only one md:Extensions element is allowed.', TooManyElementsException::class);
121
122
        $signature = Signature::getChildrenOfClass($xml);
123
        Assert::maxCount($signature, 1, 'Only one ds:Signature element is allowed.', TooManyElementsException::class);
124
125
        $roleDescriptors = [];
126
        $affiliationDescriptor = null;
127
        $organization = null;
128
        $contactPersons = [];
129
        $additionalMetadataLocation = [];
130
        foreach ($xml->childNodes as $node) {
131
            if (
132
                !($node instanceof DOMElement)
133
                || ($node->namespaceURI !== C::NS_MD)
134
            ) {
135
                continue;
136
            }
137
138
            switch ($node->localName) {
139
                case 'Extensions':
140
                    continue 2;
141
                case 'IDPSSODescriptor':
142
                    $roleDescriptors[] = IDPSSODescriptor::fromXML($node);
143
                    break;
144
                case 'SPSSODescriptor':
145
                    $roleDescriptors[] = SPSSODescriptor::fromXML($node);
146
                    break;
147
                case 'AuthnAuthorityDescriptor':
148
                    $roleDescriptors[] = AuthnAuthorityDescriptor::fromXML($node);
149
                    break;
150
                case 'AttributeAuthorityDescriptor':
151
                    $roleDescriptors[] = AttributeAuthorityDescriptor::fromXML($node);
152
                    break;
153
                case 'PDPDescriptor':
154
                    $roleDescriptors[] = PDPDescriptor::fromXML($node);
155
                    break;
156
                case 'AffiliationDescriptor':
157
                    if ($affiliationDescriptor !== null) {
158
                        throw new TooManyElementsException('More than one AffiliationDescriptor in the entity.');
159
                    }
160
                    $affiliationDescriptor = AffiliationDescriptor::fromXML($node);
161
                    break;
162
                case 'Organization':
163
                    if ($organization !== null) {
164
                        throw new TooManyElementsException('More than one Organization in the entity.');
165
                    }
166
                    $organization = Organization::fromXML($node);
167
                    break;
168
                case 'ContactPerson':
169
                    $contactPersons[] = ContactPerson::fromXML($node);
170
                    break;
171
                case 'AdditionalMetadataLocation':
172
                    $additionalMetadataLocation[] = AdditionalMetadataLocation::fromXML($node);
173
                    break;
174
                default:
175
                    $roleDescriptors[] = UnknownRoleDescriptor::fromXML($node);
176
            }
177
        }
178
179
        Assert::false(
180
            empty($roleDescriptors) && is_null($affiliationDescriptor),
181
            'Must have either one of the RoleDescriptors or an AffiliationDescriptor in EntityDescriptor.',
182
            ProtocolViolationException::class,
183
        );
184
        Assert::false(
185
            !empty($roleDescriptors) && !is_null($affiliationDescriptor),
186
            'AffiliationDescriptor cannot be combined with other RoleDescriptor elements in EntityDescriptor.',
187
            ProtocolViolationException::class,
188
        );
189
190
        $entity = new static(
191
            self::getAttribute($xml, 'entityID', EntityIDValue::class),
192
            self::getOptionalAttribute($xml, 'ID', IDValue::class, null),
193
            self::getOptionalAttribute($xml, 'validUntil', SAMLDateTimeValue::class, null),
194
            self::getOptionalAttribute($xml, 'cacheDuration', DurationValue::class, null),
195
            !empty($extensions) ? $extensions[0] : null,
196
            $roleDescriptors,
197
            $affiliationDescriptor,
198
            $organization,
199
            $contactPersons,
200
            $additionalMetadataLocation,
201
            self::getAttributesNSFromXML($xml),
202
        );
203
204
        if (!empty($signature)) {
205
            $entity->setSignature($signature[0]);
206
            $entity->setXML($xml);
207
        }
208
209
        return $entity;
210
    }
211
212
213
    /**
214
     * Collect the value of the entityID property.
215
     *
216
     * @return \SimpleSAML\SAML2\Type\EntityIDValue
217
     */
218
    public function getEntityId(): EntityIDValue
219
    {
220
        return $this->entityId;
221
    }
222
223
224
    /**
225
     * Collect the value of the RoleDescriptor property.
226
     *
227
     * @return \SimpleSAML\SAML2\XML\md\AbstractRoleDescriptorType[]
228
     */
229
    public function getRoleDescriptor(): array
230
    {
231
        return $this->roleDescriptor;
232
    }
233
234
235
    /**
236
     * Collect the value of the AffiliationDescriptor property.
237
     *
238
     * @return \SimpleSAML\SAML2\XML\md\AffiliationDescriptor|null
239
     */
240
    public function getAffiliationDescriptor(): ?AffiliationDescriptor
241
    {
242
        return $this->affiliationDescriptor;
243
    }
244
245
246
    /**
247
     * Collect the value of the Organization property.
248
     *
249
     * @return \SimpleSAML\SAML2\XML\md\Organization|null
250
     */
251
    public function getOrganization(): ?Organization
252
    {
253
        return $this->organization;
254
    }
255
256
257
    /**
258
     * Collect the value of the ContactPerson property.
259
     *
260
     * @return \SimpleSAML\SAML2\XML\md\ContactPerson[]
261
     */
262
    public function getContactPerson(): array
263
    {
264
        return $this->contactPerson;
265
    }
266
267
268
    /**
269
     * Collect the value of the AdditionalMetadataLocation property.
270
     *
271
     * @return \SimpleSAML\SAML2\XML\md\AdditionalMetadataLocation[]
272
     */
273
    public function getAdditionalMetadataLocation(): array
274
    {
275
        return $this->additionalMetadataLocation;
276
    }
277
278
279
    /**
280
     * Convert this assertion to an unsigned XML document.
281
     * This method does not sign the resulting XML document.
282
     */
283
    public function toUnsignedXML(?DOMElement $parent = null): DOMElement
284
    {
285
        $e = parent::toUnsignedXML($parent);
286
        $e->setAttribute('entityID', $this->getEntityId()->getValue());
287
288
        foreach ($this->getAttributesNS() as $attr) {
289
            $attr->toXML($e);
290
        }
291
292
        foreach ($this->getRoleDescriptor() as $n) {
293
            $n->toXML($e);
294
        }
295
296
        $this->getAffiliationDescriptor()?->toXML($e);
297
        $this->getOrganization()?->toXML($e);
298
299
        foreach ($this->getContactPerson() as $cp) {
300
            $cp->toXML($e);
301
        }
302
303
        foreach ($this->getAdditionalMetadataLocation() as $n) {
304
            $n->toXML($e);
305
        }
306
307
        return $e;
308
    }
309
}
310