Passed
Pull Request — master (#280)
by Tim
02:30
created

ArtifactResponse::toXML()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\SAML2\XML\samlp;
6
7
use DOMElement;
8
use SimpleSAML\Assert\Assert;
9
use SimpleSAML\SAML2\Exception\ProtocolViolationException;
10
use SimpleSAML\SAML2\Utils\XPath;
11
use SimpleSAML\SAML2\XML\saml\Issuer;
12
use SimpleSAML\XML\Exception\InvalidDOMElementException;
13
use SimpleSAML\XML\Exception\TooManyElementsException;
14
use SimpleSAML\XML\Utils as XMLUtils;
15
use SimpleSAML\XMLSecurity\XML\ds\Signature;
16
17
use function array_pop;
18
19
/**
20
 * The \SimpleSAML\SAML2\XML\samlp\ArtifactResponse,
21
 *  is the response to the \SimpleSAML\SAML2\XML\samlp\ArtifactResolve.
22
 *
23
 * @package simplesamlphp/saml2
24
 */
25
class ArtifactResponse extends AbstractStatusResponse
26
{
27
    /** @var \SimpleSAML\SAML2\XML\samlp\AbstractMessage|null */
28
    protected ?AbstractMessage $message;
29
30
31
    /**
32
     * Constructor for SAML 2 ArtifactResponse.
33
     *
34
     * @param \SimpleSAML\SAML2\XML\samlp\Status $status
35
     * @param \SimpleSAML\SAML2\XML\saml\Issuer|null $issuer
36
     * @param string|null $id
37
     * @param int|null $issueInstant
38
     * @param string|null $inResponseTo
39
     * @param string|null $destination
40
     * @param string|null $consent
41
     * @param \SimpleSAML\SAML2\XML\samlp\Extensions|null $extensions
42
     * @param \SimpleSAML\SAML2\XML\samlp\AbstractMessage|null $message
43
     */
44
    public function __construct(
45
        Status $status,
46
        ?Issuer $issuer = null,
47
        ?string $id = null,
48
        ?int $issueInstant = null,
49
        ?string $inResponseTo = null,
50
        ?string $destination = null,
51
        ?string $consent = null,
52
        ?Extensions $extensions = null,
53
        ?AbstractMessage $message = null
54
    ) {
55
        parent::__construct(
56
            $status,
57
            $issuer,
58
            $id,
59
            $issueInstant,
60
            $inResponseTo,
61
            $destination,
62
            $consent,
63
            $extensions
64
        );
65
66
        $this->setMessage($message);
67
    }
68
69
70
    /**
71
     * Collect the value of the any-property
72
     *
73
     * @return \SimpleSAML\SAML2\XML\samlp\AbstractMessage|null
74
     */
75
    public function getMessage(): ?AbstractMessage
76
    {
77
        return $this->message;
78
    }
79
80
81
    /**
82
     * Set the value of the any-property
83
     *
84
     * @param \SimpleSAML\SAML2\XML\samlp\AbstractMessage|null $message
85
     */
86
    private function setMessage(?AbstractMessage $message): void
87
    {
88
        $this->message = $message;
89
    }
90
91
92
    /**
93
     * Convert XML into an ArtifactResponse
94
     *
95
     * @param \DOMElement $xml
96
     * @return self
97
     *
98
     * @throws \SimpleSAML\XML\Exception\InvalidDOMElementException if the qualified name of the supplied element is wrong
99
     * @throws \SimpleSAML\XML\Exception\MissingAttributeException if the supplied element is missing one of the mandatory attributes
100
     */
101
    public static function fromXML(DOMElement $xml): object
102
    {
103
        Assert::same($xml->localName, 'ArtifactResponse', InvalidDOMElementException::class);
104
        Assert::same($xml->namespaceURI, ArtifactResponse::NS, InvalidDOMElementException::class);
105
        Assert::same('2.0', self::getAttribute($xml, 'Version'));
106
107
        $id = self::getAttribute($xml, 'ID');
108
        $inResponseTo = self::getAttribute($xml, 'InResponseTo', null);
109
        $destination = self::getAttribute($xml, 'Destination', null);
110
        $consent = self::getAttribute($xml, 'Consent', null);
111
112
        $issueInstant = self::getAttribute($xml, 'IssueInstant');
113
        Assert::validDateTimeZulu($issueInstant, ProtocolViolationException::class);
114
        $issueInstant = XMLUtils::xsDateTimeToTimestamp($issueInstant);
115
116
        $issuer = Issuer::getChildrenOfClass($xml);
117
        Assert::countBetween($issuer, 0, 1);
118
119
        // find message; it should come last, after the Status-element
120
        $status = XPath::xpQuery($xml, './saml_protocol:Status', XPath::getXPath($xml));
121
        $status = $status[0];
122
        $message = null;
123
124
        /** @psalm-suppress RedundantCondition */
125
        for ($child = $status->nextSibling; $child !== null; $child = $child->nextSibling) {
126
            if ($child instanceof DOMElement) {
127
                $message = MessageFactory::fromXML($child);
128
                break;
129
            }
130
            /* Ignore comments and text nodes. */
131
        }
132
133
        $status = Status::getChildrenOfClass($xml);
134
        Assert::count($status, 1);
135
136
        $extensions = Extensions::getChildrenOfClass($xml);
137
        Assert::maxCount($extensions, 1, 'Only one saml:Extensions element is allowed.', TooManyElementsException::class);
138
139
        $signature = Signature::getChildrenOfClass($xml);
140
        Assert::maxCount($signature, 1, 'Only one ds:Signature element is allowed.', TooManyElementsException::class);
141
142
        $response = new self(
143
            array_pop($status),
144
            empty($issuer) ? null : array_pop($issuer),
145
            $id,
146
            $issueInstant,
147
            $inResponseTo,
148
            $destination,
149
            $consent,
150
            empty($extensions) ? null : array_pop($extensions),
151
            $message
152
        );
153
154
        if (!empty($signature)) {
155
            $response->setSignature($signature[0]);
156
        }
157
158
        $response->setXML($xml);
159
        return $response;
160
    }
161
162
163
    /**
164
     * Convert this message to an unsigned XML document.
165
     * This method does not sign the resulting XML document.
166
     *
167
     * @return \DOMElement The root element of the DOM tree
168
     */
169
    protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
170
    {
171
        $e = parent::toUnsignedXML($parent);
172
173
        if ($this->message !== null) {
174
            $this->message->toXML($e);
175
        }
176
177
        return $e;
178
    }
179
}
180