Passed
Pull Request — master (#374)
by Tim
02:36
created

AbstractMessage::getOriginalXML()   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 SimpleSAML\SAML2\XML\samlp;
6
7
use DOMElement;
8
use SimpleSAML\SAML2\Constants as C;
9
use SimpleSAML\SAML2\Type\SAMLAnyURIValue;
10
use SimpleSAML\SAML2\Type\SAMLDateTimeValue;
11
use SimpleSAML\SAML2\Utils\XPath;
12
use SimpleSAML\SAML2\XML\ExtendableElementTrait;
13
use SimpleSAML\SAML2\XML\saml\Issuer;
14
use SimpleSAML\SAML2\XML\SignableElementTrait;
15
use SimpleSAML\SAML2\XML\SignedElementTrait;
16
use SimpleSAML\XMLSchema\Type\IDValue;
17
use SimpleSAML\XMLSecurity\XML\SignableElementInterface;
18
use SimpleSAML\XMLSecurity\XML\SignedElementInterface;
19
20
use function array_pop;
21
22
/**
23
 * Base class for all SAML 2 messages.
24
 *
25
 * Implements what is common between the samlp:RequestAbstractType and
26
 * samlp:StatusResponseType element types.
27
 *
28
 * @package simplesamlphp/saml2
29
 */
30
abstract class AbstractMessage extends AbstractSamlpElement implements SignableElementInterface, SignedElementInterface
31
{
32
    use ExtendableElementTrait;
33
    use SignableElementTrait;
0 ignored issues
show
introduced by
The trait SimpleSAML\SAML2\XML\SignableElementTrait requires some properties which are not provided by SimpleSAML\SAML2\XML\samlp\AbstractMessage: $ownerDocument, $documentElement
Loading history...
34
    use SignedElementTrait {
0 ignored issues
show
introduced by
The trait SimpleSAML\SAML2\XML\SignedElementTrait requires some properties which are not provided by SimpleSAML\SAML2\XML\samlp\AbstractMessage: $ownerDocument, $documentElement
Loading history...
35
        SignedElementTrait::getBlacklistedAlgorithms insteadof SignableElementTrait;
36
    }
37
38
39
    /** @var bool */
40
    protected bool $messageContainedSignatureUponConstruction = false;
41
42
    /**
43
     * The original signed XML
44
     *
45
     * @var \DOMElement
46
     * @psalm-suppress PropertyNotSetInConstructor
47
     */
48
    protected DOMElement $xml;
49
50
51
    /**
52
     * Initialize a message.
53
     *
54
     * @param \SimpleSAML\SAML2\XML\saml\Issuer|null $issuer
55
     * @param \SimpleSAML\XMLSchema\Type\IDValue $id
56
     * @param \SimpleSAML\SAML2\Type\SAMLDateTimeValue|null $issueInstant
57
     * @param \SimpleSAML\SAML2\Type\SAMLAnyURIValue|null $destination
58
     * @param \SimpleSAML\SAML2\Type\SAMLAnyURIValue|null $consent
59
     * @param \SimpleSAML\SAML2\XML\samlp\Extensions $extensions
60
     *
61
     * @throws \Exception
62
     */
63
    protected function __construct(
64
        protected IDValue $id,
65
        protected ?Issuer $issuer = null,
66
        protected ?SAMLDateTimeValue $issueInstant = null,
67
        protected ?SAMLAnyURIValue $destination = null,
68
        protected ?SAMLAnyURIValue $consent = null,
69
        ?Extensions $extensions = null,
70
    ) {
71
        $this->setExtensions($extensions);
72
    }
73
74
75
    /**
76
     * Retrieve the identifier of this message.
77
     *
78
     * @return \SimpleSAML\XMLSchema\Type\IDValue The identifier of this message
79
     */
80
    public function getId(): IDValue
81
    {
82
        return $this->id;
83
    }
84
85
86
    /**
87
     * Retrieve the issue timestamp of this message.
88
     *
89
     * @return \SimpleSAML\SAML2\Type\SAMLDateTimeValue The issue timestamp of this message, as an UNIX timestamp
90
     */
91
    public function getIssueInstant(): SAMLDateTimeValue
92
    {
93
        return $this->issueInstant;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->issueInstant could return the type null which is incompatible with the type-hinted return SimpleSAML\SAML2\Type\SAMLDateTimeValue. Consider adding an additional type-check to rule them out.
Loading history...
94
    }
95
96
97
    /**
98
     * Retrieve the destination of this message.
99
     *
100
     * @return \SimpleSAML\SAML2\Type\SAMLAnyURIValue|null The destination of this message,
101
     *   or NULL if no destination is given
102
     */
103
    public function getDestination(): ?SAMLAnyURIValue
104
    {
105
        return $this->destination;
106
    }
107
108
109
    /**
110
     * Get the given consent for this message.
111
     * Most likely (though not required) a value of urn:oasis:names:tc:SAML:2.0:consent.
112
     *
113
     * @see \SimpleSAML\SAML2\Constants
114
     * @return \SimpleSAML\SAML2\Type\SAMLAnyURIValue|null Consent
115
     */
116
    public function getConsent(): ?SAMLAnyURIValue
117
    {
118
        return $this->consent;
119
    }
120
121
122
    /**
123
     * Retrieve the issuer if this message.
124
     *
125
     * @return \SimpleSAML\SAML2\XML\saml\Issuer|null The issuer of this message, or NULL if no issuer is given
126
     */
127
    public function getIssuer(): ?Issuer
128
    {
129
        return $this->issuer;
130
    }
131
132
133
    /**
134
     * Query whether or not the message contained a signature at the root level when the object was constructed.
135
     *
136
     * @return bool
137
     */
138
    public function isMessageConstructedWithSignature(): bool
139
    {
140
        return $this->messageContainedSignatureUponConstruction;
141
    }
142
143
144
    /**
145
     * Get the XML element.
146
     *
147
     * @return \DOMElement
148
     */
149
    public function getXML(): DOMElement
150
    {
151
        return $this->xml;
152
    }
153
154
155
    /**
156
     * Set the XML element.
157
     *
158
     * @param \DOMElement $xml
159
     */
160
    protected function setXML(DOMElement $xml): void
161
    {
162
        $this->xml = $xml;
163
    }
164
165
166
    /**
167
     * @return \DOMElement
168
     */
169
    protected function getOriginalXML(): DOMElement
170
    {
171
        return $this->xml ?? $this->toUnsignedXML();
172
    }
173
174
175
    /**
176
     * Convert this message to an unsigned XML document.
177
     * This method does not sign the resulting XML document.
178
     *
179
     * @return \DOMElement The root element of the DOM tree
180
     */
181
    protected function toUnsignedXML(?DOMElement $parent = null): DOMElement
182
    {
183
        $root = $this->instantiateParentElement($parent);
184
185
        /* Ugly hack to add another namespace declaration to the root element. */
186
//        $root->setAttributeNS(C::NS_SAML, 'saml:tmp', 'tmp');
187
//        $root->removeAttributeNS(C::NS_SAML, 'tmp');
188
189
        $root->setAttribute('Version', '2.0');
190
        $root->setAttribute('ID', $this->getId()->getValue());
191
        $root->setAttribute('IssueInstant', $this->getIssueInstant()->getValue());
192
193
        if ($this->getDestination() !== null) {
194
            $root->setAttribute('Destination', $this->getDestination()->getValue());
195
        }
196
197
        if ($this->getConsent() !== null && $this->getConsent()->getValue() !== C::CONSENT_UNSPECIFIED) {
198
            $root->setAttribute('Consent', $this->getConsent()->getValue());
199
        }
200
201
        $this->getIssuer()?->toXML($root);
202
203
        $extensions = $this->getExtensions();
204
        if ($extensions !== null && !$extensions->isEmptyElement()) {
205
            $extensions->toXML($root);
206
        }
207
208
        return $root;
209
    }
210
211
212
    /**
213
     * Create XML from this class
214
     *
215
     * @param \DOMElement|null $parent
216
     * @return \DOMElement
217
     */
218
    public function toXML(?DOMElement $parent = null): DOMElement
219
    {
220
        if ($this->isSigned() === true && $this->signer === null) {
221
            // We already have a signed document and no signer was set to re-sign it
222
            if ($parent === null) {
223
                return $this->xml;
224
            }
225
226
            $node = $parent->ownerDocument?->importNode($this->getXML(), true);
227
            $parent->appendChild($node);
228
            return $parent;
229
        }
230
231
        $e = $this->toUnsignedXML($parent);
232
233
        if ($this->signer !== null) {
234
            $signedXML = $this->doSign($e);
235
236
            // Test for an Issuer
237
            $messageElements = XPath::xpQuery($signedXML, './saml_assertion:Issuer', XPath::getXPath($signedXML));
238
            $issuer = array_pop($messageElements);
239
240
            $signedXML->insertBefore($this->signature?->toXML($signedXML), $issuer->nextSibling);
241
            return $signedXML;
242
        }
243
244
        return $e;
245
    }
246
}
247