00001 <?php
00002
00008
00009 include_once(dirname(__FILE__).'/languages/languages.php');
00010
00011
00012 include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php');
00013
00022 class CASClient
00023 {
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00052 function HTMLFilterOutput($str)
00053 {
00054 $str = str_replace('__CAS_VERSION__',$this->getServerVersion(),$str);
00055 $str = str_replace('__PHPCAS_VERSION__',phpCAS::getVersion(),$str);
00056 $str = str_replace('__SERVER_BASE_URL__',$this->getServerBaseURL(),$str);
00057 echo $str;
00058 }
00059
00068 var $_output_header = '';
00069
00079 function printHTMLHeader($title)
00080 {
00081 $this->HTMLFilterOutput(str_replace('__TITLE__',
00082 $title,
00083 (empty($this->_output_header)
00084 ? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
00085 : $this->output_header)
00086 )
00087 );
00088 }
00089
00098 var $_output_footer = '';
00099
00107 function printHTMLFooter()
00108 {
00109 $this->HTMLFilterOutput(empty($this->_output_footer)
00110 ?('<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>')
00111 :$this->_output_footer);
00112 }
00113
00121 function setHTMLHeader($header)
00122 {
00123 $this->_output_header = $header;
00124 }
00125
00133 function setHTMLFooter($footer)
00134 {
00135 $this->_output_footer = $footer;
00136 }
00137
00139
00140
00141
00156 var $_lang = '';
00157
00165 function getLang()
00166 {
00167 if ( empty($this->_lang) )
00168 $this->setLang(PHPCAS_LANG_DEFAULT);
00169 return $this->_lang;
00170 }
00171
00181 var $_strings;
00182
00192 function getString($str)
00193 {
00194
00195 $this->getLang();
00196
00197 if ( !isset($this->_strings[$str]) ) {
00198 trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR);
00199 }
00200 return $this->_strings[$str];
00201 }
00202
00212 function setLang($lang)
00213 {
00214
00215 include_once(dirname(__FILE__).'/languages/'.$lang.'.php');
00216
00217 if ( !is_array($this->_strings) ) {
00218 trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR);
00219 }
00220 $this->_lang = $lang;
00221 }
00222
00224 // ########################################################################
00225 // CAS SERVER CONFIG
00226 // ########################################################################
00256 var $_server = array(
00257 'version' => -1,
00258 'hostname' => 'none',
00259 'port' => -1,
00260 'uri' => 'none'
00261 );
00262
00268 function getServerVersion()
00269 {
00270 return $this->_server['version'];
00271 }
00272
00278 function getServerHostname()
00279 { return $this->_server['hostname']; }
00280
00286 function getServerPort()
00287 { return $this->_server['port']; }
00288
00294 function getServerURI()
00295 { return $this->_server['uri']; }
00296
00302 function getServerBaseURL()
00303 {
00304 // the URL is build only when needed
00305 if ( empty($this->_server['base_url']) ) {
00306
00307 // to do: undo this
00308 //$this->_server['base_url'] = 'https:
00309 $this->_server['base_url'] = 'https://'
00310 .$this->getServerHostname()
00311 .':'
00312 .$this->getServerPort()
00313 .$this->getServerURI();
00314 }
00315
00316 return $this->_server['base_url'];
00317 }
00318
00325 function getServerLoginURL($gateway)
00326 {
00327 phpCAS::traceBegin();
00328
00329 if ( empty($this->_server['login_url']) ) {
00330 $this->_server['login_url'] = $this->getServerBaseURL();
00331 $this->_server['login_url'] .= 'login?service=';
00332 $this->_server['login_url'] .= preg_replace('/&/','%26',$this->getURL());
00333 if ($gateway) {
00334 $this->_server['login_url'] .= '&gateway=true';
00335 }
00336 }
00337 phpCAS::traceEnd($this->_server['login_url']);
00338 return $this->_server['login_url'];
00339 }
00340
00346 function getServerServiceValidateURL()
00347 {
00348
00349 if ( empty($this->_server['service_validate_url']) ) {
00350 switch ($this->getServerVersion()) {
00351 case CAS_VERSION_1_0:
00352 $this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
00353 break;
00354 case CAS_VERSION_2_0:
00355 $this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
00356 break;
00357 }
00358 }
00359 return $this->_server['service_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL());
00360 }
00361
00367 function getServerProxyValidateURL()
00368 {
00369
00370 if ( empty($this->_server['proxy_validate_url']) ) {
00371 switch ($this->getServerVersion()) {
00372 case CAS_VERSION_1_0:
00373 $this->_server['proxy_validate_url'] = '';
00374 break;
00375 case CAS_VERSION_2_0:
00376 $this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
00377 break;
00378 }
00379 }
00380 return $this->_server['proxy_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL());
00381 }
00382
00388 function getServerProxyURL()
00389 {
00390
00391 if ( empty($this->_server['proxy_url']) ) {
00392 switch ($this->getServerVersion()) {
00393 case CAS_VERSION_1_0:
00394 $this->_server['proxy_url'] = '';
00395 break;
00396 case CAS_VERSION_2_0:
00397 $this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
00398 break;
00399 }
00400 }
00401 return $this->_server['proxy_url'];
00402 }
00403
00409 function getServerLogoutURL()
00410 {
00411
00412 if ( empty($this->_server['logout_url']) ) {
00413 $this->_server['logout_url'] = $this->getServerBaseURL().'logout';
00414 }
00415 return $this->_server['logout_url'];
00416 }
00417
00418
00419
00420
00435 function CASClient($server_version,
00436 $proxy,
00437 $server_hostname,
00438 $server_port,
00439 $server_uri,
00440 $start_session = true)
00441 {
00442 phpCAS::traceBegin();
00443
00444
00445 if ($start_session) {
00446 session_start();
00447 }
00448
00449 $this->_proxy = $proxy;
00450
00451
00452 switch ($server_version) {
00453 case CAS_VERSION_1_0:
00454 if ( $this->isProxy() )
00455 phpCAS::error('CAS proxies are not supported in CAS '
00456 .$server_version);
00457 break;
00458 case CAS_VERSION_2_0:
00459 break;
00460 default:
00461 phpCAS::error('this version of CAS (`'
00462 .$server_version
00463 .'\') is not supported by phpCAS '
00464 .phpCAS::getVersion());
00465 }
00466 $this->_server['version'] = $server_version;
00467
00468 // check hostname
00469 if ( empty($server_hostname)
00470 || !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
00471 phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
00472 }
00473 $this->_server['hostname'] = $server_hostname;
00474
00475
00476 if ( $server_port == 0
00477 || !is_int($server_port) ) {
00478 phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
00479 }
00480 $this->_server['port'] = $server_port;
00481
00482 // check URI
00483 if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
00484 phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
00485 }
00486
00487 $server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
00488 $this->_server['uri'] = $server_uri;
00489
00490
00491 if ( $this->isProxy() ) {
00492 $this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
00493 }
00494
00495 if ( $this->isCallbackMode() ) {
00496
00497 if ( $_SERVER['HTTPS'] != 'on' ) {
00498 phpCAS::error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
00499 }
00500 } else {
00501
00502 $ticket = $_GET['ticket'];
00503
00504 if( preg_match('/^ST-/',$ticket)) {
00505 phpCAS::trace('ST \''.$ticket.'\' found');
00506 // ST present
00507 $this->setST($ticket);
00508 }
00509 // in a second time check for a Proxy Ticket (CAS >= 2.0)
00510 else if( ($this->getServerVersion()!=CAS_VERSION_1_0) && preg_match('/^PT-/',$ticket) ) {
00511 phpCAS::trace('PT \''.$ticket.'\' found');
00512 $this->setPT($ticket);
00513 }
00514 // ill-formed ticket, halt
00515 else if ( !empty($ticket) ) {
00516 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
00517 }
00518
00519 unset($_GET['ticket']);
00520 }
00521 phpCAS::traceEnd();
00522 }
00523
00526
00527
00528
00529
00530
00531
00544 var $_user = '';
00545
00553 function setUser($user)
00554 {
00555 $this->_user = $user;
00556 }
00557
00565 function getUser()
00566 {
00567 if ( empty($this->_user) ) {
00568 phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
00569 }
00570 return $this->_user;
00571 }
00572
00579 function forceAuthentication()
00580 {
00581 phpCAS::traceBegin();
00582
00583 if ( $this->isAuthenticated() ) {
00584
00585 phpCAS::trace('no need to authenticate');
00586 $res = TRUE;
00587 } else {
00588
00589 unset($_SESSION['phpCAS']['auth_checked']);
00590 $this->redirectToCas(FALSE);
00591
00592 $res = FALSE;
00593 }
00594 phpCAS::traceEnd($res);
00595 return $res;
00596 }
00597
00603 function checkAuthentication()
00604 {
00605 phpCAS::traceBegin();
00606
00607 if ( $this->isAuthenticated() ) {
00608 phpCAS::trace('user is authenticated');
00609 $res = TRUE;
00610 } else if (isset($_SESSION['phpCAS']['auth_checked'])) {
00611
00612 unset($_SESSION['phpCAS']['auth_checked']);
00613 $res = FALSE;
00614 } else {
00615 $_SESSION['phpCAS']['auth_checked'] = true;
00616 $this->redirectToCas(TRUE);
00617
00618 $res = FALSE;
00619 }
00620 phpCAS::traceEnd($res);
00621 return $res;
00622 }
00623
00632 function isAuthenticated()
00633 {
00634 phpCAS::traceBegin();
00635 $res = FALSE;
00636 $validate_url = '';
00637
00638 if ( $this->wasPreviouslyAuthenticated() ) {
00639
00640
00641 phpCAS::trace('user was already authenticated, no need to look for tickets');
00642 $res = TRUE;
00643 } elseif ( $this->hasST() ) {
00644
00645 phpCAS::trace('ST `'.$this->getST().'\' is present');
00646 $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
00647 phpCAS::trace('ST `'.$this->getST().'\' was validated');
00648 if ( $this->isProxy() ) {
00649 $this->validatePGT($validate_url,$text_response,$tree_response);
00650 phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
00651 $_SESSION['phpCAS']['pgt'] = $this->getPGT();
00652 }
00653 $_SESSION['phpCAS']['user'] = $this->getUser();
00654 $res = TRUE;
00655 } elseif ( $this->hasPT() ) {
00656 // if a Proxy Ticket was given, validate it
00657 phpCAS::trace('PT `'.$this->getPT().'\' is present');
00658 $this->validatePT($validate_url,$text_response,$tree_response);
00659 phpCAS::trace('PT `'.$this->getPT().'\' was validated');
00660 if ( $this->isProxy() ) {
00661 $this->validatePGT($validate_url,$text_response,$tree_response); // idem
00662 phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
00663 $_SESSION['phpCAS']['pgt'] = $this->getPGT();
00664 }
00665 $_SESSION['phpCAS']['user'] = $this->getUser();
00666 $res = TRUE;
00667 } else {
00668
00669 phpCAS::trace('no ticket found');
00670 }
00671
00672 phpCAS::traceEnd($res);
00673 return $res;
00674 }
00675
00686 function wasPreviouslyAuthenticated()
00687 {
00688 phpCAS::traceBegin();
00689
00690 if ( $this->isCallbackMode() ) {
00691 $this->callback();
00692 }
00693
00694 $auth = FALSE;
00695
00696 if ( $this->isProxy() ) {
00697
00698 if ( !empty($_SESSION['phpCAS']['user']) && !empty($_SESSION['phpCAS']['pgt']) ) {
00699
00700 $this->setUser($_SESSION['phpCAS']['user']);
00701 $this->setPGT($_SESSION['phpCAS']['pgt']);
00702 phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\'');
00703 $auth = TRUE;
00704 } elseif ( !empty($_SESSION['phpCAS']['user']) && empty($_SESSION['phpCAS']['pgt']) ) {
00705
00706 phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
00707 // unset all tickets to enforce authentication
00708 unset($_SESSION['phpCAS']);
00709 $this->setST('');
00710 $this->setPT('');
00711 } elseif ( empty($_SESSION['phpCAS']['user']) && !empty($_SESSION['phpCAS']['pgt']) ) {
00712 // these two variables should be empty or not empty at the same time
00713 phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty');
00714
00715 unset($_SESSION['phpCAS']);
00716 $this->setST('');
00717 $this->setPT('');
00718 } else {
00719 phpCAS::trace('neither user not PGT found');
00720 }
00721 } else {
00722
00723 if ( !empty($_SESSION['phpCAS']['user']) ) {
00724
00725 $this->setUser($_SESSION['phpCAS']['user']);
00726 phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\'');
00727 $auth = TRUE;
00728 } else {
00729 phpCAS::trace('no user found');
00730 }
00731 }
00732
00733 phpCAS::traceEnd($auth);
00734 return $auth;
00735 }
00736
00743 function redirectToCas($gateway)
00744 {
00745 phpCAS::traceBegin();
00746 $cas_url = $this->getServerLoginURL($gateway);
00747 header('Location: '.$cas_url);
00748 $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
00749 printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
00750 $this->printHTMLFooter();
00751 phpCAS::traceExit();
00752 exit();
00753 }
00754
00760 function logout($url = "")
00761 {
00762 phpCAS::traceBegin();
00763 $cas_url = $this->getServerLogoutURL();
00764
00765
00766 if ( $url != "" ) {
00767 $url = '?service=' . $url;
00768 }
00769 header('Location: '.$cas_url . $url);
00770 session_unset();
00771 session_destroy();
00772 $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
00773 printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
00774 $this->printHTMLFooter();
00775 phpCAS::traceExit();
00776 exit();
00777 }
00778
00781
00782
00783
00784
00785
00786
00787
00788
00789
00803 var $_st = '';
00804
00810 function getST()
00811 { return $this->_st; }
00812
00818 function setST($st)
00819 { $this->_st = $st; }
00820
00826 function hasST()
00827 { return !empty($this->_st); }
00828
00831
00832
00833
00852 function validateST($validate_url,&$text_response,&$tree_response)
00853 {
00854 phpCAS::traceBegin();
00855
00856 $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
00857 if ( $this->isProxy() ) {
00858
00859 $validate_url .= '&pgtUrl='.$this->getCallbackURL();
00860 }
00861
00862
00863 if ( !$this->readURL($validate_url,'',$headers,$text_response,$err_msg) ) {
00864 phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
00865 $this->authError('ST not validated',
00866 $validate_url,
00867 TRUE/*$no_response*/);
00868 }
00869
00870 // analyze the result depending on the version
00871 switch ($this->getServerVersion()) {
00872 case CAS_VERSION_1_0:
00873 if (preg_match('/^no\n/',$text_response)) {
00874 phpCAS::trace('ST has not been validated');
00875 $this->authError('ST not validated',
00876 $validate_url,
00877 FALSE/*$no_response*/,
00878 FALSE/*$bad_response*/,
00879 $text_response);
00880 }
00881 if (!preg_match('/^yes\n/',$text_response)) {
00882 phpCAS::trace('ill-formed response');
00883 $this->authError('ST not validated',
00884 $validate_url,
00885 FALSE/*$no_response*/,
00886 TRUE/*$bad_response*/,
00887 $text_response);
00888 }
00889 // ST has been validated, extract the user name
00890 $arr = preg_split('/\n/',$text_response);
00891 $this->setUser(trim($arr[1]));
00892 break;
00893 case CAS_VERSION_2_0:
00894 // read the response of the CAS server into a DOM object
00895 if ( !($dom = domxml_open_mem($text_response))) {
00896 phpCAS::trace('domxml_open_mem() failed');
00897 $this->authError('ST not validated',
00898 $validate_url,
00899 FALSE/*$no_response*/,
00900 TRUE/*$bad_response*/,
00901 $text_response);
00902 }
00903 // read the root node of the XML tree
00904 if ( !($tree_response = $dom->document_element()) ) {
00905 phpCAS::trace('document_element() failed');
00906 $this->authError('ST not validated',
00907 $validate_url,
00908 FALSE/*$no_response*/,
00909 TRUE/*$bad_response*/,
00910 $text_response);
00911 }
00912 // insure that tag name is 'serviceResponse'
00913 if ( $tree_response->node_name(true) != 'serviceResponse' ) {
00914 phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name(true).'\'');
00915 $this->authError('ST not validated',
00916 $validate_url,
00917 FALSE,
00918 TRUE,
00919 $text_response);
00920 }
00921 if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
00922
00923 if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
00924 phpCAS::trace('<authenticationSuccess> found, but no <user>');
00925 $this->authError('ST not validated',
00926 $validate_url,
00927 FALSE,
00928 TRUE,
00929 $text_response);
00930 }
00931 $user = trim($user_elements[0]->get_content());
00932 phpCAS::trace('user = `'.$user);
00933 $this->setUser($user);
00934
00935 } else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
00936 phpCAS::trace('<authenticationFailure> found');
00937
00938 $this->authError('ST not validated',
00939 $validate_url,
00940 FALSE,
00941 FALSE,
00942 $text_response,
00943 $failure_elements[0]->get_attribute('code'),
00944 trim($failure_elements[0]->get_content()));
00945 } else {
00946 phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
00947 $this->authError('ST not validated',
00948 $validate_url,
00949 FALSE,
00950 TRUE,
00951 $text_response);
00952 }
00953 break;
00954 }
00955
00956
00957 phpCAS::traceEnd(TRUE);
00958 return TRUE;
00959 }
00960
00963
00964
00965
00966
00967
00968
00969
00970
00971
00983 var $_proxy;
00984
00992 function isProxy()
00993 {
00994 return $this->_proxy;
00995 }
00996
00998
00999
01000
01013 var $_pgt = '';
01014
01020 function getPGT()
01021 { return $this->_pgt; }
01022
01028 function setPGT($pgt)
01029 { $this->_pgt = $pgt; }
01030
01036 function hasPGT()
01037 { return !empty($this->_pgt); }
01038
01041
01042
01043
01061 var $_callback_mode = FALSE;
01062
01070 function setCallbackMode($callback_mode)
01071 {
01072 $this->_callback_mode = $callback_mode;
01073 }
01074
01083 function isCallbackMode()
01084 {
01085 return $this->_callback_mode;
01086 }
01087
01096 var $_callback_url = '';
01097
01107 function getCallbackURL()
01108 {
01109
01110 if ( empty($this->_callback_url) ) {
01111 $final_uri = '';
01112
01113 $final_uri = 'https://';
01114
01115
01116
01117
01118 if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
01119
01120
01121
01122 if (empty($_SERVER['SERVER_NAME'])) {
01123 $final_uri .= $_SERVER['HTTP_HOST'];
01124 } else {
01125 $final_uri .= $_SERVER['SERVER_NAME'];
01126 }
01127 } else {
01128 $final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
01129 }
01130 if ( ($_SERVER['HTTPS']=='on' && $_SERVER['SERVER_PORT']!=443)
01131 || ($_SERVER['HTTPS']!='on' && $_SERVER['SERVER_PORT']!=80) ) {
01132 $final_uri .= ':';
01133 $final_uri .= $_SERVER['SERVER_PORT'];
01134 }
01135 $request_uri = $_SERVER['REQUEST_URI'];
01136 $request_uri = preg_replace('/\?.*$/','',$request_uri);
01137 $final_uri .= $request_uri;
01138 $this->setCallbackURL($final_uri);
01139 }
01140 return $this->_callback_url;
01141 }
01142
01150 function setCallbackURL($url)
01151 {
01152 return $this->_callback_url = $url;
01153 }
01154
01161 function callback()
01162 {
01163 phpCAS::traceBegin();
01164 $this->printHTMLHeader('phpCAS callback');
01165 $pgt_iou = $_GET['pgtIou'];
01166 $pgt = $_GET['pgtId'];
01167 phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
01168 echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
01169 $this->storePGT($pgt,$pgt_iou);
01170 $this->printHTMLFooter();
01171 phpCAS::traceExit();
01172 }
01173
01176
01177
01178
01192 var $_pgt_storage = null;
01193
01200 function initPGTStorage()
01201 {
01202
01203 if ( !is_object($this->_pgt_storage) ) {
01204 $this->setPGTStorageFile();
01205 }
01206
01207
01208 $this->_pgt_storage->init();
01209 }
01210
01219 function storePGT($pgt,$pgt_iou)
01220 {
01221
01222 $this->initPGTStorage();
01223
01224 $this->_pgt_storage->write($pgt,$pgt_iou);
01225 }
01226
01236 function loadPGT($pgt_iou)
01237 {
01238
01239 $this->initPGTStorage();
01240
01241 return $this->_pgt_storage->read($pgt_iou);
01242 }
01243
01253 function setPGTStorageFile($format='',
01254 $path='')
01255 {
01256
01257 if ( is_object($this->_pgt_storage) ) {
01258 phpCAS::error('PGT storage already defined');
01259 }
01260
01261
01262 $this->_pgt_storage = &new PGTStorageFile($this,$format,$path);
01263 }
01264
01282 function setPGTStorageDB($user,
01283 $password,
01284 $database_type,
01285 $hostname,
01286 $port,
01287 $database,
01288 $table)
01289 {
01290
01291 if ( is_object($this->_pgt_storage) ) {
01292 phpCAS::error('PGT storage already defined');
01293 }
01294
01295
01296 trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING);
01297
01298
01299 $this->_pgt_storage = & new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table);
01300 }
01301
01302
01303
01304
01318 function validatePGT(&$validate_url,$text_response,$tree_response)
01319 {
01320 phpCAS::traceBegin();
01321 if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) {
01322 phpCAS::trace('<proxyGrantingTicket> not found');
01323
01324 $this->authError('Ticket validated but no PGT Iou transmitted',
01325 $validate_url,
01326 FALSE,
01327 FALSE,
01328 $text_response);
01329 } else {
01330
01331 $pgt_iou = trim($arr[0]->get_content());
01332 $pgt = $this->loadPGT($pgt_iou);
01333 if ( $pgt == FALSE ) {
01334 phpCAS::trace('could not load PGT');
01335 $this->authError('PGT Iou was transmitted but PGT could not be retrieved',
01336 $validate_url,
01337 FALSE,
01338 FALSE,
01339 $text_response);
01340 }
01341 $this->setPGT($pgt);
01342 }
01343 phpCAS::traceEnd(TRUE);
01344 return TRUE;
01345 }
01346
01347
01348
01349
01350
01362 function retrievePT($target_service,&$err_code,&$err_msg)
01363 {
01364 phpCAS::traceBegin();
01365
01366
01367
01368
01369
01370 $err_msg = '';
01371
01372
01373 $cas_url = $this->getServerProxyURL().'?targetService='.preg_replace('/&/','%26',$target_service).'&pgt='.$this->getPGT();
01374
01375
01376 if ( !$this->readURL($cas_url,'',$headers,$cas_response,$err_msg) ) {
01377 phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
01378 $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
01379 $err_msg = 'could not retrieve PT (no response from the CAS server)';
01380 phpCAS::traceEnd(FALSE);
01381 return FALSE;
01382 }
01383
01384 $bad_response = FALSE;
01385
01386 if ( !$bad_response ) {
01387 // read the response of the CAS server into a DOM object
01388 if ( !($dom = @domxml_open_mem($cas_response))) {
01389 phpCAS::trace('domxml_open_mem() failed');
01390 // read failed
01391 $bad_response = TRUE;
01392 }
01393 }
01394
01395 if ( !$bad_response ) {
01396 // read the root node of the XML tree
01397 if ( !($root = $dom->document_element()) ) {
01398 phpCAS::trace('document_element() failed');
01399 // read failed
01400 $bad_response = TRUE;
01401 }
01402 }
01403
01404 if ( !$bad_response ) {
01405 // insure that tag name is 'serviceResponse'
01406 if ( $root->node_name(true) != 'serviceResponse' ) {
01407 phpCAS::trace('node_name() failed');
01408 // bad root node
01409 $bad_response = TRUE;
01410 }
01411 }
01412
01413 if ( !$bad_response ) {
01414 // look for a proxySuccess tag
01415 if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) {
01416 // authentication succeded, look for a proxyTicket tag
01417 if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) {
01418 $err_code = PHPCAS_SERVICE_OK;
01419 $err_msg = '';
01420 $pt = trim($arr[0]->get_content());
01421 phpCAS::traceEnd($pt);
01422 return $pt;
01423 } else {
01424 phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
01425 }
01426 }
01427 // look for a proxyFailure tag
01428 else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) {
01429 // authentication failed, extract the error
01430 $err_code = PHPCAS_SERVICE_PT_FAILURE;
01431 $err_msg = 'PT retrieving failed (code=`'
01432 .$arr[0]->get_attribute('code')
01433 .'\', message=`'
01434 .trim($arr[0]->get_content())
01435 .'\')';
01436 phpCAS::traceEnd(FALSE);
01437 return FALSE;
01438 } else {
01439 phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
01440 }
01441 }
01442
01443 // at this step, we are sure that the response of the CAS server was ill-formed
01444 $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
01445 $err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
01446
01447 phpCAS::traceEnd(FALSE);
01448 return FALSE;
01449 }
01450
01451
01452
01453
01454
01470 function readURL($url,$cookies,&$headers,&$body,&$err_msg)
01471 {
01472 phpCAS::traceBegin();
01473 $headers = '';
01474 $body = '';
01475 $err_msg = '';
01476
01477 $res = TRUE;
01478
01479
01480 $ch = curl_init($url);
01481
01482
01483 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
01484
01485 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
01486
01487
01488 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
01489
01490 curl_setopt($ch, CURLOPT_HEADER, 1);
01491
01492 if ( is_array($cookies) ) {
01493 curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies));
01494 }
01495
01496 $buf = curl_exec ($ch);
01497 if ( $buf === FALSE ) {
01498 phpCAS::trace('cur_exec() failed');
01499 $err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch);
01500
01501 curl_close ($ch);
01502 $res = FALSE;
01503 } else {
01504
01505 curl_close ($ch);
01506
01507
01508
01509 $pos = FALSE;
01510 for ($i=0; $i<strlen($buf); $i++) {
01511 if ( $buf[$i] == chr(13) )
01512 if ( $buf[$i+1] == chr(10) )
01513 if ( $buf[$i+2] == chr(13) )
01514 if ( $buf[$i+3] == chr(10) ) {
01515
01516 $pos = $i;
01517 break;
01518 }
01519 }
01520
01521 if ( $pos === FALSE ) {
01522
01523 $err_msg = 'no header found';
01524 phpCAS::trace($err_msg);
01525 $res = FALSE;
01526 } else {
01527
01528 $headers = preg_split ("/[\n\r]+/",substr($buf,0,$pos));
01529
01530 $body = substr($buf,$pos+4);
01531 }
01532 }
01533
01534 phpCAS::traceEnd($res);
01535 return $res;
01536 }
01537
01553 function serviceWeb($url,&$err_code,&$output)
01554 {
01555 phpCAS::traceBegin();
01556
01557 $pt = $this->retrievePT($url,$err_code,$output);
01558
01559 $res = TRUE;
01560
01561
01562 if ( !$pt ) {
01563
01564 phpCAS::trace('PT was not retrieved correctly');
01565 $res = FALSE;
01566 } else {
01567
01568 if ( is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) {
01569 foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) {
01570 $cookies[] = $name.'='.$val;
01571 }
01572 }
01573
01574
01575 if ( strstr($url,'?') === FALSE ) {
01576 $service_url = $url.'?ticket='.$pt;
01577 } else {
01578 $service_url = $url.'&ticket='.$pt;
01579 }
01580
01581 phpCAS::trace('reading URL`'.$service_url.'\'');
01582 if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
01583 phpCAS::trace('could not read URL`'.$service_url.'\'');
01584 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
01585
01586 $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
01587 $service_url,
01588 $err_msg);
01589 $res = FALSE;
01590 } else {
01591
01592 phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
01593 foreach ( $headers as $header ) {
01594 // test if the header is a cookie
01595 if ( preg_match('/^Set-Cookie:/',$header) ) {
01596 // the header is a cookie, remove the beginning
01597 $header_val = preg_replace('/^Set-Cookie: */','',$header);
01598 // extract interesting information
01599 $name_val = strtok($header_val,'; ');
01600 // extract the name and the value of the cookie
01601 $cookie_name = strtok($name_val,'=');
01602 $cookie_val = strtok('=');
01603 // store the cookie
01604 $_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val;
01605 phpCAS::trace($cookie_name.' -> '.$cookie_val);
01606 }
01607 }
01608 }
01609 }
01610
01611 phpCAS::traceEnd($res);
01612 return $res;
01613 }
01614
01633 function serviceMail($url,$flags,&$err_code,&$err_msg,&$pt)
01634 {
01635 phpCAS::traceBegin();
01636 // at first retrieve a PT
01637 $pt = $this->retrievePT($target_service,$err_code,$output);
01638
01639 $stream = FALSE;
01640
01641 // test if PT was retrieved correctly
01642 if ( !$pt ) {
01643 // note: $err_code and $err_msg are filled by CASClient::retrievePT()
01644 phpCAS::trace('PT was not retrieved correctly');
01645 } else {
01646 phpCAS::trace('opening IMAP URL `'.$url.'\'...');
01647 $stream = @imap_open($url,$this->getUser(),$pt,$flags);
01648 if ( !$stream ) {
01649 phpCAS::trace('could not open URL');
01650 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
01651
01652 $err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
01653 $service_url,
01654 var_export(imap_errors(),TRUE));
01655 $pt = FALSE;
01656 $stream = FALSE;
01657 } else {
01658 phpCAS::trace('ok');
01659 }
01660 }
01661
01662 phpCAS::traceEnd($stream);
01663 return $stream;
01664 }
01665
01668
01669
01670
01671
01672
01673
01674
01675
01676
01690 var $_pt = '';
01691
01697 function getPT()
01698 { return $this->_pt; }
01699
01705 function setPT($pt)
01706 { $this->_pt = $pt; }
01707
01713 function hasPT()
01714 { return !empty($this->_pt); }
01715
01717
01718
01719
01732 function validatePT(&$validate_url,&$text_response,&$tree_response)
01733 {
01734 phpCAS::traceBegin();
01735
01736 $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
01737
01738 if ( $this->isProxy() ) {
01739
01740 $validate_url .= '&pgtUrl='.$this->getCallbackURL();
01741 }
01742
01743
01744 if ( !$this->readURL($validate_url,'',$headers,$text_response,$err_msg) ) {
01745 phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
01746 $this->authError('PT not validated',
01747 $validate_url,
01748 TRUE/*$no_response*/);
01749 }
01750
01751 // read the response of the CAS server into a DOM object
01752 if ( !($dom = domxml_open_mem($text_response))) {
01753 // read failed
01754 $this->authError('PT not validated',
01755 $alidate_url,
01756 FALSE/*$no_response*/,
01757 TRUE/*$bad_response*/,
01758 $text_response);
01759 }
01760 // read the root node of the XML tree
01761 if ( !($tree_response = $dom->document_element()) ) {
01762 // read failed
01763 $this->authError('PT not validated',
01764 $validate_url,
01765 FALSE/*$no_response*/,
01766 TRUE/*$bad_response*/,
01767 $text_response);
01768 }
01769 // insure that tag name is 'serviceResponse'
01770 if ( $tree_response->node_name(true) != 'serviceResponse' ) {
01771 // bad root node
01772 $this->authError('PT not validated',
01773 $validate_url,
01774 FALSE/*$no_response*/,
01775 TRUE/*$bad_response*/,
01776 $text_response);
01777 }
01778 if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
01779 // authentication succeded, extract the user name
01780 if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) {
01781 // no user specified => error
01782 $this->authError('PT not validated',
01783 $validate_url,
01784 FALSE/*$no_response*/,
01785 TRUE/*$bad_response*/,
01786 $text_response);
01787 }
01788 $this->setUser(trim($arr[0]->get_content()));
01789
01790 } else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
01791 // authentication succeded, extract the error code and message
01792 $this->authError('PT not validated',
01793 $validate_url,
01794 FALSE/*$no_response*/,
01795 FALSE/*$bad_response*/,
01796 $text_response,
01797 $arr[0]->get_attribute('code')/*$err_code*/,
01798 trim($arr[0]->get_content())/*$err_msg*/);
01799 } else {
01800 $this->authError('PT not validated',
01801 $validate_url,
01802 FALSE/*$no_response*/,
01803 TRUE/*$bad_response*/,
01804 $text_response);
01805 }
01806
01807 // at this step, PT has been validated and $this->_user has been set,
01808
01809 phpCAS::traceEnd(TRUE);
01810 return TRUE;
01811 }
01812
01815 // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
01816 // XX XX
01817 // XX MISC XX
01818 // XX XX
01819 // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
01820
01826 // ########################################################################
01827 // URL
01828 // ########################################################################
01836 var $_url = '';
01837
01846 function getURL()
01847 {
01848 phpCAS::traceBegin();
01849 // the URL is built when needed only
01850 if ( empty($this->_url) ) {
01851 $final_uri = '';
01852 // remove the ticket if present in the URL
01853 $final_uri = ($_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
01854 $final_uri .= ':
01855
01856
01857
01858 if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
01859
01860
01861
01862 if (empty($_SERVER['SERVER_NAME'])) {
01863 $final_uri .= $_SERVER['HTTP_HOST'];
01864 } else {
01865 $final_uri .= $_SERVER['SERVER_NAME'];
01866 }
01867 } else {
01868 $final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
01869 }
01870 if ( ($_SERVER['HTTPS']=='on' && $_SERVER['SERVER_PORT']!=443)
01871 || ($_SERVER['HTTPS']!='on' && $_SERVER['SERVER_PORT']!=80) ) {
01872 $final_uri .= ':';
01873 $final_uri .= $_SERVER['SERVER_PORT'];
01874 }
01875
01876 $final_uri .= strtok($_SERVER['REQUEST_URI'],"?");
01877 $cgi_params = '?'.strtok("?");
01878
01879 $cgi_params = preg_replace('/&ticket=[^&]*/','',$cgi_params);
01880 $cgi_params = preg_replace('/\?ticket=[^&;]*/','?',$cgi_params);
01881 $cgi_params = preg_replace('/\?$/','',$cgi_params);
01882 $final_uri .= $cgi_params;
01883 $this->setURL($final_uri);
01884 }
01885 phpCAS::traceEnd($this->_url);
01886 return $this->_url;
01887 }
01888
01896 function setURL($url)
01897 {
01898 $this->_url = $url;
01899 }
01900
01901
01902
01903
01919 function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
01920 {
01921 phpCAS::traceBegin();
01922
01923 $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
01924 printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),$this->getURL(),$_SERVER['SERVER_ADMIN']);
01925 phpCAS::trace('CAS URL: '.$cas_url);
01926 phpCAS::trace('Authentication failure: '.$failure);
01927 if ( $no_response ) {
01928 phpCAS::trace('Reason: no response from the CAS server');
01929 } else {
01930 if ( $bad_response ) {
01931 phpCAS::trace('Reason: bad response from the CAS server');
01932 } else {
01933 switch ($this->getServerVersion()) {
01934 case CAS_VERSION_1_0:
01935 phpCAS::trace('Reason: CAS error');
01936 break;
01937 case CAS_VERSION_2_0:
01938 if ( empty($err_code) )
01939 phpCAS::trace('Reason: no CAS error');
01940 else
01941 phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
01942 break;
01943 }
01944 }
01945 phpCAS::trace('CAS response: '.$cas_response);
01946 }
01947 $this->printHTMLFooter();
01948 phpCAS::traceExit();
01949 exit();
01950 }
01951
01953 }
01954
01955 ?>