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

IDPSSODescriptor::toUnsignedXML()   B

Complexity

Conditions 8
Paths 64

Size

Total Lines 29
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 14
nc 64
nop 1
dl 0
loc 29
rs 8.4444
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;
10
use SimpleSAML\SAML2\XML\saml\Attribute;
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 preg_split;
17
18
/**
19
 * Class representing SAML 2 IDPSSODescriptor.
20
 *
21
 * @package simplesamlphp/saml2
22
 */
23
final class IDPSSODescriptor extends AbstractSSODescriptor
24
{
25
    /**
26
     * Whether AuthnRequests sent to this IdP should be signed.
27
     *
28
     * @var bool|null
29
     */
30
    protected ?bool $wantAuthnRequestsSigned = null;
31
32
    /**
33
     * List of SingleSignOnService endpoints.
34
     *
35
     * @var \SimpleSAML\SAML2\XML\md\SingleSignOnService[]
36
     */
37
    protected array $ssoServiceEndpoints = [];
38
39
    /**
40
     * List of NameIDMappingService endpoints.
41
     *
42
     * @var \SimpleSAML\SAML2\XML\md\NameIDMappingService[]
43
     */
44
    protected array $nameIDMappingServiceEndpoints = [];
45
46
    /**
47
     * List of AssertionIDRequestService endpoints.
48
     *
49
     * @var \SimpleSAML\SAML2\XML\md\AssertionIDRequestService[]
50
     */
51
    protected array $assertionIDRequestServiceEndpoints = [];
52
53
    /**
54
     * List of supported attribute profiles.
55
     *
56
     * @var \SimpleSAML\SAML2\XML\md\AttributeProfile[]
57
     */
58
    protected array $attributeProfiles = [];
59
60
    /**
61
     * List of supported attributes.
62
     *
63
     * @var \SimpleSAML\SAML2\XML\saml\Attribute[]
64
     */
65
    protected array $attributes = [];
66
67
68
    /**
69
     * IDPSSODescriptor constructor.
70
     *
71
     * @param \SimpleSAML\SAML2\XML\md\SingleSignOnService[] $ssoServiceEndpoints
72
     * @param string[] $protocolSupportEnumeration
73
     * @param bool|null $wantAuthnRequestsSigned
74
     * @param \SimpleSAML\SAML2\XML\md\NameIDMappingService[] $nameIDMappingServiceEndpoints
75
     * @param \SimpleSAML\SAML2\XML\md\AssertionIDRequestService[] $assertionIDRequestServiceEndpoints
76
     * @param \SimpleSAML\SAML2\XML\md\AttributeProfile[] $attributeProfiles
77
     * @param \SimpleSAML\SAML2\XML\saml\Attribute[] $attributes
78
     * @param string|null $ID
79
     * @param int|null $validUntil
80
     * @param string|null $cacheDuration
81
     * @param \SimpleSAML\SAML2\XML\md\Extensions|null $extensions
82
     * @param string|null $errorURL
83
     * @param \SimpleSAML\SAML2\XML\md\KeyDescriptor[] $keyDescriptors
84
     * @param \SimpleSAML\SAML2\XML\md\Organization|null $organization
85
     * @param \SimpleSAML\SAML2\XML\md\ContactPerson[] $contacts
86
     * @param \SimpleSAML\SAML2\XML\md\ArtifactResolutionService[] $artifactResolutionService
87
     * @param \SimpleSAML\SAML2\XML\md\SingleLogoutService[] $singleLogoutService
88
     * @param \SimpleSAML\SAML2\XML\md\ManageNameIDService[] $manageNameIDService
89
     * @param \SimpleSAML\SAML2\XML\md\NameIDFormat[] $nameIDFormat
90
     */
91
    public function __construct(
92
        array $ssoServiceEndpoints,
93
        array $protocolSupportEnumeration,
94
        ?bool $wantAuthnRequestsSigned = null,
95
        array $nameIDMappingServiceEndpoints = [],
96
        array $assertionIDRequestServiceEndpoints = [],
97
        array $attributeProfiles = [],
98
        array $attributes = [],
99
        ?string $ID = null,
100
        ?int $validUntil = null,
101
        ?string $cacheDuration = null,
102
        ?Extensions $extensions = null,
103
        ?string $errorURL = null,
104
        array $keyDescriptors = [],
105
        ?Organization $organization = null,
106
        array $contacts = [],
107
        array $artifactResolutionService = [],
108
        array $singleLogoutService = [],
109
        array $manageNameIDService = [],
110
        array $nameIDFormat = []
111
    ) {
112
        parent::__construct(
113
            $protocolSupportEnumeration,
114
            $ID,
115
            $validUntil,
116
            $cacheDuration,
117
            $extensions,
118
            $errorURL,
119
            $keyDescriptors,
120
            $organization,
121
            $contacts,
122
            $artifactResolutionService,
123
            $singleLogoutService,
124
            $manageNameIDService,
125
            $nameIDFormat
126
        );
127
        $this->setSingleSignOnServices($ssoServiceEndpoints);
128
        $this->setWantAuthnRequestsSigned($wantAuthnRequestsSigned);
129
        $this->setNameIDMappingServices($nameIDMappingServiceEndpoints);
130
        $this->setAssertionIDRequestService($assertionIDRequestServiceEndpoints);
131
        $this->setAttributeProfiles($attributeProfiles);
132
        $this->setSupportedAttributes($attributes);
133
    }
134
135
136
    /**
137
     * Initialize an IDPSSODescriptor.
138
     *
139
     * @param \DOMElement $xml The XML element we should load.
140
     * @return \SimpleSAML\SAML2\XML\md\IDPSSODescriptor
141
     *
142
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException if the qualified name of the supplied element is wrong
143
     * @throws \SimpleSAML\XML\Exception\MissingElementException if one of the mandatory child-elements is missing
144
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException if too many child-elements of a type are specified
145
     */
146
    public static function fromXML(DOMElement $xml): object
147
    {
148
        Assert::same($xml->localName, 'IDPSSODescriptor', InvalidDOMElementException::class);
149
        Assert::same($xml->namespaceURI, IDPSSODescriptor::NS, InvalidDOMElementException::class);
150
151
        $protocols = self::getAttribute($xml, 'protocolSupportEnumeration');
152
        $validUntil = self::getAttribute($xml, 'validUntil', null);
153
        $orgs = Organization::getChildrenOfClass($xml);
154
        Assert::maxCount($orgs, 1, 'More than one Organization found in this descriptor', TooManyElementsException::class);
155
156
        $extensions = Extensions::getChildrenOfClass($xml);
157
        Assert::maxCount($extensions, 1, 'Only one md:Extensions element is allowed.', TooManyElementsException::class);
158
159
        $signature = Signature::getChildrenOfClass($xml);
160
        Assert::maxCount($signature, 1, 'Only one ds:Signature element is allowed.', TooManyElementsException::class);
161
162
        $idpssod = new self(
163
            SingleSignOnService::getChildrenOfClass($xml),
164
            preg_split('/[\s]+/', trim($protocols)),
165
            self::getBooleanAttribute($xml, 'WantAuthnRequestsSigned', null),
166
            NameIDMappingService::getChildrenOfClass($xml),
167
            AssertionIDRequestService::getChildrenOfClass($xml),
168
            AttributeProfile::getChildrenOfClass($xml),
169
            Attribute::getChildrenOfClass($xml),
170
            self::getAttribute($xml, 'ID', null),
171
            $validUntil !== null ? XMLUtils::xsDateTimeToTimestamp($validUntil) : null,
172
            self::getAttribute($xml, 'cacheDuration', null),
173
            !empty($extensions) ? $extensions[0] : null,
174
            self::getAttribute($xml, 'errorURL', null),
175
            KeyDescriptor::getChildrenOfClass($xml),
176
            !empty($orgs) ? $orgs[0] : null,
177
            ContactPerson::getChildrenOfClass($xml),
178
            ArtifactResolutionService::getChildrenOfClass($xml),
179
            SingleLogoutService::getChildrenOfClass($xml),
180
            ManageNameIDService::getChildrenOfClass($xml),
181
            NameIDFormat::getChildrenOfClass($xml)
182
        );
183
184
        if (!empty($signature)) {
185
            $idpssod->setSignature($signature[0]);
186
        }
187
188
        $idpssod->setXML($xml);
189
190
        return $idpssod;
191
    }
192
193
194
    /**
195
     * Collect the value of the WantAuthnRequestsSigned-property
196
     *
197
     * @return bool|null
198
     */
199
    public function wantAuthnRequestsSigned(): ?bool
200
    {
201
        return $this->wantAuthnRequestsSigned;
202
    }
203
204
205
    /**
206
     * Set the value of the WantAuthnRequestsSigned-property
207
     *
208
     * @param bool|null $flag
209
     */
210
    protected function setWantAuthnRequestsSigned(?bool $flag = null): void
211
    {
212
        $this->wantAuthnRequestsSigned = $flag;
213
    }
214
215
216
    /**
217
     * Get the SingleSignOnService endpoints
218
     *
219
     * @return \SimpleSAML\SAML2\XML\md\SingleSignOnService[]
220
     */
221
    public function getSingleSignOnServices(): array
222
    {
223
        return $this->ssoServiceEndpoints;
224
    }
225
226
227
    /**
228
     * Set the SingleSignOnService endpoints
229
     *
230
     * @param \SimpleSAML\SAML2\XML\md\SingleSignOnService[] $singleSignOnServices
231
     */
232
    protected function setSingleSignOnServices(array $singleSignOnServices): void
233
    {
234
        Assert::minCount($singleSignOnServices, 1, 'At least one SingleSignOnService must be specified.');
235
        Assert::allIsInstanceOf(
236
            $singleSignOnServices,
237
            SingleSignOnService::class,
238
            'All md:SingleSignOnService endpoints must be an instance of SingleSignOnService.'
239
        );
240
        $this->ssoServiceEndpoints = $singleSignOnServices;
241
    }
242
243
244
    /**
245
     * Get the NameIDMappingService endpoints
246
     *
247
     * @return \SimpleSAML\SAML2\XML\md\NameIDMappingService[]
248
     */
249
    public function getNameIDMappingServices(): array
250
    {
251
        return $this->nameIDMappingServiceEndpoints;
252
    }
253
254
255
    /**
256
     * Set the NameIDMappingService endpoints
257
     *
258
     * @param \SimpleSAML\SAML2\XML\md\NameIDMappingService[] $nameIDMappingServices
259
     */
260
    protected function setNameIDMappingServices(array $nameIDMappingServices): void
261
    {
262
        Assert::allIsInstanceOf(
263
            $nameIDMappingServices,
264
            NameIDMappingService::class,
265
            'All md:NameIDMappingService endpoints must be an instance of NameIDMappingService.'
266
        );
267
        $this->nameIDMappingServiceEndpoints = $nameIDMappingServices;
268
    }
269
270
271
    /**
272
     * Collect the AssertionIDRequestService endpoints
273
     *
274
     * @return \SimpleSAML\SAML2\XML\md\AssertionIDRequestService[]
275
     */
276
    public function getAssertionIDRequestServices(): array
277
    {
278
        return $this->assertionIDRequestServiceEndpoints;
279
    }
280
281
282
    /**
283
     * Set the AssertionIDRequestService endpoints
284
     *
285
     * @param \SimpleSAML\SAML2\XML\md\AssertionIDRequestService[] $assertionIDRequestServices
286
     */
287
    protected function setAssertionIDRequestService(array $assertionIDRequestServices): void
288
    {
289
        Assert::allIsInstanceOf(
290
            $assertionIDRequestServices,
291
            AssertionIDRequestService::class,
292
            'All md:AssertionIDRequestService endpoints must be an instance of AssertionIDRequestService.'
293
        );
294
        $this->assertionIDRequestServiceEndpoints = $assertionIDRequestServices;
295
    }
296
297
298
    /**
299
     * Get the attribute profiles supported
300
     *
301
     * @return \SimpleSAML\SAML2\XML\md\AttributeProfile[]
302
     */
303
    public function getAttributeProfiles(): array
304
    {
305
        return $this->attributeProfiles;
306
    }
307
308
309
    /**
310
     * Set the attribute profiles supported
311
     *
312
     * @param \SimpleSAML\SAML2\XML\md\AttributeProfile[] $attributeProfiles
313
     */
314
    protected function setAttributeProfiles(array $attributeProfiles): void
315
    {
316
        Assert::allIsInstanceOf($attributeProfiles, AttributeProfile::class);
317
        $this->attributeProfiles = $attributeProfiles;
318
    }
319
320
321
    /**
322
     * Get the attributes supported by this IdP
323
     *
324
     * @return \SimpleSAML\SAML2\XML\saml\Attribute[]
325
     */
326
    public function getSupportedAttributes(): array
327
    {
328
        return $this->attributes;
329
    }
330
331
332
    /**
333
     * Set the attributes supported by this IdP
334
     *
335
     * @param \SimpleSAML\SAML2\XML\saml\Attribute[] $attributes
336
     */
337
    protected function setSupportedAttributes(array $attributes): void
338
    {
339
        Assert::allIsInstanceOf(
340
            $attributes,
341
            Attribute::class,
342
            'All md:Attribute elements must be an instance of Attribute.'
343
        );
344
        $this->attributes = $attributes;
345
    }
346
347
348
    /**
349
     * Convert this descriptor to an unsigned XML document.
350
     * This method does not sign the resulting XML document.
351
     *
352
     * @param \DOMElement|null $parent
353
     * @return \DOMElement The root element of the DOM tree
354
     */
355
    protected function toUnsignedXML(DOMElement $parent = null): DOMElement
356
    {
357
        $e = parent::toUnsignedXML($parent);
358
359
        if (is_bool($this->wantAuthnRequestsSigned)) {
360
            $e->setAttribute('WantAuthnRequestsSigned', $this->wantAuthnRequestsSigned ? 'true' : 'false');
361
        }
362
363
        foreach ($this->ssoServiceEndpoints as $ep) {
364
            $ep->toXML($e);
365
        }
366
367
        foreach ($this->nameIDMappingServiceEndpoints as $ep) {
368
            $ep->toXML($e);
369
        }
370
371
        foreach ($this->assertionIDRequestServiceEndpoints as $ep) {
372
            $ep->toXML($e);
373
        }
374
375
        foreach ($this->attributeProfiles as $ap) {
376
            $ap->toXML($e);
377
        }
378
379
        foreach ($this->attributes as $a) {
380
            $a->toXML($e);
381
        }
382
383
        return $e;
384
    }
385
}
386