Passed
Pull Request — master (#62)
by Tim
02:47
created

Cas20::isValidXmlName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
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 Beste\Clock\LocalizedClock;
29
use DateTimeZone;
30
use SimpleSAML\Assert\AssertionFailedException;
31
use SimpleSAML\CAS\Type\CodeValue;
32
use SimpleSAML\CAS\XML\Attributes;
33
use SimpleSAML\CAS\XML\AuthenticationDate;
34
use SimpleSAML\CAS\XML\AuthenticationFailure;
35
use SimpleSAML\CAS\XML\AuthenticationSuccess;
36
use SimpleSAML\CAS\XML\IsFromNewLogin;
37
use SimpleSAML\CAS\XML\LongTermAuthenticationRequestTokenUsed;
38
use SimpleSAML\CAS\XML\ProxyFailure;
39
use SimpleSAML\CAS\XML\ProxyGrantingTicket;
40
use SimpleSAML\CAS\XML\ProxySuccess;
41
use SimpleSAML\CAS\XML\ProxyTicket;
42
use SimpleSAML\CAS\XML\ServiceResponse;
43
use SimpleSAML\CAS\XML\User;
44
use SimpleSAML\Configuration;
45
use SimpleSAML\Logger;
46
use SimpleSAML\XML\Assert\Assert;
47
use SimpleSAML\XML\Chunk;
48
use SimpleSAML\XML\DOMDocumentFactory;
49
use SimpleSAML\XMLSchema\Type\BooleanValue;
50
use SimpleSAML\XMLSchema\Type\DateTimeValue;
51
use SimpleSAML\XMLSchema\Type\StringValue;
52
53
use function base64_encode;
54
use function count;
55
use function is_null;
56
use function is_string;
57
use function str_replace;
58
59
class Cas20
60
{
61
    /** @var bool $sendAttributes */
62
    private bool $sendAttributes;
63
64
    /** @var bool $base64EncodeAttributes */
65
    private bool $base64EncodeAttributes;
66
67
    /** @var string|null $base64IndicatorAttribute */
68
    private ?string $base64IndicatorAttribute;
69
70
    /** @var array $attributes */
71
    private array $attributes = [];
72
73
    /** @var string|null $proxyGrantingTicketIOU */
74
    private ?string $proxyGrantingTicketIOU = null;
75
76
77
    /**
78
     * @param \SimpleSAML\Configuration $config
79
     */
80
    public function __construct(Configuration $config)
81
    {
82
        $this->sendAttributes = $config->getOptionalValue('attributes', false);
83
        $this->base64EncodeAttributes = $config->getOptionalValue('base64attributes', false);
84
        $this->base64IndicatorAttribute = $config->getOptionalValue('base64_attributes_indicator_attribute', null);
85
    }
86
87
88
    /**
89
     * @param array $attributes
90
     */
91
    public function setAttributes(array $attributes): void
92
    {
93
        $this->attributes = $attributes;
94
    }
95
96
97
    /**
98
     * @return array
99
     */
100
    public function getAttributes(): array
101
    {
102
        return $this->attributes;
103
    }
104
105
106
    /**
107
     * @param string $proxyGrantingTicketIOU
108
     */
109
    public function setProxyGrantingTicketIOU(string $proxyGrantingTicketIOU): void
110
    {
111
        $this->proxyGrantingTicketIOU = $proxyGrantingTicketIOU;
112
    }
113
114
115
    /**
116
     * @return string|null
117
     */
118
    public function getProxyGrantingTicketIOU(): ?string
119
    {
120
        return $this->proxyGrantingTicketIOU;
121
    }
122
123
124
    /**
125
     * @param string $username
126
     * @return \SimpleSAML\CAS\XML\ServiceResponse
127
     */
128
    public function getValidateSuccessResponse(string $username): ServiceResponse
129
    {
130
        $user = new User(StringValue::fromString($username));
131
132
        $proxyGrantingTicket = null;
133
        if (is_string($this->proxyGrantingTicketIOU)) {
134
            $proxyGrantingTicket = new ProxyGrantingTicket(StringValue::fromString($this->proxyGrantingTicketIOU));
135
        }
136
137
        $attr = [];
138
        if ($this->sendAttributes && count($this->attributes) > 0) {
139
            foreach ($this->attributes as $name => $values) {
140
                // Fix the most common cause of invalid XML elements
141
                $_name = str_replace(':', '_', $name);
142
                try {
143
                    Assert::validNCName($_name);
144
                    foreach ($values as $value) {
145
                        $attr[] = $this->generateCas20Attribute($_name, $value);
146
                    }
147
                } catch (AssertionFailedException) {
148
                    Logger::warning("DOMException creating attribute '$_name'. Continuing without attribute'");
149
                }
150
            }
151
152
            if (!is_null($this->base64IndicatorAttribute)) {
153
                $attr[] = $this->generateCas20Attribute(
154
                    $this->base64IndicatorAttribute,
155
                    $this->base64EncodeAttributes ? "true" : "false",
156
                );
157
            }
158
        }
159
160
        $systemClock = LocalizedClock::in(new DateTimeZone('Z'));
161
        $attributes = new Attributes(
162
            new AuthenticationDate(DateTimeValue::now($systemClock)),
163
            new LongTermAuthenticationRequestTokenUsed(BooleanValue::fromBoolean(true)),
164
            new IsFromNewLogin(BooleanValue::fromBoolean(true)),
165
            $attr,
166
        );
167
168
        $authenticationSuccess = new AuthenticationSuccess($user, $attributes, $proxyGrantingTicket);
169
        $serviceResponse = new ServiceResponse($authenticationSuccess);
170
171
        return $serviceResponse;
172
    }
173
174
175
    /**
176
     * @param string $errorCode
177
     * @param string $explanation
178
     * @return \SimpleSAML\CAS\XML\ServiceResponse
179
     */
180
    public function getValidateFailureResponse(string $errorCode, string $explanation): ServiceResponse
181
    {
182
        $authenticationFailure = new AuthenticationFailure(
183
            StringValue::fromString($explanation),
184
            CodeValue::fromString($errorCode),
185
        );
186
        $serviceResponse = new ServiceResponse($authenticationFailure);
187
188
        return $serviceResponse;
189
    }
190
191
192
    /**
193
     * @param string $proxyTicketId
194
     * @return \SimpleSAML\CAS\XML\ServiceResponse
195
     */
196
    public function getProxySuccessResponse(string $proxyTicketId): ServiceResponse
197
    {
198
        $proxyTicket = new ProxyTicket(StringValue::fromString($proxyTicketId));
199
        $proxySuccess = new ProxySuccess($proxyTicket);
200
        $serviceResponse = new ServiceResponse($proxySuccess);
201
202
        return $serviceResponse;
203
    }
204
205
206
    /**
207
     * @param string $errorCode
208
     * @param string $explanation
209
     * @return \SimpleSAML\CAS\XML\ServiceResponse
210
     */
211
    public function getProxyFailureResponse(string $errorCode, string $explanation): ServiceResponse
212
    {
213
        $proxyFailure = new ProxyFailure(
214
            StringValue::fromString($explanation),
215
            CodeValue::fromString($errorCode),
216
        );
217
        $serviceResponse = new ServiceResponse($proxyFailure);
218
219
        return $serviceResponse;
220
    }
221
222
223
    /**
224
     * @param string $attributeName
225
     * @param string $attributeValue
226
     * @return \SimpleSAML\XML\Chunk
227
     */
228
    private function generateCas20Attribute(
229
        string $attributeName,
230
        string $attributeValue,
231
    ): Chunk {
232
        $xmlDocument = DOMDocumentFactory::create();
233
234
        $attributeValue = $this->base64EncodeAttributes ? base64_encode($attributeValue) : $attributeValue;
235
        $attributeElement = $xmlDocument->createElementNS(Attributes::NS, 'cas:' . $attributeName, $attributeValue);
236
237
        return new Chunk($attributeElement);
238
    }
239
}
240