Passed
Pull Request — master (#374)
by Tim
02:48
created

SPSSODescriptor::toUnsignedXML()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 16
nop 1
dl 0
loc 21
rs 9.6111
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\SAML2\Assert\Assert;
9
use SimpleSAML\SAML2\Type\{AnyURIListValue, SAMLAnyURIValue, SAMLDateTimeValue};
10
use SimpleSAML\XML\Constants as C;
11
use SimpleSAML\XML\{SchemaValidatableElementInterface, SchemaValidatableElementTrait};
12
use SimpleSAML\XMLSchema\Exception\{InvalidDOMElementException, TooManyElementsException};
13
use SimpleSAML\XMLSchema\Type\{BooleanValue, DurationValue, IDValue};
14
use SimpleSAML\XMLSecurity\XML\ds\Signature;
15
16
use function array_filter;
17
use function var_export;
18
19
/**
20
 * Class representing SAML 2 SPSSODescriptor.
21
 *
22
 * @package simplesamlphp/saml2
23
 */
24
final class SPSSODescriptor extends AbstractSSODescriptor implements SchemaValidatableElementInterface
25
{
26
    use SchemaValidatableElementTrait;
27
28
    /**
29
     * SPSSODescriptor constructor.
30
     *
31
     * @param array<\SimpleSAML\SAML2\XML\md\AssertionConsumerService> $assertionConsumerService
32
     * @param \SimpleSAML\SAML2\Type\AnyURIListValue $protocolSupportEnumeration
33
     * @param \SimpleSAML\XMLSchema\Type\BooleanValue|null $authnRequestsSigned
34
     * @param \SimpleSAML\XMLSchema\Type\BooleanValue|null $wantAssertionsSigned
35
     * @param array<\SimpleSAML\SAML2\XML\md\AttributeConsumingService> $attributeConsumingService
36
     * @param \SimpleSAML\XMLSchema\Type\IDValue|null $ID
37
     * @param \SimpleSAML\SAML2\Type\SAMLDateTimeValue|null $validUntil
38
     * @param \SimpleSAML\XMLSchema\Type\DurationValue|null $cacheDuration
39
     * @param \SimpleSAML\SAML2\XML\md\Extensions|null $extensions
40
     * @param \SimpleSAML\SAML2\Type\SAMLAnyURIValue|null $errorURL
41
     * @param array<\SimpleSAML\SAML2\XML\md\KeyDescriptor> $keyDescriptors
42
     * @param \SimpleSAML\SAML2\XML\md\Organization|null $organization
43
     * @param array<\SimpleSAML\SAML2\XML\md\ContactPerson> $contacts
44
     * @param array<\SimpleSAML\SAML2\XML\md\ArtifactResolutionService> $artifactResolutionService
45
     * @param array<\SimpleSAML\SAML2\XML\md\SingleLogoutService> $singleLogoutService
46
     * @param array<\SimpleSAML\SAML2\XML\md\ManageNameIDService> $manageNameIDService
47
     * @param array<\SimpleSAML\SAML2\XML\md\NameIDFormat> $nameIDFormat
48
     */
49
    public function __construct(
50
        protected array $assertionConsumerService,
51
        AnyURIListValue $protocolSupportEnumeration,
52
        protected ?BooleanValue $authnRequestsSigned = null,
53
        protected ?BooleanValue $wantAssertionsSigned = null,
54
        protected array $attributeConsumingService = [],
55
        ?IDValue $ID = null,
56
        ?SAMLDateTimeValue $validUntil = null,
57
        ?DurationValue $cacheDuration = null,
58
        ?Extensions $extensions = null,
59
        ?SAMLAnyURIValue $errorURL = null,
60
        array $keyDescriptors = [],
61
        ?Organization $organization = null,
62
        array $contacts = [],
63
        array $artifactResolutionService = [],
64
        array $singleLogoutService = [],
65
        array $manageNameIDService = [],
66
        array $nameIDFormat = [],
67
    ) {
68
        parent::__construct(
69
            $protocolSupportEnumeration,
70
            $ID,
71
            $validUntil,
72
            $cacheDuration,
73
            $extensions,
74
            $errorURL,
75
            $keyDescriptors,
76
            $organization,
77
            $contacts,
78
            $artifactResolutionService,
79
            $singleLogoutService,
80
            $manageNameIDService,
81
            $nameIDFormat,
82
        );
83
84
        Assert::maxCount($assertionConsumerService, C::UNBOUNDED_LIMIT);
85
        Assert::minCount($assertionConsumerService, 1, 'At least one AssertionConsumerService must be specified.');
86
        Assert::allIsInstanceOf(
87
            $assertionConsumerService,
88
            AssertionConsumerService::class,
89
            'All md:AssertionConsumerService endpoints must be an instance of AssertionConsumerService.',
90
        );
91
        Assert::maxCount($attributeConsumingService, C::UNBOUNDED_LIMIT);
92
        Assert::allIsInstanceOf(
93
            $attributeConsumingService,
94
            AttributeConsumingService::class,
95
            'All md:AttributeConsumingService endpoints must be an instance of AttributeConsumingService.',
96
        );
97
98
        /**
99
         * E87:  test that only one ACS is marked as default
100
         */
101
        Assert::maxCount(
102
            array_filter(
103
                $attributeConsumingService,
104
                function (AttributeConsumingService $acs) {
105
                    return $acs->getIsDefault()?->toBoolean() === true;
106
                },
107
            ),
108
            1,
109
            'At most one <AttributeConsumingService> element can have the attribute isDefault set to true.',
110
        );
111
    }
112
113
114
    /**
115
     * Collect the value of the AuthnRequestsSigned-property
116
     *
117
     * @return \SimpleSAML\XMLSchema\Type\BooleanValue|null
118
     */
119
    public function getAuthnRequestsSigned(): ?BooleanValue
120
    {
121
        return $this->authnRequestsSigned;
122
    }
123
124
125
    /**
126
     * Collect the value of the WantAssertionsSigned-property
127
     *
128
     * @return \SimpleSAML\XMLSchema\Type\BooleanValue|null
129
     */
130
    public function getWantAssertionsSigned(): ?BooleanValue
131
    {
132
        return $this->wantAssertionsSigned;
133
    }
134
135
136
    /**
137
     * Collect the value of the AssertionConsumerService-property
138
     *
139
     * @return \SimpleSAML\SAML2\XML\md\AssertionConsumerService[]
140
     */
141
    public function getAssertionConsumerService(): array
142
    {
143
        return $this->assertionConsumerService;
144
    }
145
146
147
    /**
148
     * Collect the value of the AttributeConsumingService-property
149
     *
150
     * @return \SimpleSAML\SAML2\XML\md\AttributeConsumingService[]
151
     */
152
    public function getAttributeConsumingService(): array
153
    {
154
        return $this->attributeConsumingService;
155
    }
156
157
158
    /**
159
     * Convert XML into a SPSSODescriptor
160
     *
161
     * @param \DOMElement $xml The XML element we should load
162
     * @return static
163
     *
164
     * @throws \SimpleSAML\XMLSchema\Exception\InvalidDOMElementException
165
     *   if the qualified name of the supplied element is wrong
166
     * @throws \SimpleSAML\XMLSchema\Exception\MissingAttributeException
167
     *   if the supplied element is missing one of the mandatory attributes
168
     * @throws \SimpleSAML\XMLSchema\Exception\TooManyElementsException
169
     *   if too many child-elements of a type are specified
170
     */
171
    public static function fromXML(DOMElement $xml): static
172
    {
173
        Assert::same($xml->localName, 'SPSSODescriptor', InvalidDOMElementException::class);
174
        Assert::same($xml->namespaceURI, SPSSODescriptor::NS, InvalidDOMElementException::class);
175
176
        $orgs = Organization::getChildrenOfClass($xml);
177
        Assert::maxCount(
178
            $orgs,
179
            1,
180
            'More than one Organization found in this descriptor',
181
            TooManyElementsException::class,
182
        );
183
184
        $extensions = Extensions::getChildrenOfClass($xml);
185
        Assert::maxCount(
186
            $extensions,
187
            1,
188
            'Only one md:Extensions element is allowed.',
189
            TooManyElementsException::class,
190
        );
191
192
        $signature = Signature::getChildrenOfClass($xml);
193
        Assert::maxCount(
194
            $signature,
195
            1,
196
            'Only one ds:Signature element is allowed.',
197
            TooManyElementsException::class,
198
        );
199
200
        $spssod = new static(
201
            AssertionConsumerService::getChildrenOfClass($xml),
202
            self::getAttribute($xml, 'protocolSupportEnumeration', AnyURIListValue::class),
203
            self::getOptionalAttribute($xml, 'AuthnRequestsSigned', BooleanValue::class, null),
204
            self::getOptionalAttribute($xml, 'WantAssertionsSigned', BooleanValue::class, null),
205
            AttributeConsumingService::getChildrenOfClass($xml),
206
            self::getOptionalAttribute($xml, 'ID', IDValue::class, null),
207
            self::getOptionalAttribute($xml, 'validUntil', SAMLDateTimeValue::class, null),
208
            self::getOptionalAttribute($xml, 'cacheDuration', DurationValue::class, null),
209
            !empty($extensions) ? $extensions[0] : null,
210
            self::getOptionalAttribute($xml, 'errorURL', SAMLAnyURIValue::class, null),
211
            KeyDescriptor::getChildrenOfClass($xml),
212
            !empty($orgs) ? $orgs[0] : null,
213
            ContactPerson::getChildrenOfClass($xml),
214
            ArtifactResolutionService::getChildrenOfClass($xml),
215
            SingleLogoutService::getChildrenOfClass($xml),
216
            ManageNameIDService::getChildrenOfClass($xml),
217
            NameIDFormat::getChildrenOfClass($xml),
218
        );
219
220
        if (!empty($signature)) {
221
            $spssod->setSignature($signature[0]);
222
            $spssod->setXML($xml);
223
        }
224
225
        return $spssod;
226
    }
227
228
229
    /**
230
     * Convert this assertion to an unsigned XML document.
231
     * This method does not sign the resulting XML document.
232
     *
233
     * @return \DOMElement The root element of the DOM tree
234
     */
235
    public function toUnsignedXML(?DOMElement $parent = null): DOMElement
236
    {
237
        $e = parent::toUnsignedXML($parent);
238
239
        if ($this->getAuthnRequestsSigned() !== null) {
240
            $e->setAttribute('AuthnRequestsSigned', var_export($this->getAuthnRequestsSigned()->toBoolean(), true));
241
        }
242
243
        if ($this->getWantAssertionsSigned() !== null) {
244
            $e->setAttribute('WantAssertionsSigned', var_export($this->getWantAssertionsSigned()->toBoolean(), true));
245
        }
246
247
        foreach ($this->getAssertionConsumerService() as $ep) {
248
            $ep->toXML($e);
249
        }
250
251
        foreach ($this->getAttributeConsumingService() as $acs) {
252
            $acs->toXML($e);
253
        }
254
255
        return $e;
256
    }
257
}
258