Passed
Pull Request — master (#14)
by
unknown
01:56
created

Cas20::setProxyGrantingTicketIOU()   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 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 *    simpleSAMLphp-casserver is a CAS 1.0 and 2.0 compliant CAS server in the form of a simpleSAMLphp module
5
 *
6
 *    Copyright (C) 2013  Bjorn R. Jensen
7
 *
8
 *    This library is free software; you can redistribute it and/or
9
 *    modify it under the terms of the GNU Lesser General Public
10
 *    License as published by the Free Software Foundation; either
11
 *    version 2.1 of the License, or (at your option) any later version.
12
 *
13
 *    This library is distributed in the hope that it will be useful,
14
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 *    Lesser General Public License for more details.
17
 *
18
 *    You should have received a copy of the GNU Lesser General Public
19
 *    License along with this library; if not, write to the Free Software
20
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
 *
22
 */
23
24
namespace SimpleSAML\Module\casserver\Cas\Protocol;
25
26
use SimpleSAML\Configuration;
27
use SimpleSAML\Logger;
28
29
class Cas20
30
{
31
    /** @var bool $sendAttributes */
32
    private $sendAttributes;
33
34
    /** @var bool $base64EncodeAttributes */
35
    private $base64EncodeAttributes;
36
37
    /** @var string|null $base64IndicatorAttribute */
38
    private $base64IndicatorAttribute;
39
40
    /** @var array $attributes */
41
    private $attributes = [];
42
43
    /** @var string|null $proxyGrantingTicketIOU */
44
    private $proxyGrantingTicketIOU = null;
45
46
47
    /**
48
     * @param \SimpleSAML\Configuration $config
49
     */
50
    public function __construct(Configuration $config)
51
    {
52
        $this->sendAttributes = $config->getValue('attributes', false);
53
        $this->base64EncodeAttributes = $config->getValue('base64attributes', false);
54
        $this->base64IndicatorAttribute = $config->getValue('base64_attributes_indicator_attribute', null);
55
    }
56
57
58
    /**
59
     * @param array $attributes
60
     * @return void
61
     */
62
    public function setAttributes($attributes)
63
    {
64
        $this->attributes = $attributes;
65
    }
66
67
68
    /**
69
     * @return array
70
     */
71
    public function getAttributes()
72
    {
73
        return $this->attributes;
74
    }
75
76
77
    /**
78
     * @param string $proxyGrantingTicketIOU
79
     * @return void
80
     */
81
    public function setProxyGrantingTicketIOU($proxyGrantingTicketIOU)
82
    {
83
        $this->proxyGrantingTicketIOU = $proxyGrantingTicketIOU;
84
    }
85
86
87
    /**
88
     * @return string|null
89
     */
90
    public function getProxyGrantingTicketIOU()
91
    {
92
        return $this->proxyGrantingTicketIOU;
93
    }
94
95
96
    /**
97
     * @param string $username
98
     * @return string
99
     */
100
    public function getValidateSuccessResponse($username)
101
    {
102
        $xmlDocument = new \DOMDocument("1.0");
103
104
        $root = $xmlDocument->createElement("cas:serviceResponse");
105
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cas', 'http://www.yale.edu/tp/cas');
106
107
        $usernameNode = $xmlDocument->createTextNode($username);
108
        $casUser = $xmlDocument->createElement('cas:user');
109
        $casUser->appendChild($usernameNode);
110
111
        $casSuccess = $xmlDocument->createElement('cas:authenticationSuccess');
112
        $casSuccess->appendChild($casUser);
113
114
        if (is_string($this->proxyGrantingTicketIOU)) {
115
            $iouNode = $xmlDocument->createTextNode($this->proxyGrantingTicketIOU);
116
            $iouElement = $xmlDocument->createElement("cas:proxyGrantingTicket");
117
            $iouElement->appendChild($iouNode);
118
            $casSuccess->appendChild($iouElement);
119
        }
120
121
        if ($this->sendAttributes && count($this->attributes) > 0) {
122
            $casAttributes = $xmlDocument->createElement('cas:attributes');
123
124
            foreach ($this->attributes as $name => $values) {
125
                // Fix the most common cause of invalid XML elements
126
                $_name = str_replace(':', '_', $name);
127
                if ($this->isValidXmlName($_name) === true) {
128
                    foreach ($values as $value) {
129
                        $casAttributes->appendChild(
130
                            $this->generateCas20Attribute($xmlDocument, $_name, $value)
131
                        );
132
                    }
133
                } else {
134
                    Logger::warning("Dom exception creating attribute '$_name'. Continuing without atrribute'");
135
                }
136
            }
137
138
            if (!is_null($this->base64IndicatorAttribute)) {
139
                $casAttributes->appendChild(
140
                    $this->generateCas20Attribute(
141
                        $xmlDocument,
142
                        $this->base64IndicatorAttribute,
143
                        $this->base64EncodeAttributes ? "true" : "false"
144
                    )
145
                );
146
            }
147
148
            $casSuccess->appendChild($casAttributes);
149
        }
150
151
        $root->appendChild($casSuccess);
152
        $xmlDocument->appendChild($root);
153
154
        return $this->workAroundForBuggyJasigXmlParser($xmlDocument->saveXML());
155
    }
156
157
158
    /**
159
     * @param string $errorCode
160
     * @param string $explanation
161
     * @return string
162
     */
163
    public function getValidateFailureResponse($errorCode, $explanation)
164
    {
165
        $xmlDocument = new \DOMDocument("1.0");
166
167
        $root = $xmlDocument->createElement("cas:serviceResponse");
168
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cas', 'http://www.yale.edu/tp/cas');
169
170
        $casFailureCode = $xmlDocument->createAttribute('code');
171
        $casFailureCode->value = $errorCode;
172
173
        $casFailureNode = $xmlDocument->createTextNode($explanation);
174
        $casFailure = $xmlDocument->createElement('cas:authenticationFailure');
175
        $casFailure->appendChild($casFailureNode);
176
        $casFailure->appendChild($casFailureCode);
177
178
        $root->appendChild($casFailure);
179
180
        $xmlDocument->appendChild($root);
181
182
        return $this->workAroundForBuggyJasigXmlParser($xmlDocument->saveXML());
183
    }
184
185
186
    /**
187
     * @param string $proxyTicketId
188
     * @return string
189
     */
190
    public function getProxySuccessResponse($proxyTicketId)
191
    {
192
        $xmlDocument = new \DOMDocument("1.0");
193
194
        $root = $xmlDocument->createElement("cas:serviceResponse");
195
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cas', 'http://www.yale.edu/tp/cas');
196
197
        $casProxyTicketIdNode = $xmlDocument->createTextNode($proxyTicketId);
198
        $casProxyTicketId = $xmlDocument->createElement('cas:proxyTicket');
199
        $casProxyTicketId->appendChild($casProxyTicketIdNode);
200
201
        $casProxySuccess = $xmlDocument->createElement('cas:proxySuccess');
202
        $casProxySuccess->appendChild($casProxyTicketId);
203
204
        $root->appendChild($casProxySuccess);
205
        $xmlDocument->appendChild($root);
206
207
        return $this->workAroundForBuggyJasigXmlParser($xmlDocument->saveXML());
208
    }
209
210
211
    /**
212
     * @param string $errorCode
213
     * @param string $explanation
214
     * @return string
215
     */
216
    public function getProxyFailureResponse($errorCode, $explanation)
217
    {
218
        $xmlDocument = new \DOMDocument("1.0");
219
220
        $root = $xmlDocument->createElement("cas:serviceResponse");
221
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cas', 'http://www.yale.edu/tp/cas');
222
223
        $casFailureCode = $xmlDocument->createAttribute('code');
224
        $casFailureCode->value = $errorCode;
225
226
        $casFailureNode = $xmlDocument->createTextNode($explanation);
227
        $casFailure = $xmlDocument->createElement('cas:proxyFailure');
228
        $casFailure->appendChild($casFailureNode);
229
        $casFailure->appendChild($casFailureCode);
230
231
        $root->appendChild($casFailure);
232
233
        $xmlDocument->appendChild($root);
234
235
        return $this->workAroundForBuggyJasigXmlParser($xmlDocument->saveXML());
236
    }
237
238
239
    /**
240
     * @param string $xmlString
241
     * @return string
242
     */
243
    private function workAroundForBuggyJasigXmlParser($xmlString)
244
    {
245
        // when will people stop hand coding xml handling....?
246
        return str_replace('><', '>'.PHP_EOL.'<', str_replace(PHP_EOL, '', $xmlString));
247
    }
248
249
250
    /**
251
     * @param \DOMDocument $xmlDocument
252
     * @param string $attributeName
253
     * @param string $attributeValue
254
     * @return \DOMElement
255
     */
256
    private function generateCas20Attribute($xmlDocument, $attributeName, $attributeValue)
257
    {
258
        $attributeValueNode = $xmlDocument->createTextNode($this->base64EncodeAttributes ?
259
            base64_encode($attributeValue) : $attributeValue);
260
261
        $attributeElement = $xmlDocument->createElement('cas:'.$attributeName);
262
263
        $attributeElement->appendChild($attributeValueNode);
264
265
        return $attributeElement;
266
    }
267
268
269
    /**
270
     * XML element names have a lot of rules and not every SAML attribute name can be converted.
271
     * Ref: https://www.w3.org/TR/REC-xml/#NT-NameChar
272
     * https://stackoverflow.com/q/2519845/54396
273
     * must only start with letter or underscore
274
     * cannot start with 'xml' (or maybe it can - stackoverflow commenters don't agree)
275
     * cannot contain a ':' since those are for namespaces
276
     * cannot contains space
277
     * can only  contain letters, digits, hyphens, underscores, and periods
278
     * @param string $name The attribute name to be used as an element
279
     * @return bool true if $name would make a valid xml element.
280
     */
281
    private function isValidXmlName($name)
282
    {
283
        try {
284
            new \DOMElement($name);
1 ignored issue
show
Bug introduced by
The call to DOMElement::__construct() has too few arguments starting with value. ( Ignorable by Annotation )

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

284
            /** @scrutinizer ignore-call */ 
285
            new \DOMElement($name);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
285
            return true;
286
        } catch (\DOMException $e) {
287
                return false;
288
        }
289
    }
290
}
291