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

LogoutRequest::toXML()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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

243
        $this->identifier->/** @scrutinizer ignore-call */ 
244
                           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...
244
245
        foreach ($this->sessionIndexes as $sessionIndex) {
246
            /** @psalm-suppress PossiblyNullReference */
247
            $e->appendChild(
248
                $e->ownerDocument->createElementNS(AbstractSamlpElement::NS, 'saml:SessionIndex', $sessionIndex)
249
            );
250
        }
251
252
        return $this->signElement($e);
253
    }
254
}
255