• Main Page
  • Related Pages
  • Modules
  • Classes
  • Files
  • Examples
  • File List
  • File Members

CAS/client.php

Go to the documentation of this file.
00001 <?php
00002 
00003 /*
00004  * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions are met:
00009  *
00010  *     * Redistributions of source code must retain the above copyright notice,
00011  *       this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright notice,
00013  *       this list of conditions and the following disclaimer in the documentation
00014  *       and/or other materials provided with the distribution.
00015  *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
00016  *       Collaborative nor the names of its contributors may be used to endorse or
00017  *       promote products derived from this software without specific prior
00018  *       written permission.
00019 
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00021  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00022  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00023  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
00024  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00025  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00026  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00027  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00028  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00029  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030  */
00031 
00037 // include internationalization stuff
00038 include_once(dirname(__FILE__).'/languages/languages.php');
00039 
00040 // include PGT storage classes
00041 include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php');
00042 
00043 // include class for storing service cookies.
00044 include_once(dirname(__FILE__).'/CookieJar.php');
00045 
00046 // include class for fetching web requests.
00047 include_once(dirname(__FILE__).'/Request/CurlRequest.php');
00048 
00057 class CASClient
00058 {
00059 
00060         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00061         // XX                                                                    XX
00062         // XX                          CONFIGURATION                             XX
00063         // XX                                                                    XX
00064         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00065 
00066         // ########################################################################
00067         //  HTML OUTPUT
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         //  INTERNATIONALIZATION
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                 // call CASclient::getLang() to be sure the language is initialized
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                 // include the corresponding language file
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://' . $this->getServerHostname();
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                 // the URL is build only when needed
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                                 // It is recommended that when the "renew" parameter is set, its value be "true"
00442                                 $this->_server['login_url'] .= '&renew=true';
00443                         } elseif ($gateway) {
00444                                 // It is recommended that when the "gateway" parameter is set, its value be "true"
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                 // the URL is build only when needed
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                 // the URL is build only when needed
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                 // the URL is build only when needed
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                 // the URL is build only when needed
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                 // the URL is build only when needed
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         //  CONSTRUCTOR
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                 // skip Session Handling for logout requests and if don't want it'
00678                 if ($start_session && !$this->isLogoutRequest())
00679                 {
00680                         phpCAS :: trace("Starting a new session");
00681                         session_start();
00682                 }
00683 
00684 
00685                 // are we in proxy mode ?
00686                 $this->_proxy = $proxy;
00687 
00688                 // Make cookie handling available.
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                 //check version
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                 // check port
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                 // add leading and trailing `/' and remove doubles
00735                 $server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
00736                 $this->_server['uri'] = $server_uri;
00737 
00738                 // set to callback mode if PgtIou and PgtId CGI GET parameters are provided
00739                 if ( $this->isProxy() ) {
00740                         $this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
00741                 }
00742 
00743                 if ( $this->isCallbackMode() ) {
00744                         //callback mode: check that phpCAS is secured
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                         //normal mode: get ticket and remove it from CGI parameters for developpers
00750                         $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
00751                         switch ($this->getServerVersion()) {
00752                                 case CAS_VERSION_1_0: // check for a Service Ticket
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: // check for a Service or Proxy Ticket
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: // SAML just does Service Tickets
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         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00792         // XX                                                                    XX
00793         // XX                           Session Handling                         XX
00794         // XX                                                                    XX
00795         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
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                                 // set up a new session, of name based on the ticket
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         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
00842         // XX                                                                    XX
00843         // XX                           AUTHENTICATION                           XX
00844         // XX                                                                    XX
00845         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
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          * Atrributes section
00890          *
00891          * @author Matthias Crauwels <matthias.crauwels@ugent.be>, Ghent University, Belgium
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) ) { // if no user is set, there shouldn't be any attributes also...
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                 // Either way, the user is authenticated by CAS
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                         // the user is authenticated, nothing to be done.
00954                         phpCAS::trace('no need to authenticate');
00955                         $res = TRUE;
00956                 } else {
00957                         // the user is not authenticated, redirect to the CAS server
00958                         if (isset($_SESSION['phpCAS']['auth_checked'])) {
00959                                 unset($_SESSION['phpCAS']['auth_checked']);
00960                         }
00961                         $this->redirectToCas(FALSE/* no gateway */);
00962                         // never reached
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                         // the previous request has redirected the client to the CAS server with gateway=true
01000                         unset($_SESSION['phpCAS']['auth_checked']);
01001                         $res = FALSE;
01002                 } else {
01003                         // avoid a check against CAS on every request
01004                         if (! isset($_SESSION['phpCAS']['unauth_count']) )
01005                         $_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized
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/* gateway */);
01028                                 // never reached
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                                 // User has a additional ticket but was already authenticated
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                                 // the user has already (previously during the session) been
01058                                 // authenticated, nothing to be done.
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                                 // if a Service Ticket was given, validate it
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); // idem
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); // note: if it fails, it halts
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                                 // if we have a SAML ticket, validate it.
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                                 // no ticket given, not authenticated
01110                                 phpCAS::trace('no ticket found');
01111                         }
01112                         if ($res) {
01113                                 // Mark the auth-check as complete to allow post-authentication
01114                                 // callbacks to make use of phpCAS::getUser() and similar methods
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                                 // call the post-authenticate callback if registered.
01126                                 if ($this->_postAuthenticateCallbackFunction) {
01127                                         $args = $this->_postAuthenticateCallbackArgs;
01128                                         array_unshift($args, $logoutTicket);
01129                                         call_user_func_array($this->_postAuthenticateCallbackFunction, $args);
01130                                 }
01131                                 
01132                                 // if called with a ticket parameter, we need to redirect to the app without the ticket so that CAS-ification is transparent to the browser (for later POSTS)
01133                                 // most of the checks and errors should have been made now, so we're safe for redirect without masking error messages.
01134                                 // remove the ticket as a security precaution to prevent a ticket in the HTTP_REFERRER
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                         // CAS proxy: username and PGT must be present
01178                         if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
01179                                 // authentication already done
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                                 // Include the list of proxies
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                                 // these two variables should be empty or not empty at the same time
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                                 // unset all tickets to enforce authentication
01205                                 unset($_SESSION['phpCAS']);
01206                                 $this->setST('');
01207                                 $this->setPT('');
01208                         } else {
01209                                 phpCAS::trace('neither user not PGT found');
01210                         }
01211                 } else {
01212                         // `simple' CAS client (not a proxy): username must be present
01213                         if ( $this->isSessionAuthenticated() ) {
01214                                 // authentication already done
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                                 // Include the list of proxies
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                 // Extract the ticket from the SAML Request
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                 // call the post-authenticate callback if registered.
01347                 if ($this->_signoutCallbackFunction) {
01348                         $args = $this->_signoutCallbackArgs;
01349                         array_unshift($args, $ticket2logout);
01350                         call_user_func_array($this->_signoutCallbackFunction, $args);
01351                 }
01352                 
01353                 // If phpCAS is managing the session, destroy it.
01354                 if ($this->_start_session) {
01355                         $session_id = preg_replace('/[^\w]/','',$ticket2logout);
01356                         phpCAS::trace("Session id: ".$session_id);
01357         
01358                         // destroy a possible application session created before phpcas
01359                         if(session_id() !== ""){
01360                                 session_unset();
01361                                 session_destroy();
01362                         }
01363                         // fix session ID
01364                         session_id($session_id);
01365                         $_COOKIE[session_name()]=$session_id;
01366                         $_GET[session_name()]=$session_id;
01367         
01368                         // Overwrite session
01369                         session_start();
01370                         session_unset();
01371                         session_destroy();
01372                 }
01373                 
01374                 printf("Disconnected!");
01375                 phpCAS::traceExit();
01376                 exit();
01377         }
01378 
01381         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
01382         // XX                                                                    XX
01383         // XX                  BASIC CLIENT FEATURES (CAS 1.0)                   XX
01384         // XX                                                                    XX
01385         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
01386 
01387         // ########################################################################
01388         //  ST
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         //  ST VALIDATION
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                 // build the URL to validate the ticket
01483                 $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
01484                 if ( $this->isProxy() ) {
01485                         // pass the callback url for CAS proxies
01486                         $validate_url .= '&pgtUrl='.urlencode($this->getCallbackURL());
01487                 }
01488 
01489                 // open and read the URL
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/*$no_response*/,
01550                                         TRUE/*$bad_response*/,
01551                                         $text_response);
01552                                 }
01553 
01554                                 if ( $tree_response->getElementsByTagName("authenticationSuccess")->length != 0) {
01555                                         // authentication succeded, extract the user name
01556                                         $success_elements = $tree_response->getElementsByTagName("authenticationSuccess");
01557                                         if ( $success_elements->item(0)->getElementsByTagName("user")->length == 0) {
01558                                                 // no user specified => error
01559                                                 $this->authError('ST not validated',
01560                                                 $validate_url,
01561                                                 FALSE/*$no_response*/,
01562                                                 TRUE/*$bad_response*/,
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                                         // authentication failed, extract the error code and message
01570                                         $auth_fail_list = $tree_response->getElementsByTagName("authenticationFailure");
01571                                         $this->authError('ST not validated',
01572                                         $validate_url,
01573                                         FALSE/*$no_response*/,
01574                                         FALSE/*$bad_response*/,
01575                                         $text_response,
01576                                         $auth_fail_list->item(0)->getAttribute('code')/*$err_code*/,
01577                                         trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/);
01578                                 } else {
01579                                         phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
01580                                         $this->authError('ST not validated',
01581                                         $validate_url,
01582                                         FALSE/*$no_response*/,
01583                                         TRUE/*$bad_response*/,
01584                                         $text_response);
01585                                 }
01586                                 break;
01587                 }
01588                 $this->renameSession($this->getST());
01589                 // at this step, ST has been validated and $this->_user has been set,
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                 // "Jasig Style" Attributes:
01610                 // 
01611                 //      <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
01612                 //              <cas:authenticationSuccess>
01613                 //                      <cas:user>jsmith</cas:user>
01614                 //                      <cas:attributes>
01615                 //                              <cas:attraStyle>RubyCAS</cas:attraStyle>
01616                 //                              <cas:surname>Smith</cas:surname>
01617                 //                              <cas:givenName>John</cas:givenName>
01618                 //                              <cas:memberOf>CN=Staff,OU=Groups,DC=example,DC=edu</cas:memberOf>
01619                 //                              <cas:memberOf>CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu</cas:memberOf>
01620                 //                      </cas:attributes>
01621                 //                      <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket>
01622                 //              </cas:authenticationSuccess>
01623                 //      </cas:serviceResponse>
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                                 // Nested Attributes
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                 // "RubyCAS Style" attributes
01637                 // 
01638                 //      <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
01639                 //              <cas:authenticationSuccess>
01640                 //                      <cas:user>jsmith</cas:user>
01641                 //                      
01642                 //                      <cas:attraStyle>RubyCAS</cas:attraStyle>
01643                 //                      <cas:surname>Smith</cas:surname>
01644                 //                      <cas:givenName>John</cas:givenName>
01645                 //                      <cas:memberOf>CN=Staff,OU=Groups,DC=example,DC=edu</cas:memberOf>
01646                 //                      <cas:memberOf>CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu</cas:memberOf>
01647                 //                      
01648                 //                      <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket>
01649                 //              </cas:authenticationSuccess>
01650                 //      </cas:serviceResponse>
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                 // "Name-Value" attributes.
01671                 // 
01672                 // Attribute format from these mailing list thread:
01673                 // http://jasig.275507.n4.nabble.com/CAS-attributes-and-how-they-appear-in-the-CAS-response-td264272.html
01674                 // Note: This is a less widely used format, but in use by at least two institutions.
01675                 // 
01676                 //      <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
01677                 //              <cas:authenticationSuccess>
01678                 //                      <cas:user>jsmith</cas:user>
01679                 //                      
01680                 //                      <cas:attribute name='attraStyle' value='Name-Value' />
01681                 //                      <cas:attribute name='surname' value='Smith' />
01682                 //                      <cas:attribute name='givenName' value='John' />
01683                 //                      <cas:attribute name='memberOf' value='CN=Staff,OU=Groups,DC=example,DC=edu' />
01684                 //                      <cas:attribute name='memberOf' value='CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu' />
01685                 //                      
01686                 //                      <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket>
01687                 //              </cas:authenticationSuccess>
01688                 //      </cas:serviceResponse>
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                                 // Nested Attributes
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                 // If multiple attributes exist, add as an array value
01720                 if (isset($attributeArray[$name])) {
01721                         // Initialize the array with the existing value
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         //  SAML VALIDATION
01735         // ########################################################################
01752         public function validateSA($validate_url,&$text_response,&$tree_response)
01753         {
01754                 phpCAS::traceBegin();
01755 
01756                 // build the URL to validate the ticket
01757                 $validate_url = $this->getServerSamlValidateURL();
01758 
01759                 // open and read the URL
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/*$no_response*/,
01799                                         TRUE/*$bad_response*/,
01800                                         $text_response);
01801                                 }
01802                                 // check for the NameIdentifier tag in the SAML response
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/*$no_response*/,
01815                                         TRUE/*$bad_response*/,
01816                                         $text_response);
01817                                 }
01818                                 break;
01819                 }
01820                 $this->renameSession($this->getSA());
01821                 // at this step, ST has been validated and $this->_user has been set,
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                 // create new DOMDocument Object
01842                 $dom = new DOMDocument();
01843                 // Fix possible whitspace problems
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                                 // UGent addition...
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         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
01885         // XX                                                                    XX
01886         // XX                     PROXY FEATURES (CAS 2.0)                       XX
01887         // XX                                                                    XX
01888         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
01889 
01890         // ########################################################################
01891         //  PROXYING
01892         // ########################################################################
01902         private $_proxy;
01903 
01907         private $_serviceCookieJar;
01908 
01914         public function isProxy()
01915         {
01916                 return $this->_proxy;
01917         }
01918 
01920         // ########################################################################
01921         //  PGT
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         //  CALLBACK MODE
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                 // the URL is built when needed only
02020                 if ( empty($this->_callback_url) ) {
02021                         $final_uri = '';
02022                         // remove the ticket if present in the URL
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         //  PGT STORAGE
02073         // ########################################################################
02086         private $_pgt_storage = null;
02087 
02092         private function initPGTStorage()
02093         {
02094                 // if no SetPGTStorageXxx() has been used, default to file
02095                 if ( !is_object($this->_pgt_storage) ) {
02096                         $this->setPGTStorageFile();
02097                 }
02098 
02099                 // initializes the storage
02100                 $this->_pgt_storage->init();
02101         }
02102 
02109         private function storePGT($pgt,$pgt_iou)
02110         {
02111                 // ensure that storage is initialized
02112                 $this->initPGTStorage();
02113                 // writes the PGT
02114                 $this->_pgt_storage->write($pgt,$pgt_iou);
02115         }
02116 
02124         private function loadPGT($pgt_iou)
02125         {
02126                 // ensure that storage is initialized
02127                 $this->initPGTStorage();
02128                 // read the PGT
02129                 return $this->_pgt_storage->read($pgt_iou);
02130         }
02131 
02137         public function setPGTStorage($storage)
02138         {
02139                 // check that the storage has not already been set
02140                 if ( is_object($this->_pgt_storage) ) {
02141                         phpCAS::error('PGT storage already defined');
02142                 }
02143 
02144                 // check to make sure a valid storage object was specified
02145                 if ( !($storage instanceof CAS_PGTStorage) ) {
02146                         phpCAS::error('Invalid PGT storage object');
02147                 }
02148 
02149                 // store the PGTStorage object
02150                 $this->_pgt_storage = $storage;
02151         }
02152 
02163         public function setPGTStorageDb($dsn_or_pdo, $username='', $password='', $table='', $driver_options=null)
02164         {
02165                 // create the storage object
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                 // create the storage object
02180                 $this->setPGTStorage(new CAS_PGTStorageFile($this,$format,$path));
02181         }
02182 
02183 
02184         // ########################################################################
02185         //  PGT VALIDATION
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                         // authentication succeded, but no PGT Iou was transmitted
02204                         $this->authError('Ticket validated but no PGT Iou transmitted',
02205                         $validate_url,
02206                         FALSE/*$no_response*/,
02207                         FALSE/*$bad_response*/,
02208                         $text_response);
02209                 } else {
02210                         // PGT Iou transmitted, extract it
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/*$no_response*/,
02219                                         FALSE/*$bad_response*/,
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/*$no_response*/,
02228                                 FALSE/*$bad_response*/,
02229                                 $text_response);
02230                         }
02231                 }
02232                 phpCAS::traceEnd(TRUE);
02233                 return TRUE;
02234         }
02235 
02236         // ########################################################################
02237         //  PGT VALIDATION
02238         // ########################################################################
02239 
02249         private function retrievePT($target_service,&$err_code,&$err_msg)
02250         {
02251                 phpCAS::traceBegin();
02252 
02253                 // by default, $err_msg is set empty and $pt to TRUE. On error, $pt is
02254                 // set to false and $err_msg to an error message. At the end, if $pt is FALSE
02255                 // and $error_msg is still empty, it is set to 'invalid response' (the most
02256                 // commonly encountered error).
02257                 $err_msg = '';
02258 
02259                 // build the URL to retrieve the PT
02260                 $cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT();
02261 
02262                 // open and read the URL
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         // ACCESS TO EXTERNAL SERVICES
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                 // add extra stuff if SAML
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                 //get the ticket
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                 // at first retrieve a PT
02454                 $pt = $this->retrievePT($url,$err_code,$ptoutput);
02455 
02456                 $res = TRUE;
02457 
02458                 // test if PT was retrieved correctly
02459                 if ( !$pt ) {
02460                         // note: $err_code and $err_msg are filled by CASClient::retrievePT()
02461                         phpCAS::trace('PT was not retrieved correctly');
02462                         $res = FALSE;
02463                 } else {
02464                         // add cookies if necessary
02465                         $cookies = array();
02466                         foreach ( $this->_serviceCookieJar->getCookies($url) as $name => $val ) {
02467                                 $cookies[] = $name.'='.$val;
02468                         }
02469                                 
02470                         // build the URL including the PT
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                                 // give an error message
02481                                 $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
02482                                 $service_url,
02483                                 $err_msg);
02484                                 $res = FALSE;
02485                         } else {
02486                                 // URL has been fetched, extract the cookies
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                                                 // give an error message
02505                                                 $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
02506                                                 $service_url,
02507                                                 $err_msg);
02508                                                 $res = FALSE;
02509                                         } else {
02510                                                 // URL has been fetched, extract the cookies
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                                 // give an error message
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         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
02577         // XX                                                                    XX
02578         // XX                  PROXIED CLIENT FEATURES (CAS 2.0)                 XX
02579         // XX                                                                    XX
02580         // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
02581 
02582         // ########################################################################
02583         //  PT
02584         // ########################################################################
02597         private  $_pt = '';
02598 
02603         public function getPT()
02604         {
02605                 //      return 'ST'.substr($this->_pt, 2);
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         //  PT VALIDATION
02684         // ########################################################################
02695         public function validatePT(&$validate_url,&$text_response,&$tree_response)
02696         {
02697                 phpCAS::traceBegin();
02698                 phpCAS::trace($text_response);
02699                 // build the URL to validate the ticket
02700                 $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
02701 
02702                 if ( $this->isProxy() ) {
02703                         // pass the callback url for CAS proxies
02704                         $validate_url .= '&pgtUrl='.urlencode($this->getCallbackURL());
02705                 }
02706 
02707                 // open and read the URL
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                                 // If the query string still has anything left, append it to the final URI
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                         // explode the host list separated by comma and use the first host
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         //  AUTHENTICATION ERROR HANDLING
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 ?>

Generated on Sat Mar 26 2011 12:11:03 for phpCAS by  doxygen 1.7.1