Passed
Pull Request — master (#374)
by Tim
03:49 queued 01:20
created

EntitiesDescriptor::__construct()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 99
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 41
nc 10
nop 7
dl 0
loc 99
rs 7.6666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Exception\ProtocolViolationException;
10
use SimpleSAML\SAML2\Type\{SAMLDateTimeValue, SAMLStringValue};
11
use SimpleSAML\SAML2\XML\mdrpi\{PublicationInfo, PublicationPath, RegistrationInfo};
12
use SimpleSAML\XML\Constants as C;
13
use SimpleSAML\XML\Exception\{InvalidDOMElementException, TooManyElementsException};
14
use SimpleSAML\XML\{SchemaValidatableElementInterface, SchemaValidatableElementTrait};
15
use SimpleSAML\XML\Type\{DurationValue, IDValue};
16
use SimpleSAML\XMLSecurity\XML\ds\Signature;
17
18
use function count;
19
use function array_filter;
20
use function array_merge;
21
use function array_values;
22
23
/**
24
 * Class representing SAML 2 EntitiesDescriptor element.
25
 *
26
 * @package simplesamlphp/saml2
27
 */
28
final class EntitiesDescriptor extends AbstractMetadataDocument implements SchemaValidatableElementInterface
29
{
30
    use SchemaValidatableElementTrait;
31
32
    /**
33
     * EntitiesDescriptor constructor.
34
     *
35
     * @param \SimpleSAML\SAML2\XML\md\EntityDescriptor[] $entityDescriptors
36
     * @param \SimpleSAML\SAML2\XML\md\EntitiesDescriptor[] $entitiesDescriptors
37
     * @param \SimpleSAML\SAML2\Type\SAMLStringValue|null $Name
38
     * @param \SimpleSAML\XML\Type\IDValue|null $ID
39
     * @param \SimpleSAML\SAML2\Type\SAMLDateTimeValue|null $validUntil
40
     * @param \SimpleSAML\XML\Type\DurationValue|null $cacheDuration
41
     * @param \SimpleSAML\SAML2\XML\md\Extensions|null $extensions
42
     */
43
    public function __construct(
44
        protected array $entityDescriptors = [],
45
        protected array $entitiesDescriptors = [],
46
        protected ?SAMLStringValue $Name = null,
47
        ?IDValue $ID = null,
48
        ?SAMLDateTimeValue $validUntil = null,
49
        ?DurationValue $cacheDuration = null,
50
        ?Extensions $extensions = null,
51
    ) {
52
        Assert::true(
53
            !empty($entitiesDescriptors) || !empty($entityDescriptors),
54
            'At least one md:EntityDescriptor or md:EntitiesDescriptor element is required.',
55
            ProtocolViolationException::class,
56
        );
57
        Assert::maxCount($entitiesDescriptors, C::UNBOUNDED_LIMIT);
58
        Assert::maxCount($entityDescriptors, C::UNBOUNDED_LIMIT);
59
        Assert::allIsInstanceOf($entitiesDescriptors, EntitiesDescriptor::class);
60
        Assert::allIsInstanceOf($entityDescriptors, EntityDescriptor::class);
61
62
        if ($extensions !== null) {
63
            /**
64
             * When a <mdrpi:RegistrationInfo> element appears in the <md:Extensions> element of a
65
             * <md:EntitiesDescriptor> element it applies to all descendant <md:EntitiesDescriptor> and
66
             * <md:EntityDescriptor> elements. That is to say, this is equivalent to putting an identical
67
             * <mdrpi:RegistrationInfo> on every descendant <md:EntityDescriptor>. When used in this
68
             * manner, descendant <md:EntitiesDescriptor> and <md:EntityDescriptor> elements MUST
69
             * NOT contain a <mdrpi:RegistrationInfo> element in their <md:Extensions> element.
70
             */
71
            $toplevel_regInfo = array_values(array_filter($extensions->getList(), function ($ext) {
72
                return $ext instanceof RegistrationInfo;
73
            }));
74
75
            /**
76
             * The <mdrpi:PublicationInfo> element SHOULD only be used on the root element of a metadata document.
77
             */
78
            $toplevel_pubInfo = array_values(array_filter($extensions->getList(), function ($ext) {
79
                return $ext instanceof PublicationInfo;
80
            }));
81
82
            /**
83
             * When a <mdrpi:PublicationPath> element appears in the <md:Extensions> element of a
84
             * <md:EntitiesDescriptor> element it applies to all descendant <md:EntitiesDescriptor> and
85
             * <md:EntityDescriptor> elements. That is to say, this is equivalent to putting an identical
86
             * <mdrpi:PublicationPath> on every descendant <md:EntitiesDescriptor> and <md:EntityDescriptor>.
87
             * When used in this manner, descendant <md:EntitiesDescriptor> and <md:EntityDescriptor>
88
             * elements MUST NOT contain a <mdrpi:PublicationPath> element in their <md:Extensions> element.
89
             */
90
            $toplevel_pubPath = array_values(array_filter($extensions->getList(), function ($ext) {
91
                return $ext instanceof PublicationPath;
92
            }));
93
94
            if (count($toplevel_regInfo) > 0 || count($toplevel_pubInfo) > 0 || count($toplevel_pubPath)) {
95
                $nestedExtensions = [];
96
                foreach (array_merge($entityDescriptors, $entitiesDescriptors) as $ed) {
97
                    $nestedExtensions = array_merge($nestedExtensions, $this->getRecursiveExtensions($ed));
98
                }
99
100
                if (count($toplevel_regInfo) > 0) {
101
                    $nested_regInfo = array_values(array_filter($nestedExtensions, function ($ext) {
102
                        return $ext instanceof RegistrationInfo;
103
                    }));
104
105
                    Assert::count(
106
                        $nested_regInfo,
107
                        0,
108
                        "<mdrpi:RegistrationInfo> already set at top-level.",
109
                        ProtocolViolationException::class,
110
                    );
111
                }
112
113
                if (count($toplevel_pubInfo) > 0) {
114
                    $nested_pubInfo = array_values(array_filter($nestedExtensions, function ($ext) {
115
                        return $ext instanceof PublicationInfo;
116
                    }));
117
118
                    Assert::count(
119
                        $nested_pubInfo,
120
                        0,
121
                        "<mdrpi:PublicationInfo> already set at top-level.",
122
                        ProtocolViolationException::class,
123
                    );
124
                }
125
126
                if (count($toplevel_pubPath) > 0) {
127
                    $nested_pubPath = array_values(array_filter($nestedExtensions, function ($ext) {
128
                        return $ext instanceof PublicationPath;
129
                    }));
130
131
                    Assert::count(
132
                        $nested_pubPath,
133
                        0,
134
                        "<mdrpi:PublicationPath> already set at top-level.",
135
                        ProtocolViolationException::class,
136
                    );
137
                }
138
            }
139
        }
140
141
        parent::__construct($ID, $validUntil, $cacheDuration, $extensions);
142
    }
143
144
145
    /**
146
     * Get all extensions from all nested entity/entities descriptors
147
     */
148
    private function getRecursiveExtensions(EntityDescriptor|EntitiesDescriptor $descriptor): array
149
    {
150
        $extensions = [];
151
        if ($descriptor->getExtensions() !== null) {
152
            $extensions = $descriptor->getExtensions()->getList();
0 ignored issues
show
Bug introduced by
The method getList() does not exist on SimpleSAML\XML\AbstractElement. It seems like you code against a sub-type of SimpleSAML\XML\AbstractElement such as SimpleSAML\SAML2\XML\md\Extensions or SimpleSAML\SAML2\XML\samlp\Extensions. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

152
            $extensions = $descriptor->getExtensions()->/** @scrutinizer ignore-call */ getList();
Loading history...
153
154
            if ($descriptor instanceof EntitiesDescriptor) {
155
                $eds = array_merge($descriptor->getEntitiesDescriptors(), $descriptor->getEntityDescriptors());
156
                foreach ($eds as $ed) {
157
                    $extensions = array_merge($extensions, $descriptor->getRecursiveExtensions($ed));
158
                }
159
            }
160
        }
161
162
        return $extensions;
163
    }
164
165
166
    /**
167
     * Get the EntitiesDescriptor children objects
168
     *
169
     * @return \SimpleSAML\SAML2\XML\md\EntitiesDescriptor[]
170
     */
171
    public function getEntitiesDescriptors(): array
172
    {
173
        return $this->entitiesDescriptors;
174
    }
175
176
177
    /**
178
     * Get the EntityDescriptor children objects
179
     *
180
     * @return \SimpleSAML\SAML2\XML\md\EntityDescriptor[]
181
     */
182
    public function getEntityDescriptors(): array
183
    {
184
        return $this->entityDescriptors;
185
    }
186
187
188
    /**
189
     * Collect the value of the Name property.
190
     *
191
     * @return \SimpleSAML\SAML2\Type\SAMLStringValue|null
192
     */
193
    public function getName(): ?SAMLStringValue
194
    {
195
        return $this->Name;
196
    }
197
198
199
    /**
200
     * Initialize an EntitiesDescriptor from an existing XML document.
201
     *
202
     * @param \DOMElement $xml The XML element we should load.
203
     * @return static
204
     *
205
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
206
     *   if the qualified name of the supplied element is wrong
207
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException
208
     *   if too many child-elements of a type are specified
209
     */
210
    public static function fromXML(DOMElement $xml): static
211
    {
212
        Assert::same($xml->localName, 'EntitiesDescriptor', InvalidDOMElementException::class);
213
        Assert::same($xml->namespaceURI, EntitiesDescriptor::NS, InvalidDOMElementException::class);
214
215
        $orgs = Organization::getChildrenOfClass($xml);
216
        Assert::maxCount(
217
            $orgs,
218
            1,
219
            'More than one Organization found in this descriptor',
220
            TooManyElementsException::class,
221
        );
222
223
        $extensions = Extensions::getChildrenOfClass($xml);
224
        Assert::maxCount(
225
            $extensions,
226
            1,
227
            'Only one md:Extensions element is allowed.',
228
            TooManyElementsException::class,
229
        );
230
231
        $signature = Signature::getChildrenOfClass($xml);
232
        Assert::maxCount(
233
            $signature,
234
            1,
235
            'Only one ds:Signature element is allowed.',
236
            TooManyElementsException::class,
237
        );
238
239
        $entities = new static(
240
            EntityDescriptor::getChildrenOfClass($xml),
241
            EntitiesDescriptor::getChildrenOfClass($xml),
242
            self::getOptionalAttribute($xml, 'Name', SAMLStringValue::class, null),
243
            self::getOptionalAttribute($xml, 'ID', IDValue::class, null),
244
            self::getOptionalAttribute($xml, 'validUntil', SAMLDateTimeValue::class, null),
245
            self::getOptionalAttribute($xml, 'cacheDuration', DurationValue::class, null),
246
            !empty($extensions) ? $extensions[0] : null,
247
        );
248
249
        if (!empty($signature)) {
250
            $entities->setSignature($signature[0]);
251
            $entities->setXML($xml);
252
        }
253
254
        return $entities;
255
    }
256
257
258
    /**
259
     * Convert this assertion to an unsigned XML document.
260
     * This method does not sign the resulting XML document.
261
     *
262
     * @return \DOMElement The root element of the DOM tree
263
     */
264
    public function toUnsignedXML(?DOMElement $parent = null): DOMElement
265
    {
266
        $e = parent::toUnsignedXML($parent);
267
268
        if ($this->getName() !== null) {
269
            $e->setAttribute('Name', $this->getName()->getValue());
270
        }
271
272
        foreach ($this->getEntitiesDescriptors() as $entitiesDescriptor) {
273
            $entitiesDescriptor->toXML($e);
274
        }
275
276
        foreach ($this->getEntityDescriptors() as $entityDescriptor) {
277
            $entityDescriptor->toXML($e);
278
        }
279
280
        return $e;
281
    }
282
}
283