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

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