Passed
Push — master ( 045039...d7835c )
by Tim
02:26
created

SPSSODescriptor::toUnsignedXML()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 10
nc 16
nop 1
dl 0
loc 21
rs 8.8333
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\XML\Exception\InvalidDOMElementException;
10
use SimpleSAML\XML\Exception\TooManyElementsException;
11
use SimpleSAML\XML\Utils as XMLUtils;
12
use SimpleSAML\XMLSecurity\XML\ds\Signature;
13
14
use function array_filter;
15
use function is_bool;
16
use function preg_split;
17
18
/**
19
 * Class representing SAML 2 SPSSODescriptor.
20
 *
21
 * @package simplesamlphp/saml2
22
 */
23
final class SPSSODescriptor extends AbstractSSODescriptor
24
{
25
    /**
26
     * Whether this SP signs authentication requests.
27
     *
28
     * @var bool|null
29
     */
30
    protected ?bool $authnRequestsSigned = null;
31
32
    /**
33
     * Whether this SP wants the Assertion elements to be signed.
34
     *
35
     * @var bool|null
36
     */
37
    protected ?bool $wantAssertionsSigned = null;
38
39
    /**
40
     * List of AssertionConsumerService endpoints for this SP.
41
     *
42
     * Array with IndexedEndpointType objects.
43
     *
44
     * @var \SimpleSAML\SAML2\XML\md\AssertionConsumerService[]
45
     */
46
    protected array $assertionConsumerService = [];
47
48
    /**
49
     * List of AttributeConsumingService descriptors for this SP.
50
     *
51
     * Array with \SimpleSAML\SAML2\XML\md\AttributeConsumingService objects.
52
     *
53
     * @var \SimpleSAML\SAML2\XML\md\AttributeConsumingService[]
54
     */
55
    protected array $attributeConsumingService = [];
56
57
58
59
    /**
60
     * SPSSODescriptor constructor.
61
     *
62
     * @param \SimpleSAML\SAML2\XML\md\AssertionConsumerService[] $assertionConsumerService
63
     * @param string[] $protocolSupportEnumeration
64
     * @param bool|null $authnRequestsSigned
65
     * @param bool|null $wantAssertionsSigned
66
     * @param \SimpleSAML\SAML2\XML\md\AttributeConsumingService[] $attributeConsumingService
67
     * @param string|null $ID
68
     * @param int|null $validUntil
69
     * @param string|null $cacheDuration
70
     * @param \SimpleSAML\SAML2\XML\md\Extensions|null $extensions
71
     * @param string|null $errorURL
72
     * @param \SimpleSAML\SAML2\XML\md\KeyDescriptor[] $keyDescriptors
73
     * @param \SimpleSAML\SAML2\XML\md\Organization|null $organization
74
     * @param \SimpleSAML\SAML2\XML\md\ContactPerson[] $contacts
75
     * @param \SimpleSAML\SAML2\XML\md\ArtifactResolutionService[] $artifactResolutionService
76
     * @param \SimpleSAML\SAML2\XML\md\SingleLogoutService[] $singleLogoutService
77
     * @param \SimpleSAML\SAML2\XML\md\ManageNameIDService[] $manageNameIDService
78
     * @param \SimpleSAML\SAML2\XML\md\NameIDFormat[] $nameIDFormat
79
     */
80
    public function __construct(
81
        array $assertionConsumerService,
82
        array $protocolSupportEnumeration,
83
        ?bool $authnRequestsSigned = null,
84
        ?bool $wantAssertionsSigned = null,
85
        array $attributeConsumingService = [],
86
        ?string $ID = null,
87
        ?int $validUntil = null,
88
        ?string $cacheDuration = null,
89
        ?Extensions $extensions = null,
90
        ?string $errorURL = null,
91
        array $keyDescriptors = [],
92
        ?Organization $organization = null,
93
        array $contacts = [],
94
        array $artifactResolutionService = [],
95
        array $singleLogoutService = [],
96
        array $manageNameIDService = [],
97
        array $nameIDFormat = []
98
    ) {
99
        parent::__construct(
100
            $protocolSupportEnumeration,
101
            $ID,
102
            $validUntil,
103
            $cacheDuration,
104
            $extensions,
105
            $errorURL,
106
            $keyDescriptors,
107
            $organization,
108
            $contacts,
109
            $artifactResolutionService,
110
            $singleLogoutService,
111
            $manageNameIDService,
112
            $nameIDFormat
113
        );
114
115
        $this->setAssertionConsumerService($assertionConsumerService);
116
        $this->setAuthnRequestsSigned($authnRequestsSigned);
117
        $this->setWantAssertionsSigned($wantAssertionsSigned);
118
        $this->setAttributeConsumingService($attributeConsumingService);
119
120
        // test that only one ACS is marked as default
121
        Assert::maxCount(
122
            array_filter(
123
                $this->getAttributeConsumingService(),
124
                function (AttributeConsumingService $acs) {
125
                    return $acs->getIsDefault() === true;
126
                }
127
            ),
128
            1,
129
            'Only one md:AttributeConsumingService can be set as default.'
130
        );
131
    }
132
133
134
    /**
135
     * Collect the value of the AuthnRequestsSigned-property
136
     *
137
     * @return bool|null
138
     */
139
    public function getAuthnRequestsSigned(): ?bool
140
    {
141
        return $this->authnRequestsSigned;
142
    }
143
144
145
    /**
146
     * Set the value of the AuthnRequestsSigned-property
147
     *
148
     * @param bool|null $flag
149
     */
150
    private function setAuthnRequestsSigned(?bool $flag): void
151
    {
152
        $this->authnRequestsSigned = $flag;
153
    }
154
155
156
    /**
157
     * Collect the value of the WantAssertionsSigned-property
158
     *
159
     * @return bool|null
160
     */
161
    public function getWantAssertionsSigned(): ?bool
162
    {
163
        return $this->wantAssertionsSigned;
164
    }
165
166
167
    /**
168
     * Set the value of the WantAssertionsSigned-property
169
     *
170
     * @param bool|null $flag
171
     */
172
    private function setWantAssertionsSigned(?bool $flag): void
173
    {
174
        $this->wantAssertionsSigned = $flag;
175
    }
176
177
178
    /**
179
     * Collect the value of the AssertionConsumerService-property
180
     *
181
     * @return \SimpleSAML\SAML2\XML\md\AssertionConsumerService[]
182
     */
183
    public function getAssertionConsumerService(): array
184
    {
185
        return $this->assertionConsumerService;
186
    }
187
188
189
    /**
190
     * Set the value of the AssertionConsumerService-property
191
     *
192
     * @param \SimpleSAML\SAML2\XML\md\AssertionConsumerService[] $acs
193
     * @throws \SimpleSAML\Assert\AssertionFailedException
194
     */
195
    private function setAssertionConsumerService(array $acs): void
196
    {
197
        Assert::minCount($acs, 1, 'At least one AssertionConsumerService must be specified.');
198
        Assert::allIsInstanceOf(
199
            $acs,
200
            AssertionConsumerService::class,
201
            'All md:AssertionConsumerService endpoints must be an instance of AssertionConsumerService.'
202
        );
203
        $this->assertionConsumerService = $acs;
204
    }
205
206
207
    /**
208
     * Collect the value of the AttributeConsumingService-property
209
     *
210
     * @return \SimpleSAML\SAML2\XML\md\AttributeConsumingService[]
211
     */
212
    public function getAttributeConsumingService(): array
213
    {
214
        return $this->attributeConsumingService;
215
    }
216
217
218
    /**
219
     * Set the value of the AttributeConsumingService-property
220
     *
221
     * @param \SimpleSAML\SAML2\XML\md\AttributeConsumingService[] $acs
222
     * @throws \SimpleSAML\Assert\AssertionFailedException
223
     */
224
    private function setAttributeConsumingService(array $acs): void
225
    {
226
        Assert::allIsInstanceOf(
227
            $acs,
228
            AttributeConsumingService::class,
229
            'All md:AttributeConsumingService endpoints must be an instance of AttributeConsumingService.'
230
        );
231
        $this->attributeConsumingService = $acs;
232
    }
233
234
235
    /**
236
     * Convert XML into a SPSSODescriptor
237
     *
238
     * @param \DOMElement $xml The XML element we should load
239
     *
240
     * @return self
241
     *
242
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
243
     *   if the qualified name of the supplied element is wrong
244
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException
245
     *   if the supplied element is missing one of the mandatory attributes
246
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException
247
     *   if too many child-elements of a type are specified
248
     */
249
    public static function fromXML(DOMElement $xml): static
250
    {
251
        Assert::same($xml->localName, 'SPSSODescriptor', InvalidDOMElementException::class);
252
        Assert::same($xml->namespaceURI, SPSSODescriptor::NS, InvalidDOMElementException::class);
253
254
        $protocols = self::getAttribute($xml, 'protocolSupportEnumeration');
255
        $validUntil = self::getAttribute($xml, 'validUntil', null);
256
        $orgs = Organization::getChildrenOfClass($xml);
257
        Assert::maxCount(
258
            $orgs,
259
            1,
260
            'More than one Organization found in this descriptor',
261
            TooManyElementsException::class
262
        );
263
264
        $extensions = Extensions::getChildrenOfClass($xml);
265
        Assert::maxCount(
266
            $extensions,
267
            1,
268
            'Only one md:Extensions element is allowed.',
269
            TooManyElementsException::class
270
        );
271
272
        $signature = Signature::getChildrenOfClass($xml);
273
        Assert::maxCount(
274
            $signature,
275
            1,
276
            'Only one ds:Signature element is allowed.',
277
            TooManyElementsException::class
278
        );
279
280
        $spssod = new static(
281
            AssertionConsumerService::getChildrenOfClass($xml),
282
            preg_split('/[\s]+/', trim($protocols)),
283
            self::getBooleanAttribute($xml, 'AuthnRequestsSigned', null),
284
            self::getBooleanAttribute($xml, 'WantAssertionsSigned', null),
285
            AttributeConsumingService::getChildrenOfClass($xml),
286
            self::getAttribute($xml, 'ID', null),
287
            $validUntil !== null ? XMLUtils::xsDateTimeToTimestamp($validUntil) : null,
288
            self::getAttribute($xml, 'cacheDuration', null),
289
            !empty($extensions) ? $extensions[0] : null,
290
            self::getAttribute($xml, 'errorURL', null),
291
            KeyDescriptor::getChildrenOfClass($xml),
292
            !empty($orgs) ? $orgs[0] : null,
293
            ContactPerson::getChildrenOfClass($xml),
294
            ArtifactResolutionService::getChildrenOfClass($xml),
295
            SingleLogoutService::getChildrenOfClass($xml),
296
            ManageNameIDService::getChildrenOfClass($xml),
297
            NameIDFormat::getChildrenOfClass($xml)
298
        );
299
        if (!empty($signature)) {
300
            $spssod->setSignature($signature[0]);
301
            $spssod->setXML($xml);
302
        }
303
        return $spssod;
304
    }
305
306
307
    /**
308
     * Convert this assertion to an unsigned XML document.
309
     * This method does not sign the resulting XML document.
310
     *
311
     * @return \DOMElement The root element of the DOM tree
312
     */
313
    public function toUnsignedXML(?DOMElement $parent = null): DOMElement
314
    {
315
        $e = parent::toUnsignedXML($parent);
316
317
        if (is_bool($this->getAuthnRequestsSigned())) {
318
            $e->setAttribute('AuthnRequestsSigned', $this->getAuthnRequestsSigned() ? 'true' : 'false');
319
        }
320
321
        if (is_bool($this->getWantAssertionsSigned())) {
322
            $e->setAttribute('WantAssertionsSigned', $this->getWantAssertionsSigned() ? 'true' : 'false');
323
        }
324
325
        foreach ($this->getAssertionConsumerService() as $ep) {
326
            $ep->toXML($e);
327
        }
328
329
        foreach ($this->getAttributeConsumingService() as $acs) {
330
            $acs->toXML($e);
331
        }
332
333
        return $e;
334
    }
335
}
336