Cas20   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 20
eloc 56
c 1
b 0
f 0
dl 0
loc 193
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getProxyGrantingTicketIOU() 0 3 1
A setProxyGrantingTicketIOU() 0 3 1
A setAttributes() 0 3 1
A getAttributes() 0 3 1
A __construct() 0 5 1
A getProxySuccessResponse() 0 7 1
A isValidXmlName() 0 7 1
B getValidateSuccessResponse() 0 42 9
A getProxyFailureResponse() 0 6 1
A getValidateFailureResponse() 0 6 1
A generateCas20Attribute() 0 10 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
declare(strict_types=1);
25
26
namespace SimpleSAML\Module\casserver\Cas\Protocol;
27
28
use DateTimeImmutable;
29
use SimpleSAML\CAS\XML\cas\Attributes;
30
use SimpleSAML\CAS\XML\cas\AuthenticationDate;
31
use SimpleSAML\CAS\XML\cas\AuthenticationFailure;
32
use SimpleSAML\CAS\XML\cas\AuthenticationSuccess;
33
use SimpleSAML\CAS\XML\cas\IsFromNewLogin;
34
use SimpleSAML\CAS\XML\cas\LongTermAuthenticationRequestTokenUsed;
35
use SimpleSAML\CAS\XML\cas\ProxyFailure;
36
use SimpleSAML\CAS\XML\cas\ProxyGrantingTicket;
37
use SimpleSAML\CAS\XML\cas\ProxySuccess;
38
use SimpleSAML\CAS\XML\cas\ProxyTicket;
39
use SimpleSAML\CAS\XML\cas\ServiceResponse;
40
use SimpleSAML\CAS\XML\cas\User;
41
use SimpleSAML\Configuration;
42
use SimpleSAML\Logger;
43
use SimpleSAML\XML\Chunk;
44
use SimpleSAML\XML\DOMDocumentFactory;
45
46
use function base64_encode;
47
use function count;
48
use function filter_var;
49
use function is_null;
50
use function is_string;
51
use function str_replace;
52
53
class Cas20
54
{
55
    /** @var bool $sendAttributes */
56
    private bool $sendAttributes;
57
58
    /** @var bool $base64EncodeAttributes */
59
    private bool $base64EncodeAttributes;
60
61
    /** @var string|null $base64IndicatorAttribute */
62
    private ?string $base64IndicatorAttribute;
63
64
    /** @var array $attributes */
65
    private array $attributes = [];
66
67
    /** @var string|null $proxyGrantingTicketIOU */
68
    private ?string $proxyGrantingTicketIOU = null;
69
70
71
    /**
72
     * @param \SimpleSAML\Configuration $config
73
     */
74
    public function __construct(Configuration $config)
75
    {
76
        $this->sendAttributes = $config->getOptionalValue('attributes', false);
77
        $this->base64EncodeAttributes = $config->getOptionalValue('base64attributes', false);
78
        $this->base64IndicatorAttribute = $config->getOptionalValue('base64_attributes_indicator_attribute', null);
79
    }
80
81
82
    /**
83
     * @param array $attributes
84
     */
85
    public function setAttributes(array $attributes): void
86
    {
87
        $this->attributes = $attributes;
88
    }
89
90
91
    /**
92
     * @return array
93
     */
94
    public function getAttributes(): array
95
    {
96
        return $this->attributes;
97
    }
98
99
100
    /**
101
     * @param string $proxyGrantingTicketIOU
102
     */
103
    public function setProxyGrantingTicketIOU(string $proxyGrantingTicketIOU): void
104
    {
105
        $this->proxyGrantingTicketIOU = $proxyGrantingTicketIOU;
106
    }
107
108
109
    /**
110
     * @return string|null
111
     */
112
    public function getProxyGrantingTicketIOU(): ?string
113
    {
114
        return $this->proxyGrantingTicketIOU;
115
    }
116
117
118
    /**
119
     * @param string $username
120
     * @return \SimpleSAML\CAS\XML\cas\ServiceResponse
121
     */
122
    public function getValidateSuccessResponse(string $username): ServiceResponse
123
    {
124
        $user = new User($username);
125
126
        $proxyGrantingTicket = null;
127
        if (is_string($this->proxyGrantingTicketIOU)) {
128
            $proxyGrantingTicket = new ProxyGrantingTicket($this->proxyGrantingTicketIOU);
129
        }
130
131
        $attr = [];
132
        if ($this->sendAttributes && count($this->attributes) > 0) {
133
            foreach ($this->attributes as $name => $values) {
134
                // Fix the most common cause of invalid XML elements
135
                $_name = str_replace(':', '_', $name);
136
                if ($this->isValidXmlName($_name) === true) {
137
                    foreach ($values as $value) {
138
                        $attr[] = $this->generateCas20Attribute($_name, $value);
139
                    }
140
                } else {
141
                    Logger::warning("DOMException creating attribute '$_name'. Continuing without attribute'");
142
                }
143
            }
144
145
            if (!is_null($this->base64IndicatorAttribute)) {
146
                $attr[] = $this->generateCas20Attribute(
147
                    $this->base64IndicatorAttribute,
148
                    $this->base64EncodeAttributes ? "true" : "false",
149
                );
150
            }
151
        }
152
153
        $attributes = new Attributes(
154
            new AuthenticationDate(new DateTimeImmutable('now')),
155
            new LongTermAuthenticationRequestTokenUsed('true'),
156
            new IsFromNewLogin('true'),
157
            $attr,
158
        );
159
160
        $authenticationSuccess = new AuthenticationSuccess($user, $attributes, $proxyGrantingTicket);
161
        $serviceResponse = new ServiceResponse($authenticationSuccess);
162
163
        return $serviceResponse;
164
    }
165
166
167
    /**
168
     * @param string $errorCode
169
     * @param string $explanation
170
     * @return \SimpleSAML\CAS\XML\cas\ServiceResponse
171
     */
172
    public function getValidateFailureResponse(string $errorCode, string $explanation): ServiceResponse
173
    {
174
        $authenticationFailure = new AuthenticationFailure($explanation, $errorCode);
175
        $serviceResponse = new ServiceResponse($authenticationFailure);
176
177
        return $serviceResponse;
178
    }
179
180
181
    /**
182
     * @param string $proxyTicketId
183
     * @return \SimpleSAML\CAS\XML\cas\ServiceResponse
184
     */
185
    public function getProxySuccessResponse(string $proxyTicketId): ServiceResponse
186
    {
187
        $proxyTicket = new ProxyTicket($proxyTicketId);
188
        $proxySuccess = new ProxySuccess($proxyTicket);
189
        $serviceResponse = new ServiceResponse($proxySuccess);
190
191
        return $serviceResponse;
192
    }
193
194
195
    /**
196
     * @param string $errorCode
197
     * @param string $explanation
198
     * @return \SimpleSAML\CAS\XML\cas\ServiceResponse
199
     */
200
    public function getProxyFailureResponse(string $errorCode, string $explanation): ServiceResponse
201
    {
202
        $proxyFailure = new ProxyFailure($explanation, $errorCode);
203
        $serviceResponse = new ServiceResponse($proxyFailure);
204
205
        return $serviceResponse;
206
    }
207
208
209
    /**
210
     * @param string $attributeName
211
     * @param string $attributeValue
212
     * @return \SimpleSAML\XML\Chunk
213
     */
214
    private function generateCas20Attribute(
215
        string $attributeName,
216
        string $attributeValue,
217
    ): Chunk {
218
        $xmlDocument = DOMDocumentFactory::create();
219
220
        $attributeValue = $this->base64EncodeAttributes ? base64_encode($attributeValue) : $attributeValue;
221
        $attributeElement = $xmlDocument->createElementNS(Attributes::NS, 'cas:' . $attributeName, $attributeValue);
222
223
        return new Chunk($attributeElement);
224
    }
225
226
227
    /**
228
     * XML element names have a lot of rules and not every SAML attribute name can be converted.
229
     * Ref: https://www.w3.org/TR/REC-xml/#NT-NameChar
230
     * https://stackoverflow.com/q/2519845/54396
231
     * must only start with letter or underscore
232
     * cannot start with 'xml' (or maybe it can - stackoverflow commenters don't agree)
233
     * cannot contain a ':' since those are for namespaces
234
     * cannot contains space
235
     * can only  contain letters, digits, hyphens, underscores, and periods
236
     * @param string $name The attribute name to be used as an element
237
     * @return bool true if $name would make a valid xml element.
238
     */
239
    private function isValidXmlName(string $name): bool
240
    {
241
        return filter_var(
242
            $name,
243
            FILTER_VALIDATE_REGEXP,
244
            ['options' => ['regexp' => '/^[a-zA-Z_][\w.-]*$/']],
245
        ) !== false;
246
    }
247
}
248