LogoutRequest::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 11
dl 0
loc 19
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\samlp;
6
7
use DateTimeImmutable;
8
use DOMElement;
9
use SimpleSAML\SAML2\Assert\Assert;
10
use SimpleSAML\SAML2\Constants as C;
11
use SimpleSAML\SAML2\Exception\Protocol\RequestVersionTooHighException;
12
use SimpleSAML\SAML2\Exception\Protocol\RequestVersionTooLowException;
13
use SimpleSAML\SAML2\Exception\ProtocolViolationException;
14
use SimpleSAML\SAML2\XML\IdentifierTrait;
15
use SimpleSAML\SAML2\XML\saml\AbstractBaseID;
16
use SimpleSAML\SAML2\XML\saml\EncryptedID;
17
use SimpleSAML\SAML2\XML\saml\IdentifierInterface;
18
use SimpleSAML\SAML2\XML\saml\Issuer;
19
use SimpleSAML\SAML2\XML\saml\NameID;
20
use SimpleSAML\XML\Exception\InvalidDOMElementException;
21
use SimpleSAML\XML\Exception\MissingElementException;
22
use SimpleSAML\XML\Exception\TooManyElementsException;
23
use SimpleSAML\XML\SchemaValidatableElementInterface;
24
use SimpleSAML\XML\SchemaValidatableElementTrait;
25
use SimpleSAML\XMLSecurity\XML\ds\Signature;
26
27
use function array_pop;
28
29
/**
30
 * Class for SAML 2 logout request messages.
31
 *
32
 * @package simplesamlphp/saml2
33
 */
