Passed
Pull Request — master (#25)
by
unknown
01:49
created

casAddURLParameters()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 8
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 11
rs 10
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
 * Incoming parameters:
23
 *  service
24
 *  renew
25
 *  gateway
26
 *  entityId
27
 *  scope
28
 *  language
29
 */
30
31
use SimpleSAML\Configuration;
32
use SimpleSAML\Locale\Language;
33
use SimpleSAML\Logger;
34
use SimpleSAML\Module;
35
use SimpleSAML\Module\casserver\Cas\AttributeExtractor;
36
use SimpleSAML\Module\casserver\Cas\Protocol\SamlValidateResponder;
37
use SimpleSAML\Module\casserver\Cas\ServiceValidator;
38
use SimpleSAML\Module\casserver\Cas\Ticket\TicketFactory;
39
use SimpleSAML\Module\casserver\Cas\Ticket\TicketStore;
40
use SimpleSAML\Session;
41
use SimpleSAML\Utils\HTTP;
42
43
require_once('utility/urlUtils.php');
44
45
$forceAuthn = isset($_GET['renew']) && $_GET['renew'];
46
$isPassive = isset($_GET['gateway']) && $_GET['gateway'];
47
// Determine if client wants us to post or redirect the response. Default is redirect.
48
$redirect = !(isset($_GET['method']) && 'POST' === $_GET['method']);
49
50
$casconfig = Configuration::getConfig('module_casserver.php');
51
$serviceValidator = new ServiceValidator($casconfig);
52
53
$serviceUrl = $_GET['service'] ?? $_GET['TARGET'] ?? null;
54
55
if (isset($serviceUrl)) {
56
    $serviceCasConfig = $serviceValidator->checkServiceURL(sanitize($serviceUrl));
57
    if (isset($serviceCasConfig)) {
58
        // Override the cas configuration to use for this service
59
        $casconfig = $serviceCasConfig;
60
    } else {
61
        $message = 'Service parameter provided to CAS server is not listed as a legal service: [service] = ' .
62
            var_export($serviceUrl, true);
63
        Logger::debug('casserver:' . $message);
64
65
        throw new \Exception($message);
66
    }
67
}
68
69
70
$as = new \SimpleSAML\Auth\Simple($casconfig->getValue('authsource'));
71
72
if (array_key_exists('scope', $_GET) && is_string($_GET['scope'])) {
73
    $scopes = $casconfig->getValue('scopes', []);
74
75
    if (array_key_exists($_GET['scope'], $scopes)) {
76
        $idpList = $scopes[$_GET['scope']];
77
    } else {
78
        $message = 'Scope parameter provided to CAS server is not listed as legal scope: [scope] = ' .
79
            var_export($_GET['scope'], true);
80
        Logger::debug('casserver:' . $message);
81
82
        throw new \Exception($message);
83
    }
84
}
85
86
if (array_key_exists('language', $_GET) && is_string($_GET['language'])) {
87
    Language::setLanguageCookie($_GET['language']);
88
}
89
90
$ticketStoreConfig = $casconfig->getValue('ticketstore', ['class' => 'casserver:FileSystemTicketStore']);
91
$ticketStoreClass = Module::resolveClass($ticketStoreConfig['class'], 'Cas_Ticket');
92
/** @var $ticketStore TicketStore */
93
/** @psalm-suppress InvalidStringClass */
94
$ticketStore = new $ticketStoreClass($casconfig);
95
96
$ticketFactoryClass = Module::resolveClass('casserver:TicketFactory', 'Cas_Ticket');
97
/** @var $ticketFactory TicketFactory */
98
/** @psalm-suppress InvalidStringClass */
99
$ticketFactory = new $ticketFactoryClass($casconfig);
100
101
$session = Session::getSessionFromRequest();
102
103
$sessionTicket = $ticketStore->getTicket($session->getSessionId());
104
$sessionRenewId = $sessionTicket ? $sessionTicket['renewId'] : null;
105
$requestRenewId = isset($_REQUEST['renewId']) ? $_REQUEST['renewId'] : null;
106
107
if (!$as->isAuthenticated() || ($forceAuthn && $sessionRenewId != $requestRenewId)) {
108
    $query = [];
109
110
    if ($sessionRenewId && $forceAuthn) {
111
        $query['renewId'] = $sessionRenewId;
112
    }
113
114
    if (isset($_REQUEST['service'])) {
115
        $query['service'] = $_REQUEST['service'];
116
    }
117
118
    if (isset($_REQUEST['TARGET'])) {
119
        $query['TARGET'] = $_REQUEST['TARGET'];
120
    }
121
122
    if (isset($_REQUEST['method'])) {
123
        $query['method'] = $_REQUEST['method'];
124
    }
125
126
    if (isset($_REQUEST['renew'])) {
127
        $query['renew'] = $_REQUEST['renew'];
128
    }
129
130
    if (isset($_REQUEST['gateway'])) {
131
        $query['gateway'] = $_REQUEST['gateway'];
132
    }
133
134
    if (array_key_exists('language', $_GET)) {
135
        $query['language'] = is_string($_GET['language']) ? $_GET['language'] : null;
136
    }
137
138
    $returnUrl = HTTP::getSelfURLNoQuery() . '?' . http_build_query($query);
139
140
    $params = [
141
        'ForceAuthn' => $forceAuthn,
142
        'isPassive' => $isPassive,
143
        'ReturnTo' => $returnUrl,
144
    ];
145
146
    if (isset($_GET['entityId'])) {
147
        $params['saml:idp'] = $_GET['entityId'];
148
    }
149
150
    if (isset($idpList)) {
151
        if (sizeof($idpList) > 1) {
152
            $params['saml:IDPList'] = $idpList;
153
        } else {
154
            $params['saml:idp'] = $idpList[0];
155
        }
156
    }
157
158
    $as->login($params);
159
}
160
161
$sessionExpiry = $as->getAuthData('Expire');
162
163
if (!is_array($sessionTicket) || $forceAuthn) {
164
    $sessionTicket = $ticketFactory->createSessionTicket($session->getSessionId(), $sessionExpiry);
165
166
    $ticketStore->addTicket($sessionTicket);
167
}
168
169
$parameters = [];
170
171
if (array_key_exists('language', $_GET)) {
172
    $oldLanguagePreferred = Language::getLanguageCookie();
173
174
    if (isset($oldLanguagePreferred)) {
175
        $parameters['language'] = $oldLanguagePreferred;
176
    } else {
177
        if (is_string($_GET['language'])) {
178
            $parameters['language'] = $_GET['language'];
179
        }
180
    }
181
}
182
183
if (isset($serviceUrl)) {
184
    $defaultTicketName = isset($_GET['service']) ? 'ticket' : 'SAMLart';
185
    $ticketName = $casconfig->getValue('ticketName', $defaultTicketName);
186
187
    $attributeExtractor = new AttributeExtractor();
188
    $mappedAttributes = $attributeExtractor->extractUserAndAttributes($as->getAttributes(), $casconfig);
189
190
    $serviceTicket = $ticketFactory->createServiceTicket([
191
        'service' => $serviceUrl,
192
        'forceAuthn' => $forceAuthn,
193
        'userName' => $mappedAttributes['user'],
194
        'attributes' => $mappedAttributes['attributes'],
195
        'proxies' => [],
196
        'sessionId' => $sessionTicket['id']
197
    ]);
198
199
    $ticketStore->addTicket($serviceTicket);
200
201
    $parameters[$ticketName] = $serviceTicket['id'];
202
203
    $validDebugModes = ['true', 'samlValidate'];
204
    if (
205
        array_key_exists('debugMode', $_GET) &&
206
        in_array($_GET['debugMode'], $validDebugModes) &&
207
        $casconfig->getBoolean('debugMode', false)
208
    ) {
209
        if ($_GET['debugMode'] === 'samlValidate') {
210
            $samlValidate = new SamlValidateResponder();
211
            $samlResponse = $samlValidate->convertToSaml($serviceTicket);
212
            $soap = $samlValidate->wrapInSoap($samlResponse);
213
            echo '<pre>' . htmlspecialchars($soap) . '</pre>';
214
        } else {
215
            $method = 'serviceValidate';
216
            // Fake some options for validateTicket
217
            $_GET[$ticketName] = $serviceTicket['id'];
218
            // We want to capture the output from echo used in validateTicket
219
            ob_start();
220
            require_once 'utility/validateTicket.php';
221
            $casResponse = ob_get_contents();
222
            ob_end_clean();
223
            echo '<pre>' . htmlspecialchars($casResponse) . '</pre>';
224
        }
225
    } elseif ($redirect) {
226
        if ($casconfig->getBoolean('noReencode', false)) {
227
            $redirectUrl = casAddURLParameters($serviceUrl, $parameters);
228
            HTTP::redirectTrustedURL($redirectUrl);
229
        } else {
230
            HTTP::redirectTrustedURL(HTTP::addURLParameters($serviceUrl, $parameters));
231
        }
232
    } else {
233
        HTTP::submitPOSTData($serviceUrl, $parameters);
234
    }
235
} else {
236
    HTTP::redirectTrustedURL(
237
        HTTP::addURLParameters(Module::getModuleURL('casserver/loggedIn.php'), $parameters)
238
    );
239
}
240
241
242
/**
243
 * CAS wants to ensure that a service url provided in login matches exactly that provided in service validate.
244
 * This method avoids SSP's built in redirect which can change that url in certain ways, such as
245
 * * changing how a ' ' is encoded
246
 * * not correctly handling url fragments (e.g. #)
247
 * * not correctly handling query param keys occurring multiple times
248
 * * some buggy clients don't encode query params correctly
249
 * which results in either the wrong url being returned to the client, or a service mismatch
250
 * @param string $url The url to adjust
251
 * @param array $params The query parameters to add.
252
 * @return string The url to return
253
 */
254
function casAddURLParameters($url, $params)
255
{
256
    $url_fragment = explode("#", $url);
257
    if (strpos($url_fragment[0], "?") === false) {
258
        $url_fragment[0] .= "?";
259
    } else {
260
        $url_fragment[0] .= "&";
261
    }
262
    $url_fragment[0] .= http_build_query($params);
263
    $url = implode("#", $url_fragment);
264
    return $url;
265
}