Passed
Pull Request — master (#226)
by Jaime Pérez
02:37
created

LogoutRequest::getSessionIndexes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SAML2\XML\samlp;
6
7
use DOMElement;
8
use SAML2\Utils;
9
use SAML2\XML\IdentifierTrait;
10
use SAML2\XML\ds\Signature;
11
use SAML2\XML\saml\IdentifierInterface;
12
use SAML2\XML\saml\BaseID;
13
use SAML2\XML\saml\EncryptedID;
14
use SAML2\XML\saml\NameID;
15
use SAML2\XML\saml\Issuer;
16
use Webmozart\Assert\Assert;
17
18
/**
19
 * Class for SAML 2 logout request messages.
20
 *
21
 * @package SimpleSAMLphp
22
 */
23
class LogoutRequest extends AbstractRequest
24
{
25
    use IdentifierTrait;
26
27
    /**
28
     * The expiration time of this request.
29
     *
30
     * @var int|null
31
     */
32
    protected $notOnOrAfter = null;
33
34
    /**
35
     * The SessionIndexes of the sessions that should be terminated.
36
     *
37
     * @var string[]
38
     */
39
    protected $sessionIndexes = [];
40
41
    /**
42
     * The optional reason for the logout, typically a URN
43
     * See \SAML2\Constants::LOGOUT_REASON_*
44
     * From the standard section 3.7.3: "other values MAY be agreed on between participants"
45
     *
46
     * @var string|null
47
     */
48
    protected $reason = null;
49
50
51
    /**
52
     * Constructor for SAML 2 AttributeQuery.
53
     *
54
     * @param \SAML2\XML\saml\IdentifierInterface $identifier
55
     * @param int|null $notOnOrAfter
56
     * @param string|null $reason
57
     * @param string[] $sessionIndexes
58
     * @param \SAML2\XML\saml\Issuer|null $issuer
59
     * @param string|null $id
60
     * @param string|null $version
61
     * @param int|null $issueInstant
62
     * @param string|null $destination
63
     * @param string|null $consent
64
     * @param \SAML2\XML\samlp\Extensions $extensions
65
     * @throws \Exception
66
     */
67
    public function __construct(
68
        IdentifierInterface $identifier,
69
        ?int $notOnOrAfter = null,
70
        ?string $reason = null,
71
        array $sessionIndexes = [],
72
        ?Issuer $issuer = null,
73
        ?string $id = null,
74
        ?string $version = null,
75
        ?int $issueInstant = null,
76
        ?string $destination = null,
77
        ?string $consent = null,
78
        ?Extensions $extensions = null
79
    ) {
80
        parent::__construct($issuer, $id, $version, $issueInstant, $destination, $consent, $extensions);
81
82
        $this->setIdentifier($identifier);
83
        $this->setNotOnOrAfter($notOnOrAfter);
84
        $this->setReason($reason);
85
        $this->setSessionIndexes($sessionIndexes);
86
    }
87
88
89
    /**
90
     * Retrieve the expiration time of this request.
91
     *
92
     * @return int|null The expiration time of this request.
93
     */
94
    public function getNotOnOrAfter(): ?int
95
    {
96
        return $this->notOnOrAfter;
97
    }
98
99
100
    /**
101
     * Set the expiration time of this request.
102
     *
103
     * @param int|null $notOnOrAfter The expiration time of this request.
104
     * @return void
105
     */
106
    public function setNotOnOrAfter(?int $notOnOrAfter = null): void
107
    {
108
        $this->notOnOrAfter = $notOnOrAfter;
109
    }
110
111
    /**
112
     * Retrieve the reason for this request.
113
     *
114
     * @return string|null The reason for this request.
115
     */
116
    public function getReason(): ?string
117
    {
118
        return $this->reason;
119
    }
120
121
122
    /**
123
     * Set the reason for this request.
124
     *
125
     * @param string|null $reason The optional reason for this request in URN format
126
     * @return void
127
     */
128
    public function setReason(?string $reason = null): void
129
    {
130
        $this->reason = $reason;
131
    }
132
133
134
    /**
135
     * Retrieve the SessionIndexes of the sessions that should be terminated.
136
     *
137
     * @return string[] The SessionIndexes, or an empty array if all sessions should be terminated.
138
     */
139
    public function getSessionIndexes(): array
140
    {
141
        return $this->sessionIndexes;
142
    }
143
144
145
    /**
146
     * Set the SessionIndexes of the sessions that should be terminated.
147
     *
148
     * @param string[] $sessionIndexes The SessionIndexes, or an empty array if all sessions should be terminated.
149
     * @return void
150
     */
151
    public function setSessionIndexes(array $sessionIndexes): void
152
    {
153
        Assert::allStringNotEmpty($sessionIndexes);
154
        $this->sessionIndexes = $sessionIndexes;
155
    }
156
157
158
    /**
159
     * Convert XML into a LogoutRequest
160
     *
161
     * @param \DOMElement $xml The XML element we should load
162
     * @return \SAML2\XML\samlp\LogoutRequest
163
     * @throws \InvalidArgumentException if the qualified name of the supplied element is wrong
164
     * @throws \Exception
165
     */
166
    public static function fromXML(DOMElement $xml): object
167
    {
168
        Assert::same($xml->localName, 'LogoutRequest');
169
        Assert::same($xml->namespaceURI, LogoutRequest::NS);
170
171
        $issueInstant = Utils::xsDateTimeToTimestamp(self::getAttribute($xml, 'IssueInstant'));
172
173
        $notOnOrAfter = self::getAttribute($xml, 'NotOnOrAfter', null);
174
        if ($notOnOrAfter !== null) {
175
            $notOnOrAfter = Utils::xsDateTimeToTimestamp($notOnOrAfter);
176
        }
177
178
        $issuer = Issuer::getChildrenOfClass($xml);
179
        Assert::countBetween($issuer, 0, 1);
180
181
        $extensions = Extensions::getChildrenOfClass($xml);
182
        Assert::maxCount($extensions, 1, 'Only one saml:Extensions element is allowed.');
183
184
        $identifier = self::getIdentifierFromXML($xml);
185
        Assert::notNull($identifier, 'Missing <saml:NameID>, <saml:BaseID> or <saml:EncryptedID> in <samlp:LogoutRequest>.');
186
        Assert::isInstanceOfAny($identifier, [BaseID::class, NameID::class, EncryptedID::class]);
187
188
        $signature = Signature::getChildrenOfClass($xml);
189
        Assert::maxCount($signature, 1, 'Only one ds:Signature element is allowed.');
190
191
        $request = new self(
192
            $identifier,
193
            $notOnOrAfter,
194
            self::getAttribute($xml, 'Reason', null),
195
            Utils::extractStrings($xml, AbstractSamlpElement::NS, 'SessionIndex'),
196
            array_pop($issuer),
197
            self::getAttribute($xml, 'ID'),
198
            self::getAttribute($xml, 'Version'),
199
            $issueInstant,
200
            self::getAttribute($xml, 'Destination', null),
201
            self::getAttribute($xml, 'Consent', null),
202
            array_pop($extensions)
203
        );
204
205
        if (!empty($signature)) {
206
            $request->setSignature($signature[0]);
207
            $request->messageContainedSignatureUponConstruction = true;
208
        }
209
210
        return $request;
211
    }
212
213
214
    /**
215
     * @inheritDoc
216
     * @throws \Exception
217
     */
218
    public function toXML(?DOMElement $parent = null): DOMElement
219
    {
220
        Assert::null($parent);
221
222
        $e = parent::toXML($parent);
223
224
        if ($this->notOnOrAfter !== null) {
225
            $e->setAttribute('NotOnOrAfter', gmdate('Y-m-d\TH:i:s\Z', $this->notOnOrAfter));
226
        }
227
228
        if ($this->reason !== null) {
229
            $e->setAttribute('Reason', $this->reason);
230
        }
231
232
        $this->identifier->toXML($e);
0 ignored issues
show
Bug introduced by
The method toXML() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

232
        $this->identifier->/** @scrutinizer ignore-call */ 
233
                           toXML($e);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
233
234
        foreach ($this->sessionIndexes as $sessionIndex) {
235
            $e->appendChild(
236
                $e->ownerDocument->createElementNS(AbstractSamlpElement::NS, 'samlp:SessionIndex', $sessionIndex)
237
            );
238
        }
239
240
        return $this->signElement($e);
241
    }
242
}
243