Passed
Push — master ( d3e6a5...90893b )
by Tim
02:27
created

EntityDescriptor::setAffiliationDescriptor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\md;
6
7
use DOMElement;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\SAML2\Constants as C;
10
use SimpleSAML\SAML2\Exception\ProtocolViolationException;
11
use SimpleSAML\XML\Exception\InvalidDOMElementException;
12
use SimpleSAML\XML\Exception\SchemaViolationException;
13
use SimpleSAML\XML\Exception\TooManyElementsException;
14
use SimpleSAML\XML\Utils as XMLUtils;
15
use SimpleSAML\XMLSecurity\XML\ds\Signature;
16
17
use function is_null;
18
19
/**
20
 * Class representing SAML 2 EntityDescriptor element.
21
 *
22
 * @package simplesamlphp/saml2
23
 */
24
final class EntityDescriptor extends AbstractMetadataDocument
25
{
26
    /**
27
     * The entityID this EntityDescriptor represents.
28
     *
29
     * @var string
30
     */
31
    protected string $entityID;
32
33
    /**
34
     * Array with all roles for this entity.
35
     *
36
     * Array of \SimpleSAML\SAML2\XML\md\RoleDescriptor objects (and subclasses of RoleDescriptor).
37
     *
38
     * @var \SimpleSAML\SAML2\XML\md\AbstractRoleDescriptor[]
39
     */
40
    protected array $RoleDescriptor = [];
41
42
    /**
43
     * AffiliationDescriptor of this entity.
44
     *
45
     * @var \SimpleSAML\SAML2\XML\md\AffiliationDescriptor|null
46
     */
47
    protected ?AffiliationDescriptor $AffiliationDescriptor = null;
48
49
    /**
50
     * Organization of this entity.
51
     *
52
     * @var \SimpleSAML\SAML2\XML\md\Organization|null
53
     */
54
    protected ?Organization $Organization = null;
55
56
    /**
57
     * ContactPerson elements for this entity.
58
     *
59
     * @var \SimpleSAML\SAML2\XML\md\ContactPerson[]
60
     */
61
    protected array $ContactPerson = [];
62
63
    /**
64
     * AdditionalMetadataLocation elements for this entity.
65
     *
66
     * @var \SimpleSAML\SAML2\XML\md\AdditionalMetadataLocation[]
67
     */
68
    protected array $AdditionalMetadataLocation = [];
69
70
71
    /**
72
     * Initialize an EntitiyDescriptor.
73
     *
74
     * @param string $entityID The entityID of the entity described by this descriptor.
75
     * @param string|null $id The ID for this document. Defaults to null.
76
     * @param int|null $validUntil Unix time of validify for this document. Defaults to null.
77
     * @param string|null $cacheDuration Maximum time this document can be cached. Defaults to null.
78
     * @param \SimpleSAML\SAML2\XML\md\Extensions|null $extensions An array of extensions.
79
     * @param \SimpleSAML\SAML2\XML\md\AbstractRoleDescriptor[] $roleDescriptors An array of role descriptors.
80
     * @param \SimpleSAML\SAML2\XML\md\AffiliationDescriptor|null $affiliationDescriptor An affiliation descriptor to
81
     *   use instead of role descriptors.
82
     * @param \SimpleSAML\SAML2\XML\md\Organization|null $organization The organization responsible for the SAML entity.
83
     * @param \SimpleSAML\SAML2\XML\md\ContactPerson[] $contacts A list of contact persons for this SAML entity.
84
     * @param \SimpleSAML\SAML2\XML\md\AdditionalMetadataLocation[] $additionalMdLocations A list of
85
     *   additional metadata locations.
86
     * @param \DOMAttr[] $namespacedAttributes
87
     *
88
     * @throws \Exception
89
     */
90
    public function __construct(
91
        string $entityID,
92
        ?string $id = null,
93
        ?int $validUntil = null,
94
        ?string $cacheDuration = null,
95
        Extensions $extensions = null,
96
        array $roleDescriptors = [],
97
        ?AffiliationDescriptor $affiliationDescriptor = null,
98
        ?Organization $organization = null,
99
        array $contacts = [],
100
        array $additionalMdLocations = [],
101
        array $namespacedAttributes = []
102
    ) {
103
        Assert::false(
104
            (empty($roleDescriptors) && $affiliationDescriptor === null),
105
            'Must have either one of the RoleDescriptors or an AffiliationDescriptor in EntityDescriptor.',
106
            ProtocolViolationException::class,
107
        );
108
109
        parent::__construct($id, $validUntil, $cacheDuration, $extensions, $namespacedAttributes);
110
111
        $this->setEntityID($entityID);
112
        $this->setRoleDescriptors($roleDescriptors);
113
        $this->setAffiliationDescriptor($affiliationDescriptor);
114
        $this->setOrganization($organization);
115
        $this->setContactPersons($contacts);
116
        $this->setAdditionalMetadataLocations($additionalMdLocations);
117
    }
118
119
120
    /**
121
     * Convert an existing XML into an EntityDescriptor object
122
     *
123
     * @param \DOMElement $xml An existing EntityDescriptor XML document.
124
     * @return \SimpleSAML\SAML2\XML\md\EntityDescriptor An object representing the given document.
125
     *
126
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException if the qualified name of the supplied element is wrong
127
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException if the supplied element is missing one of the mandatory attributes
128
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException if too many child-elements of a type are specified
129
     */
130
    public static function fromXML(DOMElement $xml): static
131
    {
132
        Assert::same($xml->localName, 'EntityDescriptor', InvalidDOMElementException::class);
133
        Assert::same($xml->namespaceURI, EntityDescriptor::NS, InvalidDOMElementException::class);
134
135
        $validUntil = self::getAttribute($xml, 'validUntil', null);
136
        $extensions = Extensions::getChildrenOfClass($xml);
137
        Assert::maxCount($extensions, 1, 'Only one md:Extensions element is allowed.', TooManyElementsException::class);
138
139
        $signature = Signature::getChildrenOfClass($xml);
140
        Assert::maxCount($signature, 1, 'Only one ds:Signature element is allowed.', TooManyElementsException::class);
141
142
        $entityID = self::getAttribute($xml, 'entityID');
143
        $roleDescriptors = [];
144
        $affiliationDescriptor = null;
145
        $organization = null;
146
        $contactPersons = [];
147
        $additionalMetadataLocation = [];
148
        foreach ($xml->childNodes as $node) {
149
            if (
150
                !($node instanceof DOMElement)
151
                || ($node->namespaceURI !== C::NS_MD)
152
            ) {
153
                continue;
154
            }
155
156
            switch ($node->localName) {
157
                case 'Extensions':
158
                    continue 2;
159
                case 'IDPSSODescriptor':
160
                    $roleDescriptors[] = IDPSSODescriptor::fromXML($node);
161
                    break;
162
                case 'SPSSODescriptor':
163
                    $roleDescriptors[] = SPSSODescriptor::fromXML($node);
164
                    break;
165
                case 'AuthnAuthorityDescriptor':
166
                    $roleDescriptors[] = AuthnAuthorityDescriptor::fromXML($node);
167
                    break;
168
                case 'AttributeAuthorityDescriptor':
169
                    $roleDescriptors[] = AttributeAuthorityDescriptor::fromXML($node);
170
                    break;
171
                case 'PDPDescriptor':
172
                    $roleDescriptors[] = PDPDescriptor::fromXML($node);
173
                    break;
174
                case 'AffiliationDescriptor':
175
                    if ($affiliationDescriptor !== null) {
176
                        throw new TooManyElementsException('More than one AffiliationDescriptor in the entity.');
177
                    }
178
                    $affiliationDescriptor = AffiliationDescriptor::fromXML($node);
179
                    break;
180
                case 'Organization':
181
                    if ($organization !== null) {
182
                        throw new TooManyElementsException('More than one Organization in the entity.');
183
                    }
184
                    $organization = Organization::fromXML($node);
185
                    break;
186
                case 'ContactPerson':
187
                    $contactPersons[] = ContactPerson::fromXML($node);
188
                    break;
189
                case 'AdditionalMetadataLocation':
190
                    $additionalMetadataLocation[] = AdditionalMetadataLocation::fromXML($node);
191
                    break;
192
                default:
193
                    $roleDescriptors[] = UnknownRoleDescriptor::fromXML($node);
194
            }
195
        }
196
197
        Assert::false(
198
            empty($roleDescriptors) && is_null($affiliationDescriptor),
199
            'Must have either one of the RoleDescriptors or an AffiliationDescriptor in EntityDescriptor.',
200
            ProtocolViolationException::class,
201
        );
202
        Assert::false(
203
            !empty($roleDescriptors) && !is_null($affiliationDescriptor),
204
            'AffiliationDescriptor cannot be combined with other RoleDescriptor elements in EntityDescriptor.',
205
            ProtocolViolationException::class,
206
        );
207
208
        $entity = new static(
209
            $entityID,
210
            self::getAttribute($xml, 'ID', null),
211
            $validUntil !== null ? XMLUtils::xsDateTimeToTimestamp($validUntil) : null,
212
            self::getAttribute($xml, 'cacheDuration', null),
213
            !empty($extensions) ? $extensions[0] : null,
214
            $roleDescriptors,
215
            $affiliationDescriptor,
216
            $organization,
217
            $contactPersons,
218
            $additionalMetadataLocation,
219
            self::getAttributesNSFromXML($xml)
220
        );
221
222
        if (!empty($signature)) {
223
            $entity->setSignature($signature[0]);
224
            $entity->setXML($xml);
225
        }
226
227
        return $entity;
228
    }
229
230
231
    /**
232
     * Collect the value of the entityID property.
233
     *
234
     * @return string
235
     * @throws \SimpleSAML\Assert\AssertionFailedException
236
     */
237
    public function getEntityID(): string
238
    {
239
        return $this->entityID;
240
    }
241
242
243
    /**
244
     * Set the value of the entityID-property
245
     * @param string $entityId
246
     */
247
    protected function setEntityID(string $entityId): void
248
    {
249
        Assert::validURI($entityId, SchemaViolationException::class); // Covers the empty string
250
        Assert::maxLength(
251
            $entityId,
252
            C::ENTITYID_MAX_LENGTH,
253
            sprintf('The entityID attribute cannot be longer than %d characters.', C::ENTITYID_MAX_LENGTH),
254
            ProtocolViolationException::class
255
        );
256
        $this->entityID = $entityId;
257
    }
258
259
260
    /**
261
     * Collect the value of the RoleDescriptor property.
262
     *
263
     * @return \SimpleSAML\SAML2\XML\md\AbstractRoleDescriptor[]
264
     */
265
    public function getRoleDescriptors(): array
266
    {
267
        return $this->RoleDescriptor;
268
    }
269
270
271
    /**
272
     * Set the value of the RoleDescriptor property.
273
     *
274
     * @param \SimpleSAML\SAML2\XML\md\AbstractRoleDescriptor[] $roleDescriptors
275
     * @throws \SimpleSAML\Assert\AssertionFailedException
276
     */
277
    protected function setRoleDescriptors(array $roleDescriptors): void
278
    {
279
        Assert::allIsInstanceOf(
280
            $roleDescriptors,
281
            AbstractRoleDescriptor::class,
282
            'All role descriptors must extend AbstractRoleDescriptor.'
283
        );
284
        $this->RoleDescriptor = $roleDescriptors;
285
    }
286
287
288
    /**
289
     * Collect the value of the AffiliationDescriptor property.
290
     *
291
     * @return \SimpleSAML\SAML2\XML\md\AffiliationDescriptor|null
292
     */
293
    public function getAffiliationDescriptor(): ?AffiliationDescriptor
294
    {
295
        return $this->AffiliationDescriptor;
296
    }
297
298
299
    /**
300
     * Set the value of the AffliationDescriptor property.
301
     *
302
     * @param \SimpleSAML\SAML2\XML\md\AffiliationDescriptor|null $affiliationDescriptor
303
     */
304
    protected function setAffiliationDescriptor(?AffiliationDescriptor $affiliationDescriptor = null): void
305
    {
306
        $this->AffiliationDescriptor = $affiliationDescriptor;
307
    }
308
309
310
    /**
311
     * Collect the value of the Organization property.
312
     *
313
     * @return \SimpleSAML\SAML2\XML\md\Organization|null
314
     */
315
    public function getOrganization(): ?Organization
316
    {
317
        return $this->Organization;
318
    }
319
320
321
    /**
322
     * Set the value of the Organization property.
323
     *
324
     * @param \SimpleSAML\SAML2\XML\md\Organization|null $organization
325
     */
326
    protected function setOrganization(?Organization $organization = null): void
327
    {
328
        $this->Organization = $organization;
329
    }
330
331
332
    /**
333
     * Collect the value of the ContactPerson property.
334
     *
335
     * @return \SimpleSAML\SAML2\XML\md\ContactPerson[]
336
     */
337
    public function getContactPersons(): array
338
    {
339
        return $this->ContactPerson;
340
    }
341
342
343
    /**
344
     * Set the value of the ContactPerson property.
345
     *
346
     * @param array $contactPerson
347
     * @throws \SimpleSAML\Assert\AssertionFailedException
348
     */
349
    protected function setContactPersons(array $contactPerson): void
350
    {
351
        Assert::allIsInstanceOf(
352
            $contactPerson,
353
            ContactPerson::class,
354
            'All md:ContactPerson elements must be an instance of ContactPerson.'
355
        );
356
        $this->ContactPerson = $contactPerson;
357
    }
358
359
360
    /**
361
     * Collect the value of the AdditionalMetadataLocation property.
362
     *
363
     * @return \SimpleSAML\SAML2\XML\md\AdditionalMetadataLocation[]
364
     */
365
    public function getAdditionalMetadataLocations(): array
366
    {
367
        return $this->AdditionalMetadataLocation;
368
    }
369
370
371
    /**
372
     * Set the value of the AdditionalMetadataLocation property.
373
     *
374
     * @param array $additionalMetadataLocation
375
     * @throws \SimpleSAML\Assert\AssertionFailedException
376
     */
377
    protected function setAdditionalMetadataLocations(array $additionalMetadataLocation): void
378
    {
379
        Assert::allIsInstanceOf(
380
            $additionalMetadataLocation,
381
            AdditionalMetadataLocation::class,
382
            'All md:AdditionalMetadataLocation elements must be an instance of AdditionalMetadataLocation'
383
        );
384
        $this->AdditionalMetadataLocation = $additionalMetadataLocation;
385
    }
386
387
388
    /**
389
     * Convert this assertion to an unsigned XML document.
390
     * This method does not sign the resulting XML document.
391
     *
392
     * @return \DOMElement The root element of the DOM tree
393
     */
394
    public function toUnsignedXML(?DOMElement $parent = null): DOMElement
395
    {
396
        $e = parent::toUnsignedXML($parent);
397
        $e->setAttribute('entityID', $this->entityID);
398
399
        foreach ($this->getAttributesNS() as $attr) {
400
            $e->setAttributeNS($attr['namespaceURI'], $attr['qualifiedName'], $attr['value']);
401
        }
402
403
        foreach ($this->getRoleDescriptors() as $n) {
404
            $n->toXML($e);
405
        }
406
407
        $this->getAffiliationDescriptor()?->toXML($e);
408
        $this->getOrganization()?->toXML($e);
409
410
        foreach ($this->getContactPersons() as $cp) {
411
            $cp->toXML($e);
412
        }
413
414
        foreach ($this->getAdditionalMetadataLocations() as $n) {
415
            $n->toXML($e);
416
        }
417
418
        return $e;
419
    }
420
}
421