Passed
Pull Request — master (#14)
by Tim
01:34
created

Cas20   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 92
dl 0
loc 262
rs 10
c 0
b 0
f 0
wmc 22

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getProxyGrantingTicketIOU() 0 3 1
B getValidateSuccessResponse() 0 66 9
A generateCas20Attribute() 0 10 2
A workAroundForBuggyJasigXmlParser() 0 4 1
A setProxyGrantingTicketIOU() 0 3 1
A setAttributes() 0 3 1
A getAttributes() 0 3 1
A getProxyFailureResponse() 0 20 1
A getValidateFailureResponse() 0 20 1
A __construct() 0 5 1
A getProxySuccessResponse() 0 18 1
A isValidXmlName() 0 7 2
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 DOMException;
27
use SimpleSAML\Configuration;
28
use SimpleSAML\Logger;
29
30
class Cas20
31
{
32
    /** @var bool $sendAttributes */
33
    private $sendAttributes;
34
35
    /** @var bool $base64EncodeAttributes */
36
    private $base64EncodeAttributes;
37
38
    /** @var string|null $base64IndicatorAttribute */
39
    private $base64IndicatorAttribute;
40
41
    /** @var array $attributes */
42
    private $attributes = [];
43
44
    /** @var string|null $proxyGrantingTicketIOU */
45
    private $proxyGrantingTicketIOU = null;
46
47
48
    /**
49
     * @param \SimpleSAML\Configuration $config
50
     */
51
    public function __construct(Configuration $config)
52
    {
53
        $this->sendAttributes = $config->getValue('attributes', false);
54
        $this->base64EncodeAttributes = $config->getValue('base64attributes', false);
55
        $this->base64IndicatorAttribute = $config->getValue('base64_attributes_indicator_attribute', null);
56
    }
57
58
59
    /**
60
     * @param array $attributes
61
     * @return void
62
     */
63
    public function setAttributes($attributes)
64
    {
65
        $this->attributes = $attributes;
66
    }
67
68
69
    /**
70
     * @return array
71
     */
72
    public function getAttributes()
73
    {
74
        return $this->attributes;
75
    }
76
77
78
    /**
79
     * @param string $proxyGrantingTicketIOU
80
     * @return void
81
     */
82
    public function setProxyGrantingTicketIOU($proxyGrantingTicketIOU)
83
    {
84
        $this->proxyGrantingTicketIOU = $proxyGrantingTicketIOU;
85
    }
86
87
88
    /**
89
     * @return string|null
90
     */
91
    public function getProxyGrantingTicketIOU()
92
    {
93
        return $this->proxyGrantingTicketIOU;
94
    }
95
96
97
    /**
98
     * @param string $username
99
     * @return string
100
     */
101
    public function getValidateSuccessResponse($username)
102
    {
103
        $xmlDocument = new \DOMDocument("1.0");
104
105
        $root = $xmlDocument->createElement("cas:serviceResponse");
106
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cas', 'http://www.yale.edu/tp/cas');
107
108
        $usernameNode = $xmlDocument->createTextNode($username);
109
        $casUser = $xmlDocument->createElement('cas:user');
110
        $casUser->appendChild($usernameNode);
111
112
        $casSuccess = $xmlDocument->createElement('cas:authenticationSuccess');
113
        $casSuccess->appendChild($casUser);
114
115
        if (is_string($this->proxyGrantingTicketIOU)) {
116
            $iouNode = $xmlDocument->createTextNode($this->proxyGrantingTicketIOU);
117
            $iouElement = $xmlDocument->createElement("cas:proxyGrantingTicket");
118
            $iouElement->appendChild($iouNode);
119
            $casSuccess->appendChild($iouElement);
120
        }
121
122
        if ($this->sendAttributes && count($this->attributes) > 0) {
123
            $casAttributes = $xmlDocument->createElement('cas:attributes');
124
125
            foreach ($this->attributes as $name => $values) {
126
                /**
127
                 * XML element names have a lot of rules. We handle the most probable case, an attribute name that
128
                 * contains ':' (e.g. the common oid attribute name), however for the rest we just log the name
129
                 * and continue processing.
130
                 * Ref: https://www.w3.org/TR/REC-xml/#NT-NameChar
131
                 * https://stackoverflow.com/q/2519845/54396
132
                 * must only start with letter or underscore
133
                 * cannot start with 'xml'
134
                 * cannot contain a ':' since those are for namespaces
135
                 * cannot contains space
136
                 * can only  contain letters, digits, hyphens, underscores, and periods
137
                 */
138
                $_name = str_replace(':', '_', $name);
139
                if ($this->isValidXmlName() === true) {
0 ignored issues
show
Bug introduced by
The call to SimpleSAML\Module\casser...Cas20::isValidXmlName() has too few arguments starting with name. ( Ignorable by Annotation )

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

139
                if ($this->/** @scrutinizer ignore-call */ isValidXmlName() === true) {

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...
140
                    foreach ($values as $value) {
141
                        $casAttributes->appendChild(
142
                            $this->generateCas20Attribute($xmlDocument, $_name, $value)
143
                        );
144
                    }
145
                } else {
146
                    Logger::warning("Dom exception creating attribute '$_name'. Continuing without atrribute'");
147
                }
148
            }
149
150
            if (!is_null($this->base64IndicatorAttribute)) {
151
                $casAttributes->appendChild(
152
                    $this->generateCas20Attribute(
153
                        $xmlDocument,
154
                        $this->base64IndicatorAttribute,
155
                        $this->base64EncodeAttributes ? "true" : "false"
156
                    )
157
                );
158
            }
159
160
            $casSuccess->appendChild($casAttributes);
161
        }
162
163
        $root->appendChild($casSuccess);
164
        $xmlDocument->appendChild($root);
165
166
        return $this->workAroundForBuggyJasigXmlParser($xmlDocument->saveXML());
167
    }
168
169
170
    /**
171
     * @param string $errorCode
172
     * @param string $explanation
173
     * @return string
174
     */
175
    public function getValidateFailureResponse($errorCode, $explanation)
176
    {
177
        $xmlDocument = new \DOMDocument("1.0");
178
179
        $root = $xmlDocument->createElement("cas:serviceResponse");
180
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cas', 'http://www.yale.edu/tp/cas');
181
182
        $casFailureCode = $xmlDocument->createAttribute('code');
183
        $casFailureCode->value = $errorCode;
184
185
        $casFailureNode = $xmlDocument->createTextNode($explanation);
186
        $casFailure = $xmlDocument->createElement('cas:authenticationFailure');
187
        $casFailure->appendChild($casFailureNode);
188
        $casFailure->appendChild($casFailureCode);
189
190
        $root->appendChild($casFailure);
191
192
        $xmlDocument->appendChild($root);
193
194
        return $this->workAroundForBuggyJasigXmlParser($xmlDocument->saveXML());
195
    }
196
197
198
    /**
199
     * @param string $proxyTicketId
200
     * @return string
201
     */
202
    public function getProxySuccessResponse($proxyTicketId)
203
    {
204
        $xmlDocument = new \DOMDocument("1.0");
205
206
        $root = $xmlDocument->createElement("cas:serviceResponse");
207
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cas', 'http://www.yale.edu/tp/cas');
208
209
        $casProxyTicketIdNode = $xmlDocument->createTextNode($proxyTicketId);
210
        $casProxyTicketId = $xmlDocument->createElement('cas:proxyTicket');
211
        $casProxyTicketId->appendChild($casProxyTicketIdNode);
212
213
        $casProxySuccess = $xmlDocument->createElement('cas:proxySuccess');
214
        $casProxySuccess->appendChild($casProxyTicketId);
215
216
        $root->appendChild($casProxySuccess);
217
        $xmlDocument->appendChild($root);
218
219
        return $this->workAroundForBuggyJasigXmlParser($xmlDocument->saveXML());
220
    }
221
222
223
    /**
224
     * @param string $errorCode
225
     * @param string $explanation
226
     * @return string
227
     */
228
    public function getProxyFailureResponse($errorCode, $explanation)
229
    {
230
        $xmlDocument = new \DOMDocument("1.0");
231
232
        $root = $xmlDocument->createElement("cas:serviceResponse");
233
        $root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:cas', 'http://www.yale.edu/tp/cas');
234
235
        $casFailureCode = $xmlDocument->createAttribute('code');
236
        $casFailureCode->value = $errorCode;
237
238
        $casFailureNode = $xmlDocument->createTextNode($explanation);
239
        $casFailure = $xmlDocument->createElement('cas:proxyFailure');
240
        $casFailure->appendChild($casFailureNode);
241
        $casFailure->appendChild($casFailureCode);
242
243
        $root->appendChild($casFailure);
244
245
        $xmlDocument->appendChild($root);
246
247
        return $this->workAroundForBuggyJasigXmlParser($xmlDocument->saveXML());
248
    }
249
250
251
    /**
252
     * @param string $xmlString
253
     * @return string
254
     */
255
    private function workAroundForBuggyJasigXmlParser($xmlString)
256
    {
257
        // when will people stop hand coding xml handling....?
258
        return str_replace('><', '>'.PHP_EOL.'<', str_replace(PHP_EOL, '', $xmlString));
259
    }
260
261
262
    /**
263
     * @param \DOMDocument $xmlDocument
264
     * @param string $attributeName
265
     * @param string $attributeValue
266
     * @return \DOMElement
267
     */
268
    private function generateCas20Attribute($xmlDocument, $attributeName, $attributeValue)
269
    {
270
        $attributeValueNode = $xmlDocument->createTextNode($this->base64EncodeAttributes ?
271
            base64_encode($attributeValue) : $attributeValue);
272
273
        $attributeElement = $xmlDocument->createElement('cas:'.$attributeName);
274
275
        $attributeElement->appendChild($attributeValueNode);
276
277
        return $attributeElement;
278
    }
279
280
281
    /**
282
     * @param string $name
283
     * @return bool
284
     */
285
    private function isValidXmlName($name)
286
    {
287
        try {
288
            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

288
            /** @scrutinizer ignore-call */ 
289
            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...
289
            return true;
290
        } catch(\DOMException $e) {
291
            return false;
292
        }
293
    }
294
}
295