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