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