34
final class LogoutRequest extends AbstractRequest implements SchemaValidatableElementInterface
35
{
36
    use IdentifierTrait;
37
    use SchemaValidatableElementTrait;
38
39
40
    /**
41
     * Constructor for SAML 2 AttributeQuery.
42
     *
43
     * @param \SimpleSAML\SAML2\XML\saml\IdentifierInterface $identifier
44
     * @param \DateTimeImmutable $issueInstant
45
     * @param \DateTimeImmutable|null $notOnOrAfter
46
     * @param string|null $reason
47
     * @param \SimpleSAML\SAML2\XML\samlp\SessionIndex[] $sessionIndexes
48
     * @param \SimpleSAML\SAML2\XML\saml\Issuer|null $issuer
49
     * @param string|null $id
50
     * @param string $version
51
     * @param string|null $destination
52
     * @param string|null $consent
53
     * @param \SimpleSAML\SAML2\XML\samlp\Extensions $extensions
54
     * @throws \Exception
55
     */
56
    public function __construct(
57
        IdentifierInterface $identifier,
58
        DateTimeImmutable $issueInstant,
59
        protected ?DateTimeImmutable $notOnOrAfter = null,
60
        protected ?string $reason = null,
61
        protected array $sessionIndexes = [],
62
        ?Issuer $issuer = null,
63
        ?string $id = null,
64
        string $version = '2.0',
65
        ?string $destination = null,
66
        ?string $consent = null,
67
        ?Extensions $extensions = null,
68
    ) {
69
        Assert::maxCount($sessionIndexes, C::UNBOUNDED_LIMIT);
70
        Assert::allIsInstanceOf($sessionIndexes, SessionIndex::class);
71
72
        parent::__construct($issuer, $id, $version, $issueInstant, $destination, $consent, $extensions);
73
74
        $this->setIdentifier($identifier);
75
    }
76
77
78
    /**
79
     * Retrieve the expiration time of this request.
80
     *
81
     * @return \DateTimeImmutable|null The expiration time of this request.
82
     */
83
    public function getNotOnOrAfter(): ?DateTimeImmutable
84
    {
85
        return $this->notOnOrAfter;
86
    }
87
88
89
    /**
90
     * Retrieve the reason for this request.
91
     *
92
     * @return string|null The reason for this request.
93
     */
94
    public function getReason(): ?string
95
    {
96
        return $this->reason;
97
    }
98
99
100
    /**
101
     * Retrieve the SessionIndexes of the sessions that should be terminated.
102
     *
103
     * @return \SimpleSAML\SAML2\XML\samlp\SessionIndex[]
104
     *   The SessionIndexes, or an empty array if all sessions should be terminated.
105
     */
106
    public function getSessionIndexes(): array
107
    {
108
        return $this->sessionIndexes;
109
    }
110
111
112
    /**
113
     * Convert XML into a LogoutRequest
114
     *
115
     * @param \DOMElement $xml The XML element we should load
116
     * @return static
117
     *
118
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException
119
     *   if the qualified name of the supplied element is wrong
120
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException
121
     *   if the supplied element is missing one of the mandatory attributes
122
     * @throws \SimpleSAML\XML\Exception\MissingElementException
123
     *   if one of the mandatory child-elements is missing
124
     * @throws \SimpleSAML\XML\Exception\TooManyElementsException
125
     *   if too many child-elements of a type are specified
126
     */
127
    public static function fromXML(DOMElement $xml): static
128
    {
129
        Assert::same($xml->localName, 'LogoutRequest', InvalidDOMElementException::class);
130
        Assert::same($xml->namespaceURI, LogoutRequest::NS, InvalidDOMElementException::class);
131
132
        $version = self::getAttribute($xml, 'Version');
133
        Assert::true(version_compare('2.0', $version, '<='), RequestVersionTooLowException::class);
134
        Assert::true(version_compare('2.0', $version, '>='), RequestVersionTooHighException::class);
135
136
        $id = self::getAttribute($xml, 'ID');
137
        Assert::validNCName($id); // Covers the empty string
138
139
        $issueInstant = self::getAttribute($xml, 'IssueInstant');
140
        // Strip sub-seconds - See paragraph 1.3.3 of SAML core specifications
141
        $issueInstant = preg_replace('/([.][0-9]+Z)$/', 'Z', $issueInstant, 1);
142
143
        Assert::validDateTime($issueInstant, ProtocolViolationException::class);
144
        $issueInstant = new DateTimeImmutable($issueInstant);
145
146
        $notOnOrAfter = self::getOptionalAttribute($xml, 'NotOnOrAfter', null);
147
        if ($notOnOrAfter !== null) {
148
            // Strip sub-seconds - See paragraph 1.3.3 of SAML core specifications
149
            $notOnOrAfter = preg_replace('/([.][0-9]+Z)$/', 'Z', $notOnOrAfter, 1);
150
151
            Assert::validDateTime($notOnOrAfter, ProtocolViolationException::class);
152
            $notOnOrAfter = new DateTimeImmutable($notOnOrAfter);
153
        }
154
155
        $issuer = Issuer::getChildrenOfClass($xml);
156
        Assert::countBetween($issuer, 0, 1);
157
158
        $extensions = Extensions::getChildrenOfClass($xml);
159
        Assert::maxCount(
160
            $extensions,
161
            1,
162
            'Only one saml:Extensions element is allowed.',
163
            TooManyElementsException::class,
164
        );
165
166
        $identifier = self::getIdentifierFromXML($xml);
167
        Assert::notNull(
168
            $identifier,
169
            'Missing <saml:NameID>, <saml:BaseID> or <saml:EncryptedID> in <samlp:LogoutRequest>.',
170
            MissingElementException::class,
171
        );
172
        Assert::isInstanceOfAny($identifier, [AbstractBaseID::class, NameID::class, EncryptedID::class]);
173
174
        $signature = Signature::getChildrenOfClass($xml);
175
        Assert::maxCount($signature, 1, 'Only one ds:Signature element is allowed.');
176
177
        $sessionIndex = SessionIndex::getChildrenOfClass($xml);
178
179
        $request = new static(
180
            $identifier,
181
            $issueInstant,
182
            $notOnOrAfter,
183
            self::getOptionalAttribute($xml, 'Reason', null),
184
            $sessionIndex,
185
            array_pop($issuer),
186
            $id,
187
            $version,
188
            self::getOptionalAttribute($xml, 'Destination', null),
189
            self::getOptionalAttribute($xml, 'Consent', null),
190
            array_pop($extensions),
191
        );
192
193
        if (!empty($signature)) {
194
            $request->setSignature($signature[0]);
195
            $request->messageContainedSignatureUponConstruction = true;
196
            $request->setXML($xml);
197
        }
198
199
        return $request;
200
    }
201
202
203
    /**
204
     * Convert this message to an unsigned XML document.
205
     * This method does not sign the resulting XML document.
206
     *
207
     * @return \DOMElement The root element of the DOM tree
208
     */
209
    protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
210
    {
211
        $e = parent::toUnsignedXML($parent);
212
213
        if ($this->getNotOnOrAfter() !== null) {
214
            $e->setAttribute('NotOnOrAfter', $this->getNotOnOrAfter()->format(C::DATETIME_FORMAT));
215
        }
216
217
        if ($this->getReason() !== null) {
218
            $e->setAttribute('Reason', $this->getReason());
219
        }
220
221
        /** @var \SimpleSAML\XML\SerializableElementInterface $identifier */
222
        $identifier = $this->getIdentifier();
223
        $identifier->toXML($e);
224
225
        foreach ($this->getSessionIndexes() as $sessionIndex) {
226
            $sessionIndex->toXML($e);
227
        }
228
229
        return $e;
230
    }
231
}
232