ryxeo-glpi-git / lib / phpcas / client.php @ b67d8923
Historique | Voir | Annoter | Télécharger (63,5 ko)
1 |
<?php
|
---|---|
2 |
|
3 |
/**
|
4 |
* @file CAS/client.php
|
5 |
* Main class of the phpCAS library
|
6 |
*/
|
7 |
|
8 |
// include internationalization stuff
|
9 |
include_once(dirname(__FILE__).'/languages/languages.php'); |
10 |
|
11 |
// include PGT storage classes
|
12 |
include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php'); |
13 |
|
14 |
/**
|
15 |
* @class CASClient
|
16 |
* The CASClient class is a client interface that provides CAS authentication
|
17 |
* to PHP applications.
|
18 |
*
|
19 |
* @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
|
20 |
*/
|
21 |
|
22 |
class CASClient |
23 |
{ |
24 |
|
25 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
26 |
// XX XX
|
27 |
// XX CONFIGURATION XX
|
28 |
// XX XX
|
29 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
30 |
|
31 |
// ########################################################################
|
32 |
// HTML OUTPUT
|
33 |
// ########################################################################
|
34 |
/**
|
35 |
* @addtogroup internalOutput
|
36 |
* @{
|
37 |
*/
|
38 |
|
39 |
/**
|
40 |
* This method filters a string by replacing special tokens by appropriate values
|
41 |
* and prints it. The corresponding tokens are taken into account:
|
42 |
* - __CAS_VERSION__
|
43 |
* - __PHPCAS_VERSION__
|
44 |
* - __SERVER_BASE_URL__
|
45 |
*
|
46 |
* Used by CASClient::PrintHTMLHeader() and CASClient::printHTMLFooter().
|
47 |
*
|
48 |
* @param $str the string to filter and output
|
49 |
*
|
50 |
* @private
|
51 |
*/
|
52 |
function HTMLFilterOutput($str) |
53 |
{ |
54 |
$str = str_replace('__CAS_VERSION__',$this->getServerVersion(),$str); |
55 |
$str = str_replace('__PHPCAS_VERSION__',phpCAS::getVersion(),$str); |
56 |
$str = str_replace('__SERVER_BASE_URL__',$this->getServerBaseURL(),$str); |
57 |
echo $str; |
58 |
} |
59 |
|
60 |
/**
|
61 |
* A string used to print the header of HTML pages. Written by CASClient::setHTMLHeader(),
|
62 |
* read by CASClient::printHTMLHeader().
|
63 |
*
|
64 |
* @hideinitializer
|
65 |
* @private
|
66 |
* @see CASClient::setHTMLHeader, CASClient::printHTMLHeader()
|
67 |
*/
|
68 |
var $_output_header = ''; |
69 |
|
70 |
/**
|
71 |
* This method prints the header of the HTML output (after filtering). If
|
72 |
* CASClient::setHTMLHeader() was not used, a default header is output.
|
73 |
*
|
74 |
* @param $title the title of the page
|
75 |
*
|
76 |
* @see HTMLFilterOutput()
|
77 |
* @private
|
78 |
*/
|
79 |
function printHTMLHeader($title) |
80 |
{ |
81 |
$this->HTMLFilterOutput(str_replace('__TITLE__', |
82 |
$title,
|
83 |
(empty($this->_output_header) |
84 |
? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
|
85 |
: $this->_output_header)
|
86 |
) |
87 |
); |
88 |
} |
89 |
|
90 |
/**
|
91 |
* A string used to print the footer of HTML pages. Written by CASClient::setHTMLFooter(),
|
92 |
* read by printHTMLFooter().
|
93 |
*
|
94 |
* @hideinitializer
|
95 |
* @private
|
96 |
* @see CASClient::setHTMLFooter, CASClient::printHTMLFooter()
|
97 |
*/
|
98 |
var $_output_footer = ''; |
99 |
|
100 |
/**
|
101 |
* This method prints the footer of the HTML output (after filtering). If
|
102 |
* CASClient::setHTMLFooter() was not used, a default footer is output.
|
103 |
*
|
104 |
* @see HTMLFilterOutput()
|
105 |
* @private
|
106 |
*/
|
107 |
function printHTMLFooter() |
108 |
{ |
109 |
$this->HTMLFilterOutput(empty($this->_output_footer) |
110 |
?('<hr><address>phpCAS __PHPCAS_VERSION__ '.$this->getString(CAS_STR_USING_SERVER).' <a href="__SERVER_BASE_URL__">__SERVER_BASE_URL__</a> (CAS __CAS_VERSION__)</a></address></body></html>') |
111 |
:$this->_output_footer);
|
112 |
} |
113 |
|
114 |
/**
|
115 |
* This method set the HTML header used for all outputs.
|
116 |
*
|
117 |
* @param $header the HTML header.
|
118 |
*
|
119 |
* @public
|
120 |
*/
|
121 |
function setHTMLHeader($header) |
122 |
{ |
123 |
$this->_output_header = $header; |
124 |
} |
125 |
|
126 |
/**
|
127 |
* This method set the HTML footer used for all outputs.
|
128 |
*
|
129 |
* @param $footer the HTML footer.
|
130 |
*
|
131 |
* @public
|
132 |
*/
|
133 |
function setHTMLFooter($footer) |
134 |
{ |
135 |
$this->_output_footer = $footer; |
136 |
} |
137 |
|
138 |
/** @} */
|
139 |
// ########################################################################
|
140 |
// INTERNATIONALIZATION
|
141 |
// ########################################################################
|
142 |
/**
|
143 |
* @addtogroup internalLang
|
144 |
* @{
|
145 |
*/
|
146 |
/**
|
147 |
* A string corresponding to the language used by phpCAS. Written by
|
148 |
* CASClient::setLang(), read by CASClient::getLang().
|
149 |
|
150 |
* @note debugging information is always in english (debug purposes only).
|
151 |
*
|
152 |
* @hideinitializer
|
153 |
* @private
|
154 |
* @sa CASClient::_strings, CASClient::getString()
|
155 |
*/
|
156 |
var $_lang = ''; |
157 |
|
158 |
/**
|
159 |
* This method returns the language used by phpCAS.
|
160 |
*
|
161 |
* @return a string representing the language
|
162 |
*
|
163 |
* @private
|
164 |
*/
|
165 |
function getLang() |
166 |
{ |
167 |
if ( empty($this->_lang) ) |
168 |
$this->setLang(PHPCAS_LANG_DEFAULT); |
169 |
return $this->_lang; |
170 |
} |
171 |
|
172 |
/**
|
173 |
* array containing the strings used by phpCAS. Written by CASClient::setLang(), read by
|
174 |
* CASClient::getString() and used by CASClient::setLang().
|
175 |
*
|
176 |
* @note This array is filled by instructions in CAS/languages/<$this->_lang>.php
|
177 |
*
|
178 |
* @private
|
179 |
* @see CASClient::_lang, CASClient::getString(), CASClient::setLang(), CASClient::getLang()
|
180 |
*/
|
181 |
var $_strings; |
182 |
|
183 |
/**
|
184 |
* This method returns a string depending on the language.
|
185 |
*
|
186 |
* @param $str the index of the string in $_string.
|
187 |
*
|
188 |
* @return the string corresponding to $index in $string.
|
189 |
*
|
190 |
* @private
|
191 |
*/
|
192 |
function getString($str) |
193 |
{ |
194 |
// call CASclient::getLang() to be sure the language is initialized
|
195 |
$this->getLang();
|
196 |
|
197 |
if ( !isset($this->_strings[$str]) ) { |
198 |
trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR); |
199 |
} |
200 |
return $this->_strings[$str]; |
201 |
} |
202 |
|
203 |
/**
|
204 |
* This method is used to set the language used by phpCAS.
|
205 |
* @note Can be called only once.
|
206 |
*
|
207 |
* @param $lang a string representing the language.
|
208 |
*
|
209 |
* @public
|
210 |
* @sa CAS_LANG_FRENCH, CAS_LANG_ENGLISH
|
211 |
*/
|
212 |
function setLang($lang) |
213 |
{ |
214 |
// include the corresponding language file
|
215 |
include_once(dirname(__FILE__).'/languages/'.$lang.'.php'); |
216 |
|
217 |
if ( !is_array($this->_strings) ) { |
218 |
trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR); |
219 |
} |
220 |
$this->_lang = $lang; |
221 |
} |
222 |
|
223 |
/** @} */
|
224 |
// ########################################################################
|
225 |
// CAS SERVER CONFIG
|
226 |
// ########################################################################
|
227 |
/**
|
228 |
* @addtogroup internalConfig
|
229 |
* @{
|
230 |
*/
|
231 |
|
232 |
/**
|
233 |
* a record to store information about the CAS server.
|
234 |
* - $_server["version"]: the version of the CAS server
|
235 |
* - $_server["hostname"]: the hostname of the CAS server
|
236 |
* - $_server["port"]: the port the CAS server is running on
|
237 |
* - $_server["uri"]: the base URI the CAS server is responding on
|
238 |
* - $_server["base_url"]: the base URL of the CAS server
|
239 |
* - $_server["login_url"]: the login URL of the CAS server
|
240 |
* - $_server["service_validate_url"]: the service validating URL of the CAS server
|
241 |
* - $_server["proxy_url"]: the proxy URL of the CAS server
|
242 |
* - $_server["proxy_validate_url"]: the proxy validating URL of the CAS server
|
243 |
* - $_server["logout_url"]: the logout URL of the CAS server
|
244 |
*
|
245 |
* $_server["version"], $_server["hostname"], $_server["port"] and $_server["uri"]
|
246 |
* are written by CASClient::CASClient(), read by CASClient::getServerVersion(),
|
247 |
* CASClient::getServerHostname(), CASClient::getServerPort() and CASClient::getServerURI().
|
248 |
*
|
249 |
* The other fields are written and read by CASClient::getServerBaseURL(),
|
250 |
* CASClient::getServerLoginURL(), CASClient::getServerServiceValidateURL(),
|
251 |
* CASClient::getServerProxyValidateURL() and CASClient::getServerLogoutURL().
|
252 |
*
|
253 |
* @hideinitializer
|
254 |
* @private
|
255 |
*/
|
256 |
var $_server = array( |
257 |
'version' => -1, |
258 |
'hostname' => 'none', |
259 |
'port' => -1, |
260 |
'uri' => 'none' |
261 |
); |
262 |
|
263 |
/**
|
264 |
* This method is used to retrieve the version of the CAS server.
|
265 |
* @return the version of the CAS server.
|
266 |
* @private
|
267 |
*/
|
268 |
function getServerVersion() |
269 |
{ |
270 |
return $this->_server['version']; |
271 |
} |
272 |
|
273 |
/**
|
274 |
* This method is used to retrieve the hostname of the CAS server.
|
275 |
* @return the hostname of the CAS server.
|
276 |
* @private
|
277 |
*/
|
278 |
function getServerHostname() |
279 |
{ return $this->_server['hostname']; } |
280 |
|
281 |
/**
|
282 |
* This method is used to retrieve the port of the CAS server.
|
283 |
* @return the port of the CAS server.
|
284 |
* @private
|
285 |
*/
|
286 |
function getServerPort() |
287 |
{ return $this->_server['port']; } |
288 |
|
289 |
/**
|
290 |
* This method is used to retrieve the URI of the CAS server.
|
291 |
* @return a URI.
|
292 |
* @private
|
293 |
*/
|
294 |
function getServerURI() |
295 |
{ return $this->_server['uri']; } |
296 |
|
297 |
/**
|
298 |
* This method is used to retrieve the base URL of the CAS server.
|
299 |
* @return a URL.
|
300 |
* @private
|
301 |
*/
|
302 |
function getServerBaseURL() |
303 |
{ |
304 |
// the URL is build only when needed
|
305 |
if ( empty($this->_server['base_url']) ) { |
306 |
$this->_server['base_url'] = 'https://' |
307 |
.$this->getServerHostname()
|
308 |
.':'
|
309 |
.$this->getServerPort()
|
310 |
.$this->getServerURI();
|
311 |
} |
312 |
return $this->_server['base_url']; |
313 |
} |
314 |
|
315 |
/**
|
316 |
* This method is used to retrieve the login URL of the CAS server.
|
317 |
* @param $gateway true to check authentication, false to force it
|
318 |
* @return a URL.
|
319 |
* @private
|
320 |
*/
|
321 |
function getServerLoginURL($gateway=false) |
322 |
{ |
323 |
$cas=new phpCAS(); |
324 |
$cas->traceBegin();
|
325 |
// the URL is build only when needed
|
326 |
if ( empty($this->_server['login_url']) ) { |
327 |
$this->_server['login_url'] = $this->getServerBaseURL(); |
328 |
$this->_server['login_url'] .= 'login?service='; |
329 |
// $this->_server['login_url'] .= preg_replace('/&/','%26',$this->getURL());
|
330 |
$this->_server['login_url'] .= urlencode($this->getURL()); |
331 |
if ($gateway) { |
332 |
$this->_server['login_url'] .= '&gateway=true'; |
333 |
} |
334 |
} |
335 |
$cas->traceEnd($this->_server['login_url']); |
336 |
return $this->_server['login_url']; |
337 |
} |
338 |
|
339 |
/**
|
340 |
* This method sets the login URL of the CAS server.
|
341 |
* @param $url the login URL
|
342 |
* @private
|
343 |
* @since 0.4.21 by Wyman Chan
|
344 |
*/
|
345 |
function setServerLoginURL($url) |
346 |
{ |
347 |
return $this->_server['login_url'] = $url; |
348 |
} |
349 |
|
350 |
/**
|
351 |
* This method is used to retrieve the service validating URL of the CAS server.
|
352 |
* @return a URL.
|
353 |
* @private
|
354 |
*/
|
355 |
function getServerServiceValidateURL() |
356 |
{ |
357 |
// the URL is build only when needed
|
358 |
if ( empty($this->_server['service_validate_url']) ) { |
359 |
switch ($this->getServerVersion()) { |
360 |
case CAS_VERSION_1_0: |
361 |
$this->_server['service_validate_url'] = $this->getServerBaseURL().'validate'; |
362 |
break;
|
363 |
case CAS_VERSION_2_0: |
364 |
$this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate'; |
365 |
break;
|
366 |
} |
367 |
} |
368 |
// return $this->_server['service_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL());
|
369 |
return $this->_server['service_validate_url'].'?service='.urlencode($this->getURL()); |
370 |
} |
371 |
|
372 |
/**
|
373 |
* This method is used to retrieve the proxy validating URL of the CAS server.
|
374 |
* @return a URL.
|
375 |
* @private
|
376 |
*/
|
377 |
function getServerProxyValidateURL() |
378 |
{ |
379 |
// the URL is build only when needed
|
380 |
if ( empty($this->_server['proxy_validate_url']) ) { |
381 |
switch ($this->getServerVersion()) { |
382 |
case CAS_VERSION_1_0: |
383 |
$this->_server['proxy_validate_url'] = ''; |
384 |
break;
|
385 |
case CAS_VERSION_2_0: |
386 |
$this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate'; |
387 |
break;
|
388 |
} |
389 |
} |
390 |
// return $this->_server['proxy_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL());
|
391 |
return $this->_server['proxy_validate_url'].'?service='.urlencode($this->getURL()); |
392 |
} |
393 |
|
394 |
/**
|
395 |
* This method is used to retrieve the proxy URL of the CAS server.
|
396 |
* @return a URL.
|
397 |
* @private
|
398 |
*/
|
399 |
function getServerProxyURL() |
400 |
{ |
401 |
// the URL is build only when needed
|
402 |
if ( empty($this->_server['proxy_url']) ) { |
403 |
switch ($this->getServerVersion()) { |
404 |
case CAS_VERSION_1_0: |
405 |
$this->_server['proxy_url'] = ''; |
406 |
break;
|
407 |
case CAS_VERSION_2_0: |
408 |
$this->_server['proxy_url'] = $this->getServerBaseURL().'proxy'; |
409 |
break;
|
410 |
} |
411 |
} |
412 |
return $this->_server['proxy_url']; |
413 |
} |
414 |
|
415 |
/**
|
416 |
* This method is used to retrieve the logout URL of the CAS server.
|
417 |
* @return a URL.
|
418 |
* @private
|
419 |
*/
|
420 |
function getServerLogoutURL() |
421 |
{ |
422 |
// the URL is build only when needed
|
423 |
if ( empty($this->_server['logout_url']) ) { |
424 |
$this->_server['logout_url'] = $this->getServerBaseURL().'logout'; |
425 |
} |
426 |
return $this->_server['logout_url']; |
427 |
} |
428 |
|
429 |
/**
|
430 |
* This method sets the logout URL of the CAS server.
|
431 |
* @param $url the logout URL
|
432 |
* @private
|
433 |
* @since 0.4.21 by Wyman Chan
|
434 |
*/
|
435 |
function setServerLogoutURL($url) |
436 |
{ |
437 |
return $this->_server['logout_url'] = $url; |
438 |
} |
439 |
|
440 |
/**
|
441 |
* This method checks to see if the request is secured via HTTPS
|
442 |
* @return true if https, false otherwise
|
443 |
* @private
|
444 |
*/
|
445 |
function isHttps() { |
446 |
//if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ) {
|
447 |
//0.4.24 by Hinnack
|
448 |
if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { |
449 |
return true; |
450 |
} else {
|
451 |
return false; |
452 |
} |
453 |
} |
454 |
|
455 |
// ########################################################################
|
456 |
// CONSTRUCTOR
|
457 |
// ########################################################################
|
458 |
/**
|
459 |
* CASClient constructor.
|
460 |
*
|
461 |
* @param $server_version the version of the CAS server
|
462 |
* @param $proxy TRUE if the CAS client is a CAS proxy, FALSE otherwise
|
463 |
* @param $server_hostname the hostname of the CAS server
|
464 |
* @param $server_port the port the CAS server is running on
|
465 |
* @param $server_uri the URI the CAS server is responding on
|
466 |
* @param $start_session Have phpCAS start PHP sessions (default true)
|
467 |
*
|
468 |
* @return a newly created CASClient object
|
469 |
*
|
470 |
* @public
|
471 |
*/
|
472 |
function CASClient( |
473 |
$server_version,
|
474 |
$proxy,
|
475 |
$server_hostname,
|
476 |
$server_port,
|
477 |
$server_uri,
|
478 |
$start_session = true) { |
479 |
|
480 |
$cas = new phpCAS; |
481 |
$cas->traceBegin();
|
482 |
|
483 |
//activate session mechanism if desired
|
484 |
if (!session_id()) {
|
485 |
session_start(); |
486 |
} |
487 |
|
488 |
$this->_proxy = $proxy; |
489 |
|
490 |
//check version
|
491 |
switch ($server_version) { |
492 |
case CAS_VERSION_1_0: |
493 |
if ( $this->isProxy() ) |
494 |
$cas->error('CAS proxies are not supported in CAS ' |
495 |
.$server_version);
|
496 |
break;
|
497 |
case CAS_VERSION_2_0: |
498 |
break;
|
499 |
default:
|
500 |
phpCAS::error('this version of CAS (`'
|
501 |
.$server_version
|
502 |
.'\') is not supported by phpCAS '
|
503 |
.$cas->getVersion());
|
504 |
} |
505 |
$this->_server['version'] = $server_version; |
506 |
|
507 |
//check hostname
|
508 |
if ( empty($server_hostname) |
509 |
|| !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) { |
510 |
$cas->error('bad CAS server hostname (`'.$server_hostname.'\')'); |
511 |
} |
512 |
$this->_server['hostname'] = $server_hostname; |
513 |
|
514 |
//check port
|
515 |
if ( $server_port == 0 |
516 |
|| !is_int($server_port) ) { |
517 |
$cas->error('bad CAS server port (`'.$server_hostname.'\')'); |
518 |
} |
519 |
$this->_server['port'] = $server_port; |
520 |
|
521 |
//check URI
|
522 |
if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) { |
523 |
$cas->error('bad CAS server URI (`'.$server_uri.'\')'); |
524 |
} |
525 |
//add leading and trailing `/' and remove doubles
|
526 |
$server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/'); |
527 |
$this->_server['uri'] = $server_uri; |
528 |
|
529 |
//set to callback mode if PgtIou and PgtId CGI GET parameters are provided
|
530 |
if ( $this->isProxy() ) { |
531 |
$this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId'])); |
532 |
} |
533 |
|
534 |
if ( $this->isCallbackMode() ) { |
535 |
//callback mode: check that phpCAS is secured
|
536 |
if ( !$this->isHttps() ) { |
537 |
$cas->error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server'); |
538 |
} |
539 |
} else {
|
540 |
//normal mode: get ticket and remove it from CGI parameters for developpers
|
541 |
$ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null); |
542 |
switch ($this->getServerVersion()) { |
543 |
case CAS_VERSION_1_0: // check for a Service Ticket |
544 |
if( preg_match('/^ST-/',$ticket) ) { |
545 |
$cas->trace('ST \''.$ticket.'\' found'); |
546 |
//ST present
|
547 |
$this->setST($ticket); |
548 |
//ticket has been taken into account, unset it to hide it to applications
|
549 |
unset($_GET['ticket']); |
550 |
} else if ( !empty($ticket) ) { |
551 |
//ill-formed ticket, halt
|
552 |
$cas->error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')'); |
553 |
} |
554 |
break;
|
555 |
case CAS_VERSION_2_0: // check for a Service or Proxy Ticket |
556 |
if( preg_match('/^[SP]T-/',$ticket) ) { |
557 |
$cas->trace('ST or PT \''.$ticket.'\' found'); |
558 |
$this->setPT($ticket); |
559 |
unset($_GET['ticket']); |
560 |
} else if ( !empty($ticket) ) { |
561 |
//ill-formed ticket, halt
|
562 |
$cas->error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')'); |
563 |
} |
564 |
break;
|
565 |
} |
566 |
} |
567 |
$cas->traceEnd();
|
568 |
} |
569 |
|
570 |
/** @} */
|
571 |
|
572 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
573 |
// XX XX
|
574 |
// XX AUTHENTICATION XX
|
575 |
// XX XX
|
576 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
577 |
|
578 |
/**
|
579 |
* @addtogroup internalAuthentication
|
580 |
* @{
|
581 |
*/
|
582 |
|
583 |
/**
|
584 |
* The Authenticated user. Written by CASClient::setUser(), read by CASClient::getUser().
|
585 |
* @attention client applications should use phpCAS::getUser().
|
586 |
*
|
587 |
* @hideinitializer
|
588 |
* @private
|
589 |
*/
|
590 |
var $_user = ''; |
591 |
|
592 |
/**
|
593 |
* This method sets the CAS user's login name.
|
594 |
*
|
595 |
* @param $user the login name of the authenticated user.
|
596 |
*
|
597 |
* @private
|
598 |
*/
|
599 |
function setUser($user) |
600 |
{ |
601 |
$this->_user = $user; |
602 |
} |
603 |
|
604 |
/**
|
605 |
* This method returns the CAS user's login name.
|
606 |
* @warning should be called only after CASClient::forceAuthentication() or
|
607 |
* CASClient::isAuthenticated(), otherwise halt with an error.
|
608 |
*
|
609 |
* @return the login name of the authenticated user
|
610 |
*/
|
611 |
function getUser() |
612 |
{ |
613 |
if ( empty($this->_user) ) { |
614 |
phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()'); |
615 |
} |
616 |
return $this->_user; |
617 |
} |
618 |
|
619 |
/**
|
620 |
* This method is called to be sure that the user is authenticated. When not
|
621 |
* authenticated, halt by redirecting to the CAS server; otherwise return TRUE.
|
622 |
* @return TRUE when the user is authenticated; otherwise halt.
|
623 |
* @public
|
624 |
*/
|
625 |
function forceAuthentication() |
626 |
{ |
627 |
$cas=new phpCas(); |
628 |
$cas->traceBegin();
|
629 |
|
630 |
if ( $this->isAuthenticated() ) { |
631 |
// the user is authenticated, nothing to be done.
|
632 |
$cas->trace('no need to authenticate'); |
633 |
$res = TRUE; |
634 |
} else {
|
635 |
// the user is not authenticated, redirect to the CAS server
|
636 |
unset($_SESSION['phpCAS']['auth_checked']); |
637 |
$this->redirectToCas(FALSE/* no gateway */); |
638 |
// never reached
|
639 |
$res = FALSE; |
640 |
} |
641 |
$cas->traceEnd($res); |
642 |
return $res; |
643 |
} |
644 |
|
645 |
/**
|
646 |
* An integer that gives the number of times authentication will be cached before rechecked.
|
647 |
*
|
648 |
* @hideinitializer
|
649 |
* @private
|
650 |
*/
|
651 |
var $_cache_times_for_auth_recheck = 0; |
652 |
|
653 |
/**
|
654 |
* Set the number of times authentication will be cached before rechecked.
|
655 |
*
|
656 |
* @param $n an integer.
|
657 |
*
|
658 |
* @public
|
659 |
*/
|
660 |
function setCacheTimesForAuthRequest($n) |
661 |
{ |
662 |
$this->_cache_times_for_auth_recheck = n;
|
663 |
} |
664 |
|
665 |
/**
|
666 |
* This method is called to check whether the user is authenticated or not.
|
667 |
* @return TRUE when the user is authenticated, FALSE otherwise.
|
668 |
* @public
|
669 |
*/
|
670 |
function checkAuthentication() |
671 |
{ |
672 |
phpCAS::traceBegin(); |
673 |
|
674 |
if ( $this->isAuthenticated() ) { |
675 |
phpCAS::trace('user is authenticated');
|
676 |
$res = TRUE; |
677 |
} else if (isset($_SESSION['phpCAS']['auth_checked'])) { |
678 |
// the previous request has redirected the client to the CAS server with gateway=true
|
679 |
unset($_SESSION['phpCAS']['auth_checked']); |
680 |
$res = FALSE; |
681 |
} else {
|
682 |
// $_SESSION['phpCAS']['auth_checked'] = true;
|
683 |
// $this->redirectToCas(TRUE/* gateway */);
|
684 |
// // never reached
|
685 |
// $res = FALSE;
|
686 |
// avoid a check against CAS on every request
|
687 |
if (! isset($_SESSION['phpCAS']['unauth_count']) ) |
688 |
$_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized |
689 |
|
690 |
if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1) |
691 |
|| ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck)) |
692 |
{ |
693 |
$res = FALSE; |
694 |
|
695 |
if ($this->_cache_times_for_auth_recheck != -1) |
696 |
{ |
697 |
$_SESSION['phpCAS']['unauth_count']++; |
698 |
phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')'); |
699 |
} |
700 |
else
|
701 |
{ |
702 |
phpCAS::trace('user is not authenticated (cached for until login pressed)');
|
703 |
} |
704 |
} |
705 |
else
|
706 |
{ |
707 |
$_SESSION['phpCAS']['unauth_count'] = 0; |
708 |
$_SESSION['phpCAS']['auth_checked'] = true; |
709 |
phpCAS::trace('user is not authenticated (cache reset)');
|
710 |
$this->redirectToCas(TRUE/* gateway */); |
711 |
// never reached
|
712 |
$res = FALSE; |
713 |
} |
714 |
} |
715 |
phpCAS::traceEnd($res);
|
716 |
return $res; |
717 |
} |
718 |
|
719 |
/**
|
720 |
* This method is called to check if the user is authenticated (previously or by
|
721 |
* tickets given in the URL).
|
722 |
*
|
723 |
* @return TRUE when the user is authenticated.
|
724 |
*
|
725 |
* @public
|
726 |
*/
|
727 |
function isAuthenticated() |
728 |
{ |
729 |
$cas=new phpCas(); |
730 |
$cas->traceBegin();
|
731 |
$res = FALSE; |
732 |
$validate_url = ''; |
733 |
|
734 |
if ( $this->wasPreviouslyAuthenticated() ) { |
735 |
// the user has already (previously during the session) been
|
736 |
// authenticated, nothing to be done.
|
737 |
$cas->trace('user was already authenticated, no need to look for tickets'); |
738 |
$res = TRUE; |
739 |
} |
740 |
elseif ( $this->hasST() ) { |
741 |
// if a Service Ticket was given, validate it
|
742 |
$cas->trace('ST `'.$this->getST().'\' is present'); |
743 |
$this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts |
744 |
$cas->trace('ST `'.$this->getST().'\' was validated'); |
745 |
if ( $this->isProxy() ) { |
746 |
$this->validatePGT($validate_url,$text_response,$tree_response); // idem |
747 |
$cas->trace('PGT `'.$this->getPGT().'\' was validated'); |
748 |
$_SESSION['phpCAS']['pgt'] = $this->getPGT(); |
749 |
} |
750 |
$_SESSION['phpCAS']['user'] = $this->getUser(); |
751 |
$res = TRUE; |
752 |
} |
753 |
elseif ( $this->hasPT() ) { |
754 |
// if a Proxy Ticket was given, validate it
|
755 |
$cas->trace('PT `'.$this->getPT().'\' is present'); |
756 |
$this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts |
757 |
$cas->trace('PT `'.$this->getPT().'\' was validated'); |
758 |
if ( $this->isProxy() ) { |
759 |
$this->validatePGT($validate_url,$text_response,$tree_response); // idem |
760 |
$cas->trace('PGT `'.$this->getPGT().'\' was validated'); |
761 |
$_SESSION['phpCAS']['pgt'] = $this->getPGT(); |
762 |
} |
763 |
$_SESSION['phpCAS']['user'] = $this->getUser(); |
764 |
$res = TRUE; |
765 |
} |
766 |
else {
|
767 |
// no ticket given, not authenticated
|
768 |
$cas->trace('no ticket found'); |
769 |
} |
770 |
|
771 |
$cas->traceEnd($res); |
772 |
return $res; |
773 |
} |
774 |
|
775 |
/**
|
776 |
* This method tells if the current session is authenticated.
|
777 |
* @return true if authenticated based soley on $_SESSION variable
|
778 |
* @since 0.4.22 by Brendan Arnold
|
779 |
*/
|
780 |
function isSessionAuthenticated () |
781 |
{ |
782 |
return !empty($_SESSION['phpCAS']['user']); |
783 |
} |
784 |
|
785 |
/**
|
786 |
* This method tells if the user has already been (previously) authenticated
|
787 |
* by looking into the session variables.
|
788 |
*
|
789 |
* @note This function switches to callback mode when needed.
|
790 |
*
|
791 |
* @return TRUE when the user has already been authenticated; FALSE otherwise.
|
792 |
*
|
793 |
* @private
|
794 |
*/
|
795 |
function wasPreviouslyAuthenticated() |
796 |
{ |
797 |
$cas=new phpCas(); |
798 |
$cas->traceBegin();
|
799 |
|
800 |
if ( $this->isCallbackMode() ) { |
801 |
$this->callback();
|
802 |
} |
803 |
|
804 |
$auth = FALSE; |
805 |
|
806 |
if ( $this->isProxy() ) { |
807 |
// CAS proxy: username and PGT must be present
|
808 |
if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) { |
809 |
// authentication already done
|
810 |
$this->setUser($_SESSION['phpCAS']['user']); |
811 |
$this->setPGT($_SESSION['phpCAS']['pgt']); |
812 |
$cas->trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\''); |
813 |
$auth = TRUE; |
814 |
} elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) { |
815 |
// these two variables should be empty or not empty at the same time
|
816 |
$cas->trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty'); |
817 |
// unset all tickets to enforce authentication
|
818 |
unset($_SESSION['phpCAS']); |
819 |
$this->setST(''); |
820 |
$this->setPT(''); |
821 |
} elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) { |
822 |
// these two variables should be empty or not empty at the same time
|
823 |
$cas->trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty'); |
824 |
// unset all tickets to enforce authentication
|
825 |
unset($_SESSION['phpCAS']); |
826 |
$this->setST(''); |
827 |
$this->setPT(''); |
828 |
} else {
|
829 |
$cas->trace('neither user not PGT found'); |
830 |
} |
831 |
} else {
|
832 |
// `simple' CAS client (not a proxy): username must be present
|
833 |
if ( $this->isSessionAuthenticated() ) { |
834 |
// authentication already done
|
835 |
$this->setUser($_SESSION['phpCAS']['user']); |
836 |
$cas->trace('user = `'.$_SESSION['phpCAS']['user'].'\''); |
837 |
$auth = TRUE; |
838 |
} else {
|
839 |
$cas->trace('no user found'); |
840 |
} |
841 |
} |
842 |
|
843 |
$cas->traceEnd($auth); |
844 |
return $auth; |
845 |
} |
846 |
|
847 |
/**
|
848 |
* This method is used to redirect the client to the CAS server.
|
849 |
* It is used by CASClient::forceAuthentication() and CASClient::checkAuthentication().
|
850 |
* @param $gateway true to check authentication, false to force it
|
851 |
* @public
|
852 |
*/
|
853 |
function redirectToCas($gateway=false) |
854 |
{ |
855 |
$cas=new phpCas(); |
856 |
$cas->traceBegin();
|
857 |
$cas_url = $this->getServerLoginURL($gateway); |
858 |
header('Location: '.$cas_url); |
859 |
$this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED)); |
860 |
printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url); |
861 |
$this->printHTMLFooter();
|
862 |
$cas->traceExit();
|
863 |
exit();
|
864 |
} |
865 |
|
866 |
/**
|
867 |
* This method is used to logout from CAS.
|
868 |
* @param $url a URL that will be transmitted to the CAS server (to come back to when logged out)
|
869 |
* @public
|
870 |
*/
|
871 |
function logout($url = "") |
872 |
{ |
873 |
$cas=new phpCAS(); |
874 |
$cas->traceBegin();
|
875 |
$cas_url = $this->getServerLogoutURL(); |
876 |
// v0.4.14 sebastien.gougeon at univ-rennes1.fr
|
877 |
// header('Location: '.$cas_url);
|
878 |
if ( $url != "" ) { |
879 |
$url = '?service=' . $url; |
880 |
} |
881 |
header('Location: '.$cas_url . $url); |
882 |
session_unset(); |
883 |
session_destroy(); |
884 |
$this->printHTMLHeader($this->getString(CAS_STR_LOGOUT)); |
885 |
printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url); |
886 |
$this->printHTMLFooter();
|
887 |
$cas->traceExit();
|
888 |
exit();
|
889 |
} |
890 |
|
891 |
/** @} */
|
892 |
|
893 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
894 |
// XX XX
|
895 |
// XX BASIC CLIENT FEATURES (CAS 1.0) XX
|
896 |
// XX XX
|
897 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
898 |
|
899 |
// ########################################################################
|
900 |
// ST
|
901 |
// ########################################################################
|
902 |
/**
|
903 |
* @addtogroup internalBasic
|
904 |
* @{
|
905 |
*/
|
906 |
|
907 |
/**
|
908 |
* the Service Ticket provided in the URL of the request if present
|
909 |
* (empty otherwise). Written by CASClient::CASClient(), read by
|
910 |
* CASClient::getST() and CASClient::hasPGT().
|
911 |
*
|
912 |
* @hideinitializer
|
913 |
* @private
|
914 |
*/
|
915 |
var $_st = ''; |
916 |
|
917 |
/**
|
918 |
* This method returns the Service Ticket provided in the URL of the request.
|
919 |
* @return The service ticket.
|
920 |
* @private
|
921 |
*/
|
922 |
function getST() |
923 |
{ return $this->_st; } |
924 |
|
925 |
/**
|
926 |
* This method stores the Service Ticket.
|
927 |
* @param $st The Service Ticket.
|
928 |
* @private
|
929 |
*/
|
930 |
function setST($st) |
931 |
{ $this->_st = $st; } |
932 |
|
933 |
/**
|
934 |
* This method tells if a Service Ticket was stored.
|
935 |
* @return TRUE if a Service Ticket has been stored.
|
936 |
* @private
|
937 |
*/
|
938 |
function hasST() |
939 |
{ return !empty($this->_st); } |
940 |
|
941 |
/** @} */
|
942 |
|
943 |
// ########################################################################
|
944 |
// ST VALIDATION
|
945 |
// ########################################################################
|
946 |
/**
|
947 |
* @addtogroup internalBasic
|
948 |
* @{
|
949 |
*/
|
950 |
|
951 |
/**
|
952 |
* This method is used to validate a ST; halt on failure, and sets $validate_url,
|
953 |
* $text_reponse and $tree_response on success. These parameters are used later
|
954 |
* by CASClient::validatePGT() for CAS proxies.
|
955 |
*
|
956 |
* @param $validate_url the URL of the request to the CAS server.
|
957 |
* @param $text_response the response of the CAS server, as is (XML text).
|
958 |
* @param $tree_response the response of the CAS server, as a DOM XML tree.
|
959 |
*
|
960 |
* @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
|
961 |
*
|
962 |
* @private
|
963 |
*/
|
964 |
function validateST($validate_url,&$text_response,&$tree_response) |
965 |
{ |
966 |
phpCAS::traceBegin(); |
967 |
// build the URL to validate the ticket
|
968 |
$validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST(); |
969 |
if ( $this->isProxy() ) { |
970 |
// pass the callback url for CAS proxies
|
971 |
$validate_url .= '&pgtUrl='.$this->getCallbackURL(); |
972 |
} |
973 |
|
974 |
// open and read the URL
|
975 |
if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) { |
976 |
phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')'); |
977 |
$this->authError('ST not validated', |
978 |
$validate_url,
|
979 |
TRUE/*$no_response*/); |
980 |
} |
981 |
|
982 |
// analyze the result depending on the version
|
983 |
switch ($this->getServerVersion()) { |
984 |
case CAS_VERSION_1_0: |
985 |
if (preg_match('/^no\n/',$text_response)) { |
986 |
phpCAS::trace('ST has not been validated');
|
987 |
$this->authError('ST not validated', |
988 |
$validate_url,
|
989 |
FALSE/*$no_response*/, |
990 |
FALSE/*$bad_response*/, |
991 |
$text_response);
|
992 |
} |
993 |
if (!preg_match('/^yes\n/',$text_response)) { |
994 |
phpCAS::trace('ill-formed response');
|
995 |
$this->authError('ST not validated', |
996 |
$validate_url,
|
997 |
FALSE/*$no_response*/, |
998 |
TRUE/*$bad_response*/, |
999 |
$text_response);
|
1000 |
} |
1001 |
// ST has been validated, extract the user name
|
1002 |
$arr = preg_split('/\n/',$text_response); |
1003 |
$this->setUser(trim($arr[1])); |
1004 |
break;
|
1005 |
case CAS_VERSION_2_0: |
1006 |
// read the response of the CAS server into a DOM object
|
1007 |
if ( !($dom = domxml_open_mem($text_response))) { |
1008 |
phpCAS::trace('domxml_open_mem() failed');
|
1009 |
$this->authError('ST not validated', |
1010 |
$validate_url,
|
1011 |
FALSE/*$no_response*/, |
1012 |
TRUE/*$bad_response*/, |
1013 |
$text_response);
|
1014 |
} |
1015 |
// read the root node of the XML tree
|
1016 |
if ( !($tree_response = $dom->document_element()) ) { |
1017 |
phpCAS::trace('document_element() failed');
|
1018 |
$this->authError('ST not validated', |
1019 |
$validate_url,
|
1020 |
FALSE/*$no_response*/, |
1021 |
TRUE/*$bad_response*/, |
1022 |
$text_response);
|
1023 |
} |
1024 |
// insure that tag name is 'serviceResponse'
|
1025 |
if ( $tree_response->node_name() != 'serviceResponse' ) { |
1026 |
phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\''); |
1027 |
$this->authError('ST not validated', |
1028 |
$validate_url,
|
1029 |
FALSE/*$no_response*/, |
1030 |
TRUE/*$bad_response*/, |
1031 |
$text_response);
|
1032 |
} |
1033 |
if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) { |
1034 |
// authentication succeded, extract the user name
|
1035 |
if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) { |
1036 |
phpCAS::trace('<authenticationSuccess> found, but no <user>');
|
1037 |
$this->authError('ST not validated', |
1038 |
$validate_url,
|
1039 |
FALSE/*$no_response*/, |
1040 |
TRUE/*$bad_response*/, |
1041 |
$text_response);
|
1042 |
} |
1043 |
$user = trim($user_elements[0]->get_content()); |
1044 |
phpCAS::trace('user = `'.$user); |
1045 |
$this->setUser($user); |
1046 |
|
1047 |
} else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) { |
1048 |
phpCAS::trace('<authenticationFailure> found');
|
1049 |
// authentication failed, extract the error code and message
|
1050 |
$this->authError('ST not validated', |
1051 |
$validate_url,
|
1052 |
FALSE/*$no_response*/, |
1053 |
FALSE/*$bad_response*/, |
1054 |
$text_response,
|
1055 |
$failure_elements[0]->get_attribute('code')/*$err_code*/, |
1056 |
trim($failure_elements[0]->get_content())/*$err_msg*/); |
1057 |
} else {
|
1058 |
phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
|
1059 |
$this->authError('ST not validated', |
1060 |
$validate_url,
|
1061 |
FALSE/*$no_response*/, |
1062 |
TRUE/*$bad_response*/, |
1063 |
$text_response);
|
1064 |
} |
1065 |
break;
|
1066 |
} |
1067 |
|
1068 |
// at this step, ST has been validated and $this->_user has been set,
|
1069 |
phpCAS::traceEnd(TRUE);
|
1070 |
return TRUE; |
1071 |
} |
1072 |
|
1073 |
/** @} */
|
1074 |
|
1075 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
1076 |
// XX XX
|
1077 |
// XX PROXY FEATURES (CAS 2.0) XX
|
1078 |
// XX XX
|
1079 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
1080 |
|
1081 |
// ########################################################################
|
1082 |
// PROXYING
|
1083 |
// ########################################################################
|
1084 |
/**
|
1085 |
* @addtogroup internalProxy
|
1086 |
* @{
|
1087 |
*/
|
1088 |
|
1089 |
/**
|
1090 |
* A boolean telling if the client is a CAS proxy or not. Written by CASClient::CASClient(),
|
1091 |
* read by CASClient::isProxy().
|
1092 |
*
|
1093 |
* @private
|
1094 |
*/
|
1095 |
var $_proxy; |
1096 |
|
1097 |
/**
|
1098 |
* Tells if a CAS client is a CAS proxy or not
|
1099 |
*
|
1100 |
* @return TRUE when the CAS client is a CAs proxy, FALSE otherwise
|
1101 |
*
|
1102 |
* @private
|
1103 |
*/
|
1104 |
function isProxy() |
1105 |
{ |
1106 |
return $this->_proxy; |
1107 |
} |
1108 |
|
1109 |
/** @} */
|
1110 |
// ########################################################################
|
1111 |
// PGT
|
1112 |
// ########################################################################
|
1113 |
/**
|
1114 |
* @addtogroup internalProxy
|
1115 |
* @{
|
1116 |
*/
|
1117 |
|
1118 |
/**
|
1119 |
* the Proxy Grnting Ticket given by the CAS server (empty otherwise).
|
1120 |
* Written by CASClient::setPGT(), read by CASClient::getPGT() and CASClient::hasPGT().
|
1121 |
*
|
1122 |
* @hideinitializer
|
1123 |
* @private
|
1124 |
*/
|
1125 |
var $_pgt = ''; |
1126 |
|
1127 |
/**
|
1128 |
* This method returns the Proxy Granting Ticket given by the CAS server.
|
1129 |
* @return The Proxy Granting Ticket.
|
1130 |
* @private
|
1131 |
*/
|
1132 |
function getPGT() |
1133 |
{ return $this->_pgt; } |
1134 |
|
1135 |
/**
|
1136 |
* This method stores the Proxy Granting Ticket.
|
1137 |
* @param $pgt The Proxy Granting Ticket.
|
1138 |
* @private
|
1139 |
*/
|
1140 |
function setPGT($pgt) |
1141 |
{ $this->_pgt = $pgt; } |
1142 |
|
1143 |
/**
|
1144 |
* This method tells if a Proxy Granting Ticket was stored.
|
1145 |
* @return TRUE if a Proxy Granting Ticket has been stored.
|
1146 |
* @private
|
1147 |
*/
|
1148 |
function hasPGT() |
1149 |
{ return !empty($this->_pgt); } |
1150 |
|
1151 |
/** @} */
|
1152 |
|
1153 |
// ########################################################################
|
1154 |
// CALLBACK MODE
|
1155 |
// ########################################################################
|
1156 |
/**
|
1157 |
* @addtogroup internalCallback
|
1158 |
* @{
|
1159 |
*/
|
1160 |
/**
|
1161 |
* each PHP script using phpCAS in proxy mode is its own callback to get the
|
1162 |
* PGT back from the CAS server. callback_mode is detected by the constructor
|
1163 |
* thanks to the GET parameters.
|
1164 |
*/
|
1165 |
|
1166 |
/**
|
1167 |
* a boolean to know if the CAS client is running in callback mode. Written by
|
1168 |
* CASClient::setCallBackMode(), read by CASClient::isCallbackMode().
|
1169 |
*
|
1170 |
* @hideinitializer
|
1171 |
* @private
|
1172 |
*/
|
1173 |
var $_callback_mode = FALSE; |
1174 |
|
1175 |
/**
|
1176 |
* This method sets/unsets callback mode.
|
1177 |
*
|
1178 |
* @param $callback_mode TRUE to set callback mode, FALSE otherwise.
|
1179 |
*
|
1180 |
* @private
|
1181 |
*/
|
1182 |
function setCallbackMode($callback_mode) |
1183 |
{ |
1184 |
$this->_callback_mode = $callback_mode; |
1185 |
} |
1186 |
|
1187 |
/**
|
1188 |
* This method returns TRUE when the CAs client is running i callback mode,
|
1189 |
* FALSE otherwise.
|
1190 |
*
|
1191 |
* @return A boolean.
|
1192 |
*
|
1193 |
* @private
|
1194 |
*/
|
1195 |
function isCallbackMode() |
1196 |
{ |
1197 |
return $this->_callback_mode; |
1198 |
} |
1199 |
|
1200 |
/**
|
1201 |
* the URL that should be used for the PGT callback (in fact the URL of the
|
1202 |
* current request without any CGI parameter). Written and read by
|
1203 |
* CASClient::getCallbackURL().
|
1204 |
*
|
1205 |
* @hideinitializer
|
1206 |
* @private
|
1207 |
*/
|
1208 |
var $_callback_url = ''; |
1209 |
|
1210 |
/**
|
1211 |
* This method returns the URL that should be used for the PGT callback (in
|
1212 |
* fact the URL of the current request without any CGI parameter, except if
|
1213 |
* phpCAS::setFixedCallbackURL() was used).
|
1214 |
*
|
1215 |
* @return The callback URL
|
1216 |
*
|
1217 |
* @private
|
1218 |
*/
|
1219 |
function getCallbackURL() |
1220 |
{ |
1221 |
// the URL is built when needed only
|
1222 |
if ( empty($this->_callback_url) ) { |
1223 |
$final_uri = ''; |
1224 |
// remove the ticket if present in the URL
|
1225 |
$final_uri = 'https://'; |
1226 |
/* replaced by Julien Marchal - v0.4.6
|
1227 |
* $this->uri .= $_SERVER['SERVER_NAME'];
|
1228 |
*/
|
1229 |
if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){ |
1230 |
/* replaced by teedog - v0.4.12
|
1231 |
* $final_uri .= $_SERVER['SERVER_NAME'];
|
1232 |
*/
|
1233 |
if (empty($_SERVER['SERVER_NAME'])) { |
1234 |
$final_uri .= $_SERVER['HTTP_HOST']; |
1235 |
} else {
|
1236 |
$final_uri .= $_SERVER['SERVER_NAME']; |
1237 |
} |
1238 |
} else {
|
1239 |
$final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER']; |
1240 |
} |
1241 |
if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443) |
1242 |
|| (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) { |
1243 |
$final_uri .= ':'; |
1244 |
$final_uri .= $_SERVER['SERVER_PORT']; |
1245 |
} |
1246 |
$request_uri = $_SERVER['REQUEST_URI']; |
1247 |
$request_uri = preg_replace('/\?.*$/','',$request_uri); |
1248 |
$final_uri .= $request_uri; |
1249 |
$this->setCallbackURL($final_uri); |
1250 |
} |
1251 |
return $this->_callback_url; |
1252 |
} |
1253 |
|
1254 |
/**
|
1255 |
* This method sets the callback url.
|
1256 |
*
|
1257 |
* @param $callback_url url to set callback
|
1258 |
*
|
1259 |
* @private
|
1260 |
*/
|
1261 |
function setCallbackURL($url) |
1262 |
{ |
1263 |
return $this->_callback_url = $url; |
1264 |
} |
1265 |
|
1266 |
/**
|
1267 |
* This method is called by CASClient::CASClient() when running in callback
|
1268 |
* mode. It stores the PGT and its PGT Iou, prints its output and halts.
|
1269 |
*
|
1270 |
* @private
|
1271 |
*/
|
1272 |
function callback() |
1273 |
{ |
1274 |
phpCAS::traceBegin(); |
1275 |
$this->printHTMLHeader('phpCAS callback'); |
1276 |
$pgt_iou = $_GET['pgtIou']; |
1277 |
$pgt = $_GET['pgtId']; |
1278 |
phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')'); |
1279 |
echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>'; |
1280 |
$this->storePGT($pgt,$pgt_iou); |
1281 |
$this->printHTMLFooter();
|
1282 |
phpCAS::traceExit(); |
1283 |
} |
1284 |
|
1285 |
/** @} */
|
1286 |
|
1287 |
// ########################################################################
|
1288 |
// PGT STORAGE
|
1289 |
// ########################################################################
|
1290 |
/**
|
1291 |
* @addtogroup internalPGTStorage
|
1292 |
* @{
|
1293 |
*/
|
1294 |
|
1295 |
/**
|
1296 |
* an instance of a class inheriting of PGTStorage, used to deal with PGT
|
1297 |
* storage. Created by CASClient::setPGTStorageFile() or CASClient::setPGTStorageDB(), used
|
1298 |
* by CASClient::setPGTStorageFile(), CASClient::setPGTStorageDB() and CASClient::initPGTStorage().
|
1299 |
*
|
1300 |
* @hideinitializer
|
1301 |
* @private
|
1302 |
*/
|
1303 |
var $_pgt_storage = null; |
1304 |
|
1305 |
/**
|
1306 |
* This method is used to initialize the storage of PGT's.
|
1307 |
* Halts on error.
|
1308 |
*
|
1309 |
* @private
|
1310 |
*/
|
1311 |
function initPGTStorage() |
1312 |
{ |
1313 |
// if no SetPGTStorageXxx() has been used, default to file
|
1314 |
if ( !is_object($this->_pgt_storage) ) { |
1315 |
$this->setPGTStorageFile();
|
1316 |
} |
1317 |
|
1318 |
// initializes the storage
|
1319 |
$this->_pgt_storage->init();
|
1320 |
} |
1321 |
|
1322 |
/**
|
1323 |
* This method stores a PGT. Halts on error.
|
1324 |
*
|
1325 |
* @param $pgt the PGT to store
|
1326 |
* @param $pgt_iou its corresponding Iou
|
1327 |
*
|
1328 |
* @private
|
1329 |
*/
|
1330 |
function storePGT($pgt,$pgt_iou) |
1331 |
{ |
1332 |
// ensure that storage is initialized
|
1333 |
$this->initPGTStorage();
|
1334 |
// writes the PGT
|
1335 |
$this->_pgt_storage->write($pgt,$pgt_iou); |
1336 |
} |
1337 |
|
1338 |
/**
|
1339 |
* This method reads a PGT from its Iou and deletes the corresponding storage entry.
|
1340 |
*
|
1341 |
* @param $pgt_iou the PGT Iou
|
1342 |
*
|
1343 |
* @return The PGT corresponding to the Iou, FALSE when not found.
|
1344 |
*
|
1345 |
* @private
|
1346 |
*/
|
1347 |
function loadPGT($pgt_iou) |
1348 |
{ |
1349 |
// ensure that storage is initialized
|
1350 |
$this->initPGTStorage();
|
1351 |
// read the PGT
|
1352 |
return $this->_pgt_storage->read($pgt_iou); |
1353 |
} |
1354 |
|
1355 |
/**
|
1356 |
* This method is used to tell phpCAS to store the response of the
|
1357 |
* CAS server to PGT requests onto the filesystem.
|
1358 |
*
|
1359 |
* @param $format the format used to store the PGT's (`plain' and `xml' allowed)
|
1360 |
* @param $path the path where the PGT's should be stored
|
1361 |
*
|
1362 |
* @public
|
1363 |
*/
|
1364 |
function setPGTStorageFile($format='', |
1365 |
$path='') |
1366 |
{ |
1367 |
// check that the storage has not already been set
|
1368 |
if ( is_object($this->_pgt_storage) ) { |
1369 |
phpCAS::error('PGT storage already defined');
|
1370 |
} |
1371 |
|
1372 |
// create the storage object
|
1373 |
$this->_pgt_storage = new PGTStorageFile($this,$format,$path); |
1374 |
} |
1375 |
|
1376 |
/**
|
1377 |
* This method is used to tell phpCAS to store the response of the
|
1378 |
* CAS server to PGT requests into a database.
|
1379 |
* @note The connection to the database is done only when needed.
|
1380 |
* As a consequence, bad parameters are detected only when
|
1381 |
* initializing PGT storage.
|
1382 |
*
|
1383 |
* @param $user the user to access the data with
|
1384 |
* @param $password the user's password
|
1385 |
* @param $database_type the type of the database hosting the data
|
1386 |
* @param $hostname the server hosting the database
|
1387 |
* @param $port the port the server is listening on
|
1388 |
* @param $database the name of the database
|
1389 |
* @param $table the name of the table storing the data
|
1390 |
*
|
1391 |
* @public
|
1392 |
*/
|
1393 |
function setPGTStorageDB($user, |
1394 |
$password,
|
1395 |
$database_type,
|
1396 |
$hostname,
|
1397 |
$port,
|
1398 |
$database,
|
1399 |
$table)
|
1400 |
{ |
1401 |
// check that the storage has not already been set
|
1402 |
if ( is_object($this->_pgt_storage) ) { |
1403 |
phpCAS::error('PGT storage already defined');
|
1404 |
} |
1405 |
|
1406 |
// warn the user that he should use file storage...
|
1407 |
trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING); |
1408 |
|
1409 |
// create the storage object
|
1410 |
$this->_pgt_storage = new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table); |
1411 |
} |
1412 |
|
1413 |
// ########################################################################
|
1414 |
// PGT VALIDATION
|
1415 |
// ########################################################################
|
1416 |
/**
|
1417 |
* This method is used to validate a PGT; halt on failure.
|
1418 |
*
|
1419 |
* @param $validate_url the URL of the request to the CAS server.
|
1420 |
* @param $text_response the response of the CAS server, as is (XML text); result
|
1421 |
* of CASClient::validateST() or CASClient::validatePT().
|
1422 |
* @param $tree_response the response of the CAS server, as a DOM XML tree; result
|
1423 |
* of CASClient::validateST() or CASClient::validatePT().
|
1424 |
*
|
1425 |
* @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
|
1426 |
*
|
1427 |
* @private
|
1428 |
*/
|
1429 |
function validatePGT(&$validate_url,$text_response,$tree_response) |
1430 |
{ |
1431 |
phpCAS::traceBegin(); |
1432 |
if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) { |
1433 |
phpCAS::trace('<proxyGrantingTicket> not found');
|
1434 |
// authentication succeded, but no PGT Iou was transmitted
|
1435 |
$this->authError('Ticket validated but no PGT Iou transmitted', |
1436 |
$validate_url,
|
1437 |
FALSE/*$no_response*/, |
1438 |
FALSE/*$bad_response*/, |
1439 |
$text_response);
|
1440 |
} else {
|
1441 |
// PGT Iou transmitted, extract it
|
1442 |
$pgt_iou = trim($arr[0]->get_content()); |
1443 |
$pgt = $this->loadPGT($pgt_iou); |
1444 |
if ( $pgt == FALSE ) { |
1445 |
phpCAS::trace('could not load PGT');
|
1446 |
$this->authError('PGT Iou was transmitted but PGT could not be retrieved', |
1447 |
$validate_url,
|
1448 |
FALSE/*$no_response*/, |
1449 |
FALSE/*$bad_response*/, |
1450 |
$text_response);
|
1451 |
} |
1452 |
$this->setPGT($pgt); |
1453 |
} |
1454 |
phpCAS::traceEnd(TRUE);
|
1455 |
return TRUE; |
1456 |
} |
1457 |
|
1458 |
// ########################################################################
|
1459 |
// PGT VALIDATION
|
1460 |
// ########################################################################
|
1461 |
|
1462 |
/**
|
1463 |
* This method is used to retrieve PT's from the CAS server thanks to a PGT.
|
1464 |
*
|
1465 |
* @param $target_service the service to ask for with the PT.
|
1466 |
* @param $err_code an error code (PHPCAS_SERVICE_OK on success).
|
1467 |
* @param $err_msg an error message (empty on success).
|
1468 |
*
|
1469 |
* @return a Proxy Ticket, or FALSE on error.
|
1470 |
*
|
1471 |
* @private
|
1472 |
*/
|
1473 |
function retrievePT($target_service,&$err_code,&$err_msg) |
1474 |
{ |
1475 |
phpCAS::traceBegin(); |
1476 |
|
1477 |
// by default, $err_msg is set empty and $pt to TRUE. On error, $pt is
|
1478 |
// set to false and $err_msg to an error message. At the end, if $pt is FALSE
|
1479 |
// and $error_msg is still empty, it is set to 'invalid response' (the most
|
1480 |
// commonly encountered error).
|
1481 |
$err_msg = ''; |
1482 |
|
1483 |
// build the URL to retrieve the PT
|
1484 |
// $cas_url = $this->getServerProxyURL().'?targetService='.preg_replace('/&/','%26',$target_service).'&pgt='.$this->getPGT();
|
1485 |
$cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT(); |
1486 |
|
1487 |
// open and read the URL
|
1488 |
if ( !$this->readURL($cas_url,''/*cookies*/,$headers,$cas_response,$err_msg) ) { |
1489 |
phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')'); |
1490 |
$err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE; |
1491 |
$err_msg = 'could not retrieve PT (no response from the CAS server)'; |
1492 |
phpCAS::traceEnd(FALSE);
|
1493 |
return FALSE; |
1494 |
} |
1495 |
|
1496 |
$bad_response = FALSE; |
1497 |
|
1498 |
if ( !$bad_response ) { |
1499 |
// read the response of the CAS server into a DOM object
|
1500 |
if ( !($dom = @domxml_open_mem($cas_response))) { |
1501 |
phpCAS::trace('domxml_open_mem() failed');
|
1502 |
// read failed
|
1503 |
$bad_response = TRUE; |
1504 |
} |
1505 |
} |
1506 |
|
1507 |
if ( !$bad_response ) { |
1508 |
// read the root node of the XML tree
|
1509 |
if ( !($root = $dom->document_element()) ) { |
1510 |
phpCAS::trace('document_element() failed');
|
1511 |
// read failed
|
1512 |
$bad_response = TRUE; |
1513 |
} |
1514 |
} |
1515 |
|
1516 |
if ( !$bad_response ) { |
1517 |
// insure that tag name is 'serviceResponse'
|
1518 |
if ( $root->node_name() != 'serviceResponse' ) { |
1519 |
phpCAS::trace('node_name() failed');
|
1520 |
// bad root node
|
1521 |
$bad_response = TRUE; |
1522 |
} |
1523 |
} |
1524 |
|
1525 |
if ( !$bad_response ) { |
1526 |
// look for a proxySuccess tag
|
1527 |
if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) { |
1528 |
// authentication succeded, look for a proxyTicket tag
|
1529 |
if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) { |
1530 |
$err_code = PHPCAS_SERVICE_OK; |
1531 |
$err_msg = ''; |
1532 |
phpCAS::trace('original PT: '.trim($arr[0]->get_content())); |
1533 |
$pt = trim($arr[0]->get_content()); |
1534 |
phpCAS::traceEnd($pt);
|
1535 |
return $pt; |
1536 |
} else {
|
1537 |
phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
|
1538 |
} |
1539 |
} |
1540 |
// look for a proxyFailure tag
|
1541 |
else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) { |
1542 |
// authentication failed, extract the error
|
1543 |
$err_code = PHPCAS_SERVICE_PT_FAILURE; |
1544 |
$err_msg = 'PT retrieving failed (code=`' |
1545 |
.$arr[0]->get_attribute('code') |
1546 |
.'\', message=`'
|
1547 |
.trim($arr[0]->get_content()) |
1548 |
.'\')';
|
1549 |
phpCAS::traceEnd(FALSE);
|
1550 |
return FALSE; |
1551 |
} else {
|
1552 |
phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
|
1553 |
} |
1554 |
} |
1555 |
|
1556 |
// at this step, we are sure that the response of the CAS server was ill-formed
|
1557 |
$err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE; |
1558 |
$err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')'; |
1559 |
|
1560 |
phpCAS::traceEnd(FALSE);
|
1561 |
return FALSE; |
1562 |
} |
1563 |
|
1564 |
// ########################################################################
|
1565 |
// ACCESS TO EXTERNAL SERVICES
|
1566 |
// ########################################################################
|
1567 |
|
1568 |
/**
|
1569 |
* This method is used to acces a remote URL.
|
1570 |
*
|
1571 |
* @param $url the URL to access.
|
1572 |
* @param $cookies an array containing cookies strings such as 'name=val'
|
1573 |
* @param $headers an array containing the HTTP header lines of the response
|
1574 |
* (an empty array on failure).
|
1575 |
* @param $body the body of the response, as a string (empty on failure).
|
1576 |
* @param $err_msg an error message, filled on failure.
|
1577 |
*
|
1578 |
* @return TRUE on success, FALSE otherwise (in this later case, $err_msg
|
1579 |
* contains an error message).
|
1580 |
*
|
1581 |
* @private
|
1582 |
*/
|
1583 |
function readURL($url,$cookies,&$headers,&$body,&$err_msg) |
1584 |
{ |
1585 |
$cas=new phpCAS(); |
1586 |
$cas->traceBegin();
|
1587 |
$headers = ''; |
1588 |
$body = ''; |
1589 |
$err_msg = ''; |
1590 |
|
1591 |
$res = TRUE; |
1592 |
|
1593 |
// initialize the CURL session
|
1594 |
$ch = curl_init($url); |
1595 |
|
1596 |
// verify the the server's certificate corresponds to its name
|
1597 |
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1); |
1598 |
// but do not verify the certificate itself
|
1599 |
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); |
1600 |
|
1601 |
// return the CURL output into a variable
|
1602 |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
1603 |
// include the HTTP header with the body
|
1604 |
curl_setopt($ch, CURLOPT_HEADER, 1); |
1605 |
// add cookies headers
|
1606 |
if ( is_array($cookies) ) { |
1607 |
curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies)); |
1608 |
} |
1609 |
// perform the query
|
1610 |
$buf = curl_exec ($ch); |
1611 |
if ( $buf === FALSE ) { |
1612 |
$cas->trace('cur_exec() failed'); |
1613 |
$err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch); |
1614 |
// close the CURL session
|
1615 |
curl_close ($ch);
|
1616 |
$res = FALSE; |
1617 |
} else {
|
1618 |
// close the CURL session
|
1619 |
curl_close ($ch);
|
1620 |
|
1621 |
// find the end of the headers
|
1622 |
// note: strpos($str,"\n\r\n\r") does not work (?)
|
1623 |
$pos = FALSE; |
1624 |
for ($i=0; $i<strlen($buf); $i++) { |
1625 |
if ( $buf[$i] == chr(13) ) |
1626 |
if ( $buf[$i+1] == chr(10) ) |
1627 |
if ( $buf[$i+2] == chr(13) ) |
1628 |
if ( $buf[$i+3] == chr(10) ) { |
1629 |
// header found
|
1630 |
$pos = $i; |
1631 |
break;
|
1632 |
} |
1633 |
} |
1634 |
|
1635 |
if ( $pos === FALSE ) { |
1636 |
// end of header not found
|
1637 |
$err_msg = 'no header found'; |
1638 |
$cas->trace($err_msg); |
1639 |
$res = FALSE; |
1640 |
} else {
|
1641 |
// extract headers into an array
|
1642 |
$headers = preg_split ("/[\n\r]+/",substr($buf,0,$pos)); |
1643 |
// extract body into a string
|
1644 |
$body = substr($buf,$pos+4); |
1645 |
} |
1646 |
} |
1647 |
|
1648 |
$cas->traceEnd($res); |
1649 |
return $res; |
1650 |
} |
1651 |
|
1652 |
/**
|
1653 |
* This method is used to access an HTTP[S] service.
|
1654 |
*
|
1655 |
* @param $url the service to access.
|
1656 |
* @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
|
1657 |
* success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
|
1658 |
* PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
|
1659 |
* @param $output the output of the service (also used to give an error
|
1660 |
* message on failure).
|
1661 |
*
|
1662 |
* @return TRUE on success, FALSE otherwise (in this later case, $err_code
|
1663 |
* gives the reason why it failed and $output contains an error message).
|
1664 |
*
|
1665 |
* @public
|
1666 |
*/
|
1667 |
function serviceWeb($url,&$err_code,&$output) |
1668 |
{ |
1669 |
phpCAS::traceBegin(); |
1670 |
// at first retrieve a PT
|
1671 |
$pt = $this->retrievePT($url,$err_code,$output); |
1672 |
|
1673 |
$res = TRUE; |
1674 |
|
1675 |
// test if PT was retrieved correctly
|
1676 |
if ( !$pt ) { |
1677 |
// note: $err_code and $err_msg are filled by CASClient::retrievePT()
|
1678 |
phpCAS::trace('PT was not retrieved correctly');
|
1679 |
$res = FALSE; |
1680 |
} else {
|
1681 |
// add cookies if necessary
|
1682 |
if ( is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) { |
1683 |
foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) { |
1684 |
$cookies[] = $name.'='.$val; |
1685 |
} |
1686 |
} |
1687 |
|
1688 |
// build the URL including the PT
|
1689 |
if ( strstr($url,'?') === FALSE ) { |
1690 |
$service_url = $url.'?ticket='.$pt; |
1691 |
} else {
|
1692 |
$service_url = $url.'&ticket='.$pt; |
1693 |
} |
1694 |
|
1695 |
phpCAS::trace('reading URL`'.$service_url.'\''); |
1696 |
if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) { |
1697 |
phpCAS::trace('could not read URL`'.$service_url.'\''); |
1698 |
$err_code = PHPCAS_SERVICE_NOT_AVAILABLE; |
1699 |
// give an error message
|
1700 |
$output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE), |
1701 |
$service_url,
|
1702 |
$err_msg);
|
1703 |
$res = FALSE; |
1704 |
} else {
|
1705 |
// URL has been fetched, extract the cookies
|
1706 |
phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:'); |
1707 |
foreach ( $headers as $header ) { |
1708 |
// test if the header is a cookie
|
1709 |
if ( preg_match('/^Set-Cookie:/',$header) ) { |
1710 |
// the header is a cookie, remove the beginning
|
1711 |
$header_val = preg_replace('/^Set-Cookie: */','',$header); |
1712 |
// extract interesting information
|
1713 |
$name_val = strtok($header_val,'; '); |
1714 |
// extract the name and the value of the cookie
|
1715 |
$cookie_name = strtok($name_val,'='); |
1716 |
$cookie_val = strtok('='); |
1717 |
// store the cookie
|
1718 |
$_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val; |
1719 |
phpCAS::trace($cookie_name.' -> '.$cookie_val); |
1720 |
} |
1721 |
} |
1722 |
} |
1723 |
} |
1724 |
|
1725 |
phpCAS::traceEnd($res);
|
1726 |
return $res; |
1727 |
} |
1728 |
|
1729 |
/**
|
1730 |
* This method is used to access an IMAP/POP3/NNTP service.
|
1731 |
*
|
1732 |
* @param $url a string giving the URL of the service, including the mailing box
|
1733 |
* for IMAP URLs, as accepted by imap_open().
|
1734 |
* @param $flags options given to imap_open().
|
1735 |
* @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
|
1736 |
* success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
|
1737 |
* PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
|
1738 |
* @param $err_msg an error message on failure
|
1739 |
* @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
|
1740 |
* on success, FALSE on error).
|
1741 |
*
|
1742 |
* @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
|
1743 |
* gives the reason why it failed and $err_msg contains an error message).
|
1744 |
*
|
1745 |
* @public
|
1746 |
*/
|
1747 |
function serviceMail($url,$flags,&$err_code,&$err_msg,&$pt) |
1748 |
{ |
1749 |
phpCAS::traceBegin(); |
1750 |
// at first retrieve a PT
|
1751 |
$pt = $this->retrievePT($target_service,$err_code,$output); |
1752 |
|
1753 |
$stream = FALSE; |
1754 |
|
1755 |
// test if PT was retrieved correctly
|
1756 |
if ( !$pt ) { |
1757 |
// note: $err_code and $err_msg are filled by CASClient::retrievePT()
|
1758 |
phpCAS::trace('PT was not retrieved correctly');
|
1759 |
} else {
|
1760 |
phpCAS::trace('opening IMAP URL `'.$url.'\'...'); |
1761 |
$stream = @imap_open($url,$this->getUser(),$pt,$flags); |
1762 |
if ( !$stream ) { |
1763 |
phpCAS::trace('could not open URL');
|
1764 |
$err_code = PHPCAS_SERVICE_NOT_AVAILABLE; |
1765 |
// give an error message
|
1766 |
$err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE), |
1767 |
$service_url,
|
1768 |
var_export(imap_errors(),TRUE)); |
1769 |
$pt = FALSE; |
1770 |
$stream = FALSE; |
1771 |
} else {
|
1772 |
phpCAS::trace('ok');
|
1773 |
} |
1774 |
} |
1775 |
|
1776 |
phpCAS::traceEnd($stream);
|
1777 |
return $stream; |
1778 |
} |
1779 |
|
1780 |
/** @} */
|
1781 |
|
1782 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
1783 |
// XX XX
|
1784 |
// XX PROXIED CLIENT FEATURES (CAS 2.0) XX
|
1785 |
// XX XX
|
1786 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
1787 |
|
1788 |
// ########################################################################
|
1789 |
// PT
|
1790 |
// ########################################################################
|
1791 |
/**
|
1792 |
* @addtogroup internalProxied
|
1793 |
* @{
|
1794 |
*/
|
1795 |
|
1796 |
/**
|
1797 |
* the Proxy Ticket provided in the URL of the request if present
|
1798 |
* (empty otherwise). Written by CASClient::CASClient(), read by
|
1799 |
* CASClient::getPT() and CASClient::hasPGT().
|
1800 |
*
|
1801 |
* @hideinitializer
|
1802 |
* @private
|
1803 |
*/
|
1804 |
var $_pt = ''; |
1805 |
|
1806 |
/**
|
1807 |
* This method returns the Proxy Ticket provided in the URL of the request.
|
1808 |
* @return The proxy ticket.
|
1809 |
* @private
|
1810 |
*/
|
1811 |
function getPT() |
1812 |
{ |
1813 |
return 'ST'.substr($this->_pt, 2); |
1814 |
} |
1815 |
|
1816 |
/**
|
1817 |
* This method stores the Proxy Ticket.
|
1818 |
* @param $pt The Proxy Ticket.
|
1819 |
* @private
|
1820 |
*/
|
1821 |
function setPT($pt) |
1822 |
{ $this->_pt = $pt; } |
1823 |
|
1824 |
/**
|
1825 |
* This method tells if a Proxy Ticket was stored.
|
1826 |
* @return TRUE if a Proxy Ticket has been stored.
|
1827 |
* @private
|
1828 |
*/
|
1829 |
function hasPT() |
1830 |
{ return !empty($this->_pt); } |
1831 |
|
1832 |
/** @} */
|
1833 |
// ########################################################################
|
1834 |
// PT VALIDATION
|
1835 |
// ########################################################################
|
1836 |
/**
|
1837 |
* @addtogroup internalProxied
|
1838 |
* @{
|
1839 |
*/
|
1840 |
|
1841 |
/**
|
1842 |
* This method is used to validate a PT; halt on failure
|
1843 |
*
|
1844 |
* @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
|
1845 |
*
|
1846 |
* @private
|
1847 |
*/
|
1848 |
function validatePT(&$validate_url,&$text_response,&$tree_response) |
1849 |
{ |
1850 |
$cas=new phpCAS(); |
1851 |
$cas->traceBegin();
|
1852 |
// build the URL to validate the ticket
|
1853 |
$validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT(); |
1854 |
|
1855 |
if ( $this->isProxy() ) { |
1856 |
// pass the callback url for CAS proxies
|
1857 |
$validate_url .= '&pgtUrl='.$this->getCallbackURL(); |
1858 |
} |
1859 |
|
1860 |
// open and read the URL
|
1861 |
if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) { |
1862 |
$cas->trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')'); |
1863 |
$this->authError('PT not validated', |
1864 |
$validate_url,
|
1865 |
TRUE/*$no_response*/); |
1866 |
} |
1867 |
|
1868 |
// read the response of the CAS server into a DOM object
|
1869 |
if ( !($dom = domxml_open_mem($text_response))) { |
1870 |
// read failed
|
1871 |
$this->authError('PT not validated', |
1872 |
$validate_url,
|
1873 |
FALSE/*$no_response*/, |
1874 |
TRUE/*$bad_response*/, |
1875 |
$text_response);
|
1876 |
} |
1877 |
// read the root node of the XML tree
|
1878 |
if ( !($tree_response = $dom->document_element()) ) { |
1879 |
// read failed
|
1880 |
$this->authError('PT not validated', |
1881 |
$validate_url,
|
1882 |
FALSE/*$no_response*/, |
1883 |
TRUE/*$bad_response*/, |
1884 |
$text_response);
|
1885 |
} |
1886 |
// insure that tag name is 'serviceResponse'
|
1887 |
if ( $tree_response->node_name() != 'serviceResponse' ) { |
1888 |
// bad root node
|
1889 |
$this->authError('PT not validated', |
1890 |
$validate_url,
|
1891 |
FALSE/*$no_response*/, |
1892 |
TRUE/*$bad_response*/, |
1893 |
$text_response);
|
1894 |
} |
1895 |
if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) { |
1896 |
// authentication succeded, extract the user name
|
1897 |
if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) { |
1898 |
// no user specified => error
|
1899 |
$this->authError('PT not validated', |
1900 |
$validate_url,
|
1901 |
FALSE/*$no_response*/, |
1902 |
TRUE/*$bad_response*/, |
1903 |
$text_response);
|
1904 |
} |
1905 |
$this->setUser(trim($arr[0]->get_content())); |
1906 |
|
1907 |
} else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) { |
1908 |
// authentication succeded, extract the error code and message
|
1909 |
$this->authError('PT not validated', |
1910 |
$validate_url,
|
1911 |
FALSE/*$no_response*/, |
1912 |
FALSE/*$bad_response*/, |
1913 |
$text_response,
|
1914 |
$arr[0]->get_attribute('code')/*$err_code*/, |
1915 |
trim($arr[0]->get_content())/*$err_msg*/); |
1916 |
} else {
|
1917 |
$this->authError('PT not validated', |
1918 |
$validate_url,
|
1919 |
FALSE/*$no_response*/, |
1920 |
TRUE/*$bad_response*/, |
1921 |
$text_response);
|
1922 |
} |
1923 |
|
1924 |
// at this step, PT has been validated and $this->_user has been set,
|
1925 |
|
1926 |
$cas->traceEnd(TRUE); |
1927 |
return TRUE; |
1928 |
} |
1929 |
|
1930 |
/** @} */
|
1931 |
|
1932 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
1933 |
// XX XX
|
1934 |
// XX MISC XX
|
1935 |
// XX XX
|
1936 |
// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
1937 |
|
1938 |
/**
|
1939 |
* @addtogroup internalMisc
|
1940 |
* @{
|
1941 |
*/
|
1942 |
|
1943 |
// ########################################################################
|
1944 |
// URL
|
1945 |
// ########################################################################
|
1946 |
/**
|
1947 |
* the URL of the current request (without any ticket CGI parameter). Written
|
1948 |
* and read by CASClient::getURL().
|
1949 |
*
|
1950 |
* @hideinitializer
|
1951 |
* @private
|
1952 |
*/
|
1953 |
var $_url = ''; |
1954 |
|
1955 |
/**
|
1956 |
* This method returns the URL of the current request (without any ticket
|
1957 |
* CGI parameter).
|
1958 |
*
|
1959 |
* @return The URL
|
1960 |
*
|
1961 |
* @private
|
1962 |
*/
|
1963 |
function getURL() |
1964 |
{ |
1965 |
$cas=new phpCAS(); |
1966 |
$cas->traceBegin();
|
1967 |
// the URL is built when needed only
|
1968 |
if ( empty($this->_url) ) { |
1969 |
$final_uri = ''; |
1970 |
// remove the ticket if present in the URL
|
1971 |
$final_uri = ($this->isHttps()) ? 'https' : 'http'; |
1972 |
$final_uri .= '://'; |
1973 |
/* replaced by Julien Marchal - v0.4.6
|
1974 |
* $this->_url .= $_SERVER['SERVER_NAME'];
|
1975 |
*/
|
1976 |
if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){ |
1977 |
/* replaced by teedog - v0.4.12
|
1978 |
* $this->_url .= $_SERVER['SERVER_NAME'];
|
1979 |
*/
|
1980 |
if (empty($_SERVER['SERVER_NAME'])) { |
1981 |
$server_name = $_SERVER['HTTP_HOST']; |
1982 |
} else {
|
1983 |
$server_name = $_SERVER['SERVER_NAME']; |
1984 |
} |
1985 |
} else {
|
1986 |
$server_name = $_SERVER['HTTP_X_FORWARDED_SERVER']; |
1987 |
} |
1988 |
$final_uri .= $server_name; |
1989 |
if (!strpos($server_name, ':')) { |
1990 |
if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443) |
1991 |
|| (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) { |
1992 |
$final_uri .= ':'; |
1993 |
$final_uri .= $_SERVER['SERVER_PORT']; |
1994 |
} |
1995 |
} |
1996 |
|
1997 |
$final_uri .= strtok($_SERVER['REQUEST_URI'],"?"); |
1998 |
$cgi_params = '?'.strtok("?"); |
1999 |
// remove the ticket if present in the CGI parameters
|
2000 |
$cgi_params = preg_replace('/&ticket=[^&]*/','',$cgi_params); |
2001 |
$cgi_params = preg_replace('/\?ticket=[^&;]*/','?',$cgi_params); |
2002 |
$cgi_params = preg_replace('/\?%26/','?',$cgi_params); |
2003 |
$cgi_params = preg_replace('/\?&/','?',$cgi_params); |
2004 |
$cgi_params = preg_replace('/\?$/','',$cgi_params); |
2005 |
$final_uri .= $cgi_params; |
2006 |
$this->setURL($final_uri); |
2007 |
} |
2008 |
$cas->traceEnd($this->_url); |
2009 |
return $this->_url; |
2010 |
} |
2011 |
|
2012 |
/**
|
2013 |
* This method sets the URL of the current request
|
2014 |
*
|
2015 |
* @param $url url to set for service
|
2016 |
*
|
2017 |
* @private
|
2018 |
*/
|
2019 |
function setURL($url) |
2020 |
{ |
2021 |
$this->_url = $url; |
2022 |
} |
2023 |
|
2024 |
// ########################################################################
|
2025 |
// AUTHENTICATION ERROR HANDLING
|
2026 |
// ########################################################################
|
2027 |
/**
|
2028 |
* This method is used to print the HTML output when the user was not authenticated.
|
2029 |
*
|
2030 |
* @param $failure the failure that occured
|
2031 |
* @param $cas_url the URL the CAS server was asked for
|
2032 |
* @param $no_response the response from the CAS server (other
|
2033 |
* parameters are ignored if TRUE)
|
2034 |
* @param $bad_response bad response from the CAS server ($err_code
|
2035 |
* and $err_msg ignored if TRUE)
|
2036 |
* @param $cas_response the response of the CAS server
|
2037 |
* @param $err_code the error code given by the CAS server
|
2038 |
* @param $err_msg the error message given by the CAS server
|
2039 |
*
|
2040 |
* @private
|
2041 |
*/
|
2042 |
function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='') |
2043 |
{ |
2044 |
phpCAS::traceBegin(); |
2045 |
|
2046 |
$this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED)); |
2047 |
printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),$this->getURL(),$_SERVER['SERVER_ADMIN']); |
2048 |
phpCAS::trace('CAS URL: '.$cas_url); |
2049 |
phpCAS::trace('Authentication failure: '.$failure); |
2050 |
if ( $no_response ) { |
2051 |
phpCAS::trace('Reason: no response from the CAS server');
|
2052 |
} else {
|
2053 |
if ( $bad_response ) { |
2054 |
phpCAS::trace('Reason: bad response from the CAS server');
|
2055 |
} else {
|
2056 |
switch ($this->getServerVersion()) { |
2057 |
case CAS_VERSION_1_0: |
2058 |
phpCAS::trace('Reason: CAS error');
|
2059 |
break;
|
2060 |
case CAS_VERSION_2_0: |
2061 |
if ( empty($err_code) ) |
2062 |
phpCAS::trace('Reason: no CAS error');
|
2063 |
else
|
2064 |
phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg); |
2065 |
break;
|
2066 |
} |
2067 |
} |
2068 |
phpCAS::trace('CAS response: '.$cas_response); |
2069 |
} |
2070 |
$this->printHTMLFooter();
|
2071 |
phpCAS::traceExit(); |
2072 |
exit();
|
2073 |
} |
2074 |
|
2075 |
/** @} */
|
2076 |
} |
2077 |
|
2078 |
?>
|