00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00037
00038 include_once(dirname(__FILE__).'/languages/languages.php');
00039
00040
00041 include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php');
00042
00043
00044 include_once(dirname(__FILE__).'/CookieJar.php');
00045
00046
00047 include_once(dirname(__FILE__).'/Request/CurlRequest.php');
00048
00057 class CASClient
00058 {
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00085 private function HTMLFilterOutput($str)
00086 {
00087 $str = str_replace('__CAS_VERSION__',$this->getServerVersion(),$str);
00088 $str = str_replace('__PHPCAS_VERSION__',phpCAS::getVersion(),$str);
00089 $str = str_replace('__SERVER_BASE_URL__',$this->getServerBaseURL(),$str);
00090 echo $str;
00091 }
00092
00100 private $_output_header = '';
00101
00110 private function printHTMLHeader($title)
00111 {
00112 $this->HTMLFilterOutput(str_replace('__TITLE__',
00113 $title,
00114 (empty($this->_output_header)
00115 ? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
00116 : $this->_output_header)
00117 )
00118 );
00119 }
00120
00128 private $_output_footer = '';
00129
00136 private function printHTMLFooter()
00137 {
00138 $this->HTMLFilterOutput(empty($this->_output_footer)
00139 ?('<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>')
00140 :$this->_output_footer);
00141 }
00142
00148 public function setHTMLHeader($header)
00149 {
00150 $this->_output_header = $header;
00151 }
00152
00158 public function setHTMLFooter($footer)
00159 {
00160 $this->_output_footer = $footer;
00161 }
00162
00166 private $_exitOnAuthError = true;
00167
00175 public function setNoExitOnAuthError () {
00176 $this->_exitOnAuthError = false;
00177 }
00178
00183 private $_clearTicketsFromUrl = true;
00184
00194 public function setNoClearTicketsFromUrl () {
00195 $this->_clearTicketsFromUrl = false;
00196 }
00197
00201 private $_postAuthenticateCallbackFunction = null;
00202
00206 private $_postAuthenticateCallbackArgs = array();
00207
00226 public function setPostAuthenticateCallback ($function, array $additionalArgs = array()) {
00227 $this->_postAuthenticateCallbackFunction = $function;
00228 $this->_postAuthenticateCallbackArgs = $additionalArgs;
00229 }
00230
00234 private $_signoutCallbackFunction = null;
00235
00239 private $_signoutCallbackArgs = array();
00240
00254 public function setSingleSignoutCallback ($function, array $additionalArgs = array()) {
00255 $this->_signoutCallbackFunction = $function;
00256 $this->_signoutCallbackArgs = $additionalArgs;
00257 }
00258
00259
00261
00262
00263
00277 private $_lang = '';
00278
00284 private function getLang()
00285 {
00286 if ( empty($this->_lang) )
00287 $this->setLang(PHPCAS_LANG_DEFAULT);
00288 return $this->_lang;
00289 }
00290
00299 private $_strings;
00300
00309 private function getString($str)
00310 {
00311
00312 $this->getLang();
00313
00314 if ( !isset($this->_strings[$str]) ) {
00315 trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR);
00316 }
00317 return $this->_strings[$str];
00318 }
00319
00328 public function setLang($lang)
00329 {
00330
00331 include(dirname(__FILE__).'/languages/'.$lang.'.php');
00332
00333 if ( !is_array($this->_strings) ) {
00334 trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR);
00335 }
00336 $this->_lang = $lang;
00337 }
00338
00340 // ########################################################################
00341 // CAS SERVER CONFIG
00342 // ########################################################################
00371 private $_server = array(
00372 'version' => -1,
00373 'hostname' => 'none',
00374 'port' => -1,
00375 'uri' => 'none');
00376
00381 private function getServerVersion()
00382 {
00383 return $this->_server['version'];
00384 }
00385
00390 private function getServerHostname()
00391 { return $this->_server['hostname']; }
00392
00397 private function getServerPort()
00398 { return $this->_server['port']; }
00399
00404 private function getServerURI()
00405 { return $this->_server['uri']; }
00406
00411 private function getServerBaseURL()
00412 {
00413 // the URL is build only when needed
00414 if ( empty($this->_server['base_url']) ) {
00415 $this->_server['base_url'] = 'https:
00416 if ($this->getServerPort()!=443) {
00417 $this->_server['base_url'] .= ':'
00418 .$this->getServerPort();
00419 }
00420 $this->_server['base_url'] .= $this->getServerURI();
00421 }
00422 return $this->_server['base_url'];
00423 }
00424
00433 public function getServerLoginURL($gateway=false,$renew=false) {
00434 phpCAS::traceBegin();
00435
00436 if ( empty($this->_server['login_url']) ) {
00437 $this->_server['login_url'] = $this->getServerBaseURL();
00438 $this->_server['login_url'] .= 'login?service=';
00439 $this->_server['login_url'] .= urlencode($this->getURL());
00440 if($renew) {
00441
00442 $this->_server['login_url'] .= '&renew=true';
00443 } elseif ($gateway) {
00444
00445 $this->_server['login_url'] .= '&gateway=true';
00446 }
00447 }
00448 phpCAS::traceEnd($this->_server['login_url']);
00449 return $this->_server['login_url'];
00450 }
00451
00457 public function setServerLoginURL($url)
00458 {
00459 return $this->_server['login_url'] = $url;
00460 }
00461
00462
00468 public function setServerServiceValidateURL($url)
00469 {
00470 return $this->_server['service_validate_url'] = $url;
00471 }
00472
00473
00479 public function setServerProxyValidateURL($url)
00480 {
00481 return $this->_server['proxy_validate_url'] = $url;
00482 }
00483
00484
00490 public function setServerSamlValidateURL($url)
00491 {
00492 return $this->_server['saml_validate_url'] = $url;
00493 }
00494
00495
00500 public function getServerServiceValidateURL()
00501 {
00502
00503 if ( empty($this->_server['service_validate_url']) ) {
00504 switch ($this->getServerVersion()) {
00505 case CAS_VERSION_1_0:
00506 $this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
00507 break;
00508 case CAS_VERSION_2_0:
00509 $this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
00510 break;
00511 }
00512 }
00513 return $this->_server['service_validate_url'].'?service='.urlencode($this->getURL());
00514 }
00519 public function getServerSamlValidateURL()
00520 {
00521 phpCAS::traceBegin();
00522
00523 if ( empty($this->_server['saml_validate_url']) ) {
00524 switch ($this->getServerVersion()) {
00525 case SAML_VERSION_1_1:
00526 $this->_server['saml_validate_url'] = $this->getServerBaseURL().'samlValidate';
00527 break;
00528 }
00529 }
00530 phpCAS::traceEnd($this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL()));
00531 return $this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL());
00532 }
00537 public function getServerProxyValidateURL()
00538 {
00539
00540 if ( empty($this->_server['proxy_validate_url']) ) {
00541 switch ($this->getServerVersion()) {
00542 case CAS_VERSION_1_0:
00543 $this->_server['proxy_validate_url'] = '';
00544 break;
00545 case CAS_VERSION_2_0:
00546 $this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
00547 break;
00548 }
00549 }
00550 return $this->_server['proxy_validate_url'].'?service='.urlencode($this->getURL());
00551 }
00552
00557 public function getServerProxyURL()
00558 {
00559
00560 if ( empty($this->_server['proxy_url']) ) {
00561 switch ($this->getServerVersion()) {
00562 case CAS_VERSION_1_0:
00563 $this->_server['proxy_url'] = '';
00564 break;
00565 case CAS_VERSION_2_0:
00566 $this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
00567 break;
00568 }
00569 }
00570 return $this->_server['proxy_url'];
00571 }
00572
00577 public function getServerLogoutURL()
00578 {
00579
00580 if ( empty($this->_server['logout_url']) ) {
00581 $this->_server['logout_url'] = $this->getServerBaseURL().'logout';
00582 }
00583 return $this->_server['logout_url'];
00584 }
00585
00591 public function setServerLogoutURL($url)
00592 {
00593 return $this->_server['logout_url'] = $url;
00594 }
00595
00599 private $_curl_options = array();
00600
00604 public function setExtraCurlOption($key, $value)
00605 {
00606 $this->_curl_options[$key] = $value;
00607 }
00608
00615 private $_requestImplementation = 'CAS_CurlRequest';
00616
00624 public function setRequestImplementation ($className) {
00625 $obj = new $className;
00626 if (!($obj instanceof CAS_RequestInterface))
00627 throw new InvalidArgumentException('$className must implement the CAS_RequestInterface');
00628
00629 $this->_requestImplementation = $className;
00630 }
00631
00636 private function isHttps() {
00637 if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
00638 return true;
00639 } else {
00640 return false;
00641 }
00642 }
00643
00644
00645
00646
00659 public function CASClient(
00660 $server_version,
00661 $proxy,
00662 $server_hostname,
00663 $server_port,
00664 $server_uri,
00665 $start_session = true) {
00666
00667 phpCAS::traceBegin();
00668
00669 $this->_start_session = $start_session;
00670
00671 if ($this->_start_session && session_id() !== "")
00672 {
00673 phpCAS :: error("Another session was started before phpcas. Either disable the session" .
00674 " handling for phpcas in the client() call or modify your application to leave" .
00675 " session handling to phpcas");
00676 }
00677
00678 if ($start_session && !$this->isLogoutRequest())
00679 {
00680 phpCAS :: trace("Starting a new session");
00681 session_start();
00682 }
00683
00684
00685
00686 $this->_proxy = $proxy;
00687
00688
00689 if ($this->isProxy()) {
00690 if (!isset($_SESSION['phpCAS']))
00691 $_SESSION['phpCAS'] = array();
00692 if (!isset($_SESSION['phpCAS']['service_cookies']))
00693 $_SESSION['phpCAS']['service_cookies'] = array();
00694 $this->_serviceCookieJar = new CAS_CookieJar($_SESSION['phpCAS']['service_cookies']);
00695 }
00696
00697
00698 switch ($server_version) {
00699 case CAS_VERSION_1_0:
00700 if ( $this->isProxy() )
00701 phpCAS::error('CAS proxies are not supported in CAS '
00702 .$server_version);
00703 break;
00704 case CAS_VERSION_2_0:
00705 break;
00706 case SAML_VERSION_1_1:
00707 break;
00708 default:
00709 phpCAS::error('this version of CAS (`'
00710 .$server_version
00711 .'\') is not supported by phpCAS '
00712 .phpCAS::getVersion());
00713 }
00714 $this->_server['version'] = $server_version;
00715
00716 // check hostname
00717 if ( empty($server_hostname)
00718 || !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
00719 phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
00720 }
00721 $this->_server['hostname'] = $server_hostname;
00722
00723
00724 if ( $server_port == 0
00725 || !is_int($server_port) ) {
00726 phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
00727 }
00728 $this->_server['port'] = $server_port;
00729
00730 // check URI
00731 if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
00732 phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
00733 }
00734
00735 $server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
00736 $this->_server['uri'] = $server_uri;
00737
00738
00739 if ( $this->isProxy() ) {
00740 $this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
00741 }
00742
00743 if ( $this->isCallbackMode() ) {
00744
00745 if ( !$this->isHttps() ) {
00746 phpCAS::error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
00747 }
00748 } else {
00749
00750 $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
00751 switch ($this->getServerVersion()) {
00752 case CAS_VERSION_1_0:
00753 if( preg_match('/^ST-/',$ticket) ) {
00754 phpCAS::trace('ST \''.$ticket.'\' found');
00755 //ST present
00756 $this->setST($ticket);
00757 //ticket has been taken into account, unset it to hide it to applications
00758 unset($_GET['ticket']);
00759 } else if ( !empty($ticket) ) {
00760 //ill-formed ticket, halt
00761 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
00762 }
00763 break;
00764 case CAS_VERSION_2_0:
00765 if( preg_match('/^[SP]T-/',$ticket) ) {
00766 phpCAS::trace('ST or PT \''.$ticket.'\' found');
00767 $this->setPT($ticket);
00768 unset($_GET['ticket']);
00769 } else if ( !empty($ticket) ) {
00770 //ill-formed ticket, halt
00771 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
00772 }
00773 break;
00774 case SAML_VERSION_1_1:
00775 if( preg_match('/^[SP]T-/',$ticket) ) {
00776 phpCAS::trace('SA \''.$ticket.'\' found');
00777 $this->setSA($ticket);
00778 unset($_GET['ticket']);
00779 } else if ( !empty($ticket) ) {
00780 //ill-formed ticket, halt
00781 phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
00782 }
00783 break;
00784 }
00785 }
00786 phpCAS::traceEnd();
00787 }
00788
00791
00792
00793
00794
00795
00796
00801 private $_start_session = true;
00802
00803 private function setStartSession($session)
00804 {
00805 $this->_start_session = session;
00806 }
00807
00808 public function getStartSession($session)
00809 {
00810 $this->_start_session = session;
00811 }
00812
00816 private function renameSession($ticket)
00817 {
00818 phpCAS::traceBegin();
00819 if($this->_start_session){
00820 if (!empty ($this->_user))
00821 {
00822 $old_session = $_SESSION;
00823 session_destroy();
00824
00825 $session_id = preg_replace('/[^\w]/', '', $ticket);
00826 phpCAS :: trace("Session ID: ".$session_id);
00827 session_id($session_id);
00828 session_start();
00829 phpCAS :: trace("Restoring old session vars");
00830 $_SESSION = $old_session;
00831 } else
00832 {
00833 phpCAS :: error('Session should only be renamed after successfull authentication');
00834 }
00835 }else{
00836 phpCAS :: trace("Skipping session rename since phpCAS is not handling the session.");
00837 }
00838 phpCAS::traceEnd();
00839 }
00840
00841
00842
00843
00844
00845
00846
00858 private $_user = '';
00859
00866 private function setUser($user)
00867 {
00868 $this->_user = $user;
00869 }
00870
00878 public function getUser()
00879 {
00880 if ( empty($this->_user) ) {
00881 phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
00882 }
00883 return $this->_user;
00884 }
00885
00886
00887
00888
00889
00890
00891
00892
00893
00900 private $_attributes = array();
00901
00902 public function setAttributes($attributes)
00903 { $this->_attributes = $attributes; }
00904
00905 public function getAttributes() {
00906 if ( empty($this->_user) ) {
00907 phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
00908 }
00909 return $this->_attributes;
00910 }
00911
00912 public function hasAttributes()
00913 { return !empty($this->_attributes); }
00914
00915 public function hasAttribute($key)
00916 { return (is_array($this->_attributes) && array_key_exists($key, $this->_attributes)); }
00917
00918 public function getAttribute($key) {
00919 if($this->hasAttribute($key)) {
00920 return $this->_attributes[$key];
00921 }
00922 }
00923
00929 public function renewAuthentication(){
00930 phpCAS::traceBegin();
00931
00932 if( isset( $_SESSION['phpCAS']['auth_checked'] ) )
00933 unset($_SESSION['phpCAS']['auth_checked']);
00934 if ( $this->isAuthenticated() ) {
00935 phpCAS::trace('user already authenticated; renew');
00936 $this->redirectToCas(false,true);
00937 } else {
00938 $this->redirectToCas();
00939 }
00940 phpCAS::traceEnd();
00941 }
00942
00948 public function forceAuthentication()
00949 {
00950 phpCAS::traceBegin();
00951
00952 if ( $this->isAuthenticated() ) {
00953
00954 phpCAS::trace('no need to authenticate');
00955 $res = TRUE;
00956 } else {
00957
00958 if (isset($_SESSION['phpCAS']['auth_checked'])) {
00959 unset($_SESSION['phpCAS']['auth_checked']);
00960 }
00961 $this->redirectToCas(FALSE);
00962
00963 $res = FALSE;
00964 }
00965 phpCAS::traceEnd($res);
00966 return $res;
00967 }
00968
00974 private $_cache_times_for_auth_recheck = 0;
00975
00981 public function setCacheTimesForAuthRecheck($n)
00982 {
00983 $this->_cache_times_for_auth_recheck = $n;
00984 }
00985
00991 public function checkAuthentication()
00992 {
00993 phpCAS::traceBegin();
00994
00995 if ( $this->isAuthenticated() ) {
00996 phpCAS::trace('user is authenticated');
00997 $res = TRUE;
00998 } else if (isset($_SESSION['phpCAS']['auth_checked'])) {
00999
01000 unset($_SESSION['phpCAS']['auth_checked']);
01001 $res = FALSE;
01002 } else {
01003
01004 if (! isset($_SESSION['phpCAS']['unauth_count']) )
01005 $_SESSION['phpCAS']['unauth_count'] = -2;
01006
01007 if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1)
01008 || ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck))
01009 {
01010 $res = FALSE;
01011
01012 if ($this->_cache_times_for_auth_recheck != -1)
01013 {
01014 $_SESSION['phpCAS']['unauth_count']++;
01015 phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')');
01016 }
01017 else
01018 {
01019 phpCAS::trace('user is not authenticated (cached for until login pressed)');
01020 }
01021 }
01022 else
01023 {
01024 $_SESSION['phpCAS']['unauth_count'] = 0;
01025 $_SESSION['phpCAS']['auth_checked'] = true;
01026 phpCAS::trace('user is not authenticated (cache reset)');
01027 $this->redirectToCas(TRUE);
01028
01029 $res = FALSE;
01030 }
01031 }
01032 phpCAS::traceEnd($res);
01033 return $res;
01034 }
01035
01042 public function isAuthenticated()
01043 {
01044 phpCAS::traceBegin();
01045 $res = FALSE;
01046 $validate_url = '';
01047
01048 if ( $this->wasPreviouslyAuthenticated() ) {
01049 if($this->hasST() || $this->hasPT() || $this->hasSA()){
01050
01051 phpCAS::trace('ticket was present and will be discarded, use renewAuthenticate()');
01052 header('Location: '.$this->getURL());
01053 phpCAS::trace( "Prepare redirect to remove ticket: ".$this->getURL() );
01054 phpCAS::traceExit();
01055 exit();
01056 }else{
01057
01058
01059 phpCAS::trace('user was already authenticated, no need to look for tickets');
01060 $res = TRUE;
01061 }
01062 }
01063 else {
01064 if ( $this->hasST() ) {
01065
01066 phpCAS::trace('ST `'.$this->getST().'\' is present');
01067 $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
01068 phpCAS::trace('ST `'.$this->getST().'\' was validated');
01069 if ( $this->isProxy() ) {
01070 $this->validatePGT($validate_url,$text_response,$tree_response);
01071 phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
01072 $_SESSION['phpCAS']['pgt'] = $this->getPGT();
01073 }
01074 $_SESSION['phpCAS']['user'] = $this->getUser();
01075 if($this->hasAttributes()){
01076 $_SESSION['phpCAS']['attributes'] = $this->getAttributes();
01077 }
01078 $res = TRUE;
01079 $logoutTicket = $this->getST();
01080 }
01081 elseif ( $this->hasPT() ) {
01082 // if a Proxy Ticket was given, validate it
01083 phpCAS::trace('PT `'.$this->getPT().'\' is present');
01084 $this->validatePT($validate_url,$text_response,$tree_response);
01085 phpCAS::trace('PT `'.$this->getPT().'\' was validated');
01086 if ( $this->isProxy() ) {
01087 $this->validatePGT($validate_url,$text_response,$tree_response); // idem
01088 phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
01089 $_SESSION['phpCAS']['pgt'] = $this->getPGT();
01090 }
01091 $_SESSION['phpCAS']['user'] = $this->getUser();
01092 if($this->hasAttributes()){
01093 $_SESSION['phpCAS']['attributes'] = $this->getAttributes();
01094 }
01095 $res = TRUE;
01096 $logoutTicket = $this->getPT();
01097 }
01098 elseif ( $this->hasSA() ) {
01099
01100 phpCAS::trace('SA `'.$this->getSA().'\' is present');
01101 $this->validateSA($validate_url,$text_response,$tree_response); // if it fails, it halts
01102 phpCAS::trace('SA `'.$this->getSA().'\' was validated');
01103 $_SESSION['phpCAS']['user'] = $this->getUser();
01104 $_SESSION['phpCAS']['attributes'] = $this->getAttributes();
01105 $res = TRUE;
01106 $logoutTicket = $this->getSA();
01107 }
01108 else {
01109
01110 phpCAS::trace('no ticket found');
01111 }
01112 if ($res) {
01113
01114
01115 $dbg = phpCAS :: backtrace();
01116 global $PHPCAS_AUTH_CHECK_CALL;
01117 $PHPCAS_AUTH_CHECK_CALL = array (
01118 'done' => TRUE,
01119 'file' => $dbg[0]['file'],
01120 'line' => $dbg[0]['line'],
01121 'method' => __CLASS__ . '::' . __FUNCTION__,
01122 'result' => $res
01123 );
01124
01125
01126 if ($this->_postAuthenticateCallbackFunction) {
01127 $args = $this->_postAuthenticateCallbackArgs;
01128 array_unshift($args, $logoutTicket);
01129 call_user_func_array($this->_postAuthenticateCallbackFunction, $args);
01130 }
01131
01132
01133
01134
01135 if ($this->_clearTicketsFromUrl) {
01136 header('Location: '.$this->getURL());
01137 phpCAS::trace( "Prepare redirect to : ".$this->getURL() );
01138 phpCAS::traceExit();
01139 exit();
01140 }
01141 }
01142 }
01143
01144 phpCAS::traceEnd($res);
01145 return $res;
01146 }
01147
01153 public function isSessionAuthenticated ()
01154 {
01155 return !empty($_SESSION['phpCAS']['user']);
01156 }
01157
01166 private function wasPreviouslyAuthenticated()
01167 {
01168 phpCAS::traceBegin();
01169
01170 if ( $this->isCallbackMode() ) {
01171 $this->callback();
01172 }
01173
01174 $auth = FALSE;
01175
01176 if ( $this->isProxy() ) {
01177
01178 if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
01179
01180 $this->setUser($_SESSION['phpCAS']['user']);
01181 if(isset($_SESSION['phpCAS']['attributes'])){
01182 $this->setAttributes($_SESSION['phpCAS']['attributes']);
01183 }
01184 $this->setPGT($_SESSION['phpCAS']['pgt']);
01185 phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\'');
01186
01187
01188 if (isset($_SESSION['phpCAS']['proxies'])) {
01189 $this->setProxies($_SESSION['phpCAS']['proxies']);
01190 phpCAS::trace('proxies = "'.implode('", "', $_SESSION['phpCAS']['proxies']).'"');
01191 }
01192
01193 $auth = TRUE;
01194 } elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) {
01195
01196 phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
01197 // unset all tickets to enforce authentication
01198 unset($_SESSION['phpCAS']);
01199 $this->setST('');
01200 $this->setPT('');
01201 } elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
01202 // these two variables should be empty or not empty at the same time
01203 phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty');
01204
01205 unset($_SESSION['phpCAS']);
01206 $this->setST('');
01207 $this->setPT('');
01208 } else {
01209 phpCAS::trace('neither user not PGT found');
01210 }
01211 } else {
01212
01213 if ( $this->isSessionAuthenticated() ) {
01214
01215 $this->setUser($_SESSION['phpCAS']['user']);
01216 if(isset($_SESSION['phpCAS']['attributes'])){
01217 $this->setAttributes($_SESSION['phpCAS']['attributes']);
01218 }
01219 phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\'');
01220
01221
01222 if (isset($_SESSION['phpCAS']['proxies'])) {
01223 $this->setProxies($_SESSION['phpCAS']['proxies']);
01224 phpCAS::trace('proxies = "'.implode('", "', $_SESSION['phpCAS']['proxies']).'"');
01225 }
01226
01227 $auth = TRUE;
01228 } else {
01229 phpCAS::trace('no user found');
01230 }
01231 }
01232
01233 phpCAS::traceEnd($auth);
01234 return $auth;
01235 }
01236
01243 public function redirectToCas($gateway=false,$renew=false){
01244 phpCAS::traceBegin();
01245 $cas_url = $this->getServerLoginURL($gateway,$renew);
01246 header('Location: '.$cas_url);
01247 phpCAS::trace( "Redirect to : ".$cas_url );
01248
01249 $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
01250
01251 printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
01252 $this->printHTMLFooter();
01253
01254 phpCAS::traceExit();
01255 exit();
01256 }
01257
01258
01263 public function logout($params) {
01264 phpCAS::traceBegin();
01265 $cas_url = $this->getServerLogoutURL();
01266 $paramSeparator = '?';
01267 if (isset($params['url'])) {
01268 $cas_url = $cas_url . $paramSeparator . "url=" . urlencode($params['url']);
01269 $paramSeparator = '&';
01270 }
01271 if (isset($params['service'])) {
01272 $cas_url = $cas_url . $paramSeparator . "service=" . urlencode($params['service']);
01273 }
01274 header('Location: '.$cas_url);
01275 phpCAS::trace( "Prepare redirect to : ".$cas_url );
01276
01277 session_unset();
01278 session_destroy();
01279
01280 $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
01281 printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
01282 $this->printHTMLFooter();
01283
01284 phpCAS::traceExit();
01285 exit();
01286 }
01287
01291 private function isLogoutRequest() {
01292 return !empty($_POST['logoutRequest']);
01293 }
01294
01302 public function handleLogoutRequests($check_client=true, $allowed_clients=false) {
01303 phpCAS::traceBegin();
01304 if (!$this->isLogoutRequest()) {
01305 phpCAS::trace("Not a logout request");
01306 phpCAS::traceEnd();
01307 return;
01308 }
01309 if(!$this->_start_session && is_null($this->_signoutCallbackFunction)){
01310 phpCAS::trace("phpCAS can't handle logout requests if it does not manage the session.");
01311 }
01312 phpCAS::trace("Logout requested");
01313 phpCAS::trace("SAML REQUEST: ".$_POST['logoutRequest']);
01314 if ($check_client) {
01315 if (!$allowed_clients) {
01316 $allowed_clients = array( $this->getServerHostname() );
01317 }
01318 $client_ip = $_SERVER['REMOTE_ADDR'];
01319 $client = gethostbyaddr($client_ip);
01320 phpCAS::trace("Client: ".$client."/".$client_ip);
01321 $allowed = false;
01322 foreach ($allowed_clients as $allowed_client) {
01323 if (($client == $allowed_client) or ($client_ip == $allowed_client)) {
01324 phpCAS::trace("Allowed client '".$allowed_client."' matches, logout request is allowed");
01325 $allowed = true;
01326 break;
01327 } else {
01328 phpCAS::trace("Allowed client '".$allowed_client."' does not match");
01329 }
01330 }
01331 if (!$allowed) {
01332 phpCAS::error("Unauthorized logout request from client '".$client."'");
01333 printf("Unauthorized!");
01334 phpCAS::traceExit();
01335 exit();
01336 }
01337 } else {
01338 phpCAS::trace("No access control set");
01339 }
01340
01341 preg_match("|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|", $_POST['logoutRequest'], $tick, PREG_OFFSET_CAPTURE, 3);
01342 $wrappedSamlSessionIndex = preg_replace('|<samlp:SessionIndex>|','',$tick[0][0]);
01343 $ticket2logout = preg_replace('|</samlp:SessionIndex>|','',$wrappedSamlSessionIndex);
01344 phpCAS::trace("Ticket to logout: ".$ticket2logout);
01345
01346
01347 if ($this->_signoutCallbackFunction) {
01348 $args = $this->_signoutCallbackArgs;
01349 array_unshift($args, $ticket2logout);
01350 call_user_func_array($this->_signoutCallbackFunction, $args);
01351 }
01352
01353
01354 if ($this->_start_session) {
01355 $session_id = preg_replace('/[^\w]/','',$ticket2logout);
01356 phpCAS::trace("Session id: ".$session_id);
01357
01358
01359 if(session_id() !== ""){
01360 session_unset();
01361 session_destroy();
01362 }
01363
01364 session_id($session_id);
01365 $_COOKIE[session_name()]=$session_id;
01366 $_GET[session_name()]=$session_id;
01367
01368
01369 session_start();
01370 session_unset();
01371 session_destroy();
01372 }
01373
01374 printf("Disconnected!");
01375 phpCAS::traceExit();
01376 exit();
01377 }
01378
01381
01382
01383
01384
01385
01386
01387
01388
01389
01402 private $_st = '';
01403
01408 public function getST()
01409 { return $this->_st; }
01410
01415 public function setST($st)
01416 { $this->_st = $st; }
01417
01422 public function hasST()
01423 { return !empty($this->_st); }
01424
01427
01428
01429
01440 private $_cas_server_ca_cert = '';
01441
01447 private $_no_cas_server_validation = false;
01448
01449
01455 public function setCasServerCACert($cert)
01456 {
01457 $this->_cas_server_ca_cert = $cert;
01458 }
01459
01463 public function setNoCasServerValidation()
01464 {
01465 $this->_no_cas_server_validation = true;
01466 }
01467
01479 public function validateST($validate_url,&$text_response,&$tree_response)
01480 {
01481 phpCAS::traceBegin();
01482
01483 $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
01484 if ( $this->isProxy() ) {
01485
01486 $validate_url .= '&pgtUrl='.urlencode($this->getCallbackURL());
01487 }
01488
01489
01490 if ( !$this->readURL($validate_url,array(),$headers,$text_response,$err_msg) ) {
01491 phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
01492 $this->authError('ST not validated',
01493 $validate_url,
01494 TRUE/*$no_response*/);
01495 }
01496
01497 // analyze the result depending on the version
01498 switch ($this->getServerVersion()) {
01499 case CAS_VERSION_1_0:
01500 if (preg_match('/^no\n/',$text_response)) {
01501 phpCAS::trace('ST has not been validated');
01502 $this->authError('ST not validated',
01503 $validate_url,
01504 FALSE/*$no_response*/,
01505 FALSE/*$bad_response*/,
01506 $text_response);
01507 }
01508 if (!preg_match('/^yes\n/',$text_response)) {
01509 phpCAS::trace('ill-formed response');
01510 $this->authError('ST not validated',
01511 $validate_url,
01512 FALSE/*$no_response*/,
01513 TRUE/*$bad_response*/,
01514 $text_response);
01515 }
01516 // ST has been validated, extract the user name
01517 $arr = preg_split('/\n/',$text_response);
01518 $this->setUser(trim($arr[1]));
01519 break;
01520 case CAS_VERSION_2_0:
01521
01522 // create new DOMDocument object
01523 $dom = new DOMDocument();
01524 // Fix possible whitspace problems
01525 $dom->preserveWhiteSpace = false;
01526 // read the response of the CAS server into a DOM object
01527 if ( !($dom->loadXML($text_response))) {
01528 phpCAS::trace('dom->loadXML() failed');
01529 $this->authError('ST not validated',
01530 $validate_url,
01531 FALSE/*$no_response*/,
01532 TRUE/*$bad_response*/,
01533 $text_response);
01534 }
01535 // read the root node of the XML tree
01536 if ( !($tree_response = $dom->documentElement) ) {
01537 phpCAS::trace('documentElement() failed');
01538 $this->authError('ST not validated',
01539 $validate_url,
01540 FALSE/*$no_response*/,
01541 TRUE/*$bad_response*/,
01542 $text_response);
01543 }
01544 // insure that tag name is 'serviceResponse'
01545 if ( $tree_response->localName != 'serviceResponse' ) {
01546 phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->localName.'\'');
01547 $this->authError('ST not validated',
01548 $validate_url,
01549 FALSE,
01550 TRUE,
01551 $text_response);
01552 }
01553
01554 if ( $tree_response->getElementsByTagName("authenticationSuccess")->length != 0) {
01555
01556 $success_elements = $tree_response->getElementsByTagName("authenticationSuccess");
01557 if ( $success_elements->item(0)->getElementsByTagName("user")->length == 0) {
01558
01559 $this->authError('ST not validated',
01560 $validate_url,
01561 FALSE,
01562 TRUE,
01563 $text_response);
01564 }
01565 $this->setUser(trim($success_elements->item(0)->getElementsByTagName("user")->item(0)->nodeValue));
01566 $this->readExtraAttributesCas20($success_elements);
01567 } else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) {
01568 phpCAS::trace('<authenticationFailure> found');
01569
01570 $auth_fail_list = $tree_response->getElementsByTagName("authenticationFailure");
01571 $this->authError('ST not validated',
01572 $validate_url,
01573 FALSE,
01574 FALSE,
01575 $text_response,
01576 $auth_fail_list->item(0)->getAttribute('code'),
01577 trim($auth_fail_list->item(0)->nodeValue));
01578 } else {
01579 phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
01580 $this->authError('ST not validated',
01581 $validate_url,
01582 FALSE,
01583 TRUE,
01584 $text_response);
01585 }
01586 break;
01587 }
01588 $this->renameSession($this->getST());
01589
01590 phpCAS::traceEnd(TRUE);
01591 return TRUE;
01592 }
01593
01594
01602 private function readExtraAttributesCas20($success_elements)
01603 {
01604 # PHPCAS-43 add CAS-2.0 extra attributes
01605 phpCAS::traceBegin();
01606
01607 $extra_attributes = array();
01608
01609
01610
01611
01612
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622
01623
01624
01625 if ( $success_elements->item(0)->getElementsByTagName("attributes")->length != 0) {
01626 $attr_nodes = $success_elements->item(0)->getElementsByTagName("attributes");
01627 phpCas :: trace("Found nested jasig style attributes");
01628 if($attr_nodes->item(0)->hasChildNodes()){
01629
01630 foreach ($attr_nodes->item(0)->childNodes as $attr_child) {
01631 phpCas :: trace("Attribute [".$attr_child->localName."] = ".$attr_child->nodeValue);
01632 $this->addAttributeToArray($extra_attributes, $attr_child->localName, $attr_child->nodeValue);
01633 }
01634 }
01635 }
01636
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649
01650
01651
01652 else {
01653 phpCas :: trace("Testing for rubycas style attributes");
01654 $childnodes = $success_elements->item(0)->childNodes;
01655 foreach ($childnodes as $attr_node) {
01656 switch ($attr_node->localName) {
01657 case 'user':
01658 case 'proxies':
01659 case 'proxyGrantingTicket':
01660 continue;
01661 default:
01662 if (strlen(trim($attr_node->nodeValue))) {
01663 phpCas :: trace("Attribute [".$attr_node->localName."] = ".$attr_node->nodeValue);
01664 $this->addAttributeToArray($extra_attributes, $attr_node->localName, $attr_node->nodeValue);
01665 }
01666 }
01667 }
01668 }
01669
01670
01671
01672
01673
01674
01675
01676
01677
01678
01679
01680
01681
01682
01683
01684
01685
01686
01687
01688
01689
01690 if (!count($extra_attributes) && $success_elements->item(0)->getElementsByTagName("attribute")->length != 0) {
01691 $attr_nodes = $success_elements->item(0)->getElementsByTagName("attribute");
01692 $firstAttr = $attr_nodes->item(0);
01693 if (!$firstAttr->hasChildNodes() && $firstAttr->hasAttribute('name') && $firstAttr->hasAttribute('value')) {
01694 phpCas :: trace("Found Name-Value style attributes");
01695
01696 foreach ($attr_nodes as $attr_node) {
01697 if ($attr_node->hasAttribute('name') && $attr_node->hasAttribute('value')) {
01698 phpCas :: trace("Attribute [".$attr_node->getAttribute('name')."] = ".$attr_node->getAttribute('value'));
01699 $this->addAttributeToArray($extra_attributes, $attr_node->getAttribute('name'), $attr_node->getAttribute('value'));
01700 }
01701 }
01702 }
01703 }
01704
01705 $this->setAttributes($extra_attributes);
01706 phpCAS::traceEnd();
01707 return TRUE;
01708 }
01709
01718 private function addAttributeToArray (array &$attributeArray, $name, $value) {
01719
01720 if (isset($attributeArray[$name])) {
01721
01722 if (!is_array($attributeArray[$name])) {
01723 $existingValue = $attributeArray[$name];
01724 $attributeArray[$name] = array($existingValue);
01725 }
01726
01727 $attributeArray[$name][] = trim($value);
01728 } else {
01729 $attributeArray[$name] = trim($value);
01730 }
01731 }
01732
01733
01734
01735
01752 public function validateSA($validate_url,&$text_response,&$tree_response)
01753 {
01754 phpCAS::traceBegin();
01755
01756
01757 $validate_url = $this->getServerSamlValidateURL();
01758
01759
01760 if ( !$this->readURL($validate_url,array(),$headers,$text_response,$err_msg) ) {
01761 phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
01762 $this->authError('SA not validated', $validate_url, TRUE/*$no_response*/);
01763 }
01764
01765 phpCAS::trace('server version: '.$this->getServerVersion());
01766
01767 // analyze the result depending on the version
01768 switch ($this->getServerVersion()) {
01769 case SAML_VERSION_1_1:
01770
01771 // create new DOMDocument Object
01772 $dom = new DOMDocument();
01773 // Fix possible whitspace problems
01774 $dom->preserveWhiteSpace = false;
01775 // read the response of the CAS server into a DOM object
01776 if ( !($dom->loadXML($text_response))) {
01777 phpCAS::trace('dom->loadXML() failed');
01778 $this->authError('SA not validated',
01779 $validate_url,
01780 FALSE/*$no_response*/,
01781 TRUE/*$bad_response*/,
01782 $text_response);
01783 }
01784 // read the root node of the XML tree
01785 if ( !($tree_response = $dom->documentElement) ) {
01786 phpCAS::trace('documentElement() failed');
01787 $this->authError('SA not validated',
01788 $validate_url,
01789 FALSE/*$no_response*/,
01790 TRUE/*$bad_response*/,
01791 $text_response);
01792 }
01793 // insure that tag name is 'Envelope'
01794 if ( $tree_response->localName != 'Envelope' ) {
01795 phpCAS::trace('bad XML root node (should be `Envelope\' instead of `'.$tree_response->localName.'\'');
01796 $this->authError('SA not validated',
01797 $validate_url,
01798 FALSE,
01799 TRUE,
01800 $text_response);
01801 }
01802
01803 if ( $tree_response->getElementsByTagName("NameIdentifier")->length != 0) {
01804 $success_elements = $tree_response->getElementsByTagName("NameIdentifier");
01805 phpCAS::trace('NameIdentifier found');
01806 $user = trim($success_elements->item(0)->nodeValue);
01807 phpCAS::trace('user = `'.$user.'`');
01808 $this->setUser($user);
01809 $this->setSessionAttributes($text_response);
01810 } else {
01811 phpCAS::trace('no <NameIdentifier> tag found in SAML payload');
01812 $this->authError('SA not validated',
01813 $validate_url,
01814 FALSE,
01815 TRUE,
01816 $text_response);
01817 }
01818 break;
01819 }
01820 $this->renameSession($this->getSA());
01821
01822 phpCAS::traceEnd(TRUE);
01823 return TRUE;
01824 }
01825
01833 private function setSessionAttributes($text_response)
01834 {
01835 phpCAS::traceBegin();
01836
01837 $result = FALSE;
01838
01839 $attr_array = array();
01840
01841
01842 $dom = new DOMDocument();
01843
01844 $dom->preserveWhiteSpace = false;
01845 if (($dom->loadXML($text_response))) {
01846 $xPath = new DOMXpath($dom);
01847 $xPath->registerNamespace('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
01848 $xPath->registerNamespace('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
01849 $nodelist = $xPath->query("//saml:Attribute");
01850
01851 if($nodelist){
01852 foreach($nodelist as $node){
01853 $xres = $xPath->query("saml:AttributeValue", $node);
01854 $name = $node->getAttribute("AttributeName");
01855 $value_array = array();
01856 foreach($xres as $node2){
01857 $value_array[] = $node2->nodeValue;
01858 }
01859 $attr_array[$name] = $value_array;
01860 }
01861
01862 foreach($attr_array as $attr_key => $attr_value) {
01863 if(count($attr_value) > 1) {
01864 $this->_attributes[$attr_key] = $attr_value;
01865 phpCAS::trace("* " . $attr_key . "=" . $attr_value);
01866 }
01867 else {
01868 $this->_attributes[$attr_key] = $attr_value[0];
01869 phpCAS::trace("* " . $attr_key . "=" . $attr_value[0]);
01870 }
01871 }
01872 $result = TRUE;
01873 }else{
01874 phpCAS::trace("SAML Attributes are empty");
01875 $result = FALSE;
01876 }
01877 }
01878 phpCAS::traceEnd($result);
01879 return $result;
01880 }
01881
01884
01885
01886
01887
01888
01889
01890
01891
01892
01902 private $_proxy;
01903
01907 private $_serviceCookieJar;
01908
01914 public function isProxy()
01915 {
01916 return $this->_proxy;
01917 }
01918
01920
01921
01922
01934 private $_pgt = '';
01935
01940 private function getPGT()
01941 { return $this->_pgt; }
01942
01947 private function setPGT($pgt)
01948 { $this->_pgt = $pgt; }
01949
01954 private function hasPGT()
01955 { return !empty($this->_pgt); }
01956
01959
01960
01961
01978 private $_callback_mode = FALSE;
01979
01985 private function setCallbackMode($callback_mode)
01986 {
01987 $this->_callback_mode = $callback_mode;
01988 }
01989
01996 private function isCallbackMode()
01997 {
01998 return $this->_callback_mode;
01999 }
02000
02008 private $_callback_url = '';
02009
02017 private function getCallbackURL()
02018 {
02019
02020 if ( empty($this->_callback_url) ) {
02021 $final_uri = '';
02022
02023 $final_uri = 'https://';
02024 $final_uri .= $this->getServerUrl();
02025 $request_uri = $_SERVER['REQUEST_URI'];
02026 $request_uri = preg_replace('/\?.*$/','',$request_uri);
02027 $final_uri .= $request_uri;
02028 $this->setCallbackURL($final_uri);
02029 }
02030 return $this->_callback_url;
02031 }
02032
02038 public function setCallbackURL($url)
02039 {
02040 return $this->_callback_url = $url;
02041 }
02042
02047 private function callback()
02048 {
02049 phpCAS::traceBegin();
02050 if (preg_match('/PGTIOU-[\.\-\w]/', $_GET['pgtIou'])){
02051 if(preg_match('/[PT]GT-[\.\-\w]/', $_GET['pgtId'])){
02052 $this->printHTMLHeader('phpCAS callback');
02053 $pgt_iou = $_GET['pgtIou'];
02054 $pgt = $_GET['pgtId'];
02055 phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
02056 echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
02057 $this->storePGT($pgt,$pgt_iou);
02058 $this->printHTMLFooter();
02059 }else{
02060 phpCAS::error('PGT format invalid' . $_GET['pgtId']);
02061 }
02062 }else{
02063 phpCAS::error('PGTiou format invalid' . $_GET['pgtIou']);
02064 }
02065 phpCAS::traceExit();
02066 exit();
02067 }
02068
02071
02072
02073
02086 private $_pgt_storage = null;
02087
02092 private function initPGTStorage()
02093 {
02094
02095 if ( !is_object($this->_pgt_storage) ) {
02096 $this->setPGTStorageFile();
02097 }
02098
02099
02100 $this->_pgt_storage->init();
02101 }
02102
02109 private function storePGT($pgt,$pgt_iou)
02110 {
02111
02112 $this->initPGTStorage();
02113
02114 $this->_pgt_storage->write($pgt,$pgt_iou);
02115 }
02116
02124 private function loadPGT($pgt_iou)
02125 {
02126
02127 $this->initPGTStorage();
02128
02129 return $this->_pgt_storage->read($pgt_iou);
02130 }
02131
02137 public function setPGTStorage($storage)
02138 {
02139
02140 if ( is_object($this->_pgt_storage) ) {
02141 phpCAS::error('PGT storage already defined');
02142 }
02143
02144
02145 if ( !($storage instanceof CAS_PGTStorage) ) {
02146 phpCAS::error('Invalid PGT storage object');
02147 }
02148
02149
02150 $this->_pgt_storage = $storage;
02151 }
02152
02163 public function setPGTStorageDb($dsn_or_pdo, $username='', $password='', $table='', $driver_options=null)
02164 {
02165
02166 $this->setPGTStorage(new CAS_PGTStorageDb($this, $dsn_or_pdo, $username, $password, $table, $driver_options));
02167 }
02168
02176 public function setPGTStorageFile($format='',
02177 $path='')
02178 {
02179
02180 $this->setPGTStorage(new CAS_PGTStorageFile($this,$format,$path));
02181 }
02182
02183
02184
02185
02186
02198 private function validatePGT(&$validate_url,$text_response,$tree_response)
02199 {
02200 phpCAS::traceBegin();
02201 if ( $tree_response->getElementsByTagName("proxyGrantingTicket")->length == 0) {
02202 phpCAS::trace('<proxyGrantingTicket> not found');
02203
02204 $this->authError('Ticket validated but no PGT Iou transmitted',
02205 $validate_url,
02206 FALSE,
02207 FALSE,
02208 $text_response);
02209 } else {
02210
02211 $pgt_iou = trim($tree_response->getElementsByTagName("proxyGrantingTicket")->item(0)->nodeValue);
02212 if(preg_match('/PGTIOU-[\.\-\w]/',$pgt_iou)){
02213 $pgt = $this->loadPGT($pgt_iou);
02214 if ( $pgt == FALSE ) {
02215 phpCAS::trace('could not load PGT');
02216 $this->authError('PGT Iou was transmitted but PGT could not be retrieved',
02217 $validate_url,
02218 FALSE,
02219 FALSE,
02220 $text_response);
02221 }
02222 $this->setPGT($pgt);
02223 }else{
02224 phpCAS::trace('PGTiou format error');
02225 $this->authError('PGT Iou was transmitted but has wrong fromat',
02226 $validate_url,
02227 FALSE,
02228 FALSE,
02229 $text_response);
02230 }
02231 }
02232 phpCAS::traceEnd(TRUE);
02233 return TRUE;
02234 }
02235
02236
02237
02238
02239
02249 private function retrievePT($target_service,&$err_code,&$err_msg)
02250 {
02251 phpCAS::traceBegin();
02252
02253
02254
02255
02256
02257 $err_msg = '';
02258
02259
02260 $cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT();
02261
02262
02263 if ( !$this->readURL($cas_url,array(),$headers,$cas_response,$err_msg) ) {
02264 phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
02265 $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
02266 $err_msg = 'could not retrieve PT (no response from the CAS server)';
02267 phpCAS::traceEnd(FALSE);
02268 return FALSE;
02269 }
02270
02271 $bad_response = FALSE;
02272
02273 if ( !$bad_response ) {
02274 // create new DOMDocument object
02275 $dom = new DOMDocument();
02276 // Fix possible whitspace problems
02277 $dom->preserveWhiteSpace = false;
02278 // read the response of the CAS server into a DOM object
02279 if ( !($dom->loadXML($cas_response))) {
02280 phpCAS::trace('dom->loadXML() failed');
02281 // read failed
02282 $bad_response = TRUE;
02283 }
02284 }
02285
02286 if ( !$bad_response ) {
02287 // read the root node of the XML tree
02288 if ( !($root = $dom->documentElement) ) {
02289 phpCAS::trace('documentElement failed');
02290 // read failed
02291 $bad_response = TRUE;
02292 }
02293 }
02294
02295 if ( !$bad_response ) {
02296 // insure that tag name is 'serviceResponse'
02297 if ( $root->localName != 'serviceResponse' ) {
02298 phpCAS::trace('localName failed');
02299 // bad root node
02300 $bad_response = TRUE;
02301 }
02302 }
02303
02304 if ( !$bad_response ) {
02305 // look for a proxySuccess tag
02306 if ( $root->getElementsByTagName("proxySuccess")->length != 0) {
02307 $proxy_success_list = $root->getElementsByTagName("proxySuccess");
02308
02309 // authentication succeded, look for a proxyTicket tag
02310 if ( $proxy_success_list->item(0)->getElementsByTagName("proxyTicket")->length != 0) {
02311 $err_code = PHPCAS_SERVICE_OK;
02312 $err_msg = '';
02313 $pt = trim($proxy_success_list->item(0)->getElementsByTagName("proxyTicket")->item(0)->nodeValue);
02314 phpCAS::trace('original PT: '.trim($pt));
02315 phpCAS::traceEnd($pt);
02316 return $pt;
02317 } else {
02318 phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
02319 }
02320 }
02321 // look for a proxyFailure tag
02322 else if ( $root->getElementsByTagName("proxyFailure")->length != 0) {
02323 $proxy_failure_list = $root->getElementsByTagName("proxyFailure");
02324
02325 // authentication failed, extract the error
02326 $err_code = PHPCAS_SERVICE_PT_FAILURE;
02327 $err_msg = 'PT retrieving failed (code=`'
02328 .$proxy_failure_list->item(0)->getAttribute('code')
02329 .'\', message=`'
02330 .trim($proxy_failure_list->item(0)->nodeValue)
02331 .'\')';
02332 phpCAS::traceEnd(FALSE);
02333 return FALSE;
02334 } else {
02335 phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
02336 }
02337 }
02338
02339 // at this step, we are sure that the response of the CAS server was ill-formed
02340 $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
02341 $err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
02342
02343 phpCAS::traceEnd(FALSE);
02344 return FALSE;
02345 }
02346
02347
02348
02349
02350
02364 private function readURL($url, array $cookies, &$headers, &$body, &$err_msg)
02365 {
02366 $className = $this->_requestImplementation;
02367 $request = new $className();
02368
02369 if (count($this->_curl_options)) {
02370 $request->setCurlOptions($this->_curl_options);
02371 }
02372
02373 $request->setUrl($url);
02374 $request->addCookies($cookies);
02375
02376 if (empty($this->_cas_server_ca_cert) && !$this->_no_cas_server_validation) {
02377 phpCAS::error('one of the methods phpCAS::setCasServerCACert() or phpCAS::setNoCasServerValidation() must be called.');
02378 }
02379 if ($this->_cas_server_ca_cert != '') {
02380 $request->setSslCaCert($this->_cas_server_ca_cert);
02381 }
02382
02383
02384 if ($this->hasSA()) {
02385 $request->addHeader("soapaction: http://www.oasis-open.org/committees/security");
02386 $request->addHeader("cache-control: no-cache");
02387 $request->addHeader("pragma: no-cache");
02388 $request->addHeader("accept: text/xml");
02389 $request->addHeader("connection: keep-alive");
02390 $request->addHeader("content-type: text/xml");
02391 $request->makePost();
02392 $request->setPostBody($this->buildSAMLPayload());
02393 }
02394
02395 if ($request->send()) {
02396 $headers = $request->getResponseHeaders();
02397 $body = $request->getResponseBody();
02398 $err_msg = '';
02399 return true;
02400 } else {
02401 $headers = '';
02402 $body = '';
02403 $err_msg = $request->getErrorMessage();
02404 return false;
02405 }
02406 }
02407
02413 private function buildSAMLPayload()
02414 {
02415 phpCAS::traceBegin();
02416
02417
02418 $sa = $this->getSA();
02419
02420 $body=SAML_SOAP_ENV.SAML_SOAP_BODY.SAMLP_REQUEST.SAML_ASSERTION_ARTIFACT.$sa.SAML_ASSERTION_ARTIFACT_CLOSE.SAMLP_REQUEST_CLOSE.SAML_SOAP_BODY_CLOSE.SAML_SOAP_ENV_CLOSE;
02421
02422 phpCAS::traceEnd($body);
02423 return ($body);
02424 }
02425
02426 private $_curl_headers = array();
02430 public function _curl_read_headers($ch, $header)
02431 {
02432 $this->_curl_headers[] = $header;
02433 return strlen($header);
02434 }
02435
02449 public function serviceWeb($url,&$err_code,&$output)
02450 {
02451 phpCAS::traceBegin();
02452 $cookies = array();
02453
02454 $pt = $this->retrievePT($url,$err_code,$ptoutput);
02455
02456 $res = TRUE;
02457
02458
02459 if ( !$pt ) {
02460
02461 phpCAS::trace('PT was not retrieved correctly');
02462 $res = FALSE;
02463 } else {
02464
02465 $cookies = array();
02466 foreach ( $this->_serviceCookieJar->getCookies($url) as $name => $val ) {
02467 $cookies[] = $name.'='.$val;
02468 }
02469
02470
02471 if ( strstr($url,'?') === FALSE ) {
02472 $service_url = $url.'?ticket='.$pt;
02473 } else {
02474 $service_url = $url.'&ticket='.$pt;
02475 }
02476 phpCAS::trace('reading URL`'.$service_url.'\'');
02477 if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
02478 phpCAS::trace('could not read URL`'.$service_url.'\'');
02479 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
02480
02481 $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
02482 $service_url,
02483 $err_msg);
02484 $res = FALSE;
02485 } else {
02486
02487 phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
02488 $this->_serviceCookieJar->storeCookies($service_url, $headers);
02489 }
02490 // Check for the redirect after authentication
02491 foreach($headers as $header){
02492 if (preg_match('/^(Location:|URI:)\s*([^\s]+.*)$/', $header, $matches))
02493 {
02494 $redirect_url = trim(array_pop($matches));
02495 phpCAS :: trace('Found redirect:'.$redirect_url);
02496 $cookies = array();
02497 foreach ( $this->_serviceCookieJar->getCookies($redirect_url) as $name => $val ) {
02498 $cookies[] = $name.'='.$val;
02499 }
02500 phpCAS::trace('reading URL`'.$redirect_url.'\'');
02501 if ( !$this->readURL($redirect_url,$cookies,$headers,$output,$err_msg) ) {
02502 phpCAS::trace('could not read URL`'.$redirect_url.'\'');
02503 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
02504
02505 $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
02506 $service_url,
02507 $err_msg);
02508 $res = FALSE;
02509 } else {
02510
02511 phpCAS::trace('URL`'.$redirect_url.'\' has been read, storing cookies:');
02512 $this->_serviceCookieJar->storeCookies($redirect_url, $headers);
02513 }
02514 break;
02515 }
02516
02517 }
02518 }
02519
02520 phpCAS::traceEnd($res);
02521 return $res;
02522 }
02523
02541 public function serviceMail($url,$service,$flags,&$err_code,&$err_msg,&$pt)
02542 {
02543 phpCAS::traceBegin();
02544 // at first retrieve a PT
02545 $pt = $this->retrievePT($service,$err_code,$ptoutput);
02546
02547 $stream = FALSE;
02548
02549 // test if PT was retrieved correctly
02550 if ( !$pt ) {
02551 // note: $err_code and $err_msg are filled by CASClient::retrievePT()
02552 phpCAS::trace('PT was not retrieved correctly');
02553 } else {
02554 phpCAS::trace('opening IMAP URL `'.$url.'\'...');
02555 $stream = @imap_open($url,$this->getUser(),$pt,$flags);
02556 if ( !$stream ) {
02557 phpCAS::trace('could not open URL');
02558 $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
02559
02560 $err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
02561 $url,
02562 var_export(imap_errors(),TRUE));
02563 $pt = FALSE;
02564 $stream = FALSE;
02565 } else {
02566 phpCAS::trace('ok');
02567 }
02568 }
02569
02570 phpCAS::traceEnd($stream);
02571 return $stream;
02572 }
02573
02576
02577
02578
02579
02580
02581
02582
02583
02584
02597 private $_pt = '';
02598
02603 public function getPT()
02604 {
02605
02606 return $this->_pt;
02607 }
02608
02613 public function setPT($pt)
02614 { $this->_pt = $pt; }
02615
02620 public function hasPT()
02621 { return !empty($this->_pt); }
02622
02623
02632 private $_proxies = array();
02633
02644 public function getProxies () {
02645 return $this->_proxies;
02646 }
02647
02656 private function setProxies ($proxies) {
02657 $this->_proxies = $proxies;
02658 }
02659
02664 public function getSA()
02665 { return 'ST'.substr($this->_sa, 2); }
02666
02671 public function setSA($sa)
02672 { $this->_sa = $sa; }
02673
02678 public function hasSA()
02679 { return !empty($this->_sa); }
02680
02682
02683
02684
02695 public function validatePT(&$validate_url,&$text_response,&$tree_response)
02696 {
02697 phpCAS::traceBegin();
02698 phpCAS::trace($text_response);
02699
02700 $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
02701
02702 if ( $this->isProxy() ) {
02703
02704 $validate_url .= '&pgtUrl='.urlencode($this->getCallbackURL());
02705 }
02706
02707
02708 if ( !$this->readURL($validate_url,array(),$headers,$text_response,$err_msg) ) {
02709 phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
02710 $this->authError('PT not validated',
02711 $validate_url,
02712 TRUE/*$no_response*/);
02713 }
02714
02715 // create new DOMDocument object
02716 $dom = new DOMDocument();
02717 // Fix possible whitspace problems
02718 $dom->preserveWhiteSpace = false;
02719 // read the response of the CAS server into a DOMDocument object
02720 if ( !($dom->loadXML($text_response))) {
02721 // read failed
02722 $this->authError('PT not validated',
02723 $validate_url,
02724 FALSE/*$no_response*/,
02725 TRUE/*$bad_response*/,
02726 $text_response);
02727 }
02728
02729 // read the root node of the XML tree
02730 if ( !($tree_response = $dom->documentElement) ) {
02731 // read failed
02732 $this->authError('PT not validated',
02733 $validate_url,
02734 FALSE/*$no_response*/,
02735 TRUE/*$bad_response*/,
02736 $text_response);
02737 }
02738 // insure that tag name is 'serviceResponse'
02739 if ( $tree_response->localName != 'serviceResponse' ) {
02740 // bad root node
02741 $this->authError('PT not validated',
02742 $validate_url,
02743 FALSE/*$no_response*/,
02744 TRUE/*$bad_response*/,
02745 $text_response);
02746 }
02747 if ( $tree_response->getElementsByTagName("authenticationSuccess")->length != 0) {
02748 // authentication succeded, extract the user name
02749 $success_elements = $tree_response->getElementsByTagName("authenticationSuccess");
02750 if ( $success_elements->item(0)->getElementsByTagName("user")->length == 0) {
02751 // no user specified => error
02752 $this->authError('PT not validated',
02753 $validate_url,
02754 FALSE/*$no_response*/,
02755 TRUE/*$bad_response*/,
02756 $text_response);
02757 }
02758
02759 $this->setUser(trim($success_elements->item(0)->getElementsByTagName("user")->item(0)->nodeValue));
02760 $this->readExtraAttributesCas20($success_elements);
02761
02762 // Store the proxies we are sitting behind for authorization checking
02763 if ( sizeof($arr = $success_elements->item(0)->getElementsByTagName("proxy")) > 0) {
02764 foreach ($arr as $proxyElem) {
02765 phpCAS::trace("Storing Proxy: ".$proxyElem->nodeValue);
02766 $this->_proxies[] = trim($proxyElem->nodeValue);
02767 }
02768 $_SESSION['phpCAS']['proxies'] = $this->_proxies;
02769 }
02770
02771 } else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) {
02772 // authentication succeded, extract the error code and message
02773 $auth_fail_list = $tree_response->getElementsByTagName("authenticationFailure");
02774 $this->authError('PT not validated',
02775 $validate_url,
02776 FALSE/*$no_response*/,
02777 FALSE/*$bad_response*/,
02778 $text_response,
02779 $auth_fail_list->item(0)->getAttribute('code')/*$err_code*/,
02780 trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/);
02781 } else {
02782 $this->authError('PT not validated',
02783 $validate_url,
02784 FALSE/*$no_response*/,
02785 TRUE/*$bad_response*/,
02786 $text_response);
02787 }
02788
02789 $this->renameSession($this->getPT());
02790 // at this step, PT has been validated and $this->_user has been set,
02791
02792 phpCAS::traceEnd(TRUE);
02793 return TRUE;
02794 }
02795
02798 // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
02799 // XX XX
02800 // XX MISC XX
02801 // XX XX
02802 // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
02803
02809 // ########################################################################
02810 // URL
02811 // ########################################################################
02818 private $_url = '';
02819
02826 private function getURL()
02827 {
02828 phpCAS::traceBegin();
02829 // the URL is built when needed only
02830 if ( empty($this->_url) ) {
02831 $final_uri = '';
02832 // remove the ticket if present in the URL
02833 $final_uri = ($this->isHttps()) ? 'https' : 'http';
02834 $final_uri .= ':
02835
02836 $final_uri .= $this->getServerUrl();
02837 $request_uri = explode('?', $_SERVER['REQUEST_URI'], 2);
02838 $final_uri .= $request_uri[0];
02839
02840 if (isset($request_uri[1]) && $request_uri[1])
02841 {
02842 $query_string = $this->removeParameterFromQueryString('ticket', $request_uri[1]);
02843
02844
02845 if ($query_string !== '')
02846 $final_uri .= "?$query_string";
02847
02848 }
02849
02850 phpCAS::trace("Final URI: $final_uri");
02851 $this->setURL($final_uri);
02852 }
02853 phpCAS::traceEnd($this->_url);
02854 return $this->_url;
02855 }
02856
02861 private function getServerUrl(){
02862 $server_url = '';
02863 if(!empty($_SERVER['HTTP_X_FORWARDED_HOST'])){
02864
02865 $hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
02866 $server_url = $hosts[0];
02867 }else if(!empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
02868 $server_url = $_SERVER['HTTP_X_FORWARDED_SERVER'];
02869 }else{
02870 if (empty($_SERVER['SERVER_NAME'])) {
02871 $server_url = $_SERVER['HTTP_HOST'];
02872 } else {
02873 $server_url = $_SERVER['SERVER_NAME'];
02874 }
02875 }
02876 if (!strpos($server_url, ':')) {
02877 if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
02878 || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
02879 $server_url .= ':';
02880 $server_url .= $_SERVER['SERVER_PORT'];
02881 }
02882 }
02883 return $server_url;
02884 }
02885
02886
02887
02897 private function removeParameterFromQueryString($parameterName, $queryString)
02898 {
02899 $parameterName = preg_quote($parameterName);
02900 return preg_replace("/&$parameterName(=[^&]*)?|^$parameterName(=[^&]*)?&?/", '', $queryString);
02901 }
02902
02903
02909 public function setURL($url)
02910 {
02911 $this->_url = $url;
02912 }
02913
02914
02915
02916
02930 private function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
02931 {
02932 phpCAS::traceBegin();
02933
02934 $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
02935 printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),htmlentities($this->getURL()),$_SERVER['SERVER_ADMIN']);
02936 phpCAS::trace('CAS URL: '.$cas_url);
02937 phpCAS::trace('Authentication failure: '.$failure);
02938 if ( $no_response ) {
02939 phpCAS::trace('Reason: no response from the CAS server');
02940 } else {
02941 if ( $bad_response ) {
02942 phpCAS::trace('Reason: bad response from the CAS server');
02943 } else {
02944 switch ($this->getServerVersion()) {
02945 case CAS_VERSION_1_0:
02946 phpCAS::trace('Reason: CAS error');
02947 break;
02948 case CAS_VERSION_2_0:
02949 if ( empty($err_code) )
02950 phpCAS::trace('Reason: no CAS error');
02951 else
02952 phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
02953 break;
02954 }
02955 }
02956 phpCAS::trace('CAS response: '.$cas_response);
02957 }
02958 $this->printHTMLFooter();
02959 phpCAS::traceExit();
02960
02961 if ($this->_exitOnAuthError)
02962 exit();
02963 }
02964
02966 }
02967
02968 ?>