Passed
Pull Request — master (#280)
by Tim
02:24
created

EntityDescriptor   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 399
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 137
dl 0
loc 399
rs 9.0399
c 0
b 0
f 0
wmc 42

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 27 2
A setAffiliationDescriptor() 0 3 1
A getEntityID() 0 5 1
A getOrganization() 0 3 1
A setOrganization() 0 3 1
A setRoleDescriptors() 0 8 1
A getAffiliationDescriptor() 0 3 1
D fromXML() 0 99 21
A getAdditionalMetadataLocations() 0 3 1
A setEntityID() 0 5 1
A setAdditionalMetadataLocations() 0 8 1
A getRoleDescriptors() 0 3 1
B toUnsignedXML() 0 30 7
A setContactPersons() 0 8 1
A getContactPersons() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like EntityDescriptor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EntityDescriptor, and based on these observations, apply Extract Interface, too.

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