1*d10b5556SXylle<?php 2*d10b5556SXylle 3*d10b5556SXylle/** 4*d10b5556SXylle * Licensed to Jasig under one or more contributor license 5*d10b5556SXylle * agreements. See the NOTICE file distributed with this work for 6*d10b5556SXylle * additional information regarding copyright ownership. 7*d10b5556SXylle * 8*d10b5556SXylle * Jasig licenses this file to you under the Apache License, 9*d10b5556SXylle * Version 2.0 (the "License"); you may not use this file except in 10*d10b5556SXylle * compliance with the License. You may obtain a copy of the License at: 11*d10b5556SXylle * 12*d10b5556SXylle * http://www.apache.org/licenses/LICENSE-2.0 13*d10b5556SXylle * 14*d10b5556SXylle * Unless required by applicable law or agreed to in writing, software 15*d10b5556SXylle * distributed under the License is distributed on an "AS IS" BASIS, 16*d10b5556SXylle * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17*d10b5556SXylle * See the License for the specific language governing permissions and 18*d10b5556SXylle * limitations under the License. 19*d10b5556SXylle * 20*d10b5556SXylle * PHP Version 7 21*d10b5556SXylle * 22*d10b5556SXylle * @file CAS/Client.php 23*d10b5556SXylle * @category Authentication 24*d10b5556SXylle * @package PhpCAS 25*d10b5556SXylle * @author Pascal Aubry <pascal.aubry@univ-rennes1.fr> 26*d10b5556SXylle * @author Olivier Berger <olivier.berger@it-sudparis.eu> 27*d10b5556SXylle * @author Brett Bieber <brett.bieber@gmail.com> 28*d10b5556SXylle * @author Joachim Fritschi <jfritschi@freenet.de> 29*d10b5556SXylle * @author Adam Franco <afranco@middlebury.edu> 30*d10b5556SXylle * @author Tobias Schiebeck <tobias.schiebeck@manchester.ac.uk> 31*d10b5556SXylle * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 32*d10b5556SXylle * @link https://wiki.jasig.org/display/CASC/phpCAS 33*d10b5556SXylle */ 34*d10b5556SXylle 35*d10b5556SXylle/** 36*d10b5556SXylle * The CAS_Client class is a client interface that provides CAS authentication 37*d10b5556SXylle * to PHP applications. 38*d10b5556SXylle * 39*d10b5556SXylle * @class CAS_Client 40*d10b5556SXylle * @category Authentication 41*d10b5556SXylle * @package PhpCAS 42*d10b5556SXylle * @author Pascal Aubry <pascal.aubry@univ-rennes1.fr> 43*d10b5556SXylle * @author Olivier Berger <olivier.berger@it-sudparis.eu> 44*d10b5556SXylle * @author Brett Bieber <brett.bieber@gmail.com> 45*d10b5556SXylle * @author Joachim Fritschi <jfritschi@freenet.de> 46*d10b5556SXylle * @author Adam Franco <afranco@middlebury.edu> 47*d10b5556SXylle * @author Tobias Schiebeck <tobias.schiebeck@manchester.ac.uk> 48*d10b5556SXylle * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 49*d10b5556SXylle * @link https://wiki.jasig.org/display/CASC/phpCAS 50*d10b5556SXylle * 51*d10b5556SXylle */ 52*d10b5556SXylle 53*d10b5556SXylleclass CAS_Client 54*d10b5556SXylle{ 55*d10b5556SXylle 56*d10b5556SXylle // ######################################################################## 57*d10b5556SXylle // HTML OUTPUT 58*d10b5556SXylle // ######################################################################## 59*d10b5556SXylle /** 60*d10b5556SXylle * @addtogroup internalOutput 61*d10b5556SXylle * @{ 62*d10b5556SXylle */ 63*d10b5556SXylle 64*d10b5556SXylle /** 65*d10b5556SXylle * This method filters a string by replacing special tokens by appropriate values 66*d10b5556SXylle * and prints it. The corresponding tokens are taken into account: 67*d10b5556SXylle * - __CAS_VERSION__ 68*d10b5556SXylle * - __PHPCAS_VERSION__ 69*d10b5556SXylle * - __SERVER_BASE_URL__ 70*d10b5556SXylle * 71*d10b5556SXylle * Used by CAS_Client::PrintHTMLHeader() and CAS_Client::printHTMLFooter(). 72*d10b5556SXylle * 73*d10b5556SXylle * @param string $str the string to filter and output 74*d10b5556SXylle * 75*d10b5556SXylle * @return void 76*d10b5556SXylle */ 77*d10b5556SXylle private function _htmlFilterOutput($str) 78*d10b5556SXylle { 79*d10b5556SXylle $str = str_replace('__CAS_VERSION__', $this->getServerVersion(), $str); 80*d10b5556SXylle $str = str_replace('__PHPCAS_VERSION__', phpCAS::getVersion(), $str); 81*d10b5556SXylle $str = str_replace('__SERVER_BASE_URL__', $this->_getServerBaseURL(), $str); 82*d10b5556SXylle echo $str; 83*d10b5556SXylle } 84*d10b5556SXylle 85*d10b5556SXylle /** 86*d10b5556SXylle * A string used to print the header of HTML pages. Written by 87*d10b5556SXylle * CAS_Client::setHTMLHeader(), read by CAS_Client::printHTMLHeader(). 88*d10b5556SXylle * 89*d10b5556SXylle * @hideinitializer 90*d10b5556SXylle * @see CAS_Client::setHTMLHeader, CAS_Client::printHTMLHeader() 91*d10b5556SXylle */ 92*d10b5556SXylle private $_output_header = ''; 93*d10b5556SXylle 94*d10b5556SXylle /** 95*d10b5556SXylle * This method prints the header of the HTML output (after filtering). If 96*d10b5556SXylle * CAS_Client::setHTMLHeader() was not used, a default header is output. 97*d10b5556SXylle * 98*d10b5556SXylle * @param string $title the title of the page 99*d10b5556SXylle * 100*d10b5556SXylle * @return void 101*d10b5556SXylle * @see _htmlFilterOutput() 102*d10b5556SXylle */ 103*d10b5556SXylle public function printHTMLHeader($title) 104*d10b5556SXylle { 105*d10b5556SXylle if (!phpCAS::getVerbose()) { 106*d10b5556SXylle return; 107*d10b5556SXylle } 108*d10b5556SXylle 109*d10b5556SXylle $this->_htmlFilterOutput( 110*d10b5556SXylle str_replace( 111*d10b5556SXylle '__TITLE__', $title, 112*d10b5556SXylle (empty($this->_output_header) 113*d10b5556SXylle ? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>' 114*d10b5556SXylle : $this->_output_header) 115*d10b5556SXylle ) 116*d10b5556SXylle ); 117*d10b5556SXylle } 118*d10b5556SXylle 119*d10b5556SXylle /** 120*d10b5556SXylle * A string used to print the footer of HTML pages. Written by 121*d10b5556SXylle * CAS_Client::setHTMLFooter(), read by printHTMLFooter(). 122*d10b5556SXylle * 123*d10b5556SXylle * @hideinitializer 124*d10b5556SXylle * @see CAS_Client::setHTMLFooter, CAS_Client::printHTMLFooter() 125*d10b5556SXylle */ 126*d10b5556SXylle private $_output_footer = ''; 127*d10b5556SXylle 128*d10b5556SXylle /** 129*d10b5556SXylle * This method prints the footer of the HTML output (after filtering). If 130*d10b5556SXylle * CAS_Client::setHTMLFooter() was not used, a default footer is output. 131*d10b5556SXylle * 132*d10b5556SXylle * @return void 133*d10b5556SXylle * @see _htmlFilterOutput() 134*d10b5556SXylle */ 135*d10b5556SXylle public function printHTMLFooter() 136*d10b5556SXylle { 137*d10b5556SXylle if (!phpCAS::getVerbose()) { 138*d10b5556SXylle return; 139*d10b5556SXylle } 140*d10b5556SXylle 141*d10b5556SXylle $lang = $this->getLangObj(); 142*d10b5556SXylle $message = empty($this->_output_footer) 143*d10b5556SXylle ? '<hr><address>phpCAS __PHPCAS_VERSION__ ' . $lang->getUsingServer() . 144*d10b5556SXylle ' <a href="__SERVER_BASE_URL__">__SERVER_BASE_URL__</a> (CAS __CAS_VERSION__)</a></address></body></html>' 145*d10b5556SXylle : $this->_output_footer; 146*d10b5556SXylle 147*d10b5556SXylle $this->_htmlFilterOutput($message); 148*d10b5556SXylle } 149*d10b5556SXylle 150*d10b5556SXylle /** 151*d10b5556SXylle * This method set the HTML header used for all outputs. 152*d10b5556SXylle * 153*d10b5556SXylle * @param string $header the HTML header. 154*d10b5556SXylle * 155*d10b5556SXylle * @return void 156*d10b5556SXylle */ 157*d10b5556SXylle public function setHTMLHeader($header) 158*d10b5556SXylle { 159*d10b5556SXylle // Argument Validation 160*d10b5556SXylle if (gettype($header) != 'string') 161*d10b5556SXylle throw new CAS_TypeMismatchException($header, '$header', 'string'); 162*d10b5556SXylle 163*d10b5556SXylle $this->_output_header = $header; 164*d10b5556SXylle } 165*d10b5556SXylle 166*d10b5556SXylle /** 167*d10b5556SXylle * This method set the HTML footer used for all outputs. 168*d10b5556SXylle * 169*d10b5556SXylle * @param string $footer the HTML footer. 170*d10b5556SXylle * 171*d10b5556SXylle * @return void 172*d10b5556SXylle */ 173*d10b5556SXylle public function setHTMLFooter($footer) 174*d10b5556SXylle { 175*d10b5556SXylle // Argument Validation 176*d10b5556SXylle if (gettype($footer) != 'string') 177*d10b5556SXylle throw new CAS_TypeMismatchException($footer, '$footer', 'string'); 178*d10b5556SXylle 179*d10b5556SXylle $this->_output_footer = $footer; 180*d10b5556SXylle } 181*d10b5556SXylle 182*d10b5556SXylle /** 183*d10b5556SXylle * Simple wrapper for printf function, that respects 184*d10b5556SXylle * phpCAS verbosity setting. 185*d10b5556SXylle * 186*d10b5556SXylle * @param string $format 187*d10b5556SXylle * @param string|int|float ...$values 188*d10b5556SXylle * 189*d10b5556SXylle * @see printf() 190*d10b5556SXylle */ 191*d10b5556SXylle private function printf(string $format, ...$values): void 192*d10b5556SXylle { 193*d10b5556SXylle if (phpCAS::getVerbose()) { 194*d10b5556SXylle printf($format, ...$values); 195*d10b5556SXylle } 196*d10b5556SXylle } 197*d10b5556SXylle 198*d10b5556SXylle /** @} */ 199*d10b5556SXylle 200*d10b5556SXylle 201*d10b5556SXylle // ######################################################################## 202*d10b5556SXylle // INTERNATIONALIZATION 203*d10b5556SXylle // ######################################################################## 204*d10b5556SXylle /** 205*d10b5556SXylle * @addtogroup internalLang 206*d10b5556SXylle * @{ 207*d10b5556SXylle */ 208*d10b5556SXylle /** 209*d10b5556SXylle * A string corresponding to the language used by phpCAS. Written by 210*d10b5556SXylle * CAS_Client::setLang(), read by CAS_Client::getLang(). 211*d10b5556SXylle 212*d10b5556SXylle * @note debugging information is always in english (debug purposes only). 213*d10b5556SXylle */ 214*d10b5556SXylle private $_lang = PHPCAS_LANG_DEFAULT; 215*d10b5556SXylle 216*d10b5556SXylle /** 217*d10b5556SXylle * This method is used to set the language used by phpCAS. 218*d10b5556SXylle * 219*d10b5556SXylle * @param string $lang representing the language. 220*d10b5556SXylle * 221*d10b5556SXylle * @return void 222*d10b5556SXylle */ 223*d10b5556SXylle public function setLang($lang) 224*d10b5556SXylle { 225*d10b5556SXylle // Argument Validation 226*d10b5556SXylle if (gettype($lang) != 'string') 227*d10b5556SXylle throw new CAS_TypeMismatchException($lang, '$lang', 'string'); 228*d10b5556SXylle 229*d10b5556SXylle phpCAS::traceBegin(); 230*d10b5556SXylle $obj = new $lang(); 231*d10b5556SXylle if (!($obj instanceof CAS_Languages_LanguageInterface)) { 232*d10b5556SXylle throw new CAS_InvalidArgumentException( 233*d10b5556SXylle '$className must implement the CAS_Languages_LanguageInterface' 234*d10b5556SXylle ); 235*d10b5556SXylle } 236*d10b5556SXylle $this->_lang = $lang; 237*d10b5556SXylle phpCAS::traceEnd(); 238*d10b5556SXylle } 239*d10b5556SXylle /** 240*d10b5556SXylle * Create the language 241*d10b5556SXylle * 242*d10b5556SXylle * @return CAS_Languages_LanguageInterface object implementing the class 243*d10b5556SXylle */ 244*d10b5556SXylle public function getLangObj() 245*d10b5556SXylle { 246*d10b5556SXylle $classname = $this->_lang; 247*d10b5556SXylle return new $classname(); 248*d10b5556SXylle } 249*d10b5556SXylle 250*d10b5556SXylle /** @} */ 251*d10b5556SXylle // ######################################################################## 252*d10b5556SXylle // CAS SERVER CONFIG 253*d10b5556SXylle // ######################################################################## 254*d10b5556SXylle /** 255*d10b5556SXylle * @addtogroup internalConfig 256*d10b5556SXylle * @{ 257*d10b5556SXylle */ 258*d10b5556SXylle 259*d10b5556SXylle /** 260*d10b5556SXylle * a record to store information about the CAS server. 261*d10b5556SXylle * - $_server['version']: the version of the CAS server 262*d10b5556SXylle * - $_server['hostname']: the hostname of the CAS server 263*d10b5556SXylle * - $_server['port']: the port the CAS server is running on 264*d10b5556SXylle * - $_server['uri']: the base URI the CAS server is responding on 265*d10b5556SXylle * - $_server['base_url']: the base URL of the CAS server 266*d10b5556SXylle * - $_server['login_url']: the login URL of the CAS server 267*d10b5556SXylle * - $_server['service_validate_url']: the service validating URL of the 268*d10b5556SXylle * CAS server 269*d10b5556SXylle * - $_server['proxy_url']: the proxy URL of the CAS server 270*d10b5556SXylle * - $_server['proxy_validate_url']: the proxy validating URL of the CAS server 271*d10b5556SXylle * - $_server['logout_url']: the logout URL of the CAS server 272*d10b5556SXylle * 273*d10b5556SXylle * $_server['version'], $_server['hostname'], $_server['port'] and 274*d10b5556SXylle * $_server['uri'] are written by CAS_Client::CAS_Client(), read by 275*d10b5556SXylle * CAS_Client::getServerVersion(), CAS_Client::_getServerHostname(), 276*d10b5556SXylle * CAS_Client::_getServerPort() and CAS_Client::_getServerURI(). 277*d10b5556SXylle * 278*d10b5556SXylle * The other fields are written and read by CAS_Client::_getServerBaseURL(), 279*d10b5556SXylle * CAS_Client::getServerLoginURL(), CAS_Client::getServerServiceValidateURL(), 280*d10b5556SXylle * CAS_Client::getServerProxyValidateURL() and CAS_Client::getServerLogoutURL(). 281*d10b5556SXylle * 282*d10b5556SXylle * @hideinitializer 283*d10b5556SXylle */ 284*d10b5556SXylle private $_server = array( 285*d10b5556SXylle 'version' => '', 286*d10b5556SXylle 'hostname' => 'none', 287*d10b5556SXylle 'port' => -1, 288*d10b5556SXylle 'uri' => 'none'); 289*d10b5556SXylle 290*d10b5556SXylle /** 291*d10b5556SXylle * This method is used to retrieve the version of the CAS server. 292*d10b5556SXylle * 293*d10b5556SXylle * @return string the version of the CAS server. 294*d10b5556SXylle */ 295*d10b5556SXylle public function getServerVersion() 296*d10b5556SXylle { 297*d10b5556SXylle return $this->_server['version']; 298*d10b5556SXylle } 299*d10b5556SXylle 300*d10b5556SXylle /** 301*d10b5556SXylle * This method is used to retrieve the hostname of the CAS server. 302*d10b5556SXylle * 303*d10b5556SXylle * @return string the hostname of the CAS server. 304*d10b5556SXylle */ 305*d10b5556SXylle private function _getServerHostname() 306*d10b5556SXylle { 307*d10b5556SXylle return $this->_server['hostname']; 308*d10b5556SXylle } 309*d10b5556SXylle 310*d10b5556SXylle /** 311*d10b5556SXylle * This method is used to retrieve the port of the CAS server. 312*d10b5556SXylle * 313*d10b5556SXylle * @return int the port of the CAS server. 314*d10b5556SXylle */ 315*d10b5556SXylle private function _getServerPort() 316*d10b5556SXylle { 317*d10b5556SXylle return $this->_server['port']; 318*d10b5556SXylle } 319*d10b5556SXylle 320*d10b5556SXylle /** 321*d10b5556SXylle * This method is used to retrieve the URI of the CAS server. 322*d10b5556SXylle * 323*d10b5556SXylle * @return string a URI. 324*d10b5556SXylle */ 325*d10b5556SXylle private function _getServerURI() 326*d10b5556SXylle { 327*d10b5556SXylle return $this->_server['uri']; 328*d10b5556SXylle } 329*d10b5556SXylle 330*d10b5556SXylle /** 331*d10b5556SXylle * This method is used to retrieve the base URL of the CAS server. 332*d10b5556SXylle * 333*d10b5556SXylle * @return string a URL. 334*d10b5556SXylle */ 335*d10b5556SXylle private function _getServerBaseURL() 336*d10b5556SXylle { 337*d10b5556SXylle // the URL is build only when needed 338*d10b5556SXylle if ( empty($this->_server['base_url']) ) { 339*d10b5556SXylle $this->_server['base_url'] = 'https://' . $this->_getServerHostname(); 340*d10b5556SXylle if ($this->_getServerPort()!=443) { 341*d10b5556SXylle $this->_server['base_url'] .= ':' 342*d10b5556SXylle .$this->_getServerPort(); 343*d10b5556SXylle } 344*d10b5556SXylle $this->_server['base_url'] .= $this->_getServerURI(); 345*d10b5556SXylle } 346*d10b5556SXylle return $this->_server['base_url']; 347*d10b5556SXylle } 348*d10b5556SXylle 349*d10b5556SXylle /** 350*d10b5556SXylle * This method is used to retrieve the login URL of the CAS server. 351*d10b5556SXylle * 352*d10b5556SXylle * @param bool $gateway true to check authentication, false to force it 353*d10b5556SXylle * @param bool $renew true to force the authentication with the CAS server 354*d10b5556SXylle * 355*d10b5556SXylle * @return string a URL. 356*d10b5556SXylle * @note It is recommended that CAS implementations ignore the "gateway" 357*d10b5556SXylle * parameter if "renew" is set 358*d10b5556SXylle */ 359*d10b5556SXylle public function getServerLoginURL($gateway=false,$renew=false) 360*d10b5556SXylle { 361*d10b5556SXylle phpCAS::traceBegin(); 362*d10b5556SXylle // the URL is build only when needed 363*d10b5556SXylle if ( empty($this->_server['login_url']) ) { 364*d10b5556SXylle $this->_server['login_url'] = $this->_buildQueryUrl($this->_getServerBaseURL().'login','service='.urlencode($this->getURL())); 365*d10b5556SXylle } 366*d10b5556SXylle $url = $this->_server['login_url']; 367*d10b5556SXylle if ($renew) { 368*d10b5556SXylle // It is recommended that when the "renew" parameter is set, its 369*d10b5556SXylle // value be "true" 370*d10b5556SXylle $url = $this->_buildQueryUrl($url, 'renew=true'); 371*d10b5556SXylle } elseif ($gateway) { 372*d10b5556SXylle // It is recommended that when the "gateway" parameter is set, its 373*d10b5556SXylle // value be "true" 374*d10b5556SXylle $url = $this->_buildQueryUrl($url, 'gateway=true'); 375*d10b5556SXylle } 376*d10b5556SXylle phpCAS::traceEnd($url); 377*d10b5556SXylle return $url; 378*d10b5556SXylle } 379*d10b5556SXylle 380*d10b5556SXylle /** 381*d10b5556SXylle * This method sets the login URL of the CAS server. 382*d10b5556SXylle * 383*d10b5556SXylle * @param string $url the login URL 384*d10b5556SXylle * 385*d10b5556SXylle * @return string login url 386*d10b5556SXylle */ 387*d10b5556SXylle public function setServerLoginURL($url) 388*d10b5556SXylle { 389*d10b5556SXylle // Argument Validation 390*d10b5556SXylle if (gettype($url) != 'string') 391*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 392*d10b5556SXylle 393*d10b5556SXylle return $this->_server['login_url'] = $url; 394*d10b5556SXylle } 395*d10b5556SXylle 396*d10b5556SXylle 397*d10b5556SXylle /** 398*d10b5556SXylle * This method sets the serviceValidate URL of the CAS server. 399*d10b5556SXylle * 400*d10b5556SXylle * @param string $url the serviceValidate URL 401*d10b5556SXylle * 402*d10b5556SXylle * @return string serviceValidate URL 403*d10b5556SXylle */ 404*d10b5556SXylle public function setServerServiceValidateURL($url) 405*d10b5556SXylle { 406*d10b5556SXylle // Argument Validation 407*d10b5556SXylle if (gettype($url) != 'string') 408*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 409*d10b5556SXylle 410*d10b5556SXylle return $this->_server['service_validate_url'] = $url; 411*d10b5556SXylle } 412*d10b5556SXylle 413*d10b5556SXylle 414*d10b5556SXylle /** 415*d10b5556SXylle * This method sets the proxyValidate URL of the CAS server. 416*d10b5556SXylle * 417*d10b5556SXylle * @param string $url the proxyValidate URL 418*d10b5556SXylle * 419*d10b5556SXylle * @return string proxyValidate URL 420*d10b5556SXylle */ 421*d10b5556SXylle public function setServerProxyValidateURL($url) 422*d10b5556SXylle { 423*d10b5556SXylle // Argument Validation 424*d10b5556SXylle if (gettype($url) != 'string') 425*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 426*d10b5556SXylle 427*d10b5556SXylle return $this->_server['proxy_validate_url'] = $url; 428*d10b5556SXylle } 429*d10b5556SXylle 430*d10b5556SXylle 431*d10b5556SXylle /** 432*d10b5556SXylle * This method sets the samlValidate URL of the CAS server. 433*d10b5556SXylle * 434*d10b5556SXylle * @param string $url the samlValidate URL 435*d10b5556SXylle * 436*d10b5556SXylle * @return string samlValidate URL 437*d10b5556SXylle */ 438*d10b5556SXylle public function setServerSamlValidateURL($url) 439*d10b5556SXylle { 440*d10b5556SXylle // Argument Validation 441*d10b5556SXylle if (gettype($url) != 'string') 442*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 443*d10b5556SXylle 444*d10b5556SXylle return $this->_server['saml_validate_url'] = $url; 445*d10b5556SXylle } 446*d10b5556SXylle 447*d10b5556SXylle 448*d10b5556SXylle /** 449*d10b5556SXylle * This method is used to retrieve the service validating URL of the CAS server. 450*d10b5556SXylle * 451*d10b5556SXylle * @return string serviceValidate URL. 452*d10b5556SXylle */ 453*d10b5556SXylle public function getServerServiceValidateURL() 454*d10b5556SXylle { 455*d10b5556SXylle phpCAS::traceBegin(); 456*d10b5556SXylle // the URL is build only when needed 457*d10b5556SXylle if ( empty($this->_server['service_validate_url']) ) { 458*d10b5556SXylle switch ($this->getServerVersion()) { 459*d10b5556SXylle case CAS_VERSION_1_0: 460*d10b5556SXylle $this->_server['service_validate_url'] = $this->_getServerBaseURL() 461*d10b5556SXylle .'validate'; 462*d10b5556SXylle break; 463*d10b5556SXylle case CAS_VERSION_2_0: 464*d10b5556SXylle $this->_server['service_validate_url'] = $this->_getServerBaseURL() 465*d10b5556SXylle .'serviceValidate'; 466*d10b5556SXylle break; 467*d10b5556SXylle case CAS_VERSION_3_0: 468*d10b5556SXylle $this->_server['service_validate_url'] = $this->_getServerBaseURL() 469*d10b5556SXylle .'p3/serviceValidate'; 470*d10b5556SXylle break; 471*d10b5556SXylle } 472*d10b5556SXylle } 473*d10b5556SXylle $url = $this->_buildQueryUrl( 474*d10b5556SXylle $this->_server['service_validate_url'], 475*d10b5556SXylle 'service='.urlencode($this->getURL()) 476*d10b5556SXylle ); 477*d10b5556SXylle phpCAS::traceEnd($url); 478*d10b5556SXylle return $url; 479*d10b5556SXylle } 480*d10b5556SXylle /** 481*d10b5556SXylle * This method is used to retrieve the SAML validating URL of the CAS server. 482*d10b5556SXylle * 483*d10b5556SXylle * @return string samlValidate URL. 484*d10b5556SXylle */ 485*d10b5556SXylle public function getServerSamlValidateURL() 486*d10b5556SXylle { 487*d10b5556SXylle phpCAS::traceBegin(); 488*d10b5556SXylle // the URL is build only when needed 489*d10b5556SXylle if ( empty($this->_server['saml_validate_url']) ) { 490*d10b5556SXylle switch ($this->getServerVersion()) { 491*d10b5556SXylle case SAML_VERSION_1_1: 492*d10b5556SXylle $this->_server['saml_validate_url'] = $this->_getServerBaseURL().'samlValidate'; 493*d10b5556SXylle break; 494*d10b5556SXylle } 495*d10b5556SXylle } 496*d10b5556SXylle 497*d10b5556SXylle $url = $this->_buildQueryUrl( 498*d10b5556SXylle $this->_server['saml_validate_url'], 499*d10b5556SXylle 'TARGET='.urlencode($this->getURL()) 500*d10b5556SXylle ); 501*d10b5556SXylle phpCAS::traceEnd($url); 502*d10b5556SXylle return $url; 503*d10b5556SXylle } 504*d10b5556SXylle 505*d10b5556SXylle /** 506*d10b5556SXylle * This method is used to retrieve the proxy validating URL of the CAS server. 507*d10b5556SXylle * 508*d10b5556SXylle * @return string proxyValidate URL. 509*d10b5556SXylle */ 510*d10b5556SXylle public function getServerProxyValidateURL() 511*d10b5556SXylle { 512*d10b5556SXylle phpCAS::traceBegin(); 513*d10b5556SXylle // the URL is build only when needed 514*d10b5556SXylle if ( empty($this->_server['proxy_validate_url']) ) { 515*d10b5556SXylle switch ($this->getServerVersion()) { 516*d10b5556SXylle case CAS_VERSION_1_0: 517*d10b5556SXylle $this->_server['proxy_validate_url'] = ''; 518*d10b5556SXylle break; 519*d10b5556SXylle case CAS_VERSION_2_0: 520*d10b5556SXylle $this->_server['proxy_validate_url'] = $this->_getServerBaseURL().'proxyValidate'; 521*d10b5556SXylle break; 522*d10b5556SXylle case CAS_VERSION_3_0: 523*d10b5556SXylle $this->_server['proxy_validate_url'] = $this->_getServerBaseURL().'p3/proxyValidate'; 524*d10b5556SXylle break; 525*d10b5556SXylle } 526*d10b5556SXylle } 527*d10b5556SXylle $url = $this->_buildQueryUrl( 528*d10b5556SXylle $this->_server['proxy_validate_url'], 529*d10b5556SXylle 'service='.urlencode($this->getURL()) 530*d10b5556SXylle ); 531*d10b5556SXylle phpCAS::traceEnd($url); 532*d10b5556SXylle return $url; 533*d10b5556SXylle } 534*d10b5556SXylle 535*d10b5556SXylle 536*d10b5556SXylle /** 537*d10b5556SXylle * This method is used to retrieve the proxy URL of the CAS server. 538*d10b5556SXylle * 539*d10b5556SXylle * @return string proxy URL. 540*d10b5556SXylle */ 541*d10b5556SXylle public function getServerProxyURL() 542*d10b5556SXylle { 543*d10b5556SXylle // the URL is build only when needed 544*d10b5556SXylle if ( empty($this->_server['proxy_url']) ) { 545*d10b5556SXylle switch ($this->getServerVersion()) { 546*d10b5556SXylle case CAS_VERSION_1_0: 547*d10b5556SXylle $this->_server['proxy_url'] = ''; 548*d10b5556SXylle break; 549*d10b5556SXylle case CAS_VERSION_2_0: 550*d10b5556SXylle case CAS_VERSION_3_0: 551*d10b5556SXylle $this->_server['proxy_url'] = $this->_getServerBaseURL().'proxy'; 552*d10b5556SXylle break; 553*d10b5556SXylle } 554*d10b5556SXylle } 555*d10b5556SXylle return $this->_server['proxy_url']; 556*d10b5556SXylle } 557*d10b5556SXylle 558*d10b5556SXylle /** 559*d10b5556SXylle * This method is used to retrieve the logout URL of the CAS server. 560*d10b5556SXylle * 561*d10b5556SXylle * @return string logout URL. 562*d10b5556SXylle */ 563*d10b5556SXylle public function getServerLogoutURL() 564*d10b5556SXylle { 565*d10b5556SXylle // the URL is build only when needed 566*d10b5556SXylle if ( empty($this->_server['logout_url']) ) { 567*d10b5556SXylle $this->_server['logout_url'] = $this->_getServerBaseURL().'logout'; 568*d10b5556SXylle } 569*d10b5556SXylle return $this->_server['logout_url']; 570*d10b5556SXylle } 571*d10b5556SXylle 572*d10b5556SXylle /** 573*d10b5556SXylle * This method sets the logout URL of the CAS server. 574*d10b5556SXylle * 575*d10b5556SXylle * @param string $url the logout URL 576*d10b5556SXylle * 577*d10b5556SXylle * @return string logout url 578*d10b5556SXylle */ 579*d10b5556SXylle public function setServerLogoutURL($url) 580*d10b5556SXylle { 581*d10b5556SXylle // Argument Validation 582*d10b5556SXylle if (gettype($url) != 'string') 583*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 584*d10b5556SXylle 585*d10b5556SXylle return $this->_server['logout_url'] = $url; 586*d10b5556SXylle } 587*d10b5556SXylle 588*d10b5556SXylle /** 589*d10b5556SXylle * An array to store extra curl options. 590*d10b5556SXylle */ 591*d10b5556SXylle private $_curl_options = array(); 592*d10b5556SXylle 593*d10b5556SXylle /** 594*d10b5556SXylle * This method is used to set additional user curl options. 595*d10b5556SXylle * 596*d10b5556SXylle * @param string $key name of the curl option 597*d10b5556SXylle * @param string $value value of the curl option 598*d10b5556SXylle * 599*d10b5556SXylle * @return void 600*d10b5556SXylle */ 601*d10b5556SXylle public function setExtraCurlOption($key, $value) 602*d10b5556SXylle { 603*d10b5556SXylle $this->_curl_options[$key] = $value; 604*d10b5556SXylle } 605*d10b5556SXylle 606*d10b5556SXylle /** @} */ 607*d10b5556SXylle 608*d10b5556SXylle // ######################################################################## 609*d10b5556SXylle // Change the internal behaviour of phpcas 610*d10b5556SXylle // ######################################################################## 611*d10b5556SXylle 612*d10b5556SXylle /** 613*d10b5556SXylle * @addtogroup internalBehave 614*d10b5556SXylle * @{ 615*d10b5556SXylle */ 616*d10b5556SXylle 617*d10b5556SXylle /** 618*d10b5556SXylle * The class to instantiate for making web requests in readUrl(). 619*d10b5556SXylle * The class specified must implement the CAS_Request_RequestInterface. 620*d10b5556SXylle * By default CAS_Request_CurlRequest is used, but this may be overridden to 621*d10b5556SXylle * supply alternate request mechanisms for testing. 622*d10b5556SXylle */ 623*d10b5556SXylle private $_requestImplementation = 'CAS_Request_CurlRequest'; 624*d10b5556SXylle 625*d10b5556SXylle /** 626*d10b5556SXylle * Override the default implementation used to make web requests in readUrl(). 627*d10b5556SXylle * This class must implement the CAS_Request_RequestInterface. 628*d10b5556SXylle * 629*d10b5556SXylle * @param string $className name of the RequestImplementation class 630*d10b5556SXylle * 631*d10b5556SXylle * @return void 632*d10b5556SXylle */ 633*d10b5556SXylle public function setRequestImplementation ($className) 634*d10b5556SXylle { 635*d10b5556SXylle $obj = new $className; 636*d10b5556SXylle if (!($obj instanceof CAS_Request_RequestInterface)) { 637*d10b5556SXylle throw new CAS_InvalidArgumentException( 638*d10b5556SXylle '$className must implement the CAS_Request_RequestInterface' 639*d10b5556SXylle ); 640*d10b5556SXylle } 641*d10b5556SXylle $this->_requestImplementation = $className; 642*d10b5556SXylle } 643*d10b5556SXylle 644*d10b5556SXylle /** 645*d10b5556SXylle * @var boolean $_clearTicketsFromUrl; If true, phpCAS will clear session 646*d10b5556SXylle * tickets from the URL after a successful authentication. 647*d10b5556SXylle */ 648*d10b5556SXylle private $_clearTicketsFromUrl = true; 649*d10b5556SXylle 650*d10b5556SXylle /** 651*d10b5556SXylle * Configure the client to not send redirect headers and call exit() on 652*d10b5556SXylle * authentication success. The normal redirect is used to remove the service 653*d10b5556SXylle * ticket from the client's URL, but for running unit tests we need to 654*d10b5556SXylle * continue without exiting. 655*d10b5556SXylle * 656*d10b5556SXylle * Needed for testing authentication 657*d10b5556SXylle * 658*d10b5556SXylle * @return void 659*d10b5556SXylle */ 660*d10b5556SXylle public function setNoClearTicketsFromUrl () 661*d10b5556SXylle { 662*d10b5556SXylle $this->_clearTicketsFromUrl = false; 663*d10b5556SXylle } 664*d10b5556SXylle 665*d10b5556SXylle /** 666*d10b5556SXylle * @var callback $_attributeParserCallbackFunction; 667*d10b5556SXylle */ 668*d10b5556SXylle private $_casAttributeParserCallbackFunction = null; 669*d10b5556SXylle 670*d10b5556SXylle /** 671*d10b5556SXylle * @var array $_attributeParserCallbackArgs; 672*d10b5556SXylle */ 673*d10b5556SXylle private $_casAttributeParserCallbackArgs = array(); 674*d10b5556SXylle 675*d10b5556SXylle /** 676*d10b5556SXylle * Set a callback function to be run when parsing CAS attributes 677*d10b5556SXylle * 678*d10b5556SXylle * The callback function will be passed a XMLNode as its first parameter, 679*d10b5556SXylle * followed by any $additionalArgs you pass. 680*d10b5556SXylle * 681*d10b5556SXylle * @param string $function callback function to call 682*d10b5556SXylle * @param array $additionalArgs optional array of arguments 683*d10b5556SXylle * 684*d10b5556SXylle * @return void 685*d10b5556SXylle */ 686*d10b5556SXylle public function setCasAttributeParserCallback($function, array $additionalArgs = array()) 687*d10b5556SXylle { 688*d10b5556SXylle $this->_casAttributeParserCallbackFunction = $function; 689*d10b5556SXylle $this->_casAttributeParserCallbackArgs = $additionalArgs; 690*d10b5556SXylle } 691*d10b5556SXylle 692*d10b5556SXylle /** @var callable $_postAuthenticateCallbackFunction; 693*d10b5556SXylle */ 694*d10b5556SXylle private $_postAuthenticateCallbackFunction = null; 695*d10b5556SXylle 696*d10b5556SXylle /** 697*d10b5556SXylle * @var array $_postAuthenticateCallbackArgs; 698*d10b5556SXylle */ 699*d10b5556SXylle private $_postAuthenticateCallbackArgs = array(); 700*d10b5556SXylle 701*d10b5556SXylle /** 702*d10b5556SXylle * Set a callback function to be run when a user authenticates. 703*d10b5556SXylle * 704*d10b5556SXylle * The callback function will be passed a $logoutTicket as its first parameter, 705*d10b5556SXylle * followed by any $additionalArgs you pass. The $logoutTicket parameter is an 706*d10b5556SXylle * opaque string that can be used to map a session-id to the logout request 707*d10b5556SXylle * in order to support single-signout in applications that manage their own 708*d10b5556SXylle * sessions (rather than letting phpCAS start the session). 709*d10b5556SXylle * 710*d10b5556SXylle * phpCAS::forceAuthentication() will always exit and forward client unless 711*d10b5556SXylle * they are already authenticated. To perform an action at the moment the user 712*d10b5556SXylle * logs in (such as registering an account, performing logging, etc), register 713*d10b5556SXylle * a callback function here. 714*d10b5556SXylle * 715*d10b5556SXylle * @param callable $function callback function to call 716*d10b5556SXylle * @param array $additionalArgs optional array of arguments 717*d10b5556SXylle * 718*d10b5556SXylle * @return void 719*d10b5556SXylle */ 720*d10b5556SXylle public function setPostAuthenticateCallback ($function, array $additionalArgs = array()) 721*d10b5556SXylle { 722*d10b5556SXylle $this->_postAuthenticateCallbackFunction = $function; 723*d10b5556SXylle $this->_postAuthenticateCallbackArgs = $additionalArgs; 724*d10b5556SXylle } 725*d10b5556SXylle 726*d10b5556SXylle /** 727*d10b5556SXylle * @var callable $_signoutCallbackFunction; 728*d10b5556SXylle */ 729*d10b5556SXylle private $_signoutCallbackFunction = null; 730*d10b5556SXylle 731*d10b5556SXylle /** 732*d10b5556SXylle * @var array $_signoutCallbackArgs; 733*d10b5556SXylle */ 734*d10b5556SXylle private $_signoutCallbackArgs = array(); 735*d10b5556SXylle 736*d10b5556SXylle /** 737*d10b5556SXylle * Set a callback function to be run when a single-signout request is received. 738*d10b5556SXylle * 739*d10b5556SXylle * The callback function will be passed a $logoutTicket as its first parameter, 740*d10b5556SXylle * followed by any $additionalArgs you pass. The $logoutTicket parameter is an 741*d10b5556SXylle * opaque string that can be used to map a session-id to the logout request in 742*d10b5556SXylle * order to support single-signout in applications that manage their own sessions 743*d10b5556SXylle * (rather than letting phpCAS start and destroy the session). 744*d10b5556SXylle * 745*d10b5556SXylle * @param callable $function callback function to call 746*d10b5556SXylle * @param array $additionalArgs optional array of arguments 747*d10b5556SXylle * 748*d10b5556SXylle * @return void 749*d10b5556SXylle */ 750*d10b5556SXylle public function setSingleSignoutCallback ($function, array $additionalArgs = array()) 751*d10b5556SXylle { 752*d10b5556SXylle $this->_signoutCallbackFunction = $function; 753*d10b5556SXylle $this->_signoutCallbackArgs = $additionalArgs; 754*d10b5556SXylle } 755*d10b5556SXylle 756*d10b5556SXylle // ######################################################################## 757*d10b5556SXylle // Methods for supplying code-flow feedback to integrators. 758*d10b5556SXylle // ######################################################################## 759*d10b5556SXylle 760*d10b5556SXylle /** 761*d10b5556SXylle * Ensure that this is actually a proxy object or fail with an exception 762*d10b5556SXylle * 763*d10b5556SXylle * @throws CAS_OutOfSequenceBeforeProxyException 764*d10b5556SXylle * 765*d10b5556SXylle * @return void 766*d10b5556SXylle */ 767*d10b5556SXylle public function ensureIsProxy() 768*d10b5556SXylle { 769*d10b5556SXylle if (!$this->isProxy()) { 770*d10b5556SXylle throw new CAS_OutOfSequenceBeforeProxyException(); 771*d10b5556SXylle } 772*d10b5556SXylle } 773*d10b5556SXylle 774*d10b5556SXylle /** 775*d10b5556SXylle * Mark the caller of authentication. This will help client integraters determine 776*d10b5556SXylle * problems with their code flow if they call a function such as getUser() before 777*d10b5556SXylle * authentication has occurred. 778*d10b5556SXylle * 779*d10b5556SXylle * @param bool $auth True if authentication was successful, false otherwise. 780*d10b5556SXylle * 781*d10b5556SXylle * @return null 782*d10b5556SXylle */ 783*d10b5556SXylle public function markAuthenticationCall ($auth) 784*d10b5556SXylle { 785*d10b5556SXylle // store where the authentication has been checked and the result 786*d10b5556SXylle $dbg = debug_backtrace(); 787*d10b5556SXylle $this->_authentication_caller = array ( 788*d10b5556SXylle 'file' => $dbg[1]['file'], 789*d10b5556SXylle 'line' => $dbg[1]['line'], 790*d10b5556SXylle 'method' => $dbg[1]['class'] . '::' . $dbg[1]['function'], 791*d10b5556SXylle 'result' => (boolean)$auth 792*d10b5556SXylle ); 793*d10b5556SXylle } 794*d10b5556SXylle private $_authentication_caller; 795*d10b5556SXylle 796*d10b5556SXylle /** 797*d10b5556SXylle * Answer true if authentication has been checked. 798*d10b5556SXylle * 799*d10b5556SXylle * @return bool 800*d10b5556SXylle */ 801*d10b5556SXylle public function wasAuthenticationCalled () 802*d10b5556SXylle { 803*d10b5556SXylle return !empty($this->_authentication_caller); 804*d10b5556SXylle } 805*d10b5556SXylle 806*d10b5556SXylle /** 807*d10b5556SXylle * Ensure that authentication was checked. Terminate with exception if no 808*d10b5556SXylle * authentication was performed 809*d10b5556SXylle * 810*d10b5556SXylle * @throws CAS_OutOfSequenceBeforeAuthenticationCallException 811*d10b5556SXylle * 812*d10b5556SXylle * @return void 813*d10b5556SXylle */ 814*d10b5556SXylle private function _ensureAuthenticationCalled() 815*d10b5556SXylle { 816*d10b5556SXylle if (!$this->wasAuthenticationCalled()) { 817*d10b5556SXylle throw new CAS_OutOfSequenceBeforeAuthenticationCallException(); 818*d10b5556SXylle } 819*d10b5556SXylle } 820*d10b5556SXylle 821*d10b5556SXylle /** 822*d10b5556SXylle * Answer the result of the authentication call. 823*d10b5556SXylle * 824*d10b5556SXylle * Throws a CAS_OutOfSequenceException if wasAuthenticationCalled() is false 825*d10b5556SXylle * and markAuthenticationCall() didn't happen. 826*d10b5556SXylle * 827*d10b5556SXylle * @return bool 828*d10b5556SXylle */ 829*d10b5556SXylle public function wasAuthenticationCallSuccessful () 830*d10b5556SXylle { 831*d10b5556SXylle $this->_ensureAuthenticationCalled(); 832*d10b5556SXylle return $this->_authentication_caller['result']; 833*d10b5556SXylle } 834*d10b5556SXylle 835*d10b5556SXylle 836*d10b5556SXylle /** 837*d10b5556SXylle * Ensure that authentication was checked. Terminate with exception if no 838*d10b5556SXylle * authentication was performed 839*d10b5556SXylle * 840*d10b5556SXylle * @throws CAS_OutOfSequenceException 841*d10b5556SXylle * 842*d10b5556SXylle * @return void 843*d10b5556SXylle */ 844*d10b5556SXylle public function ensureAuthenticationCallSuccessful() 845*d10b5556SXylle { 846*d10b5556SXylle $this->_ensureAuthenticationCalled(); 847*d10b5556SXylle if (!$this->_authentication_caller['result']) { 848*d10b5556SXylle throw new CAS_OutOfSequenceException( 849*d10b5556SXylle 'authentication was checked (by ' 850*d10b5556SXylle . $this->getAuthenticationCallerMethod() 851*d10b5556SXylle . '() at ' . $this->getAuthenticationCallerFile() 852*d10b5556SXylle . ':' . $this->getAuthenticationCallerLine() 853*d10b5556SXylle . ') but the method returned false' 854*d10b5556SXylle ); 855*d10b5556SXylle } 856*d10b5556SXylle } 857*d10b5556SXylle 858*d10b5556SXylle /** 859*d10b5556SXylle * Answer information about the authentication caller. 860*d10b5556SXylle * 861*d10b5556SXylle * Throws a CAS_OutOfSequenceException if wasAuthenticationCalled() is false 862*d10b5556SXylle * and markAuthenticationCall() didn't happen. 863*d10b5556SXylle * 864*d10b5556SXylle * @return string the file that called authentication 865*d10b5556SXylle */ 866*d10b5556SXylle public function getAuthenticationCallerFile () 867*d10b5556SXylle { 868*d10b5556SXylle $this->_ensureAuthenticationCalled(); 869*d10b5556SXylle return $this->_authentication_caller['file']; 870*d10b5556SXylle } 871*d10b5556SXylle 872*d10b5556SXylle /** 873*d10b5556SXylle * Answer information about the authentication caller. 874*d10b5556SXylle * 875*d10b5556SXylle * Throws a CAS_OutOfSequenceException if wasAuthenticationCalled() is false 876*d10b5556SXylle * and markAuthenticationCall() didn't happen. 877*d10b5556SXylle * 878*d10b5556SXylle * @return int the line that called authentication 879*d10b5556SXylle */ 880*d10b5556SXylle public function getAuthenticationCallerLine () 881*d10b5556SXylle { 882*d10b5556SXylle $this->_ensureAuthenticationCalled(); 883*d10b5556SXylle return $this->_authentication_caller['line']; 884*d10b5556SXylle } 885*d10b5556SXylle 886*d10b5556SXylle /** 887*d10b5556SXylle * Answer information about the authentication caller. 888*d10b5556SXylle * 889*d10b5556SXylle * Throws a CAS_OutOfSequenceException if wasAuthenticationCalled() is false 890*d10b5556SXylle * and markAuthenticationCall() didn't happen. 891*d10b5556SXylle * 892*d10b5556SXylle * @return string the method that called authentication 893*d10b5556SXylle */ 894*d10b5556SXylle public function getAuthenticationCallerMethod () 895*d10b5556SXylle { 896*d10b5556SXylle $this->_ensureAuthenticationCalled(); 897*d10b5556SXylle return $this->_authentication_caller['method']; 898*d10b5556SXylle } 899*d10b5556SXylle 900*d10b5556SXylle /** @} */ 901*d10b5556SXylle 902*d10b5556SXylle // ######################################################################## 903*d10b5556SXylle // CONSTRUCTOR 904*d10b5556SXylle // ######################################################################## 905*d10b5556SXylle /** 906*d10b5556SXylle * @addtogroup internalConfig 907*d10b5556SXylle * @{ 908*d10b5556SXylle */ 909*d10b5556SXylle 910*d10b5556SXylle /** 911*d10b5556SXylle * CAS_Client constructor. 912*d10b5556SXylle * 913*d10b5556SXylle * @param string $server_version the version of the CAS server 914*d10b5556SXylle * @param bool $proxy true if the CAS client is a CAS proxy 915*d10b5556SXylle * @param string $server_hostname the hostname of the CAS server 916*d10b5556SXylle * @param int $server_port the port the CAS server is running on 917*d10b5556SXylle * @param string $server_uri the URI the CAS server is responding on 918*d10b5556SXylle * @param bool $changeSessionID Allow phpCAS to change the session_id 919*d10b5556SXylle * (Single Sign Out/handleLogoutRequests 920*d10b5556SXylle * is based on that change) 921*d10b5556SXylle * @param string|string[]|CAS_ServiceBaseUrl_Interface 922*d10b5556SXylle * $service_base_url the base URL (protocol, host and the 923*d10b5556SXylle * optional port) of the CAS client; pass 924*d10b5556SXylle * in an array to use auto discovery with 925*d10b5556SXylle * an allowlist; pass in 926*d10b5556SXylle * CAS_ServiceBaseUrl_Interface for custom 927*d10b5556SXylle * behavior. Added in 1.6.0. Similar to 928*d10b5556SXylle * serverName config in other CAS clients. 929*d10b5556SXylle * @param \SessionHandlerInterface $sessionHandler the session handler 930*d10b5556SXylle * 931*d10b5556SXylle * @return self a newly created CAS_Client object 932*d10b5556SXylle */ 933*d10b5556SXylle public function __construct( 934*d10b5556SXylle $server_version, 935*d10b5556SXylle $proxy, 936*d10b5556SXylle $server_hostname, 937*d10b5556SXylle $server_port, 938*d10b5556SXylle $server_uri, 939*d10b5556SXylle $service_base_url, 940*d10b5556SXylle $changeSessionID = true, 941*d10b5556SXylle \SessionHandlerInterface $sessionHandler = null 942*d10b5556SXylle ) { 943*d10b5556SXylle // Argument validation 944*d10b5556SXylle if (gettype($server_version) != 'string') 945*d10b5556SXylle throw new CAS_TypeMismatchException($server_version, '$server_version', 'string'); 946*d10b5556SXylle if (gettype($proxy) != 'boolean') 947*d10b5556SXylle throw new CAS_TypeMismatchException($proxy, '$proxy', 'boolean'); 948*d10b5556SXylle if (gettype($server_hostname) != 'string') 949*d10b5556SXylle throw new CAS_TypeMismatchException($server_hostname, '$server_hostname', 'string'); 950*d10b5556SXylle if (gettype($server_port) != 'integer') 951*d10b5556SXylle throw new CAS_TypeMismatchException($server_port, '$server_port', 'integer'); 952*d10b5556SXylle if (gettype($server_uri) != 'string') 953*d10b5556SXylle throw new CAS_TypeMismatchException($server_uri, '$server_uri', 'string'); 954*d10b5556SXylle if (gettype($changeSessionID) != 'boolean') 955*d10b5556SXylle throw new CAS_TypeMismatchException($changeSessionID, '$changeSessionID', 'boolean'); 956*d10b5556SXylle 957*d10b5556SXylle $this->_setServiceBaseUrl($service_base_url); 958*d10b5556SXylle 959*d10b5556SXylle if (empty($sessionHandler)) { 960*d10b5556SXylle $sessionHandler = new CAS_Session_PhpSession; 961*d10b5556SXylle } 962*d10b5556SXylle 963*d10b5556SXylle phpCAS::traceBegin(); 964*d10b5556SXylle // true : allow to change the session_id(), false session_id won't be 965*d10b5556SXylle // changed and logout won't be handled because of that 966*d10b5556SXylle $this->_setChangeSessionID($changeSessionID); 967*d10b5556SXylle 968*d10b5556SXylle $this->setSessionHandler($sessionHandler); 969*d10b5556SXylle 970*d10b5556SXylle if (!$this->_isLogoutRequest()) { 971*d10b5556SXylle if (session_id() === "") { 972*d10b5556SXylle // skip Session Handling for logout requests and if don't want it 973*d10b5556SXylle session_start(); 974*d10b5556SXylle phpCAS :: trace("Starting a new session " . session_id()); 975*d10b5556SXylle } 976*d10b5556SXylle } 977*d10b5556SXylle 978*d10b5556SXylle // Only for debug purposes 979*d10b5556SXylle if ($this->isSessionAuthenticated()){ 980*d10b5556SXylle phpCAS :: trace("Session is authenticated as: " . $this->getSessionValue('user')); 981*d10b5556SXylle } else { 982*d10b5556SXylle phpCAS :: trace("Session is not authenticated"); 983*d10b5556SXylle } 984*d10b5556SXylle // are we in proxy mode ? 985*d10b5556SXylle $this->_proxy = $proxy; 986*d10b5556SXylle 987*d10b5556SXylle // Make cookie handling available. 988*d10b5556SXylle if ($this->isProxy()) { 989*d10b5556SXylle if (!$this->hasSessionValue('service_cookies')) { 990*d10b5556SXylle $this->setSessionValue('service_cookies', array()); 991*d10b5556SXylle } 992*d10b5556SXylle // TODO remove explicit call to $_SESSION 993*d10b5556SXylle $this->_serviceCookieJar = new CAS_CookieJar( 994*d10b5556SXylle $_SESSION[static::PHPCAS_SESSION_PREFIX]['service_cookies'] 995*d10b5556SXylle ); 996*d10b5556SXylle } 997*d10b5556SXylle 998*d10b5556SXylle // check version 999*d10b5556SXylle $supportedProtocols = phpCAS::getSupportedProtocols(); 1000*d10b5556SXylle if (isset($supportedProtocols[$server_version]) === false) { 1001*d10b5556SXylle phpCAS::error( 1002*d10b5556SXylle 'this version of CAS (`'.$server_version 1003*d10b5556SXylle .'\') is not supported by phpCAS '.phpCAS::getVersion() 1004*d10b5556SXylle ); 1005*d10b5556SXylle } 1006*d10b5556SXylle 1007*d10b5556SXylle if ($server_version === CAS_VERSION_1_0 && $this->isProxy()) { 1008*d10b5556SXylle phpCAS::error( 1009*d10b5556SXylle 'CAS proxies are not supported in CAS '.$server_version 1010*d10b5556SXylle ); 1011*d10b5556SXylle } 1012*d10b5556SXylle 1013*d10b5556SXylle $this->_server['version'] = $server_version; 1014*d10b5556SXylle 1015*d10b5556SXylle // check hostname 1016*d10b5556SXylle if ( empty($server_hostname) 1017*d10b5556SXylle || !preg_match('/[\.\d\-a-z]*/', $server_hostname) 1018*d10b5556SXylle ) { 1019*d10b5556SXylle phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')'); 1020*d10b5556SXylle } 1021*d10b5556SXylle $this->_server['hostname'] = $server_hostname; 1022*d10b5556SXylle 1023*d10b5556SXylle // check port 1024*d10b5556SXylle if ( $server_port == 0 1025*d10b5556SXylle || !is_int($server_port) 1026*d10b5556SXylle ) { 1027*d10b5556SXylle phpCAS::error('bad CAS server port (`'.$server_hostname.'\')'); 1028*d10b5556SXylle } 1029*d10b5556SXylle $this->_server['port'] = $server_port; 1030*d10b5556SXylle 1031*d10b5556SXylle // check URI 1032*d10b5556SXylle if ( !preg_match('/[\.\d\-_a-z\/]*/', $server_uri) ) { 1033*d10b5556SXylle phpCAS::error('bad CAS server URI (`'.$server_uri.'\')'); 1034*d10b5556SXylle } 1035*d10b5556SXylle // add leading and trailing `/' and remove doubles 1036*d10b5556SXylle if(strstr($server_uri, '?') === false) $server_uri .= '/'; 1037*d10b5556SXylle $server_uri = preg_replace('/\/\//', '/', '/'.$server_uri); 1038*d10b5556SXylle $this->_server['uri'] = $server_uri; 1039*d10b5556SXylle 1040*d10b5556SXylle // set to callback mode if PgtIou and PgtId CGI GET parameters are provided 1041*d10b5556SXylle if ( $this->isProxy() ) { 1042*d10b5556SXylle if(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId'])) { 1043*d10b5556SXylle $this->_setCallbackMode(true); 1044*d10b5556SXylle $this->_setCallbackModeUsingPost(false); 1045*d10b5556SXylle } elseif (!empty($_POST['pgtIou'])&&!empty($_POST['pgtId'])) { 1046*d10b5556SXylle $this->_setCallbackMode(true); 1047*d10b5556SXylle $this->_setCallbackModeUsingPost(true); 1048*d10b5556SXylle } else { 1049*d10b5556SXylle $this->_setCallbackMode(false); 1050*d10b5556SXylle $this->_setCallbackModeUsingPost(false); 1051*d10b5556SXylle } 1052*d10b5556SXylle 1053*d10b5556SXylle 1054*d10b5556SXylle } 1055*d10b5556SXylle 1056*d10b5556SXylle if ( $this->_isCallbackMode() ) { 1057*d10b5556SXylle //callback mode: check that phpCAS is secured 1058*d10b5556SXylle if ( !$this->getServiceBaseUrl()->isHttps() ) { 1059*d10b5556SXylle phpCAS::error( 1060*d10b5556SXylle 'CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server' 1061*d10b5556SXylle ); 1062*d10b5556SXylle } 1063*d10b5556SXylle } else { 1064*d10b5556SXylle //normal mode: get ticket and remove it from CGI parameters for 1065*d10b5556SXylle // developers 1066*d10b5556SXylle $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : ''); 1067*d10b5556SXylle if (preg_match('/^[SP]T-/', $ticket) ) { 1068*d10b5556SXylle phpCAS::trace('Ticket \''.$ticket.'\' found'); 1069*d10b5556SXylle $this->setTicket($ticket); 1070*d10b5556SXylle unset($_GET['ticket']); 1071*d10b5556SXylle } else if ( !empty($ticket) ) { 1072*d10b5556SXylle //ill-formed ticket, halt 1073*d10b5556SXylle phpCAS::error( 1074*d10b5556SXylle 'ill-formed ticket found in the URL (ticket=`' 1075*d10b5556SXylle .htmlentities($ticket).'\')' 1076*d10b5556SXylle ); 1077*d10b5556SXylle } 1078*d10b5556SXylle 1079*d10b5556SXylle } 1080*d10b5556SXylle phpCAS::traceEnd(); 1081*d10b5556SXylle } 1082*d10b5556SXylle 1083*d10b5556SXylle /** @} */ 1084*d10b5556SXylle 1085*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 1086*d10b5556SXylle // XX XX 1087*d10b5556SXylle // XX Session Handling XX 1088*d10b5556SXylle // XX XX 1089*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 1090*d10b5556SXylle 1091*d10b5556SXylle /** 1092*d10b5556SXylle * @addtogroup internalConfig 1093*d10b5556SXylle * @{ 1094*d10b5556SXylle */ 1095*d10b5556SXylle 1096*d10b5556SXylle /** The session prefix for phpCAS values */ 1097*d10b5556SXylle const PHPCAS_SESSION_PREFIX = 'phpCAS'; 1098*d10b5556SXylle 1099*d10b5556SXylle /** 1100*d10b5556SXylle * @var bool A variable to whether phpcas will use its own session handling. Default = true 1101*d10b5556SXylle * @hideinitializer 1102*d10b5556SXylle */ 1103*d10b5556SXylle private $_change_session_id = true; 1104*d10b5556SXylle 1105*d10b5556SXylle /** 1106*d10b5556SXylle * @var SessionHandlerInterface 1107*d10b5556SXylle */ 1108*d10b5556SXylle private $_sessionHandler; 1109*d10b5556SXylle 1110*d10b5556SXylle /** 1111*d10b5556SXylle * Set a parameter whether to allow phpCAS to change session_id 1112*d10b5556SXylle * 1113*d10b5556SXylle * @param bool $allowed allow phpCAS to change session_id 1114*d10b5556SXylle * 1115*d10b5556SXylle * @return void 1116*d10b5556SXylle */ 1117*d10b5556SXylle private function _setChangeSessionID($allowed) 1118*d10b5556SXylle { 1119*d10b5556SXylle $this->_change_session_id = $allowed; 1120*d10b5556SXylle } 1121*d10b5556SXylle 1122*d10b5556SXylle /** 1123*d10b5556SXylle * Get whether phpCAS is allowed to change session_id 1124*d10b5556SXylle * 1125*d10b5556SXylle * @return bool 1126*d10b5556SXylle */ 1127*d10b5556SXylle public function getChangeSessionID() 1128*d10b5556SXylle { 1129*d10b5556SXylle return $this->_change_session_id; 1130*d10b5556SXylle } 1131*d10b5556SXylle 1132*d10b5556SXylle /** 1133*d10b5556SXylle * Set the session handler. 1134*d10b5556SXylle * 1135*d10b5556SXylle * @param \SessionHandlerInterface $sessionHandler 1136*d10b5556SXylle * 1137*d10b5556SXylle * @return bool 1138*d10b5556SXylle */ 1139*d10b5556SXylle public function setSessionHandler(\SessionHandlerInterface $sessionHandler) 1140*d10b5556SXylle { 1141*d10b5556SXylle $this->_sessionHandler = $sessionHandler; 1142*d10b5556SXylle if (session_status() !== PHP_SESSION_ACTIVE) { 1143*d10b5556SXylle return session_set_save_handler($this->_sessionHandler, true); 1144*d10b5556SXylle } 1145*d10b5556SXylle return true; 1146*d10b5556SXylle } 1147*d10b5556SXylle 1148*d10b5556SXylle /** 1149*d10b5556SXylle * Get a session value using the given key. 1150*d10b5556SXylle * 1151*d10b5556SXylle * @param string $key 1152*d10b5556SXylle * @param mixed $default default value if the key is not set 1153*d10b5556SXylle * 1154*d10b5556SXylle * @return mixed 1155*d10b5556SXylle */ 1156*d10b5556SXylle protected function getSessionValue($key, $default = null) 1157*d10b5556SXylle { 1158*d10b5556SXylle $this->validateSession($key); 1159*d10b5556SXylle 1160*d10b5556SXylle if (isset($_SESSION[static::PHPCAS_SESSION_PREFIX][$key])) { 1161*d10b5556SXylle return $_SESSION[static::PHPCAS_SESSION_PREFIX][$key]; 1162*d10b5556SXylle } 1163*d10b5556SXylle 1164*d10b5556SXylle return $default; 1165*d10b5556SXylle } 1166*d10b5556SXylle 1167*d10b5556SXylle /** 1168*d10b5556SXylle * Determine whether a session value is set or not. 1169*d10b5556SXylle * 1170*d10b5556SXylle * To check if a session value is empty or not please use 1171*d10b5556SXylle * !!(getSessionValue($key)). 1172*d10b5556SXylle * 1173*d10b5556SXylle * @param string $key 1174*d10b5556SXylle * 1175*d10b5556SXylle * @return bool 1176*d10b5556SXylle */ 1177*d10b5556SXylle protected function hasSessionValue($key) 1178*d10b5556SXylle { 1179*d10b5556SXylle $this->validateSession($key); 1180*d10b5556SXylle 1181*d10b5556SXylle return isset($_SESSION[static::PHPCAS_SESSION_PREFIX][$key]); 1182*d10b5556SXylle } 1183*d10b5556SXylle 1184*d10b5556SXylle /** 1185*d10b5556SXylle * Set a session value using the given key and value. 1186*d10b5556SXylle * 1187*d10b5556SXylle * @param string $key 1188*d10b5556SXylle * @param mixed $value 1189*d10b5556SXylle * 1190*d10b5556SXylle * @return string 1191*d10b5556SXylle */ 1192*d10b5556SXylle protected function setSessionValue($key, $value) 1193*d10b5556SXylle { 1194*d10b5556SXylle $this->validateSession($key); 1195*d10b5556SXylle 1196*d10b5556SXylle $this->ensureSessionArray(); 1197*d10b5556SXylle $_SESSION[static::PHPCAS_SESSION_PREFIX][$key] = $value; 1198*d10b5556SXylle } 1199*d10b5556SXylle 1200*d10b5556SXylle /** 1201*d10b5556SXylle * Ensure that the session array is initialized before writing to it. 1202*d10b5556SXylle */ 1203*d10b5556SXylle protected function ensureSessionArray() { 1204*d10b5556SXylle // init phpCAS session array 1205*d10b5556SXylle if (!isset($_SESSION[static::PHPCAS_SESSION_PREFIX]) 1206*d10b5556SXylle || !is_array($_SESSION[static::PHPCAS_SESSION_PREFIX])) { 1207*d10b5556SXylle $_SESSION[static::PHPCAS_SESSION_PREFIX] = array(); 1208*d10b5556SXylle } 1209*d10b5556SXylle } 1210*d10b5556SXylle 1211*d10b5556SXylle /** 1212*d10b5556SXylle * Remove a session value with the given key. 1213*d10b5556SXylle * 1214*d10b5556SXylle * @param string $key 1215*d10b5556SXylle */ 1216*d10b5556SXylle protected function removeSessionValue($key) 1217*d10b5556SXylle { 1218*d10b5556SXylle $this->validateSession($key); 1219*d10b5556SXylle 1220*d10b5556SXylle if (isset($_SESSION[static::PHPCAS_SESSION_PREFIX][$key])) { 1221*d10b5556SXylle unset($_SESSION[static::PHPCAS_SESSION_PREFIX][$key]); 1222*d10b5556SXylle return true; 1223*d10b5556SXylle } 1224*d10b5556SXylle 1225*d10b5556SXylle return false; 1226*d10b5556SXylle } 1227*d10b5556SXylle 1228*d10b5556SXylle /** 1229*d10b5556SXylle * Remove all phpCAS session values. 1230*d10b5556SXylle */ 1231*d10b5556SXylle protected function clearSessionValues() 1232*d10b5556SXylle { 1233*d10b5556SXylle unset($_SESSION[static::PHPCAS_SESSION_PREFIX]); 1234*d10b5556SXylle } 1235*d10b5556SXylle 1236*d10b5556SXylle /** 1237*d10b5556SXylle * Ensure $key is a string for session utils input 1238*d10b5556SXylle * 1239*d10b5556SXylle * @param string $key 1240*d10b5556SXylle * 1241*d10b5556SXylle * @return bool 1242*d10b5556SXylle */ 1243*d10b5556SXylle protected function validateSession($key) 1244*d10b5556SXylle { 1245*d10b5556SXylle if (!is_string($key)) { 1246*d10b5556SXylle throw new InvalidArgumentException('Session key must be a string.'); 1247*d10b5556SXylle } 1248*d10b5556SXylle 1249*d10b5556SXylle return true; 1250*d10b5556SXylle } 1251*d10b5556SXylle 1252*d10b5556SXylle /** 1253*d10b5556SXylle * Renaming the session 1254*d10b5556SXylle * 1255*d10b5556SXylle * @param string $ticket name of the ticket 1256*d10b5556SXylle * 1257*d10b5556SXylle * @return void 1258*d10b5556SXylle */ 1259*d10b5556SXylle protected function _renameSession($ticket) 1260*d10b5556SXylle { 1261*d10b5556SXylle phpCAS::traceBegin(); 1262*d10b5556SXylle if ($this->getChangeSessionID()) { 1263*d10b5556SXylle if (!empty($this->_user)) { 1264*d10b5556SXylle $old_session = $_SESSION; 1265*d10b5556SXylle phpCAS :: trace("Killing session: ". session_id()); 1266*d10b5556SXylle session_destroy(); 1267*d10b5556SXylle // set up a new session, of name based on the ticket 1268*d10b5556SXylle $session_id = $this->_sessionIdForTicket($ticket); 1269*d10b5556SXylle phpCAS :: trace("Starting session: ". $session_id); 1270*d10b5556SXylle session_id($session_id); 1271*d10b5556SXylle session_start(); 1272*d10b5556SXylle phpCAS :: trace("Restoring old session vars"); 1273*d10b5556SXylle $_SESSION = $old_session; 1274*d10b5556SXylle } else { 1275*d10b5556SXylle phpCAS :: trace ( 1276*d10b5556SXylle 'Session should only be renamed after successfull authentication' 1277*d10b5556SXylle ); 1278*d10b5556SXylle } 1279*d10b5556SXylle } else { 1280*d10b5556SXylle phpCAS :: trace( 1281*d10b5556SXylle "Skipping session rename since phpCAS is not handling the session." 1282*d10b5556SXylle ); 1283*d10b5556SXylle } 1284*d10b5556SXylle phpCAS::traceEnd(); 1285*d10b5556SXylle } 1286*d10b5556SXylle 1287*d10b5556SXylle /** @} */ 1288*d10b5556SXylle 1289*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 1290*d10b5556SXylle // XX XX 1291*d10b5556SXylle // XX AUTHENTICATION XX 1292*d10b5556SXylle // XX XX 1293*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 1294*d10b5556SXylle 1295*d10b5556SXylle /** 1296*d10b5556SXylle * @addtogroup internalAuthentication 1297*d10b5556SXylle * @{ 1298*d10b5556SXylle */ 1299*d10b5556SXylle 1300*d10b5556SXylle /** 1301*d10b5556SXylle * The Authenticated user. Written by CAS_Client::_setUser(), read by 1302*d10b5556SXylle * CAS_Client::getUser(). 1303*d10b5556SXylle * 1304*d10b5556SXylle * @hideinitializer 1305*d10b5556SXylle */ 1306*d10b5556SXylle private $_user = ''; 1307*d10b5556SXylle 1308*d10b5556SXylle /** 1309*d10b5556SXylle * This method sets the CAS user's login name. 1310*d10b5556SXylle * 1311*d10b5556SXylle * @param string $user the login name of the authenticated user. 1312*d10b5556SXylle * 1313*d10b5556SXylle * @return void 1314*d10b5556SXylle */ 1315*d10b5556SXylle private function _setUser($user) 1316*d10b5556SXylle { 1317*d10b5556SXylle $this->_user = $user; 1318*d10b5556SXylle } 1319*d10b5556SXylle 1320*d10b5556SXylle /** 1321*d10b5556SXylle * This method returns the CAS user's login name. 1322*d10b5556SXylle * 1323*d10b5556SXylle * @return string the login name of the authenticated user 1324*d10b5556SXylle * 1325*d10b5556SXylle * @warning should be called only after CAS_Client::forceAuthentication() or 1326*d10b5556SXylle * CAS_Client::isAuthenticated(), otherwise halt with an error. 1327*d10b5556SXylle */ 1328*d10b5556SXylle public function getUser() 1329*d10b5556SXylle { 1330*d10b5556SXylle // Sequence validation 1331*d10b5556SXylle $this->ensureAuthenticationCallSuccessful(); 1332*d10b5556SXylle 1333*d10b5556SXylle return $this->_getUser(); 1334*d10b5556SXylle } 1335*d10b5556SXylle 1336*d10b5556SXylle /** 1337*d10b5556SXylle * This method returns the CAS user's login name. 1338*d10b5556SXylle * 1339*d10b5556SXylle * @return string the login name of the authenticated user 1340*d10b5556SXylle * 1341*d10b5556SXylle * @warning should be called only after CAS_Client::forceAuthentication() or 1342*d10b5556SXylle * CAS_Client::isAuthenticated(), otherwise halt with an error. 1343*d10b5556SXylle */ 1344*d10b5556SXylle private function _getUser() 1345*d10b5556SXylle { 1346*d10b5556SXylle // This is likely a duplicate check that could be removed.... 1347*d10b5556SXylle if ( empty($this->_user) ) { 1348*d10b5556SXylle phpCAS::error( 1349*d10b5556SXylle 'this method should be used only after '.__CLASS__ 1350*d10b5556SXylle .'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()' 1351*d10b5556SXylle ); 1352*d10b5556SXylle } 1353*d10b5556SXylle return $this->_user; 1354*d10b5556SXylle } 1355*d10b5556SXylle 1356*d10b5556SXylle /** 1357*d10b5556SXylle * The Authenticated users attributes. Written by 1358*d10b5556SXylle * CAS_Client::setAttributes(), read by CAS_Client::getAttributes(). 1359*d10b5556SXylle * @attention client applications should use phpCAS::getAttributes(). 1360*d10b5556SXylle * 1361*d10b5556SXylle * @hideinitializer 1362*d10b5556SXylle */ 1363*d10b5556SXylle private $_attributes = array(); 1364*d10b5556SXylle 1365*d10b5556SXylle /** 1366*d10b5556SXylle * Set an array of attributes 1367*d10b5556SXylle * 1368*d10b5556SXylle * @param array $attributes a key value array of attributes 1369*d10b5556SXylle * 1370*d10b5556SXylle * @return void 1371*d10b5556SXylle */ 1372*d10b5556SXylle public function setAttributes($attributes) 1373*d10b5556SXylle { 1374*d10b5556SXylle $this->_attributes = $attributes; 1375*d10b5556SXylle } 1376*d10b5556SXylle 1377*d10b5556SXylle /** 1378*d10b5556SXylle * Get an key values arry of attributes 1379*d10b5556SXylle * 1380*d10b5556SXylle * @return array of attributes 1381*d10b5556SXylle */ 1382*d10b5556SXylle public function getAttributes() 1383*d10b5556SXylle { 1384*d10b5556SXylle // Sequence validation 1385*d10b5556SXylle $this->ensureAuthenticationCallSuccessful(); 1386*d10b5556SXylle // This is likely a duplicate check that could be removed.... 1387*d10b5556SXylle if ( empty($this->_user) ) { 1388*d10b5556SXylle // if no user is set, there shouldn't be any attributes also... 1389*d10b5556SXylle phpCAS::error( 1390*d10b5556SXylle 'this method should be used only after '.__CLASS__ 1391*d10b5556SXylle .'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()' 1392*d10b5556SXylle ); 1393*d10b5556SXylle } 1394*d10b5556SXylle return $this->_attributes; 1395*d10b5556SXylle } 1396*d10b5556SXylle 1397*d10b5556SXylle /** 1398*d10b5556SXylle * Check whether attributes are available 1399*d10b5556SXylle * 1400*d10b5556SXylle * @return bool attributes available 1401*d10b5556SXylle */ 1402*d10b5556SXylle public function hasAttributes() 1403*d10b5556SXylle { 1404*d10b5556SXylle // Sequence validation 1405*d10b5556SXylle $this->ensureAuthenticationCallSuccessful(); 1406*d10b5556SXylle 1407*d10b5556SXylle return !empty($this->_attributes); 1408*d10b5556SXylle } 1409*d10b5556SXylle /** 1410*d10b5556SXylle * Check whether a specific attribute with a name is available 1411*d10b5556SXylle * 1412*d10b5556SXylle * @param string $key name of attribute 1413*d10b5556SXylle * 1414*d10b5556SXylle * @return bool is attribute available 1415*d10b5556SXylle */ 1416*d10b5556SXylle public function hasAttribute($key) 1417*d10b5556SXylle { 1418*d10b5556SXylle // Sequence validation 1419*d10b5556SXylle $this->ensureAuthenticationCallSuccessful(); 1420*d10b5556SXylle 1421*d10b5556SXylle return $this->_hasAttribute($key); 1422*d10b5556SXylle } 1423*d10b5556SXylle 1424*d10b5556SXylle /** 1425*d10b5556SXylle * Check whether a specific attribute with a name is available 1426*d10b5556SXylle * 1427*d10b5556SXylle * @param string $key name of attribute 1428*d10b5556SXylle * 1429*d10b5556SXylle * @return bool is attribute available 1430*d10b5556SXylle */ 1431*d10b5556SXylle private function _hasAttribute($key) 1432*d10b5556SXylle { 1433*d10b5556SXylle return (is_array($this->_attributes) 1434*d10b5556SXylle && array_key_exists($key, $this->_attributes)); 1435*d10b5556SXylle } 1436*d10b5556SXylle 1437*d10b5556SXylle /** 1438*d10b5556SXylle * Get a specific attribute by name 1439*d10b5556SXylle * 1440*d10b5556SXylle * @param string $key name of attribute 1441*d10b5556SXylle * 1442*d10b5556SXylle * @return string attribute values 1443*d10b5556SXylle */ 1444*d10b5556SXylle public function getAttribute($key) 1445*d10b5556SXylle { 1446*d10b5556SXylle // Sequence validation 1447*d10b5556SXylle $this->ensureAuthenticationCallSuccessful(); 1448*d10b5556SXylle 1449*d10b5556SXylle if ($this->_hasAttribute($key)) { 1450*d10b5556SXylle return $this->_attributes[$key]; 1451*d10b5556SXylle } 1452*d10b5556SXylle } 1453*d10b5556SXylle 1454*d10b5556SXylle /** 1455*d10b5556SXylle * This method is called to renew the authentication of the user 1456*d10b5556SXylle * If the user is authenticated, renew the connection 1457*d10b5556SXylle * If not, redirect to CAS 1458*d10b5556SXylle * 1459*d10b5556SXylle * @return bool true when the user is authenticated; otherwise halt. 1460*d10b5556SXylle */ 1461*d10b5556SXylle public function renewAuthentication() 1462*d10b5556SXylle { 1463*d10b5556SXylle phpCAS::traceBegin(); 1464*d10b5556SXylle // Either way, the user is authenticated by CAS 1465*d10b5556SXylle $this->removeSessionValue('auth_checked'); 1466*d10b5556SXylle if ( $this->isAuthenticated(true) ) { 1467*d10b5556SXylle phpCAS::trace('user already authenticated'); 1468*d10b5556SXylle $res = true; 1469*d10b5556SXylle } else { 1470*d10b5556SXylle $this->redirectToCas(false, true); 1471*d10b5556SXylle // never reached 1472*d10b5556SXylle $res = false; 1473*d10b5556SXylle } 1474*d10b5556SXylle phpCAS::traceEnd(); 1475*d10b5556SXylle return $res; 1476*d10b5556SXylle } 1477*d10b5556SXylle 1478*d10b5556SXylle /** 1479*d10b5556SXylle * This method is called to be sure that the user is authenticated. When not 1480*d10b5556SXylle * authenticated, halt by redirecting to the CAS server; otherwise return true. 1481*d10b5556SXylle * 1482*d10b5556SXylle * @return bool true when the user is authenticated; otherwise halt. 1483*d10b5556SXylle */ 1484*d10b5556SXylle public function forceAuthentication() 1485*d10b5556SXylle { 1486*d10b5556SXylle phpCAS::traceBegin(); 1487*d10b5556SXylle 1488*d10b5556SXylle if ( $this->isAuthenticated() ) { 1489*d10b5556SXylle // the user is authenticated, nothing to be done. 1490*d10b5556SXylle phpCAS::trace('no need to authenticate'); 1491*d10b5556SXylle $res = true; 1492*d10b5556SXylle } else { 1493*d10b5556SXylle // the user is not authenticated, redirect to the CAS server 1494*d10b5556SXylle $this->removeSessionValue('auth_checked'); 1495*d10b5556SXylle $this->redirectToCas(false/* no gateway */); 1496*d10b5556SXylle // never reached 1497*d10b5556SXylle $res = false; 1498*d10b5556SXylle } 1499*d10b5556SXylle phpCAS::traceEnd($res); 1500*d10b5556SXylle return $res; 1501*d10b5556SXylle } 1502*d10b5556SXylle 1503*d10b5556SXylle /** 1504*d10b5556SXylle * An integer that gives the number of times authentication will be cached 1505*d10b5556SXylle * before rechecked. 1506*d10b5556SXylle * 1507*d10b5556SXylle * @hideinitializer 1508*d10b5556SXylle */ 1509*d10b5556SXylle private $_cache_times_for_auth_recheck = 0; 1510*d10b5556SXylle 1511*d10b5556SXylle /** 1512*d10b5556SXylle * Set the number of times authentication will be cached before rechecked. 1513*d10b5556SXylle * 1514*d10b5556SXylle * @param int $n number of times to wait for a recheck 1515*d10b5556SXylle * 1516*d10b5556SXylle * @return void 1517*d10b5556SXylle */ 1518*d10b5556SXylle public function setCacheTimesForAuthRecheck($n) 1519*d10b5556SXylle { 1520*d10b5556SXylle if (gettype($n) != 'integer') 1521*d10b5556SXylle throw new CAS_TypeMismatchException($n, '$n', 'string'); 1522*d10b5556SXylle 1523*d10b5556SXylle $this->_cache_times_for_auth_recheck = $n; 1524*d10b5556SXylle } 1525*d10b5556SXylle 1526*d10b5556SXylle /** 1527*d10b5556SXylle * This method is called to check whether the user is authenticated or not. 1528*d10b5556SXylle * 1529*d10b5556SXylle * @return bool true when the user is authenticated, false when a previous 1530*d10b5556SXylle * gateway login failed or the function will not return if the user is 1531*d10b5556SXylle * redirected to the cas server for a gateway login attempt 1532*d10b5556SXylle */ 1533*d10b5556SXylle public function checkAuthentication() 1534*d10b5556SXylle { 1535*d10b5556SXylle phpCAS::traceBegin(); 1536*d10b5556SXylle $res = false; // default 1537*d10b5556SXylle if ( $this->isAuthenticated() ) { 1538*d10b5556SXylle phpCAS::trace('user is authenticated'); 1539*d10b5556SXylle /* The 'auth_checked' variable is removed just in case it's set. */ 1540*d10b5556SXylle $this->removeSessionValue('auth_checked'); 1541*d10b5556SXylle $res = true; 1542*d10b5556SXylle } else if ($this->getSessionValue('auth_checked')) { 1543*d10b5556SXylle // the previous request has redirected the client to the CAS server 1544*d10b5556SXylle // with gateway=true 1545*d10b5556SXylle $this->removeSessionValue('auth_checked'); 1546*d10b5556SXylle } else { 1547*d10b5556SXylle // avoid a check against CAS on every request 1548*d10b5556SXylle // we need to write this back to session later 1549*d10b5556SXylle $unauth_count = $this->getSessionValue('unauth_count', -2); 1550*d10b5556SXylle 1551*d10b5556SXylle if (($unauth_count != -2 1552*d10b5556SXylle && $this->_cache_times_for_auth_recheck == -1) 1553*d10b5556SXylle || ($unauth_count >= 0 1554*d10b5556SXylle && $unauth_count < $this->_cache_times_for_auth_recheck) 1555*d10b5556SXylle ) { 1556*d10b5556SXylle if ($this->_cache_times_for_auth_recheck != -1) { 1557*d10b5556SXylle $unauth_count++; 1558*d10b5556SXylle phpCAS::trace( 1559*d10b5556SXylle 'user is not authenticated (cached for ' 1560*d10b5556SXylle .$unauth_count.' times of ' 1561*d10b5556SXylle .$this->_cache_times_for_auth_recheck.')' 1562*d10b5556SXylle ); 1563*d10b5556SXylle } else { 1564*d10b5556SXylle phpCAS::trace( 1565*d10b5556SXylle 'user is not authenticated (cached for until login pressed)' 1566*d10b5556SXylle ); 1567*d10b5556SXylle } 1568*d10b5556SXylle $this->setSessionValue('unauth_count', $unauth_count); 1569*d10b5556SXylle } else { 1570*d10b5556SXylle $this->setSessionValue('unauth_count', 0); 1571*d10b5556SXylle $this->setSessionValue('auth_checked', true); 1572*d10b5556SXylle phpCAS::trace('user is not authenticated (cache reset)'); 1573*d10b5556SXylle $this->redirectToCas(true/* gateway */); 1574*d10b5556SXylle // never reached 1575*d10b5556SXylle } 1576*d10b5556SXylle } 1577*d10b5556SXylle phpCAS::traceEnd($res); 1578*d10b5556SXylle return $res; 1579*d10b5556SXylle } 1580*d10b5556SXylle 1581*d10b5556SXylle /** 1582*d10b5556SXylle * This method is called to check if the user is authenticated (previously or by 1583*d10b5556SXylle * tickets given in the URL). 1584*d10b5556SXylle * 1585*d10b5556SXylle * @param bool $renew true to force the authentication with the CAS server 1586*d10b5556SXylle * 1587*d10b5556SXylle * @return bool true when the user is authenticated. Also may redirect to the 1588*d10b5556SXylle * same URL without the ticket. 1589*d10b5556SXylle */ 1590*d10b5556SXylle public function isAuthenticated($renew=false) 1591*d10b5556SXylle { 1592*d10b5556SXylle phpCAS::traceBegin(); 1593*d10b5556SXylle $res = false; 1594*d10b5556SXylle 1595*d10b5556SXylle if ( $this->_wasPreviouslyAuthenticated() ) { 1596*d10b5556SXylle if ($this->hasTicket()) { 1597*d10b5556SXylle // User has a additional ticket but was already authenticated 1598*d10b5556SXylle phpCAS::trace( 1599*d10b5556SXylle 'ticket was present and will be discarded, use renewAuthenticate()' 1600*d10b5556SXylle ); 1601*d10b5556SXylle if ($this->_clearTicketsFromUrl) { 1602*d10b5556SXylle phpCAS::trace("Prepare redirect to : ".$this->getURL()); 1603*d10b5556SXylle session_write_close(); 1604*d10b5556SXylle header('Location: '.$this->getURL()); 1605*d10b5556SXylle flush(); 1606*d10b5556SXylle phpCAS::traceExit(); 1607*d10b5556SXylle throw new CAS_GracefullTerminationException(); 1608*d10b5556SXylle } else { 1609*d10b5556SXylle phpCAS::trace( 1610*d10b5556SXylle 'Already authenticated, but skipping ticket clearing since setNoClearTicketsFromUrl() was used.' 1611*d10b5556SXylle ); 1612*d10b5556SXylle $res = true; 1613*d10b5556SXylle } 1614*d10b5556SXylle } else { 1615*d10b5556SXylle // the user has already (previously during the session) been 1616*d10b5556SXylle // authenticated, nothing to be done. 1617*d10b5556SXylle phpCAS::trace( 1618*d10b5556SXylle 'user was already authenticated, no need to look for tickets' 1619*d10b5556SXylle ); 1620*d10b5556SXylle $res = true; 1621*d10b5556SXylle } 1622*d10b5556SXylle 1623*d10b5556SXylle // Mark the auth-check as complete to allow post-authentication 1624*d10b5556SXylle // callbacks to make use of phpCAS::getUser() and similar methods 1625*d10b5556SXylle $this->markAuthenticationCall($res); 1626*d10b5556SXylle } else { 1627*d10b5556SXylle if ($this->hasTicket()) { 1628*d10b5556SXylle $validate_url = ''; 1629*d10b5556SXylle $text_response = ''; 1630*d10b5556SXylle $tree_response = ''; 1631*d10b5556SXylle 1632*d10b5556SXylle switch ($this->getServerVersion()) { 1633*d10b5556SXylle case CAS_VERSION_1_0: 1634*d10b5556SXylle // if a Service Ticket was given, validate it 1635*d10b5556SXylle phpCAS::trace( 1636*d10b5556SXylle 'CAS 1.0 ticket `'.$this->getTicket().'\' is present' 1637*d10b5556SXylle ); 1638*d10b5556SXylle $this->validateCAS10( 1639*d10b5556SXylle $validate_url, $text_response, $tree_response, $renew 1640*d10b5556SXylle ); // if it fails, it halts 1641*d10b5556SXylle phpCAS::trace( 1642*d10b5556SXylle 'CAS 1.0 ticket `'.$this->getTicket().'\' was validated' 1643*d10b5556SXylle ); 1644*d10b5556SXylle $this->setSessionValue('user', $this->_getUser()); 1645*d10b5556SXylle $res = true; 1646*d10b5556SXylle $logoutTicket = $this->getTicket(); 1647*d10b5556SXylle break; 1648*d10b5556SXylle case CAS_VERSION_2_0: 1649*d10b5556SXylle case CAS_VERSION_3_0: 1650*d10b5556SXylle // if a Proxy Ticket was given, validate it 1651*d10b5556SXylle phpCAS::trace( 1652*d10b5556SXylle 'CAS '.$this->getServerVersion().' ticket `'.$this->getTicket().'\' is present' 1653*d10b5556SXylle ); 1654*d10b5556SXylle $this->validateCAS20( 1655*d10b5556SXylle $validate_url, $text_response, $tree_response, $renew 1656*d10b5556SXylle ); // note: if it fails, it halts 1657*d10b5556SXylle phpCAS::trace( 1658*d10b5556SXylle 'CAS '.$this->getServerVersion().' ticket `'.$this->getTicket().'\' was validated' 1659*d10b5556SXylle ); 1660*d10b5556SXylle if ( $this->isProxy() ) { 1661*d10b5556SXylle $this->_validatePGT( 1662*d10b5556SXylle $validate_url, $text_response, $tree_response 1663*d10b5556SXylle ); // idem 1664*d10b5556SXylle phpCAS::trace('PGT `'.$this->_getPGT().'\' was validated'); 1665*d10b5556SXylle $this->setSessionValue('pgt', $this->_getPGT()); 1666*d10b5556SXylle } 1667*d10b5556SXylle $this->setSessionValue('user', $this->_getUser()); 1668*d10b5556SXylle if (!empty($this->_attributes)) { 1669*d10b5556SXylle $this->setSessionValue('attributes', $this->_attributes); 1670*d10b5556SXylle } 1671*d10b5556SXylle $proxies = $this->getProxies(); 1672*d10b5556SXylle if (!empty($proxies)) { 1673*d10b5556SXylle $this->setSessionValue('proxies', $this->getProxies()); 1674*d10b5556SXylle } 1675*d10b5556SXylle $res = true; 1676*d10b5556SXylle $logoutTicket = $this->getTicket(); 1677*d10b5556SXylle break; 1678*d10b5556SXylle case SAML_VERSION_1_1: 1679*d10b5556SXylle // if we have a SAML ticket, validate it. 1680*d10b5556SXylle phpCAS::trace( 1681*d10b5556SXylle 'SAML 1.1 ticket `'.$this->getTicket().'\' is present' 1682*d10b5556SXylle ); 1683*d10b5556SXylle $this->validateSA( 1684*d10b5556SXylle $validate_url, $text_response, $tree_response, $renew 1685*d10b5556SXylle ); // if it fails, it halts 1686*d10b5556SXylle phpCAS::trace( 1687*d10b5556SXylle 'SAML 1.1 ticket `'.$this->getTicket().'\' was validated' 1688*d10b5556SXylle ); 1689*d10b5556SXylle $this->setSessionValue('user', $this->_getUser()); 1690*d10b5556SXylle $this->setSessionValue('attributes', $this->_attributes); 1691*d10b5556SXylle $res = true; 1692*d10b5556SXylle $logoutTicket = $this->getTicket(); 1693*d10b5556SXylle break; 1694*d10b5556SXylle default: 1695*d10b5556SXylle phpCAS::trace('Protocol error'); 1696*d10b5556SXylle break; 1697*d10b5556SXylle } 1698*d10b5556SXylle } else { 1699*d10b5556SXylle // no ticket given, not authenticated 1700*d10b5556SXylle phpCAS::trace('no ticket found'); 1701*d10b5556SXylle } 1702*d10b5556SXylle 1703*d10b5556SXylle // Mark the auth-check as complete to allow post-authentication 1704*d10b5556SXylle // callbacks to make use of phpCAS::getUser() and similar methods 1705*d10b5556SXylle $this->markAuthenticationCall($res); 1706*d10b5556SXylle 1707*d10b5556SXylle if ($res) { 1708*d10b5556SXylle // call the post-authenticate callback if registered. 1709*d10b5556SXylle if ($this->_postAuthenticateCallbackFunction) { 1710*d10b5556SXylle $args = $this->_postAuthenticateCallbackArgs; 1711*d10b5556SXylle array_unshift($args, $logoutTicket); 1712*d10b5556SXylle call_user_func_array( 1713*d10b5556SXylle $this->_postAuthenticateCallbackFunction, $args 1714*d10b5556SXylle ); 1715*d10b5556SXylle } 1716*d10b5556SXylle 1717*d10b5556SXylle // if called with a ticket parameter, we need to redirect to the 1718*d10b5556SXylle // app without the ticket so that CAS-ification is transparent 1719*d10b5556SXylle // to the browser (for later POSTS) most of the checks and 1720*d10b5556SXylle // errors should have been made now, so we're safe for redirect 1721*d10b5556SXylle // without masking error messages. remove the ticket as a 1722*d10b5556SXylle // security precaution to prevent a ticket in the HTTP_REFERRER 1723*d10b5556SXylle if ($this->_clearTicketsFromUrl) { 1724*d10b5556SXylle phpCAS::trace("Prepare redirect to : ".$this->getURL()); 1725*d10b5556SXylle session_write_close(); 1726*d10b5556SXylle header('Location: '.$this->getURL()); 1727*d10b5556SXylle flush(); 1728*d10b5556SXylle phpCAS::traceExit(); 1729*d10b5556SXylle throw new CAS_GracefullTerminationException(); 1730*d10b5556SXylle } 1731*d10b5556SXylle } 1732*d10b5556SXylle } 1733*d10b5556SXylle phpCAS::traceEnd($res); 1734*d10b5556SXylle return $res; 1735*d10b5556SXylle } 1736*d10b5556SXylle 1737*d10b5556SXylle /** 1738*d10b5556SXylle * This method tells if the current session is authenticated. 1739*d10b5556SXylle * 1740*d10b5556SXylle * @return bool true if authenticated based soley on $_SESSION variable 1741*d10b5556SXylle */ 1742*d10b5556SXylle public function isSessionAuthenticated () 1743*d10b5556SXylle { 1744*d10b5556SXylle return !!$this->getSessionValue('user'); 1745*d10b5556SXylle } 1746*d10b5556SXylle 1747*d10b5556SXylle /** 1748*d10b5556SXylle * This method tells if the user has already been (previously) authenticated 1749*d10b5556SXylle * by looking into the session variables. 1750*d10b5556SXylle * 1751*d10b5556SXylle * @note This function switches to callback mode when needed. 1752*d10b5556SXylle * 1753*d10b5556SXylle * @return bool true when the user has already been authenticated; false otherwise. 1754*d10b5556SXylle */ 1755*d10b5556SXylle private function _wasPreviouslyAuthenticated() 1756*d10b5556SXylle { 1757*d10b5556SXylle phpCAS::traceBegin(); 1758*d10b5556SXylle 1759*d10b5556SXylle if ( $this->_isCallbackMode() ) { 1760*d10b5556SXylle // Rebroadcast the pgtIou and pgtId to all nodes 1761*d10b5556SXylle if ($this->_rebroadcast&&!isset($_POST['rebroadcast'])) { 1762*d10b5556SXylle $this->_rebroadcast(self::PGTIOU); 1763*d10b5556SXylle } 1764*d10b5556SXylle $this->_callback(); 1765*d10b5556SXylle } 1766*d10b5556SXylle 1767*d10b5556SXylle $auth = false; 1768*d10b5556SXylle 1769*d10b5556SXylle if ( $this->isProxy() ) { 1770*d10b5556SXylle // CAS proxy: username and PGT must be present 1771*d10b5556SXylle if ( $this->isSessionAuthenticated() 1772*d10b5556SXylle && $this->getSessionValue('pgt') 1773*d10b5556SXylle ) { 1774*d10b5556SXylle // authentication already done 1775*d10b5556SXylle $this->_setUser($this->getSessionValue('user')); 1776*d10b5556SXylle if ($this->hasSessionValue('attributes')) { 1777*d10b5556SXylle $this->setAttributes($this->getSessionValue('attributes')); 1778*d10b5556SXylle } 1779*d10b5556SXylle $this->_setPGT($this->getSessionValue('pgt')); 1780*d10b5556SXylle phpCAS::trace( 1781*d10b5556SXylle 'user = `'.$this->getSessionValue('user').'\', PGT = `' 1782*d10b5556SXylle .$this->getSessionValue('pgt').'\'' 1783*d10b5556SXylle ); 1784*d10b5556SXylle 1785*d10b5556SXylle // Include the list of proxies 1786*d10b5556SXylle if ($this->hasSessionValue('proxies')) { 1787*d10b5556SXylle $this->_setProxies($this->getSessionValue('proxies')); 1788*d10b5556SXylle phpCAS::trace( 1789*d10b5556SXylle 'proxies = "' 1790*d10b5556SXylle .implode('", "', $this->getSessionValue('proxies')).'"' 1791*d10b5556SXylle ); 1792*d10b5556SXylle } 1793*d10b5556SXylle 1794*d10b5556SXylle $auth = true; 1795*d10b5556SXylle } elseif ( $this->isSessionAuthenticated() 1796*d10b5556SXylle && !$this->getSessionValue('pgt') 1797*d10b5556SXylle ) { 1798*d10b5556SXylle // these two variables should be empty or not empty at the same time 1799*d10b5556SXylle phpCAS::trace( 1800*d10b5556SXylle 'username found (`'.$this->getSessionValue('user') 1801*d10b5556SXylle .'\') but PGT is empty' 1802*d10b5556SXylle ); 1803*d10b5556SXylle // unset all tickets to enforce authentication 1804*d10b5556SXylle $this->clearSessionValues(); 1805*d10b5556SXylle $this->setTicket(''); 1806*d10b5556SXylle } elseif ( !$this->isSessionAuthenticated() 1807*d10b5556SXylle && $this->getSessionValue('pgt') 1808*d10b5556SXylle ) { 1809*d10b5556SXylle // these two variables should be empty or not empty at the same time 1810*d10b5556SXylle phpCAS::trace( 1811*d10b5556SXylle 'PGT found (`'.$this->getSessionValue('pgt') 1812*d10b5556SXylle .'\') but username is empty' 1813*d10b5556SXylle ); 1814*d10b5556SXylle // unset all tickets to enforce authentication 1815*d10b5556SXylle $this->clearSessionValues(); 1816*d10b5556SXylle $this->setTicket(''); 1817*d10b5556SXylle } else { 1818*d10b5556SXylle phpCAS::trace('neither user nor PGT found'); 1819*d10b5556SXylle } 1820*d10b5556SXylle } else { 1821*d10b5556SXylle // `simple' CAS client (not a proxy): username must be present 1822*d10b5556SXylle if ( $this->isSessionAuthenticated() ) { 1823*d10b5556SXylle // authentication already done 1824*d10b5556SXylle $this->_setUser($this->getSessionValue('user')); 1825*d10b5556SXylle if ($this->hasSessionValue('attributes')) { 1826*d10b5556SXylle $this->setAttributes($this->getSessionValue('attributes')); 1827*d10b5556SXylle } 1828*d10b5556SXylle phpCAS::trace('user = `'.$this->getSessionValue('user').'\''); 1829*d10b5556SXylle 1830*d10b5556SXylle // Include the list of proxies 1831*d10b5556SXylle if ($this->hasSessionValue('proxies')) { 1832*d10b5556SXylle $this->_setProxies($this->getSessionValue('proxies')); 1833*d10b5556SXylle phpCAS::trace( 1834*d10b5556SXylle 'proxies = "' 1835*d10b5556SXylle .implode('", "', $this->getSessionValue('proxies')).'"' 1836*d10b5556SXylle ); 1837*d10b5556SXylle } 1838*d10b5556SXylle 1839*d10b5556SXylle $auth = true; 1840*d10b5556SXylle } else { 1841*d10b5556SXylle phpCAS::trace('no user found'); 1842*d10b5556SXylle } 1843*d10b5556SXylle } 1844*d10b5556SXylle 1845*d10b5556SXylle phpCAS::traceEnd($auth); 1846*d10b5556SXylle return $auth; 1847*d10b5556SXylle } 1848*d10b5556SXylle 1849*d10b5556SXylle /** 1850*d10b5556SXylle * This method is used to redirect the client to the CAS server. 1851*d10b5556SXylle * It is used by CAS_Client::forceAuthentication() and 1852*d10b5556SXylle * CAS_Client::checkAuthentication(). 1853*d10b5556SXylle * 1854*d10b5556SXylle * @param bool $gateway true to check authentication, false to force it 1855*d10b5556SXylle * @param bool $renew true to force the authentication with the CAS server 1856*d10b5556SXylle * 1857*d10b5556SXylle * @return void 1858*d10b5556SXylle */ 1859*d10b5556SXylle public function redirectToCas($gateway=false,$renew=false) 1860*d10b5556SXylle { 1861*d10b5556SXylle phpCAS::traceBegin(); 1862*d10b5556SXylle $cas_url = $this->getServerLoginURL($gateway, $renew); 1863*d10b5556SXylle session_write_close(); 1864*d10b5556SXylle if (php_sapi_name() === 'cli') { 1865*d10b5556SXylle @header('Location: '.$cas_url); 1866*d10b5556SXylle } else { 1867*d10b5556SXylle header('Location: '.$cas_url); 1868*d10b5556SXylle } 1869*d10b5556SXylle phpCAS::trace("Redirect to : ".$cas_url); 1870*d10b5556SXylle $lang = $this->getLangObj(); 1871*d10b5556SXylle $this->printHTMLHeader($lang->getAuthenticationWanted()); 1872*d10b5556SXylle $this->printf('<p>'. $lang->getShouldHaveBeenRedirected(). '</p>', $cas_url); 1873*d10b5556SXylle $this->printHTMLFooter(); 1874*d10b5556SXylle phpCAS::traceExit(); 1875*d10b5556SXylle throw new CAS_GracefullTerminationException(); 1876*d10b5556SXylle } 1877*d10b5556SXylle 1878*d10b5556SXylle 1879*d10b5556SXylle /** 1880*d10b5556SXylle * This method is used to logout from CAS. 1881*d10b5556SXylle * 1882*d10b5556SXylle * @param array $params an array that contains the optional url and service 1883*d10b5556SXylle * parameters that will be passed to the CAS server 1884*d10b5556SXylle * 1885*d10b5556SXylle * @return void 1886*d10b5556SXylle */ 1887*d10b5556SXylle public function logout($params) 1888*d10b5556SXylle { 1889*d10b5556SXylle phpCAS::traceBegin(); 1890*d10b5556SXylle $cas_url = $this->getServerLogoutURL(); 1891*d10b5556SXylle $paramSeparator = '?'; 1892*d10b5556SXylle if (isset($params['url'])) { 1893*d10b5556SXylle $cas_url = $cas_url . $paramSeparator . "url=" 1894*d10b5556SXylle . urlencode($params['url']); 1895*d10b5556SXylle $paramSeparator = '&'; 1896*d10b5556SXylle } 1897*d10b5556SXylle if (isset($params['service'])) { 1898*d10b5556SXylle $cas_url = $cas_url . $paramSeparator . "service=" 1899*d10b5556SXylle . urlencode($params['service']); 1900*d10b5556SXylle } 1901*d10b5556SXylle header('Location: '.$cas_url); 1902*d10b5556SXylle phpCAS::trace("Prepare redirect to : ".$cas_url); 1903*d10b5556SXylle 1904*d10b5556SXylle phpCAS::trace("Destroying session : ".session_id()); 1905*d10b5556SXylle session_unset(); 1906*d10b5556SXylle session_destroy(); 1907*d10b5556SXylle if (session_status() === PHP_SESSION_NONE) { 1908*d10b5556SXylle phpCAS::trace("Session terminated"); 1909*d10b5556SXylle } else { 1910*d10b5556SXylle phpCAS::error("Session was not terminated"); 1911*d10b5556SXylle phpCAS::trace("Session was not terminated"); 1912*d10b5556SXylle } 1913*d10b5556SXylle $lang = $this->getLangObj(); 1914*d10b5556SXylle $this->printHTMLHeader($lang->getLogout()); 1915*d10b5556SXylle $this->printf('<p>'.$lang->getShouldHaveBeenRedirected(). '</p>', $cas_url); 1916*d10b5556SXylle $this->printHTMLFooter(); 1917*d10b5556SXylle phpCAS::traceExit(); 1918*d10b5556SXylle throw new CAS_GracefullTerminationException(); 1919*d10b5556SXylle } 1920*d10b5556SXylle 1921*d10b5556SXylle /** 1922*d10b5556SXylle * Check of the current request is a logout request 1923*d10b5556SXylle * 1924*d10b5556SXylle * @return bool is logout request. 1925*d10b5556SXylle */ 1926*d10b5556SXylle private function _isLogoutRequest() 1927*d10b5556SXylle { 1928*d10b5556SXylle return !empty($_POST['logoutRequest']); 1929*d10b5556SXylle } 1930*d10b5556SXylle 1931*d10b5556SXylle /** 1932*d10b5556SXylle * This method handles logout requests. 1933*d10b5556SXylle * 1934*d10b5556SXylle * @param bool $check_client true to check the client bofore handling 1935*d10b5556SXylle * the request, false not to perform any access control. True by default. 1936*d10b5556SXylle * @param array $allowed_clients an array of host names allowed to send 1937*d10b5556SXylle * logout requests. 1938*d10b5556SXylle * 1939*d10b5556SXylle * @return void 1940*d10b5556SXylle */ 1941*d10b5556SXylle public function handleLogoutRequests($check_client=true, $allowed_clients=array()) 1942*d10b5556SXylle { 1943*d10b5556SXylle phpCAS::traceBegin(); 1944*d10b5556SXylle if (!$this->_isLogoutRequest()) { 1945*d10b5556SXylle phpCAS::trace("Not a logout request"); 1946*d10b5556SXylle phpCAS::traceEnd(); 1947*d10b5556SXylle return; 1948*d10b5556SXylle } 1949*d10b5556SXylle if (!$this->getChangeSessionID() 1950*d10b5556SXylle && is_null($this->_signoutCallbackFunction) 1951*d10b5556SXylle ) { 1952*d10b5556SXylle phpCAS::trace( 1953*d10b5556SXylle "phpCAS can't handle logout requests if it is not allowed to change session_id." 1954*d10b5556SXylle ); 1955*d10b5556SXylle } 1956*d10b5556SXylle phpCAS::trace("Logout requested"); 1957*d10b5556SXylle $decoded_logout_rq = urldecode($_POST['logoutRequest']); 1958*d10b5556SXylle phpCAS::trace("SAML REQUEST: ".$decoded_logout_rq); 1959*d10b5556SXylle $allowed = false; 1960*d10b5556SXylle if ($check_client) { 1961*d10b5556SXylle if ($allowed_clients === array()) { 1962*d10b5556SXylle $allowed_clients = array( $this->_getServerHostname() ); 1963*d10b5556SXylle } 1964*d10b5556SXylle $client_ip = $_SERVER['REMOTE_ADDR']; 1965*d10b5556SXylle $client = gethostbyaddr($client_ip); 1966*d10b5556SXylle phpCAS::trace("Client: ".$client."/".$client_ip); 1967*d10b5556SXylle foreach ($allowed_clients as $allowed_client) { 1968*d10b5556SXylle if (($client == $allowed_client) 1969*d10b5556SXylle || ($client_ip == $allowed_client) 1970*d10b5556SXylle ) { 1971*d10b5556SXylle phpCAS::trace( 1972*d10b5556SXylle "Allowed client '".$allowed_client 1973*d10b5556SXylle ."' matches, logout request is allowed" 1974*d10b5556SXylle ); 1975*d10b5556SXylle $allowed = true; 1976*d10b5556SXylle break; 1977*d10b5556SXylle } else { 1978*d10b5556SXylle phpCAS::trace( 1979*d10b5556SXylle "Allowed client '".$allowed_client."' does not match" 1980*d10b5556SXylle ); 1981*d10b5556SXylle } 1982*d10b5556SXylle } 1983*d10b5556SXylle } else { 1984*d10b5556SXylle phpCAS::trace("No access control set"); 1985*d10b5556SXylle $allowed = true; 1986*d10b5556SXylle } 1987*d10b5556SXylle // If Logout command is permitted proceed with the logout 1988*d10b5556SXylle if ($allowed) { 1989*d10b5556SXylle phpCAS::trace("Logout command allowed"); 1990*d10b5556SXylle // Rebroadcast the logout request 1991*d10b5556SXylle if ($this->_rebroadcast && !isset($_POST['rebroadcast'])) { 1992*d10b5556SXylle $this->_rebroadcast(self::LOGOUT); 1993*d10b5556SXylle } 1994*d10b5556SXylle // Extract the ticket from the SAML Request 1995*d10b5556SXylle preg_match( 1996*d10b5556SXylle "|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|", 1997*d10b5556SXylle $decoded_logout_rq, $tick, PREG_OFFSET_CAPTURE, 3 1998*d10b5556SXylle ); 1999*d10b5556SXylle $wrappedSamlSessionIndex = preg_replace( 2000*d10b5556SXylle '|<samlp:SessionIndex>|', '', $tick[0][0] 2001*d10b5556SXylle ); 2002*d10b5556SXylle $ticket2logout = preg_replace( 2003*d10b5556SXylle '|</samlp:SessionIndex>|', '', $wrappedSamlSessionIndex 2004*d10b5556SXylle ); 2005*d10b5556SXylle phpCAS::trace("Ticket to logout: ".$ticket2logout); 2006*d10b5556SXylle 2007*d10b5556SXylle // call the post-authenticate callback if registered. 2008*d10b5556SXylle if ($this->_signoutCallbackFunction) { 2009*d10b5556SXylle $args = $this->_signoutCallbackArgs; 2010*d10b5556SXylle array_unshift($args, $ticket2logout); 2011*d10b5556SXylle call_user_func_array($this->_signoutCallbackFunction, $args); 2012*d10b5556SXylle } 2013*d10b5556SXylle 2014*d10b5556SXylle // If phpCAS is managing the session_id, destroy session thanks to 2015*d10b5556SXylle // session_id. 2016*d10b5556SXylle if ($this->getChangeSessionID()) { 2017*d10b5556SXylle $session_id = $this->_sessionIdForTicket($ticket2logout); 2018*d10b5556SXylle phpCAS::trace("Session id: ".$session_id); 2019*d10b5556SXylle 2020*d10b5556SXylle // destroy a possible application session created before phpcas 2021*d10b5556SXylle if (session_id() !== "") { 2022*d10b5556SXylle session_unset(); 2023*d10b5556SXylle session_destroy(); 2024*d10b5556SXylle } 2025*d10b5556SXylle // fix session ID 2026*d10b5556SXylle session_id($session_id); 2027*d10b5556SXylle $_COOKIE[session_name()]=$session_id; 2028*d10b5556SXylle $_GET[session_name()]=$session_id; 2029*d10b5556SXylle 2030*d10b5556SXylle // Overwrite session 2031*d10b5556SXylle session_start(); 2032*d10b5556SXylle session_unset(); 2033*d10b5556SXylle session_destroy(); 2034*d10b5556SXylle phpCAS::trace("Session ". $session_id . " destroyed"); 2035*d10b5556SXylle } 2036*d10b5556SXylle } else { 2037*d10b5556SXylle phpCAS::error("Unauthorized logout request from client '".$client."'"); 2038*d10b5556SXylle phpCAS::trace("Unauthorized logout request from client '".$client."'"); 2039*d10b5556SXylle } 2040*d10b5556SXylle flush(); 2041*d10b5556SXylle phpCAS::traceExit(); 2042*d10b5556SXylle throw new CAS_GracefullTerminationException(); 2043*d10b5556SXylle 2044*d10b5556SXylle } 2045*d10b5556SXylle 2046*d10b5556SXylle /** @} */ 2047*d10b5556SXylle 2048*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2049*d10b5556SXylle // XX XX 2050*d10b5556SXylle // XX BASIC CLIENT FEATURES (CAS 1.0) XX 2051*d10b5556SXylle // XX XX 2052*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2053*d10b5556SXylle 2054*d10b5556SXylle // ######################################################################## 2055*d10b5556SXylle // ST 2056*d10b5556SXylle // ######################################################################## 2057*d10b5556SXylle /** 2058*d10b5556SXylle * @addtogroup internalBasic 2059*d10b5556SXylle * @{ 2060*d10b5556SXylle */ 2061*d10b5556SXylle 2062*d10b5556SXylle /** 2063*d10b5556SXylle * The Ticket provided in the URL of the request if present 2064*d10b5556SXylle * (empty otherwise). Written by CAS_Client::CAS_Client(), read by 2065*d10b5556SXylle * CAS_Client::getTicket() and CAS_Client::_hasPGT(). 2066*d10b5556SXylle * 2067*d10b5556SXylle * @hideinitializer 2068*d10b5556SXylle */ 2069*d10b5556SXylle private $_ticket = ''; 2070*d10b5556SXylle 2071*d10b5556SXylle /** 2072*d10b5556SXylle * This method returns the Service Ticket provided in the URL of the request. 2073*d10b5556SXylle * 2074*d10b5556SXylle * @return string service ticket. 2075*d10b5556SXylle */ 2076*d10b5556SXylle public function getTicket() 2077*d10b5556SXylle { 2078*d10b5556SXylle return $this->_ticket; 2079*d10b5556SXylle } 2080*d10b5556SXylle 2081*d10b5556SXylle /** 2082*d10b5556SXylle * This method stores the Service Ticket. 2083*d10b5556SXylle * 2084*d10b5556SXylle * @param string $st The Service Ticket. 2085*d10b5556SXylle * 2086*d10b5556SXylle * @return void 2087*d10b5556SXylle */ 2088*d10b5556SXylle public function setTicket($st) 2089*d10b5556SXylle { 2090*d10b5556SXylle $this->_ticket = $st; 2091*d10b5556SXylle } 2092*d10b5556SXylle 2093*d10b5556SXylle /** 2094*d10b5556SXylle * This method tells if a Service Ticket was stored. 2095*d10b5556SXylle * 2096*d10b5556SXylle * @return bool if a Service Ticket has been stored. 2097*d10b5556SXylle */ 2098*d10b5556SXylle public function hasTicket() 2099*d10b5556SXylle { 2100*d10b5556SXylle return !empty($this->_ticket); 2101*d10b5556SXylle } 2102*d10b5556SXylle 2103*d10b5556SXylle /** @} */ 2104*d10b5556SXylle 2105*d10b5556SXylle // ######################################################################## 2106*d10b5556SXylle // ST VALIDATION 2107*d10b5556SXylle // ######################################################################## 2108*d10b5556SXylle /** 2109*d10b5556SXylle * @addtogroup internalBasic 2110*d10b5556SXylle * @{ 2111*d10b5556SXylle */ 2112*d10b5556SXylle 2113*d10b5556SXylle /** 2114*d10b5556SXylle * @var string the certificate of the CAS server CA. 2115*d10b5556SXylle * 2116*d10b5556SXylle * @hideinitializer 2117*d10b5556SXylle */ 2118*d10b5556SXylle private $_cas_server_ca_cert = null; 2119*d10b5556SXylle 2120*d10b5556SXylle 2121*d10b5556SXylle /** 2122*d10b5556SXylle 2123*d10b5556SXylle * validate CN of the CAS server certificate 2124*d10b5556SXylle 2125*d10b5556SXylle * 2126*d10b5556SXylle 2127*d10b5556SXylle * @hideinitializer 2128*d10b5556SXylle 2129*d10b5556SXylle */ 2130*d10b5556SXylle 2131*d10b5556SXylle private $_cas_server_cn_validate = true; 2132*d10b5556SXylle 2133*d10b5556SXylle /** 2134*d10b5556SXylle * Set to true not to validate the CAS server. 2135*d10b5556SXylle * 2136*d10b5556SXylle * @hideinitializer 2137*d10b5556SXylle */ 2138*d10b5556SXylle private $_no_cas_server_validation = false; 2139*d10b5556SXylle 2140*d10b5556SXylle 2141*d10b5556SXylle /** 2142*d10b5556SXylle * Set the CA certificate of the CAS server. 2143*d10b5556SXylle * 2144*d10b5556SXylle * @param string $cert the PEM certificate file name of the CA that emited 2145*d10b5556SXylle * the cert of the server 2146*d10b5556SXylle * @param bool $validate_cn valiate CN of the CAS server certificate 2147*d10b5556SXylle * 2148*d10b5556SXylle * @return void 2149*d10b5556SXylle */ 2150*d10b5556SXylle public function setCasServerCACert($cert, $validate_cn) 2151*d10b5556SXylle { 2152*d10b5556SXylle // Argument validation 2153*d10b5556SXylle if (gettype($cert) != 'string') { 2154*d10b5556SXylle throw new CAS_TypeMismatchException($cert, '$cert', 'string'); 2155*d10b5556SXylle } 2156*d10b5556SXylle if (gettype($validate_cn) != 'boolean') { 2157*d10b5556SXylle throw new CAS_TypeMismatchException($validate_cn, '$validate_cn', 'boolean'); 2158*d10b5556SXylle } 2159*d10b5556SXylle if (!file_exists($cert)) { 2160*d10b5556SXylle throw new CAS_InvalidArgumentException("Certificate file does not exist " . $this->_requestImplementation); 2161*d10b5556SXylle } 2162*d10b5556SXylle $this->_cas_server_ca_cert = $cert; 2163*d10b5556SXylle $this->_cas_server_cn_validate = $validate_cn; 2164*d10b5556SXylle } 2165*d10b5556SXylle 2166*d10b5556SXylle /** 2167*d10b5556SXylle * Set no SSL validation for the CAS server. 2168*d10b5556SXylle * 2169*d10b5556SXylle * @return void 2170*d10b5556SXylle */ 2171*d10b5556SXylle public function setNoCasServerValidation() 2172*d10b5556SXylle { 2173*d10b5556SXylle $this->_no_cas_server_validation = true; 2174*d10b5556SXylle } 2175*d10b5556SXylle 2176*d10b5556SXylle /** 2177*d10b5556SXylle * This method is used to validate a CAS 1,0 ticket; halt on failure, and 2178*d10b5556SXylle * sets $validate_url, $text_reponse and $tree_response on success. 2179*d10b5556SXylle * 2180*d10b5556SXylle * @param string &$validate_url reference to the the URL of the request to 2181*d10b5556SXylle * the CAS server. 2182*d10b5556SXylle * @param string &$text_response reference to the response of the CAS 2183*d10b5556SXylle * server, as is (XML text). 2184*d10b5556SXylle * @param string &$tree_response reference to the response of the CAS 2185*d10b5556SXylle * server, as a DOM XML tree. 2186*d10b5556SXylle * @param bool $renew true to force the authentication with the CAS server 2187*d10b5556SXylle * 2188*d10b5556SXylle * @return bool true when successfull and issue a CAS_AuthenticationException 2189*d10b5556SXylle * and false on an error 2190*d10b5556SXylle * @throws CAS_AuthenticationException 2191*d10b5556SXylle */ 2192*d10b5556SXylle public function validateCAS10(&$validate_url,&$text_response,&$tree_response,$renew=false) 2193*d10b5556SXylle { 2194*d10b5556SXylle phpCAS::traceBegin(); 2195*d10b5556SXylle // build the URL to validate the ticket 2196*d10b5556SXylle $validate_url = $this->getServerServiceValidateURL() 2197*d10b5556SXylle .'&ticket='.urlencode($this->getTicket()); 2198*d10b5556SXylle 2199*d10b5556SXylle if ( $renew ) { 2200*d10b5556SXylle // pass the renew 2201*d10b5556SXylle $validate_url .= '&renew=true'; 2202*d10b5556SXylle } 2203*d10b5556SXylle 2204*d10b5556SXylle $headers = ''; 2205*d10b5556SXylle $err_msg = ''; 2206*d10b5556SXylle // open and read the URL 2207*d10b5556SXylle if ( !$this->_readURL($validate_url, $headers, $text_response, $err_msg) ) { 2208*d10b5556SXylle phpCAS::trace( 2209*d10b5556SXylle 'could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')' 2210*d10b5556SXylle ); 2211*d10b5556SXylle throw new CAS_AuthenticationException( 2212*d10b5556SXylle $this, 'CAS 1.0 ticket not validated', $validate_url, 2213*d10b5556SXylle true/*$no_response*/ 2214*d10b5556SXylle ); 2215*d10b5556SXylle } 2216*d10b5556SXylle 2217*d10b5556SXylle if (preg_match('/^no\n/', $text_response)) { 2218*d10b5556SXylle phpCAS::trace('Ticket has not been validated'); 2219*d10b5556SXylle throw new CAS_AuthenticationException( 2220*d10b5556SXylle $this, 'ST not validated', $validate_url, false/*$no_response*/, 2221*d10b5556SXylle false/*$bad_response*/, $text_response 2222*d10b5556SXylle ); 2223*d10b5556SXylle } else if (!preg_match('/^yes\n/', $text_response)) { 2224*d10b5556SXylle phpCAS::trace('ill-formed response'); 2225*d10b5556SXylle throw new CAS_AuthenticationException( 2226*d10b5556SXylle $this, 'Ticket not validated', $validate_url, 2227*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, $text_response 2228*d10b5556SXylle ); 2229*d10b5556SXylle } 2230*d10b5556SXylle // ticket has been validated, extract the user name 2231*d10b5556SXylle $arr = preg_split('/\n/', $text_response); 2232*d10b5556SXylle $this->_setUser(trim($arr[1])); 2233*d10b5556SXylle 2234*d10b5556SXylle $this->_renameSession($this->getTicket()); 2235*d10b5556SXylle 2236*d10b5556SXylle // at this step, ticket has been validated and $this->_user has been set, 2237*d10b5556SXylle phpCAS::traceEnd(true); 2238*d10b5556SXylle return true; 2239*d10b5556SXylle } 2240*d10b5556SXylle 2241*d10b5556SXylle /** @} */ 2242*d10b5556SXylle 2243*d10b5556SXylle 2244*d10b5556SXylle // ######################################################################## 2245*d10b5556SXylle // SAML VALIDATION 2246*d10b5556SXylle // ######################################################################## 2247*d10b5556SXylle /** 2248*d10b5556SXylle * @addtogroup internalSAML 2249*d10b5556SXylle * @{ 2250*d10b5556SXylle */ 2251*d10b5556SXylle 2252*d10b5556SXylle /** 2253*d10b5556SXylle * This method is used to validate a SAML TICKET; halt on failure, and sets 2254*d10b5556SXylle * $validate_url, $text_reponse and $tree_response on success. These 2255*d10b5556SXylle * parameters are used later by CAS_Client::_validatePGT() for CAS proxies. 2256*d10b5556SXylle * 2257*d10b5556SXylle * @param string &$validate_url reference to the the URL of the request to 2258*d10b5556SXylle * the CAS server. 2259*d10b5556SXylle * @param string &$text_response reference to the response of the CAS 2260*d10b5556SXylle * server, as is (XML text). 2261*d10b5556SXylle * @param string &$tree_response reference to the response of the CAS 2262*d10b5556SXylle * server, as a DOM XML tree. 2263*d10b5556SXylle * @param bool $renew true to force the authentication with the CAS server 2264*d10b5556SXylle * 2265*d10b5556SXylle * @return bool true when successfull and issue a CAS_AuthenticationException 2266*d10b5556SXylle * and false on an error 2267*d10b5556SXylle * 2268*d10b5556SXylle * @throws CAS_AuthenticationException 2269*d10b5556SXylle */ 2270*d10b5556SXylle public function validateSA(&$validate_url,&$text_response,&$tree_response,$renew=false) 2271*d10b5556SXylle { 2272*d10b5556SXylle phpCAS::traceBegin(); 2273*d10b5556SXylle $result = false; 2274*d10b5556SXylle // build the URL to validate the ticket 2275*d10b5556SXylle $validate_url = $this->getServerSamlValidateURL(); 2276*d10b5556SXylle 2277*d10b5556SXylle if ( $renew ) { 2278*d10b5556SXylle // pass the renew 2279*d10b5556SXylle $validate_url .= '&renew=true'; 2280*d10b5556SXylle } 2281*d10b5556SXylle 2282*d10b5556SXylle $headers = ''; 2283*d10b5556SXylle $err_msg = ''; 2284*d10b5556SXylle // open and read the URL 2285*d10b5556SXylle if ( !$this->_readURL($validate_url, $headers, $text_response, $err_msg) ) { 2286*d10b5556SXylle phpCAS::trace( 2287*d10b5556SXylle 'could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')' 2288*d10b5556SXylle ); 2289*d10b5556SXylle throw new CAS_AuthenticationException( 2290*d10b5556SXylle $this, 'SA not validated', $validate_url, true/*$no_response*/ 2291*d10b5556SXylle ); 2292*d10b5556SXylle } 2293*d10b5556SXylle 2294*d10b5556SXylle phpCAS::trace('server version: '.$this->getServerVersion()); 2295*d10b5556SXylle 2296*d10b5556SXylle // analyze the result depending on the version 2297*d10b5556SXylle switch ($this->getServerVersion()) { 2298*d10b5556SXylle case SAML_VERSION_1_1: 2299*d10b5556SXylle // create new DOMDocument Object 2300*d10b5556SXylle $dom = new DOMDocument(); 2301*d10b5556SXylle // Fix possible whitspace problems 2302*d10b5556SXylle $dom->preserveWhiteSpace = false; 2303*d10b5556SXylle // read the response of the CAS server into a DOM object 2304*d10b5556SXylle if (!($dom->loadXML($text_response))) { 2305*d10b5556SXylle phpCAS::trace('dom->loadXML() failed'); 2306*d10b5556SXylle throw new CAS_AuthenticationException( 2307*d10b5556SXylle $this, 'SA not validated', $validate_url, 2308*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, 2309*d10b5556SXylle $text_response 2310*d10b5556SXylle ); 2311*d10b5556SXylle } 2312*d10b5556SXylle // read the root node of the XML tree 2313*d10b5556SXylle if (!($tree_response = $dom->documentElement)) { 2314*d10b5556SXylle phpCAS::trace('documentElement() failed'); 2315*d10b5556SXylle throw new CAS_AuthenticationException( 2316*d10b5556SXylle $this, 'SA not validated', $validate_url, 2317*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, 2318*d10b5556SXylle $text_response 2319*d10b5556SXylle ); 2320*d10b5556SXylle } else if ( $tree_response->localName != 'Envelope' ) { 2321*d10b5556SXylle // insure that tag name is 'Envelope' 2322*d10b5556SXylle phpCAS::trace( 2323*d10b5556SXylle 'bad XML root node (should be `Envelope\' instead of `' 2324*d10b5556SXylle .$tree_response->localName.'\'' 2325*d10b5556SXylle ); 2326*d10b5556SXylle throw new CAS_AuthenticationException( 2327*d10b5556SXylle $this, 'SA not validated', $validate_url, 2328*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, 2329*d10b5556SXylle $text_response 2330*d10b5556SXylle ); 2331*d10b5556SXylle } else if ($tree_response->getElementsByTagName("NameIdentifier")->length != 0) { 2332*d10b5556SXylle // check for the NameIdentifier tag in the SAML response 2333*d10b5556SXylle $success_elements = $tree_response->getElementsByTagName("NameIdentifier"); 2334*d10b5556SXylle phpCAS::trace('NameIdentifier found'); 2335*d10b5556SXylle $user = trim($success_elements->item(0)->nodeValue); 2336*d10b5556SXylle phpCAS::trace('user = `'.$user.'`'); 2337*d10b5556SXylle $this->_setUser($user); 2338*d10b5556SXylle $this->_setSessionAttributes($text_response); 2339*d10b5556SXylle $result = true; 2340*d10b5556SXylle } else { 2341*d10b5556SXylle phpCAS::trace('no <NameIdentifier> tag found in SAML payload'); 2342*d10b5556SXylle throw new CAS_AuthenticationException( 2343*d10b5556SXylle $this, 'SA not validated', $validate_url, 2344*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, 2345*d10b5556SXylle $text_response 2346*d10b5556SXylle ); 2347*d10b5556SXylle } 2348*d10b5556SXylle } 2349*d10b5556SXylle if ($result) { 2350*d10b5556SXylle $this->_renameSession($this->getTicket()); 2351*d10b5556SXylle } 2352*d10b5556SXylle // at this step, ST has been validated and $this->_user has been set, 2353*d10b5556SXylle phpCAS::traceEnd($result); 2354*d10b5556SXylle return $result; 2355*d10b5556SXylle } 2356*d10b5556SXylle 2357*d10b5556SXylle /** 2358*d10b5556SXylle * This method will parse the DOM and pull out the attributes from the SAML 2359*d10b5556SXylle * payload and put them into an array, then put the array into the session. 2360*d10b5556SXylle * 2361*d10b5556SXylle * @param string $text_response the SAML payload. 2362*d10b5556SXylle * 2363*d10b5556SXylle * @return bool true when successfull and false if no attributes a found 2364*d10b5556SXylle */ 2365*d10b5556SXylle private function _setSessionAttributes($text_response) 2366*d10b5556SXylle { 2367*d10b5556SXylle phpCAS::traceBegin(); 2368*d10b5556SXylle 2369*d10b5556SXylle $result = false; 2370*d10b5556SXylle 2371*d10b5556SXylle $attr_array = array(); 2372*d10b5556SXylle 2373*d10b5556SXylle // create new DOMDocument Object 2374*d10b5556SXylle $dom = new DOMDocument(); 2375*d10b5556SXylle // Fix possible whitspace problems 2376*d10b5556SXylle $dom->preserveWhiteSpace = false; 2377*d10b5556SXylle if (($dom->loadXML($text_response))) { 2378*d10b5556SXylle $xPath = new DOMXPath($dom); 2379*d10b5556SXylle $xPath->registerNamespace('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol'); 2380*d10b5556SXylle $xPath->registerNamespace('saml', 'urn:oasis:names:tc:SAML:1.0:assertion'); 2381*d10b5556SXylle $nodelist = $xPath->query("//saml:Attribute"); 2382*d10b5556SXylle 2383*d10b5556SXylle if ($nodelist) { 2384*d10b5556SXylle foreach ($nodelist as $node) { 2385*d10b5556SXylle $xres = $xPath->query("saml:AttributeValue", $node); 2386*d10b5556SXylle $name = $node->getAttribute("AttributeName"); 2387*d10b5556SXylle $value_array = array(); 2388*d10b5556SXylle foreach ($xres as $node2) { 2389*d10b5556SXylle $value_array[] = $node2->nodeValue; 2390*d10b5556SXylle } 2391*d10b5556SXylle $attr_array[$name] = $value_array; 2392*d10b5556SXylle } 2393*d10b5556SXylle // UGent addition... 2394*d10b5556SXylle foreach ($attr_array as $attr_key => $attr_value) { 2395*d10b5556SXylle if (count($attr_value) > 1) { 2396*d10b5556SXylle $this->_attributes[$attr_key] = $attr_value; 2397*d10b5556SXylle phpCAS::trace("* " . $attr_key . "=" . print_r($attr_value, true)); 2398*d10b5556SXylle } else { 2399*d10b5556SXylle $this->_attributes[$attr_key] = $attr_value[0]; 2400*d10b5556SXylle phpCAS::trace("* " . $attr_key . "=" . $attr_value[0]); 2401*d10b5556SXylle } 2402*d10b5556SXylle } 2403*d10b5556SXylle $result = true; 2404*d10b5556SXylle } else { 2405*d10b5556SXylle phpCAS::trace("SAML Attributes are empty"); 2406*d10b5556SXylle $result = false; 2407*d10b5556SXylle } 2408*d10b5556SXylle } 2409*d10b5556SXylle phpCAS::traceEnd($result); 2410*d10b5556SXylle return $result; 2411*d10b5556SXylle } 2412*d10b5556SXylle 2413*d10b5556SXylle /** @} */ 2414*d10b5556SXylle 2415*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2416*d10b5556SXylle // XX XX 2417*d10b5556SXylle // XX PROXY FEATURES (CAS 2.0) XX 2418*d10b5556SXylle // XX XX 2419*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2420*d10b5556SXylle 2421*d10b5556SXylle // ######################################################################## 2422*d10b5556SXylle // PROXYING 2423*d10b5556SXylle // ######################################################################## 2424*d10b5556SXylle /** 2425*d10b5556SXylle * @addtogroup internalProxy 2426*d10b5556SXylle * @{ 2427*d10b5556SXylle */ 2428*d10b5556SXylle 2429*d10b5556SXylle /** 2430*d10b5556SXylle * @var bool is the client a proxy 2431*d10b5556SXylle * A boolean telling if the client is a CAS proxy or not. Written by 2432*d10b5556SXylle * CAS_Client::CAS_Client(), read by CAS_Client::isProxy(). 2433*d10b5556SXylle */ 2434*d10b5556SXylle private $_proxy; 2435*d10b5556SXylle 2436*d10b5556SXylle /** 2437*d10b5556SXylle * @var CAS_CookieJar Handler for managing service cookies. 2438*d10b5556SXylle */ 2439*d10b5556SXylle private $_serviceCookieJar; 2440*d10b5556SXylle 2441*d10b5556SXylle /** 2442*d10b5556SXylle * Tells if a CAS client is a CAS proxy or not 2443*d10b5556SXylle * 2444*d10b5556SXylle * @return bool true when the CAS client is a CAS proxy, false otherwise 2445*d10b5556SXylle */ 2446*d10b5556SXylle public function isProxy() 2447*d10b5556SXylle { 2448*d10b5556SXylle return $this->_proxy; 2449*d10b5556SXylle } 2450*d10b5556SXylle 2451*d10b5556SXylle 2452*d10b5556SXylle /** @} */ 2453*d10b5556SXylle // ######################################################################## 2454*d10b5556SXylle // PGT 2455*d10b5556SXylle // ######################################################################## 2456*d10b5556SXylle /** 2457*d10b5556SXylle * @addtogroup internalProxy 2458*d10b5556SXylle * @{ 2459*d10b5556SXylle */ 2460*d10b5556SXylle 2461*d10b5556SXylle /** 2462*d10b5556SXylle * the Proxy Grnting Ticket given by the CAS server (empty otherwise). 2463*d10b5556SXylle * Written by CAS_Client::_setPGT(), read by CAS_Client::_getPGT() and 2464*d10b5556SXylle * CAS_Client::_hasPGT(). 2465*d10b5556SXylle * 2466*d10b5556SXylle * @hideinitializer 2467*d10b5556SXylle */ 2468*d10b5556SXylle private $_pgt = ''; 2469*d10b5556SXylle 2470*d10b5556SXylle /** 2471*d10b5556SXylle * This method returns the Proxy Granting Ticket given by the CAS server. 2472*d10b5556SXylle * 2473*d10b5556SXylle * @return string the Proxy Granting Ticket. 2474*d10b5556SXylle */ 2475*d10b5556SXylle private function _getPGT() 2476*d10b5556SXylle { 2477*d10b5556SXylle return $this->_pgt; 2478*d10b5556SXylle } 2479*d10b5556SXylle 2480*d10b5556SXylle /** 2481*d10b5556SXylle * This method stores the Proxy Granting Ticket. 2482*d10b5556SXylle * 2483*d10b5556SXylle * @param string $pgt The Proxy Granting Ticket. 2484*d10b5556SXylle * 2485*d10b5556SXylle * @return void 2486*d10b5556SXylle */ 2487*d10b5556SXylle private function _setPGT($pgt) 2488*d10b5556SXylle { 2489*d10b5556SXylle $this->_pgt = $pgt; 2490*d10b5556SXylle } 2491*d10b5556SXylle 2492*d10b5556SXylle /** 2493*d10b5556SXylle * This method tells if a Proxy Granting Ticket was stored. 2494*d10b5556SXylle * 2495*d10b5556SXylle * @return bool true if a Proxy Granting Ticket has been stored. 2496*d10b5556SXylle */ 2497*d10b5556SXylle private function _hasPGT() 2498*d10b5556SXylle { 2499*d10b5556SXylle return !empty($this->_pgt); 2500*d10b5556SXylle } 2501*d10b5556SXylle 2502*d10b5556SXylle /** @} */ 2503*d10b5556SXylle 2504*d10b5556SXylle // ######################################################################## 2505*d10b5556SXylle // CALLBACK MODE 2506*d10b5556SXylle // ######################################################################## 2507*d10b5556SXylle /** 2508*d10b5556SXylle * @addtogroup internalCallback 2509*d10b5556SXylle * @{ 2510*d10b5556SXylle */ 2511*d10b5556SXylle /** 2512*d10b5556SXylle * each PHP script using phpCAS in proxy mode is its own callback to get the 2513*d10b5556SXylle * PGT back from the CAS server. callback_mode is detected by the constructor 2514*d10b5556SXylle * thanks to the GET parameters. 2515*d10b5556SXylle */ 2516*d10b5556SXylle 2517*d10b5556SXylle /** 2518*d10b5556SXylle * @var bool a boolean to know if the CAS client is running in callback mode. Written by 2519*d10b5556SXylle * CAS_Client::setCallBackMode(), read by CAS_Client::_isCallbackMode(). 2520*d10b5556SXylle * 2521*d10b5556SXylle * @hideinitializer 2522*d10b5556SXylle */ 2523*d10b5556SXylle private $_callback_mode = false; 2524*d10b5556SXylle 2525*d10b5556SXylle /** 2526*d10b5556SXylle * This method sets/unsets callback mode. 2527*d10b5556SXylle * 2528*d10b5556SXylle * @param bool $callback_mode true to set callback mode, false otherwise. 2529*d10b5556SXylle * 2530*d10b5556SXylle * @return void 2531*d10b5556SXylle */ 2532*d10b5556SXylle private function _setCallbackMode($callback_mode) 2533*d10b5556SXylle { 2534*d10b5556SXylle $this->_callback_mode = $callback_mode; 2535*d10b5556SXylle } 2536*d10b5556SXylle 2537*d10b5556SXylle /** 2538*d10b5556SXylle * This method returns true when the CAS client is running in callback mode, 2539*d10b5556SXylle * false otherwise. 2540*d10b5556SXylle * 2541*d10b5556SXylle * @return bool A boolean. 2542*d10b5556SXylle */ 2543*d10b5556SXylle private function _isCallbackMode() 2544*d10b5556SXylle { 2545*d10b5556SXylle return $this->_callback_mode; 2546*d10b5556SXylle } 2547*d10b5556SXylle 2548*d10b5556SXylle /** 2549*d10b5556SXylle * @var bool a boolean to know if the CAS client is using POST parameters when in callback mode. 2550*d10b5556SXylle * Written by CAS_Client::_setCallbackModeUsingPost(), read by CAS_Client::_isCallbackModeUsingPost(). 2551*d10b5556SXylle * 2552*d10b5556SXylle * @hideinitializer 2553*d10b5556SXylle */ 2554*d10b5556SXylle private $_callback_mode_using_post = false; 2555*d10b5556SXylle 2556*d10b5556SXylle /** 2557*d10b5556SXylle * This method sets/unsets usage of POST parameters in callback mode (default/false is GET parameters) 2558*d10b5556SXylle * 2559*d10b5556SXylle * @param bool $callback_mode_using_post true to use POST, false to use GET (default). 2560*d10b5556SXylle * 2561*d10b5556SXylle * @return void 2562*d10b5556SXylle */ 2563*d10b5556SXylle private function _setCallbackModeUsingPost($callback_mode_using_post) 2564*d10b5556SXylle { 2565*d10b5556SXylle $this->_callback_mode_using_post = $callback_mode_using_post; 2566*d10b5556SXylle } 2567*d10b5556SXylle 2568*d10b5556SXylle /** 2569*d10b5556SXylle * This method returns true when the callback mode is using POST, false otherwise. 2570*d10b5556SXylle * 2571*d10b5556SXylle * @return bool A boolean. 2572*d10b5556SXylle */ 2573*d10b5556SXylle private function _isCallbackModeUsingPost() 2574*d10b5556SXylle { 2575*d10b5556SXylle return $this->_callback_mode_using_post; 2576*d10b5556SXylle } 2577*d10b5556SXylle 2578*d10b5556SXylle /** 2579*d10b5556SXylle * the URL that should be used for the PGT callback (in fact the URL of the 2580*d10b5556SXylle * current request without any CGI parameter). Written and read by 2581*d10b5556SXylle * CAS_Client::_getCallbackURL(). 2582*d10b5556SXylle * 2583*d10b5556SXylle * @hideinitializer 2584*d10b5556SXylle */ 2585*d10b5556SXylle private $_callback_url = ''; 2586*d10b5556SXylle 2587*d10b5556SXylle /** 2588*d10b5556SXylle * This method returns the URL that should be used for the PGT callback (in 2589*d10b5556SXylle * fact the URL of the current request without any CGI parameter, except if 2590*d10b5556SXylle * phpCAS::setFixedCallbackURL() was used). 2591*d10b5556SXylle * 2592*d10b5556SXylle * @return string The callback URL 2593*d10b5556SXylle */ 2594*d10b5556SXylle private function _getCallbackURL() 2595*d10b5556SXylle { 2596*d10b5556SXylle // the URL is built when needed only 2597*d10b5556SXylle if ( empty($this->_callback_url) ) { 2598*d10b5556SXylle // remove the ticket if present in the URL 2599*d10b5556SXylle $final_uri = $this->getServiceBaseUrl()->get(); 2600*d10b5556SXylle $request_uri = $_SERVER['REQUEST_URI']; 2601*d10b5556SXylle $request_uri = preg_replace('/\?.*$/', '', $request_uri); 2602*d10b5556SXylle $final_uri .= $request_uri; 2603*d10b5556SXylle $this->_callback_url = $final_uri; 2604*d10b5556SXylle } 2605*d10b5556SXylle return $this->_callback_url; 2606*d10b5556SXylle } 2607*d10b5556SXylle 2608*d10b5556SXylle /** 2609*d10b5556SXylle * This method sets the callback url. 2610*d10b5556SXylle * 2611*d10b5556SXylle * @param string $url url to set callback 2612*d10b5556SXylle * 2613*d10b5556SXylle * @return string the callback url 2614*d10b5556SXylle */ 2615*d10b5556SXylle public function setCallbackURL($url) 2616*d10b5556SXylle { 2617*d10b5556SXylle // Sequence validation 2618*d10b5556SXylle $this->ensureIsProxy(); 2619*d10b5556SXylle // Argument Validation 2620*d10b5556SXylle if (gettype($url) != 'string') 2621*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 2622*d10b5556SXylle 2623*d10b5556SXylle return $this->_callback_url = $url; 2624*d10b5556SXylle } 2625*d10b5556SXylle 2626*d10b5556SXylle /** 2627*d10b5556SXylle * This method is called by CAS_Client::CAS_Client() when running in callback 2628*d10b5556SXylle * mode. It stores the PGT and its PGT Iou, prints its output and halts. 2629*d10b5556SXylle * 2630*d10b5556SXylle * @return void 2631*d10b5556SXylle */ 2632*d10b5556SXylle private function _callback() 2633*d10b5556SXylle { 2634*d10b5556SXylle phpCAS::traceBegin(); 2635*d10b5556SXylle if ($this->_isCallbackModeUsingPost()) { 2636*d10b5556SXylle $pgtId = $_POST['pgtId']; 2637*d10b5556SXylle $pgtIou = $_POST['pgtIou']; 2638*d10b5556SXylle } else { 2639*d10b5556SXylle $pgtId = $_GET['pgtId']; 2640*d10b5556SXylle $pgtIou = $_GET['pgtIou']; 2641*d10b5556SXylle } 2642*d10b5556SXylle if (preg_match('/^PGTIOU-[\.\-\w]+$/', $pgtIou)) { 2643*d10b5556SXylle if (preg_match('/^[PT]GT-[\.\-\w]+$/', $pgtId)) { 2644*d10b5556SXylle phpCAS::trace('Storing PGT `'.$pgtId.'\' (id=`'.$pgtIou.'\')'); 2645*d10b5556SXylle $this->_storePGT($pgtId, $pgtIou); 2646*d10b5556SXylle if ($this->isXmlResponse()) { 2647*d10b5556SXylle echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n"; 2648*d10b5556SXylle echo '<proxySuccess xmlns="http://www.yale.edu/tp/cas" />'; 2649*d10b5556SXylle phpCAS::traceExit("XML response sent"); 2650*d10b5556SXylle } else { 2651*d10b5556SXylle $this->printHTMLHeader('phpCAS callback'); 2652*d10b5556SXylle echo '<p>Storing PGT `'.$pgtId.'\' (id=`'.$pgtIou.'\').</p>'; 2653*d10b5556SXylle $this->printHTMLFooter(); 2654*d10b5556SXylle phpCAS::traceExit("HTML response sent"); 2655*d10b5556SXylle } 2656*d10b5556SXylle phpCAS::traceExit("Successfull Callback"); 2657*d10b5556SXylle } else { 2658*d10b5556SXylle phpCAS::error('PGT format invalid' . $pgtId); 2659*d10b5556SXylle phpCAS::traceExit('PGT format invalid' . $pgtId); 2660*d10b5556SXylle } 2661*d10b5556SXylle } else { 2662*d10b5556SXylle phpCAS::error('PGTiou format invalid' . $pgtIou); 2663*d10b5556SXylle phpCAS::traceExit('PGTiou format invalid' . $pgtIou); 2664*d10b5556SXylle } 2665*d10b5556SXylle 2666*d10b5556SXylle // Flush the buffer to prevent from sending anything other then a 200 2667*d10b5556SXylle // Success Status back to the CAS Server. The Exception would normally 2668*d10b5556SXylle // report as a 500 error. 2669*d10b5556SXylle flush(); 2670*d10b5556SXylle throw new CAS_GracefullTerminationException(); 2671*d10b5556SXylle } 2672*d10b5556SXylle 2673*d10b5556SXylle /** 2674*d10b5556SXylle * Check if application/xml or text/xml is pressent in HTTP_ACCEPT header values 2675*d10b5556SXylle * when return value is complex and contains attached q parameters. 2676*d10b5556SXylle * Example: HTTP_ACCEPT = text/html,application/xhtml+xml,application/xml;q=0.9 2677*d10b5556SXylle * @return bool 2678*d10b5556SXylle */ 2679*d10b5556SXylle private function isXmlResponse() 2680*d10b5556SXylle { 2681*d10b5556SXylle if (!array_key_exists('HTTP_ACCEPT', $_SERVER)) { 2682*d10b5556SXylle return false; 2683*d10b5556SXylle } 2684*d10b5556SXylle if (strpos($_SERVER['HTTP_ACCEPT'], 'application/xml') === false && strpos($_SERVER['HTTP_ACCEPT'], 'text/xml') === false) { 2685*d10b5556SXylle return false; 2686*d10b5556SXylle } 2687*d10b5556SXylle 2688*d10b5556SXylle return true; 2689*d10b5556SXylle } 2690*d10b5556SXylle 2691*d10b5556SXylle /** @} */ 2692*d10b5556SXylle 2693*d10b5556SXylle // ######################################################################## 2694*d10b5556SXylle // PGT STORAGE 2695*d10b5556SXylle // ######################################################################## 2696*d10b5556SXylle /** 2697*d10b5556SXylle * @addtogroup internalPGTStorage 2698*d10b5556SXylle * @{ 2699*d10b5556SXylle */ 2700*d10b5556SXylle 2701*d10b5556SXylle /** 2702*d10b5556SXylle * @var CAS_PGTStorage_AbstractStorage 2703*d10b5556SXylle * an instance of a class inheriting of PGTStorage, used to deal with PGT 2704*d10b5556SXylle * storage. Created by CAS_Client::setPGTStorageFile(), used 2705*d10b5556SXylle * by CAS_Client::setPGTStorageFile() and CAS_Client::_initPGTStorage(). 2706*d10b5556SXylle * 2707*d10b5556SXylle * @hideinitializer 2708*d10b5556SXylle */ 2709*d10b5556SXylle private $_pgt_storage = null; 2710*d10b5556SXylle 2711*d10b5556SXylle /** 2712*d10b5556SXylle * This method is used to initialize the storage of PGT's. 2713*d10b5556SXylle * Halts on error. 2714*d10b5556SXylle * 2715*d10b5556SXylle * @return void 2716*d10b5556SXylle */ 2717*d10b5556SXylle private function _initPGTStorage() 2718*d10b5556SXylle { 2719*d10b5556SXylle // if no SetPGTStorageXxx() has been used, default to file 2720*d10b5556SXylle if ( !is_object($this->_pgt_storage) ) { 2721*d10b5556SXylle $this->setPGTStorageFile(); 2722*d10b5556SXylle } 2723*d10b5556SXylle 2724*d10b5556SXylle // initializes the storage 2725*d10b5556SXylle $this->_pgt_storage->init(); 2726*d10b5556SXylle } 2727*d10b5556SXylle 2728*d10b5556SXylle /** 2729*d10b5556SXylle * This method stores a PGT. Halts on error. 2730*d10b5556SXylle * 2731*d10b5556SXylle * @param string $pgt the PGT to store 2732*d10b5556SXylle * @param string $pgt_iou its corresponding Iou 2733*d10b5556SXylle * 2734*d10b5556SXylle * @return void 2735*d10b5556SXylle */ 2736*d10b5556SXylle private function _storePGT($pgt,$pgt_iou) 2737*d10b5556SXylle { 2738*d10b5556SXylle // ensure that storage is initialized 2739*d10b5556SXylle $this->_initPGTStorage(); 2740*d10b5556SXylle // writes the PGT 2741*d10b5556SXylle $this->_pgt_storage->write($pgt, $pgt_iou); 2742*d10b5556SXylle } 2743*d10b5556SXylle 2744*d10b5556SXylle /** 2745*d10b5556SXylle * This method reads a PGT from its Iou and deletes the corresponding 2746*d10b5556SXylle * storage entry. 2747*d10b5556SXylle * 2748*d10b5556SXylle * @param string $pgt_iou the PGT Iou 2749*d10b5556SXylle * 2750*d10b5556SXylle * @return string mul The PGT corresponding to the Iou, false when not found. 2751*d10b5556SXylle */ 2752*d10b5556SXylle private function _loadPGT($pgt_iou) 2753*d10b5556SXylle { 2754*d10b5556SXylle // ensure that storage is initialized 2755*d10b5556SXylle $this->_initPGTStorage(); 2756*d10b5556SXylle // read the PGT 2757*d10b5556SXylle return $this->_pgt_storage->read($pgt_iou); 2758*d10b5556SXylle } 2759*d10b5556SXylle 2760*d10b5556SXylle /** 2761*d10b5556SXylle * This method can be used to set a custom PGT storage object. 2762*d10b5556SXylle * 2763*d10b5556SXylle * @param CAS_PGTStorage_AbstractStorage $storage a PGT storage object that 2764*d10b5556SXylle * inherits from the CAS_PGTStorage_AbstractStorage class 2765*d10b5556SXylle * 2766*d10b5556SXylle * @return void 2767*d10b5556SXylle */ 2768*d10b5556SXylle public function setPGTStorage($storage) 2769*d10b5556SXylle { 2770*d10b5556SXylle // Sequence validation 2771*d10b5556SXylle $this->ensureIsProxy(); 2772*d10b5556SXylle 2773*d10b5556SXylle // check that the storage has not already been set 2774*d10b5556SXylle if ( is_object($this->_pgt_storage) ) { 2775*d10b5556SXylle phpCAS::error('PGT storage already defined'); 2776*d10b5556SXylle } 2777*d10b5556SXylle 2778*d10b5556SXylle // check to make sure a valid storage object was specified 2779*d10b5556SXylle if ( !($storage instanceof CAS_PGTStorage_AbstractStorage) ) 2780*d10b5556SXylle throw new CAS_TypeMismatchException($storage, '$storage', 'CAS_PGTStorage_AbstractStorage object'); 2781*d10b5556SXylle 2782*d10b5556SXylle // store the PGTStorage object 2783*d10b5556SXylle $this->_pgt_storage = $storage; 2784*d10b5556SXylle } 2785*d10b5556SXylle 2786*d10b5556SXylle /** 2787*d10b5556SXylle * This method is used to tell phpCAS to store the response of the 2788*d10b5556SXylle * CAS server to PGT requests in a database. 2789*d10b5556SXylle * 2790*d10b5556SXylle * @param string|PDO $dsn_or_pdo a dsn string to use for creating a PDO 2791*d10b5556SXylle * object or a PDO object 2792*d10b5556SXylle * @param string $username the username to use when connecting to the 2793*d10b5556SXylle * database 2794*d10b5556SXylle * @param string $password the password to use when connecting to the 2795*d10b5556SXylle * database 2796*d10b5556SXylle * @param string $table the table to use for storing and retrieving 2797*d10b5556SXylle * PGTs 2798*d10b5556SXylle * @param string $driver_options any driver options to use when connecting 2799*d10b5556SXylle * to the database 2800*d10b5556SXylle * 2801*d10b5556SXylle * @return void 2802*d10b5556SXylle */ 2803*d10b5556SXylle public function setPGTStorageDb( 2804*d10b5556SXylle $dsn_or_pdo, $username='', $password='', $table='', $driver_options=null 2805*d10b5556SXylle ) { 2806*d10b5556SXylle // Sequence validation 2807*d10b5556SXylle $this->ensureIsProxy(); 2808*d10b5556SXylle 2809*d10b5556SXylle // Argument validation 2810*d10b5556SXylle if (!(is_object($dsn_or_pdo) && $dsn_or_pdo instanceof PDO) && !is_string($dsn_or_pdo)) 2811*d10b5556SXylle throw new CAS_TypeMismatchException($dsn_or_pdo, '$dsn_or_pdo', 'string or PDO object'); 2812*d10b5556SXylle if (gettype($username) != 'string') 2813*d10b5556SXylle throw new CAS_TypeMismatchException($username, '$username', 'string'); 2814*d10b5556SXylle if (gettype($password) != 'string') 2815*d10b5556SXylle throw new CAS_TypeMismatchException($password, '$password', 'string'); 2816*d10b5556SXylle if (gettype($table) != 'string') 2817*d10b5556SXylle throw new CAS_TypeMismatchException($table, '$password', 'string'); 2818*d10b5556SXylle 2819*d10b5556SXylle // create the storage object 2820*d10b5556SXylle $this->setPGTStorage( 2821*d10b5556SXylle new CAS_PGTStorage_Db( 2822*d10b5556SXylle $this, $dsn_or_pdo, $username, $password, $table, $driver_options 2823*d10b5556SXylle ) 2824*d10b5556SXylle ); 2825*d10b5556SXylle } 2826*d10b5556SXylle 2827*d10b5556SXylle /** 2828*d10b5556SXylle * This method is used to tell phpCAS to store the response of the 2829*d10b5556SXylle * CAS server to PGT requests onto the filesystem. 2830*d10b5556SXylle * 2831*d10b5556SXylle * @param string $path the path where the PGT's should be stored 2832*d10b5556SXylle * 2833*d10b5556SXylle * @return void 2834*d10b5556SXylle */ 2835*d10b5556SXylle public function setPGTStorageFile($path='') 2836*d10b5556SXylle { 2837*d10b5556SXylle // Sequence validation 2838*d10b5556SXylle $this->ensureIsProxy(); 2839*d10b5556SXylle 2840*d10b5556SXylle // Argument validation 2841*d10b5556SXylle if (gettype($path) != 'string') 2842*d10b5556SXylle throw new CAS_TypeMismatchException($path, '$path', 'string'); 2843*d10b5556SXylle 2844*d10b5556SXylle // create the storage object 2845*d10b5556SXylle $this->setPGTStorage(new CAS_PGTStorage_File($this, $path)); 2846*d10b5556SXylle } 2847*d10b5556SXylle 2848*d10b5556SXylle 2849*d10b5556SXylle // ######################################################################## 2850*d10b5556SXylle // PGT VALIDATION 2851*d10b5556SXylle // ######################################################################## 2852*d10b5556SXylle /** 2853*d10b5556SXylle * This method is used to validate a PGT; halt on failure. 2854*d10b5556SXylle * 2855*d10b5556SXylle * @param string &$validate_url the URL of the request to the CAS server. 2856*d10b5556SXylle * @param string $text_response the response of the CAS server, as is 2857*d10b5556SXylle * (XML text); result of 2858*d10b5556SXylle * CAS_Client::validateCAS10() or 2859*d10b5556SXylle * CAS_Client::validateCAS20(). 2860*d10b5556SXylle * @param DOMElement $tree_response the response of the CAS server, as a DOM XML 2861*d10b5556SXylle * tree; result of CAS_Client::validateCAS10() or CAS_Client::validateCAS20(). 2862*d10b5556SXylle * 2863*d10b5556SXylle * @return bool true when successfull and issue a CAS_AuthenticationException 2864*d10b5556SXylle * and false on an error 2865*d10b5556SXylle * 2866*d10b5556SXylle * @throws CAS_AuthenticationException 2867*d10b5556SXylle */ 2868*d10b5556SXylle private function _validatePGT(&$validate_url,$text_response,$tree_response) 2869*d10b5556SXylle { 2870*d10b5556SXylle phpCAS::traceBegin(); 2871*d10b5556SXylle if ( $tree_response->getElementsByTagName("proxyGrantingTicket")->length == 0) { 2872*d10b5556SXylle phpCAS::trace('<proxyGrantingTicket> not found'); 2873*d10b5556SXylle // authentication succeded, but no PGT Iou was transmitted 2874*d10b5556SXylle throw new CAS_AuthenticationException( 2875*d10b5556SXylle $this, 'Ticket validated but no PGT Iou transmitted', 2876*d10b5556SXylle $validate_url, false/*$no_response*/, false/*$bad_response*/, 2877*d10b5556SXylle $text_response 2878*d10b5556SXylle ); 2879*d10b5556SXylle } else { 2880*d10b5556SXylle // PGT Iou transmitted, extract it 2881*d10b5556SXylle $pgt_iou = trim( 2882*d10b5556SXylle $tree_response->getElementsByTagName("proxyGrantingTicket")->item(0)->nodeValue 2883*d10b5556SXylle ); 2884*d10b5556SXylle if (preg_match('/^PGTIOU-[\.\-\w]+$/', $pgt_iou)) { 2885*d10b5556SXylle $pgt = $this->_loadPGT($pgt_iou); 2886*d10b5556SXylle if ( $pgt == false ) { 2887*d10b5556SXylle phpCAS::trace('could not load PGT'); 2888*d10b5556SXylle throw new CAS_AuthenticationException( 2889*d10b5556SXylle $this, 2890*d10b5556SXylle 'PGT Iou was transmitted but PGT could not be retrieved', 2891*d10b5556SXylle $validate_url, false/*$no_response*/, 2892*d10b5556SXylle false/*$bad_response*/, $text_response 2893*d10b5556SXylle ); 2894*d10b5556SXylle } 2895*d10b5556SXylle $this->_setPGT($pgt); 2896*d10b5556SXylle } else { 2897*d10b5556SXylle phpCAS::trace('PGTiou format error'); 2898*d10b5556SXylle throw new CAS_AuthenticationException( 2899*d10b5556SXylle $this, 'PGT Iou was transmitted but has wrong format', 2900*d10b5556SXylle $validate_url, false/*$no_response*/, false/*$bad_response*/, 2901*d10b5556SXylle $text_response 2902*d10b5556SXylle ); 2903*d10b5556SXylle } 2904*d10b5556SXylle } 2905*d10b5556SXylle phpCAS::traceEnd(true); 2906*d10b5556SXylle return true; 2907*d10b5556SXylle } 2908*d10b5556SXylle 2909*d10b5556SXylle // ######################################################################## 2910*d10b5556SXylle // PGT VALIDATION 2911*d10b5556SXylle // ######################################################################## 2912*d10b5556SXylle 2913*d10b5556SXylle /** 2914*d10b5556SXylle * This method is used to retrieve PT's from the CAS server thanks to a PGT. 2915*d10b5556SXylle * 2916*d10b5556SXylle * @param string $target_service the service to ask for with the PT. 2917*d10b5556SXylle * @param int &$err_code an error code (PHPCAS_SERVICE_OK on success). 2918*d10b5556SXylle * @param string &$err_msg an error message (empty on success). 2919*d10b5556SXylle * 2920*d10b5556SXylle * @return string|false a Proxy Ticket, or false on error. 2921*d10b5556SXylle */ 2922*d10b5556SXylle public function retrievePT($target_service,&$err_code,&$err_msg) 2923*d10b5556SXylle { 2924*d10b5556SXylle // Argument validation 2925*d10b5556SXylle if (gettype($target_service) != 'string') 2926*d10b5556SXylle throw new CAS_TypeMismatchException($target_service, '$target_service', 'string'); 2927*d10b5556SXylle 2928*d10b5556SXylle phpCAS::traceBegin(); 2929*d10b5556SXylle 2930*d10b5556SXylle // by default, $err_msg is set empty and $pt to true. On error, $pt is 2931*d10b5556SXylle // set to false and $err_msg to an error message. At the end, if $pt is false 2932*d10b5556SXylle // and $error_msg is still empty, it is set to 'invalid response' (the most 2933*d10b5556SXylle // commonly encountered error). 2934*d10b5556SXylle $err_msg = ''; 2935*d10b5556SXylle 2936*d10b5556SXylle // build the URL to retrieve the PT 2937*d10b5556SXylle $cas_url = $this->getServerProxyURL().'?targetService=' 2938*d10b5556SXylle .urlencode($target_service).'&pgt='.$this->_getPGT(); 2939*d10b5556SXylle 2940*d10b5556SXylle $headers = ''; 2941*d10b5556SXylle $cas_response = ''; 2942*d10b5556SXylle // open and read the URL 2943*d10b5556SXylle if ( !$this->_readURL($cas_url, $headers, $cas_response, $err_msg) ) { 2944*d10b5556SXylle phpCAS::trace( 2945*d10b5556SXylle 'could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')' 2946*d10b5556SXylle ); 2947*d10b5556SXylle $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE; 2948*d10b5556SXylle $err_msg = 'could not retrieve PT (no response from the CAS server)'; 2949*d10b5556SXylle phpCAS::traceEnd(false); 2950*d10b5556SXylle return false; 2951*d10b5556SXylle } 2952*d10b5556SXylle 2953*d10b5556SXylle $bad_response = false; 2954*d10b5556SXylle 2955*d10b5556SXylle // create new DOMDocument object 2956*d10b5556SXylle $dom = new DOMDocument(); 2957*d10b5556SXylle // Fix possible whitspace problems 2958*d10b5556SXylle $dom->preserveWhiteSpace = false; 2959*d10b5556SXylle // read the response of the CAS server into a DOM object 2960*d10b5556SXylle if ( !($dom->loadXML($cas_response))) { 2961*d10b5556SXylle phpCAS::trace('dom->loadXML() failed'); 2962*d10b5556SXylle // read failed 2963*d10b5556SXylle $bad_response = true; 2964*d10b5556SXylle } 2965*d10b5556SXylle 2966*d10b5556SXylle if ( !$bad_response ) { 2967*d10b5556SXylle // read the root node of the XML tree 2968*d10b5556SXylle if ( !($root = $dom->documentElement) ) { 2969*d10b5556SXylle phpCAS::trace('documentElement failed'); 2970*d10b5556SXylle // read failed 2971*d10b5556SXylle $bad_response = true; 2972*d10b5556SXylle } 2973*d10b5556SXylle } 2974*d10b5556SXylle 2975*d10b5556SXylle if ( !$bad_response ) { 2976*d10b5556SXylle // insure that tag name is 'serviceResponse' 2977*d10b5556SXylle if ( $root->localName != 'serviceResponse' ) { 2978*d10b5556SXylle phpCAS::trace('localName failed'); 2979*d10b5556SXylle // bad root node 2980*d10b5556SXylle $bad_response = true; 2981*d10b5556SXylle } 2982*d10b5556SXylle } 2983*d10b5556SXylle 2984*d10b5556SXylle if ( !$bad_response ) { 2985*d10b5556SXylle // look for a proxySuccess tag 2986*d10b5556SXylle if ( $root->getElementsByTagName("proxySuccess")->length != 0) { 2987*d10b5556SXylle $proxy_success_list = $root->getElementsByTagName("proxySuccess"); 2988*d10b5556SXylle 2989*d10b5556SXylle // authentication succeded, look for a proxyTicket tag 2990*d10b5556SXylle if ( $proxy_success_list->item(0)->getElementsByTagName("proxyTicket")->length != 0) { 2991*d10b5556SXylle $err_code = PHPCAS_SERVICE_OK; 2992*d10b5556SXylle $err_msg = ''; 2993*d10b5556SXylle $pt = trim( 2994*d10b5556SXylle $proxy_success_list->item(0)->getElementsByTagName("proxyTicket")->item(0)->nodeValue 2995*d10b5556SXylle ); 2996*d10b5556SXylle phpCAS::trace('original PT: '.trim($pt)); 2997*d10b5556SXylle phpCAS::traceEnd($pt); 2998*d10b5556SXylle return $pt; 2999*d10b5556SXylle } else { 3000*d10b5556SXylle phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>'); 3001*d10b5556SXylle } 3002*d10b5556SXylle } else if ($root->getElementsByTagName("proxyFailure")->length != 0) { 3003*d10b5556SXylle // look for a proxyFailure tag 3004*d10b5556SXylle $proxy_failure_list = $root->getElementsByTagName("proxyFailure"); 3005*d10b5556SXylle 3006*d10b5556SXylle // authentication failed, extract the error 3007*d10b5556SXylle $err_code = PHPCAS_SERVICE_PT_FAILURE; 3008*d10b5556SXylle $err_msg = 'PT retrieving failed (code=`' 3009*d10b5556SXylle .$proxy_failure_list->item(0)->getAttribute('code') 3010*d10b5556SXylle .'\', message=`' 3011*d10b5556SXylle .trim($proxy_failure_list->item(0)->nodeValue) 3012*d10b5556SXylle .'\')'; 3013*d10b5556SXylle phpCAS::traceEnd(false); 3014*d10b5556SXylle return false; 3015*d10b5556SXylle } else { 3016*d10b5556SXylle phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found'); 3017*d10b5556SXylle } 3018*d10b5556SXylle } 3019*d10b5556SXylle 3020*d10b5556SXylle // at this step, we are sure that the response of the CAS server was 3021*d10b5556SXylle // illformed 3022*d10b5556SXylle $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE; 3023*d10b5556SXylle $err_msg = 'Invalid response from the CAS server (response=`' 3024*d10b5556SXylle .$cas_response.'\')'; 3025*d10b5556SXylle 3026*d10b5556SXylle phpCAS::traceEnd(false); 3027*d10b5556SXylle return false; 3028*d10b5556SXylle } 3029*d10b5556SXylle 3030*d10b5556SXylle /** @} */ 3031*d10b5556SXylle 3032*d10b5556SXylle // ######################################################################## 3033*d10b5556SXylle // READ CAS SERVER ANSWERS 3034*d10b5556SXylle // ######################################################################## 3035*d10b5556SXylle 3036*d10b5556SXylle /** 3037*d10b5556SXylle * @addtogroup internalMisc 3038*d10b5556SXylle * @{ 3039*d10b5556SXylle */ 3040*d10b5556SXylle 3041*d10b5556SXylle /** 3042*d10b5556SXylle * This method is used to acces a remote URL. 3043*d10b5556SXylle * 3044*d10b5556SXylle * @param string $url the URL to access. 3045*d10b5556SXylle * @param string &$headers an array containing the HTTP header lines of the 3046*d10b5556SXylle * response (an empty array on failure). 3047*d10b5556SXylle * @param string &$body the body of the response, as a string (empty on 3048*d10b5556SXylle * failure). 3049*d10b5556SXylle * @param string &$err_msg an error message, filled on failure. 3050*d10b5556SXylle * 3051*d10b5556SXylle * @return bool true on success, false otherwise (in this later case, $err_msg 3052*d10b5556SXylle * contains an error message). 3053*d10b5556SXylle */ 3054*d10b5556SXylle private function _readURL($url, &$headers, &$body, &$err_msg) 3055*d10b5556SXylle { 3056*d10b5556SXylle phpCAS::traceBegin(); 3057*d10b5556SXylle $className = $this->_requestImplementation; 3058*d10b5556SXylle $request = new $className(); 3059*d10b5556SXylle 3060*d10b5556SXylle if (count($this->_curl_options)) { 3061*d10b5556SXylle $request->setCurlOptions($this->_curl_options); 3062*d10b5556SXylle } 3063*d10b5556SXylle 3064*d10b5556SXylle $request->setUrl($url); 3065*d10b5556SXylle 3066*d10b5556SXylle if (empty($this->_cas_server_ca_cert) && !$this->_no_cas_server_validation) { 3067*d10b5556SXylle phpCAS::error( 3068*d10b5556SXylle 'one of the methods phpCAS::setCasServerCACert() or phpCAS::setNoCasServerValidation() must be called.' 3069*d10b5556SXylle ); 3070*d10b5556SXylle } 3071*d10b5556SXylle if ($this->_cas_server_ca_cert != '') { 3072*d10b5556SXylle $request->setSslCaCert( 3073*d10b5556SXylle $this->_cas_server_ca_cert, $this->_cas_server_cn_validate 3074*d10b5556SXylle ); 3075*d10b5556SXylle } 3076*d10b5556SXylle 3077*d10b5556SXylle // add extra stuff if SAML 3078*d10b5556SXylle if ($this->getServerVersion() == SAML_VERSION_1_1) { 3079*d10b5556SXylle $request->addHeader("soapaction: http://www.oasis-open.org/committees/security"); 3080*d10b5556SXylle $request->addHeader("cache-control: no-cache"); 3081*d10b5556SXylle $request->addHeader("pragma: no-cache"); 3082*d10b5556SXylle $request->addHeader("accept: text/xml"); 3083*d10b5556SXylle $request->addHeader("connection: keep-alive"); 3084*d10b5556SXylle $request->addHeader("content-type: text/xml"); 3085*d10b5556SXylle $request->makePost(); 3086*d10b5556SXylle $request->setPostBody($this->_buildSAMLPayload()); 3087*d10b5556SXylle } 3088*d10b5556SXylle 3089*d10b5556SXylle if ($request->send()) { 3090*d10b5556SXylle $headers = $request->getResponseHeaders(); 3091*d10b5556SXylle $body = $request->getResponseBody(); 3092*d10b5556SXylle $err_msg = ''; 3093*d10b5556SXylle phpCAS::traceEnd(true); 3094*d10b5556SXylle return true; 3095*d10b5556SXylle } else { 3096*d10b5556SXylle $headers = ''; 3097*d10b5556SXylle $body = ''; 3098*d10b5556SXylle $err_msg = $request->getErrorMessage(); 3099*d10b5556SXylle phpCAS::traceEnd(false); 3100*d10b5556SXylle return false; 3101*d10b5556SXylle } 3102*d10b5556SXylle } 3103*d10b5556SXylle 3104*d10b5556SXylle /** 3105*d10b5556SXylle * This method is used to build the SAML POST body sent to /samlValidate URL. 3106*d10b5556SXylle * 3107*d10b5556SXylle * @return string the SOAP-encased SAMLP artifact (the ticket). 3108*d10b5556SXylle */ 3109*d10b5556SXylle private function _buildSAMLPayload() 3110*d10b5556SXylle { 3111*d10b5556SXylle phpCAS::traceBegin(); 3112*d10b5556SXylle 3113*d10b5556SXylle //get the ticket 3114*d10b5556SXylle $sa = urlencode($this->getTicket()); 3115*d10b5556SXylle 3116*d10b5556SXylle $body = SAML_SOAP_ENV.SAML_SOAP_BODY.SAMLP_REQUEST 3117*d10b5556SXylle .SAML_ASSERTION_ARTIFACT.$sa.SAML_ASSERTION_ARTIFACT_CLOSE 3118*d10b5556SXylle .SAMLP_REQUEST_CLOSE.SAML_SOAP_BODY_CLOSE.SAML_SOAP_ENV_CLOSE; 3119*d10b5556SXylle 3120*d10b5556SXylle phpCAS::traceEnd($body); 3121*d10b5556SXylle return ($body); 3122*d10b5556SXylle } 3123*d10b5556SXylle 3124*d10b5556SXylle /** @} **/ 3125*d10b5556SXylle 3126*d10b5556SXylle // ######################################################################## 3127*d10b5556SXylle // ACCESS TO EXTERNAL SERVICES 3128*d10b5556SXylle // ######################################################################## 3129*d10b5556SXylle 3130*d10b5556SXylle /** 3131*d10b5556SXylle * @addtogroup internalProxyServices 3132*d10b5556SXylle * @{ 3133*d10b5556SXylle */ 3134*d10b5556SXylle 3135*d10b5556SXylle 3136*d10b5556SXylle /** 3137*d10b5556SXylle * Answer a proxy-authenticated service handler. 3138*d10b5556SXylle * 3139*d10b5556SXylle * @param string $type The service type. One of: 3140*d10b5556SXylle * PHPCAS_PROXIED_SERVICE_HTTP_GET, PHPCAS_PROXIED_SERVICE_HTTP_POST, 3141*d10b5556SXylle * PHPCAS_PROXIED_SERVICE_IMAP 3142*d10b5556SXylle * 3143*d10b5556SXylle * @return CAS_ProxiedService 3144*d10b5556SXylle * @throws InvalidArgumentException If the service type is unknown. 3145*d10b5556SXylle */ 3146*d10b5556SXylle public function getProxiedService ($type) 3147*d10b5556SXylle { 3148*d10b5556SXylle // Sequence validation 3149*d10b5556SXylle $this->ensureIsProxy(); 3150*d10b5556SXylle $this->ensureAuthenticationCallSuccessful(); 3151*d10b5556SXylle 3152*d10b5556SXylle // Argument validation 3153*d10b5556SXylle if (gettype($type) != 'string') 3154*d10b5556SXylle throw new CAS_TypeMismatchException($type, '$type', 'string'); 3155*d10b5556SXylle 3156*d10b5556SXylle switch ($type) { 3157*d10b5556SXylle case PHPCAS_PROXIED_SERVICE_HTTP_GET: 3158*d10b5556SXylle case PHPCAS_PROXIED_SERVICE_HTTP_POST: 3159*d10b5556SXylle $requestClass = $this->_requestImplementation; 3160*d10b5556SXylle $request = new $requestClass(); 3161*d10b5556SXylle if (count($this->_curl_options)) { 3162*d10b5556SXylle $request->setCurlOptions($this->_curl_options); 3163*d10b5556SXylle } 3164*d10b5556SXylle $proxiedService = new $type($request, $this->_serviceCookieJar); 3165*d10b5556SXylle if ($proxiedService instanceof CAS_ProxiedService_Testable) { 3166*d10b5556SXylle $proxiedService->setCasClient($this); 3167*d10b5556SXylle } 3168*d10b5556SXylle return $proxiedService; 3169*d10b5556SXylle case PHPCAS_PROXIED_SERVICE_IMAP; 3170*d10b5556SXylle $proxiedService = new CAS_ProxiedService_Imap($this->_getUser()); 3171*d10b5556SXylle if ($proxiedService instanceof CAS_ProxiedService_Testable) { 3172*d10b5556SXylle $proxiedService->setCasClient($this); 3173*d10b5556SXylle } 3174*d10b5556SXylle return $proxiedService; 3175*d10b5556SXylle default: 3176*d10b5556SXylle throw new CAS_InvalidArgumentException( 3177*d10b5556SXylle "Unknown proxied-service type, $type." 3178*d10b5556SXylle ); 3179*d10b5556SXylle } 3180*d10b5556SXylle } 3181*d10b5556SXylle 3182*d10b5556SXylle /** 3183*d10b5556SXylle * Initialize a proxied-service handler with the proxy-ticket it should use. 3184*d10b5556SXylle * 3185*d10b5556SXylle * @param CAS_ProxiedService $proxiedService service handler 3186*d10b5556SXylle * 3187*d10b5556SXylle * @return void 3188*d10b5556SXylle * 3189*d10b5556SXylle * @throws CAS_ProxyTicketException If there is a proxy-ticket failure. 3190*d10b5556SXylle * The code of the Exception will be one of: 3191*d10b5556SXylle * PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE 3192*d10b5556SXylle * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE 3193*d10b5556SXylle * PHPCAS_SERVICE_PT_FAILURE 3194*d10b5556SXylle * @throws CAS_ProxiedService_Exception If there is a failure getting the 3195*d10b5556SXylle * url from the proxied service. 3196*d10b5556SXylle */ 3197*d10b5556SXylle public function initializeProxiedService (CAS_ProxiedService $proxiedService) 3198*d10b5556SXylle { 3199*d10b5556SXylle // Sequence validation 3200*d10b5556SXylle $this->ensureIsProxy(); 3201*d10b5556SXylle $this->ensureAuthenticationCallSuccessful(); 3202*d10b5556SXylle 3203*d10b5556SXylle $url = $proxiedService->getServiceUrl(); 3204*d10b5556SXylle if (!is_string($url)) { 3205*d10b5556SXylle throw new CAS_ProxiedService_Exception( 3206*d10b5556SXylle "Proxied Service ".get_class($proxiedService) 3207*d10b5556SXylle ."->getServiceUrl() should have returned a string, returned a " 3208*d10b5556SXylle .gettype($url)." instead." 3209*d10b5556SXylle ); 3210*d10b5556SXylle } 3211*d10b5556SXylle $pt = $this->retrievePT($url, $err_code, $err_msg); 3212*d10b5556SXylle if (!$pt) { 3213*d10b5556SXylle throw new CAS_ProxyTicketException($err_msg, $err_code); 3214*d10b5556SXylle } 3215*d10b5556SXylle $proxiedService->setProxyTicket($pt); 3216*d10b5556SXylle } 3217*d10b5556SXylle 3218*d10b5556SXylle /** 3219*d10b5556SXylle * This method is used to access an HTTP[S] service. 3220*d10b5556SXylle * 3221*d10b5556SXylle * @param string $url the service to access. 3222*d10b5556SXylle * @param int &$err_code an error code Possible values are 3223*d10b5556SXylle * PHPCAS_SERVICE_OK (on success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, 3224*d10b5556SXylle * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE, PHPCAS_SERVICE_PT_FAILURE, 3225*d10b5556SXylle * PHPCAS_SERVICE_NOT_AVAILABLE. 3226*d10b5556SXylle * @param string &$output the output of the service (also used to give an error 3227*d10b5556SXylle * message on failure). 3228*d10b5556SXylle * 3229*d10b5556SXylle * @return bool true on success, false otherwise (in this later case, $err_code 3230*d10b5556SXylle * gives the reason why it failed and $output contains an error message). 3231*d10b5556SXylle */ 3232*d10b5556SXylle public function serviceWeb($url,&$err_code,&$output) 3233*d10b5556SXylle { 3234*d10b5556SXylle // Sequence validation 3235*d10b5556SXylle $this->ensureIsProxy(); 3236*d10b5556SXylle $this->ensureAuthenticationCallSuccessful(); 3237*d10b5556SXylle 3238*d10b5556SXylle // Argument validation 3239*d10b5556SXylle if (gettype($url) != 'string') 3240*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 3241*d10b5556SXylle 3242*d10b5556SXylle try { 3243*d10b5556SXylle $service = $this->getProxiedService(PHPCAS_PROXIED_SERVICE_HTTP_GET); 3244*d10b5556SXylle $service->setUrl($url); 3245*d10b5556SXylle $service->send(); 3246*d10b5556SXylle $output = $service->getResponseBody(); 3247*d10b5556SXylle $err_code = PHPCAS_SERVICE_OK; 3248*d10b5556SXylle return true; 3249*d10b5556SXylle } catch (CAS_ProxyTicketException $e) { 3250*d10b5556SXylle $err_code = $e->getCode(); 3251*d10b5556SXylle $output = $e->getMessage(); 3252*d10b5556SXylle return false; 3253*d10b5556SXylle } catch (CAS_ProxiedService_Exception $e) { 3254*d10b5556SXylle $lang = $this->getLangObj(); 3255*d10b5556SXylle $output = sprintf( 3256*d10b5556SXylle $lang->getServiceUnavailable(), $url, $e->getMessage() 3257*d10b5556SXylle ); 3258*d10b5556SXylle $err_code = PHPCAS_SERVICE_NOT_AVAILABLE; 3259*d10b5556SXylle return false; 3260*d10b5556SXylle } 3261*d10b5556SXylle } 3262*d10b5556SXylle 3263*d10b5556SXylle /** 3264*d10b5556SXylle * This method is used to access an IMAP/POP3/NNTP service. 3265*d10b5556SXylle * 3266*d10b5556SXylle * @param string $url a string giving the URL of the service, including 3267*d10b5556SXylle * the mailing box for IMAP URLs, as accepted by imap_open(). 3268*d10b5556SXylle * @param string $serviceUrl a string giving for CAS retrieve Proxy ticket 3269*d10b5556SXylle * @param string $flags options given to imap_open(). 3270*d10b5556SXylle * @param int &$err_code an error code Possible values are 3271*d10b5556SXylle * PHPCAS_SERVICE_OK (on success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, 3272*d10b5556SXylle * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE, PHPCAS_SERVICE_PT_FAILURE, 3273*d10b5556SXylle * PHPCAS_SERVICE_NOT_AVAILABLE. 3274*d10b5556SXylle * @param string &$err_msg an error message on failure 3275*d10b5556SXylle * @param string &$pt the Proxy Ticket (PT) retrieved from the CAS 3276*d10b5556SXylle * server to access the URL on success, false on error). 3277*d10b5556SXylle * 3278*d10b5556SXylle * @return object|false an IMAP stream on success, false otherwise (in this later 3279*d10b5556SXylle * case, $err_code gives the reason why it failed and $err_msg contains an 3280*d10b5556SXylle * error message). 3281*d10b5556SXylle */ 3282*d10b5556SXylle public function serviceMail($url,$serviceUrl,$flags,&$err_code,&$err_msg,&$pt) 3283*d10b5556SXylle { 3284*d10b5556SXylle // Sequence validation 3285*d10b5556SXylle $this->ensureIsProxy(); 3286*d10b5556SXylle $this->ensureAuthenticationCallSuccessful(); 3287*d10b5556SXylle 3288*d10b5556SXylle // Argument validation 3289*d10b5556SXylle if (gettype($url) != 'string') 3290*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 3291*d10b5556SXylle if (gettype($serviceUrl) != 'string') 3292*d10b5556SXylle throw new CAS_TypeMismatchException($serviceUrl, '$serviceUrl', 'string'); 3293*d10b5556SXylle if (gettype($flags) != 'integer') 3294*d10b5556SXylle throw new CAS_TypeMismatchException($flags, '$flags', 'string'); 3295*d10b5556SXylle 3296*d10b5556SXylle try { 3297*d10b5556SXylle $service = $this->getProxiedService(PHPCAS_PROXIED_SERVICE_IMAP); 3298*d10b5556SXylle $service->setServiceUrl($serviceUrl); 3299*d10b5556SXylle $service->setMailbox($url); 3300*d10b5556SXylle $service->setOptions($flags); 3301*d10b5556SXylle 3302*d10b5556SXylle $stream = $service->open(); 3303*d10b5556SXylle $err_code = PHPCAS_SERVICE_OK; 3304*d10b5556SXylle $pt = $service->getImapProxyTicket(); 3305*d10b5556SXylle return $stream; 3306*d10b5556SXylle } catch (CAS_ProxyTicketException $e) { 3307*d10b5556SXylle $err_msg = $e->getMessage(); 3308*d10b5556SXylle $err_code = $e->getCode(); 3309*d10b5556SXylle $pt = false; 3310*d10b5556SXylle return false; 3311*d10b5556SXylle } catch (CAS_ProxiedService_Exception $e) { 3312*d10b5556SXylle $lang = $this->getLangObj(); 3313*d10b5556SXylle $err_msg = sprintf( 3314*d10b5556SXylle $lang->getServiceUnavailable(), 3315*d10b5556SXylle $url, 3316*d10b5556SXylle $e->getMessage() 3317*d10b5556SXylle ); 3318*d10b5556SXylle $err_code = PHPCAS_SERVICE_NOT_AVAILABLE; 3319*d10b5556SXylle $pt = false; 3320*d10b5556SXylle return false; 3321*d10b5556SXylle } 3322*d10b5556SXylle } 3323*d10b5556SXylle 3324*d10b5556SXylle /** @} **/ 3325*d10b5556SXylle 3326*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 3327*d10b5556SXylle // XX XX 3328*d10b5556SXylle // XX PROXIED CLIENT FEATURES (CAS 2.0) XX 3329*d10b5556SXylle // XX XX 3330*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 3331*d10b5556SXylle 3332*d10b5556SXylle // ######################################################################## 3333*d10b5556SXylle // PT 3334*d10b5556SXylle // ######################################################################## 3335*d10b5556SXylle /** 3336*d10b5556SXylle * @addtogroup internalService 3337*d10b5556SXylle * @{ 3338*d10b5556SXylle */ 3339*d10b5556SXylle 3340*d10b5556SXylle /** 3341*d10b5556SXylle * This array will store a list of proxies in front of this application. This 3342*d10b5556SXylle * property will only be populated if this script is being proxied rather than 3343*d10b5556SXylle * accessed directly. 3344*d10b5556SXylle * 3345*d10b5556SXylle * It is set in CAS_Client::validateCAS20() and can be read by 3346*d10b5556SXylle * CAS_Client::getProxies() 3347*d10b5556SXylle * 3348*d10b5556SXylle * @access private 3349*d10b5556SXylle */ 3350*d10b5556SXylle private $_proxies = array(); 3351*d10b5556SXylle 3352*d10b5556SXylle /** 3353*d10b5556SXylle * Answer an array of proxies that are sitting in front of this application. 3354*d10b5556SXylle * 3355*d10b5556SXylle * This method will only return a non-empty array if we have received and 3356*d10b5556SXylle * validated a Proxy Ticket. 3357*d10b5556SXylle * 3358*d10b5556SXylle * @return array 3359*d10b5556SXylle * @access public 3360*d10b5556SXylle */ 3361*d10b5556SXylle public function getProxies() 3362*d10b5556SXylle { 3363*d10b5556SXylle return $this->_proxies; 3364*d10b5556SXylle } 3365*d10b5556SXylle 3366*d10b5556SXylle /** 3367*d10b5556SXylle * Set the Proxy array, probably from persistant storage. 3368*d10b5556SXylle * 3369*d10b5556SXylle * @param array $proxies An array of proxies 3370*d10b5556SXylle * 3371*d10b5556SXylle * @return void 3372*d10b5556SXylle * @access private 3373*d10b5556SXylle */ 3374*d10b5556SXylle private function _setProxies($proxies) 3375*d10b5556SXylle { 3376*d10b5556SXylle $this->_proxies = $proxies; 3377*d10b5556SXylle if (!empty($proxies)) { 3378*d10b5556SXylle // For proxy-authenticated requests people are not viewing the URL 3379*d10b5556SXylle // directly since the client is another application making a 3380*d10b5556SXylle // web-service call. 3381*d10b5556SXylle // Because of this, stripping the ticket from the URL is unnecessary 3382*d10b5556SXylle // and causes another web-service request to be performed. Additionally, 3383*d10b5556SXylle // if session handling on either the client or the server malfunctions 3384*d10b5556SXylle // then the subsequent request will not complete successfully. 3385*d10b5556SXylle $this->setNoClearTicketsFromUrl(); 3386*d10b5556SXylle } 3387*d10b5556SXylle } 3388*d10b5556SXylle 3389*d10b5556SXylle /** 3390*d10b5556SXylle * A container of patterns to be allowed as proxies in front of the cas client. 3391*d10b5556SXylle * 3392*d10b5556SXylle * @var CAS_ProxyChain_AllowedList 3393*d10b5556SXylle */ 3394*d10b5556SXylle private $_allowed_proxy_chains; 3395*d10b5556SXylle 3396*d10b5556SXylle /** 3397*d10b5556SXylle * Answer the CAS_ProxyChain_AllowedList object for this client. 3398*d10b5556SXylle * 3399*d10b5556SXylle * @return CAS_ProxyChain_AllowedList 3400*d10b5556SXylle */ 3401*d10b5556SXylle public function getAllowedProxyChains () 3402*d10b5556SXylle { 3403*d10b5556SXylle if (empty($this->_allowed_proxy_chains)) { 3404*d10b5556SXylle $this->_allowed_proxy_chains = new CAS_ProxyChain_AllowedList(); 3405*d10b5556SXylle } 3406*d10b5556SXylle return $this->_allowed_proxy_chains; 3407*d10b5556SXylle } 3408*d10b5556SXylle 3409*d10b5556SXylle /** @} */ 3410*d10b5556SXylle // ######################################################################## 3411*d10b5556SXylle // PT VALIDATION 3412*d10b5556SXylle // ######################################################################## 3413*d10b5556SXylle /** 3414*d10b5556SXylle * @addtogroup internalProxied 3415*d10b5556SXylle * @{ 3416*d10b5556SXylle */ 3417*d10b5556SXylle 3418*d10b5556SXylle /** 3419*d10b5556SXylle * This method is used to validate a cas 2.0 ST or PT; halt on failure 3420*d10b5556SXylle * Used for all CAS 2.0 validations 3421*d10b5556SXylle * 3422*d10b5556SXylle * @param string &$validate_url the url of the reponse 3423*d10b5556SXylle * @param string &$text_response the text of the repsones 3424*d10b5556SXylle * @param DOMElement &$tree_response the domxml tree of the respones 3425*d10b5556SXylle * @param bool $renew true to force the authentication with the CAS server 3426*d10b5556SXylle * 3427*d10b5556SXylle * @return bool true when successfull and issue a CAS_AuthenticationException 3428*d10b5556SXylle * and false on an error 3429*d10b5556SXylle * 3430*d10b5556SXylle * @throws CAS_AuthenticationException 3431*d10b5556SXylle */ 3432*d10b5556SXylle public function validateCAS20(&$validate_url,&$text_response,&$tree_response, $renew=false) 3433*d10b5556SXylle { 3434*d10b5556SXylle phpCAS::traceBegin(); 3435*d10b5556SXylle phpCAS::trace($text_response); 3436*d10b5556SXylle // build the URL to validate the ticket 3437*d10b5556SXylle if ($this->getAllowedProxyChains()->isProxyingAllowed()) { 3438*d10b5556SXylle $validate_url = $this->getServerProxyValidateURL().'&ticket=' 3439*d10b5556SXylle .urlencode($this->getTicket()); 3440*d10b5556SXylle } else { 3441*d10b5556SXylle $validate_url = $this->getServerServiceValidateURL().'&ticket=' 3442*d10b5556SXylle .urlencode($this->getTicket()); 3443*d10b5556SXylle } 3444*d10b5556SXylle 3445*d10b5556SXylle if ( $this->isProxy() ) { 3446*d10b5556SXylle // pass the callback url for CAS proxies 3447*d10b5556SXylle $validate_url .= '&pgtUrl='.urlencode($this->_getCallbackURL()); 3448*d10b5556SXylle } 3449*d10b5556SXylle 3450*d10b5556SXylle if ( $renew ) { 3451*d10b5556SXylle // pass the renew 3452*d10b5556SXylle $validate_url .= '&renew=true'; 3453*d10b5556SXylle } 3454*d10b5556SXylle 3455*d10b5556SXylle // open and read the URL 3456*d10b5556SXylle if ( !$this->_readURL($validate_url, $headers, $text_response, $err_msg) ) { 3457*d10b5556SXylle phpCAS::trace( 3458*d10b5556SXylle 'could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')' 3459*d10b5556SXylle ); 3460*d10b5556SXylle throw new CAS_AuthenticationException( 3461*d10b5556SXylle $this, 'Ticket not validated', $validate_url, 3462*d10b5556SXylle true/*$no_response*/ 3463*d10b5556SXylle ); 3464*d10b5556SXylle } 3465*d10b5556SXylle 3466*d10b5556SXylle // create new DOMDocument object 3467*d10b5556SXylle $dom = new DOMDocument(); 3468*d10b5556SXylle // Fix possible whitspace problems 3469*d10b5556SXylle $dom->preserveWhiteSpace = false; 3470*d10b5556SXylle // CAS servers should only return data in utf-8 3471*d10b5556SXylle $dom->encoding = "utf-8"; 3472*d10b5556SXylle // read the response of the CAS server into a DOMDocument object 3473*d10b5556SXylle if ( !($dom->loadXML($text_response))) { 3474*d10b5556SXylle // read failed 3475*d10b5556SXylle throw new CAS_AuthenticationException( 3476*d10b5556SXylle $this, 'Ticket not validated', $validate_url, 3477*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, $text_response 3478*d10b5556SXylle ); 3479*d10b5556SXylle } else if ( !($tree_response = $dom->documentElement) ) { 3480*d10b5556SXylle // read the root node of the XML tree 3481*d10b5556SXylle // read failed 3482*d10b5556SXylle throw new CAS_AuthenticationException( 3483*d10b5556SXylle $this, 'Ticket not validated', $validate_url, 3484*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, $text_response 3485*d10b5556SXylle ); 3486*d10b5556SXylle } else if ($tree_response->localName != 'serviceResponse') { 3487*d10b5556SXylle // insure that tag name is 'serviceResponse' 3488*d10b5556SXylle // bad root node 3489*d10b5556SXylle throw new CAS_AuthenticationException( 3490*d10b5556SXylle $this, 'Ticket not validated', $validate_url, 3491*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, $text_response 3492*d10b5556SXylle ); 3493*d10b5556SXylle } else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) { 3494*d10b5556SXylle // authentication failed, extract the error code and message and throw exception 3495*d10b5556SXylle $auth_fail_list = $tree_response 3496*d10b5556SXylle ->getElementsByTagName("authenticationFailure"); 3497*d10b5556SXylle throw new CAS_AuthenticationException( 3498*d10b5556SXylle $this, 'Ticket not validated', $validate_url, 3499*d10b5556SXylle false/*$no_response*/, false/*$bad_response*/, 3500*d10b5556SXylle $text_response, 3501*d10b5556SXylle $auth_fail_list->item(0)->getAttribute('code')/*$err_code*/, 3502*d10b5556SXylle trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/ 3503*d10b5556SXylle ); 3504*d10b5556SXylle } else if ($tree_response->getElementsByTagName("authenticationSuccess")->length != 0) { 3505*d10b5556SXylle // authentication succeded, extract the user name 3506*d10b5556SXylle $success_elements = $tree_response 3507*d10b5556SXylle ->getElementsByTagName("authenticationSuccess"); 3508*d10b5556SXylle if ( $success_elements->item(0)->getElementsByTagName("user")->length == 0) { 3509*d10b5556SXylle // no user specified => error 3510*d10b5556SXylle throw new CAS_AuthenticationException( 3511*d10b5556SXylle $this, 'Ticket not validated', $validate_url, 3512*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, $text_response 3513*d10b5556SXylle ); 3514*d10b5556SXylle } else { 3515*d10b5556SXylle $this->_setUser( 3516*d10b5556SXylle trim( 3517*d10b5556SXylle $success_elements->item(0)->getElementsByTagName("user")->item(0)->nodeValue 3518*d10b5556SXylle ) 3519*d10b5556SXylle ); 3520*d10b5556SXylle $this->_readExtraAttributesCas20($success_elements); 3521*d10b5556SXylle // Store the proxies we are sitting behind for authorization checking 3522*d10b5556SXylle $proxyList = array(); 3523*d10b5556SXylle if ( sizeof($arr = $success_elements->item(0)->getElementsByTagName("proxy")) > 0) { 3524*d10b5556SXylle foreach ($arr as $proxyElem) { 3525*d10b5556SXylle phpCAS::trace("Found Proxy: ".$proxyElem->nodeValue); 3526*d10b5556SXylle $proxyList[] = trim($proxyElem->nodeValue); 3527*d10b5556SXylle } 3528*d10b5556SXylle $this->_setProxies($proxyList); 3529*d10b5556SXylle phpCAS::trace("Storing Proxy List"); 3530*d10b5556SXylle } 3531*d10b5556SXylle // Check if the proxies in front of us are allowed 3532*d10b5556SXylle if (!$this->getAllowedProxyChains()->isProxyListAllowed($proxyList)) { 3533*d10b5556SXylle throw new CAS_AuthenticationException( 3534*d10b5556SXylle $this, 'Proxy not allowed', $validate_url, 3535*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, 3536*d10b5556SXylle $text_response 3537*d10b5556SXylle ); 3538*d10b5556SXylle } else { 3539*d10b5556SXylle $result = true; 3540*d10b5556SXylle } 3541*d10b5556SXylle } 3542*d10b5556SXylle } else { 3543*d10b5556SXylle throw new CAS_AuthenticationException( 3544*d10b5556SXylle $this, 'Ticket not validated', $validate_url, 3545*d10b5556SXylle false/*$no_response*/, true/*$bad_response*/, 3546*d10b5556SXylle $text_response 3547*d10b5556SXylle ); 3548*d10b5556SXylle } 3549*d10b5556SXylle 3550*d10b5556SXylle $this->_renameSession($this->getTicket()); 3551*d10b5556SXylle 3552*d10b5556SXylle // at this step, Ticket has been validated and $this->_user has been set, 3553*d10b5556SXylle 3554*d10b5556SXylle phpCAS::traceEnd($result); 3555*d10b5556SXylle return $result; 3556*d10b5556SXylle } 3557*d10b5556SXylle 3558*d10b5556SXylle /** 3559*d10b5556SXylle * This method recursively parses the attribute XML. 3560*d10b5556SXylle * It also collapses name-value pairs into a single 3561*d10b5556SXylle * array entry. It parses all common formats of 3562*d10b5556SXylle * attributes and well formed XML files. 3563*d10b5556SXylle * 3564*d10b5556SXylle * @param string $root the DOM root element to be parsed 3565*d10b5556SXylle * @param string $namespace namespace of the elements 3566*d10b5556SXylle * 3567*d10b5556SXylle * @return an array of the parsed XML elements 3568*d10b5556SXylle * 3569*d10b5556SXylle * Formats tested: 3570*d10b5556SXylle * 3571*d10b5556SXylle * "Jasig Style" Attributes: 3572*d10b5556SXylle * 3573*d10b5556SXylle * <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> 3574*d10b5556SXylle * <cas:authenticationSuccess> 3575*d10b5556SXylle * <cas:user>jsmith</cas:user> 3576*d10b5556SXylle * <cas:attributes> 3577*d10b5556SXylle * <cas:attraStyle>RubyCAS</cas:attraStyle> 3578*d10b5556SXylle * <cas:surname>Smith</cas:surname> 3579*d10b5556SXylle * <cas:givenName>John</cas:givenName> 3580*d10b5556SXylle * <cas:memberOf>CN=Staff,OU=Groups,DC=example,DC=edu</cas:memberOf> 3581*d10b5556SXylle * <cas:memberOf>CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu</cas:memberOf> 3582*d10b5556SXylle * </cas:attributes> 3583*d10b5556SXylle * <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket> 3584*d10b5556SXylle * </cas:authenticationSuccess> 3585*d10b5556SXylle * </cas:serviceResponse> 3586*d10b5556SXylle * 3587*d10b5556SXylle * "Jasig Style" Attributes (longer version): 3588*d10b5556SXylle * 3589*d10b5556SXylle * <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> 3590*d10b5556SXylle * <cas:authenticationSuccess> 3591*d10b5556SXylle * <cas:user>jsmith</cas:user> 3592*d10b5556SXylle * <cas:attributes> 3593*d10b5556SXylle * <cas:attribute> 3594*d10b5556SXylle * <cas:name>surname</cas:name> 3595*d10b5556SXylle * <cas:value>Smith</cas:value> 3596*d10b5556SXylle * </cas:attribute> 3597*d10b5556SXylle * <cas:attribute> 3598*d10b5556SXylle * <cas:name>givenName</cas:name> 3599*d10b5556SXylle * <cas:value>John</cas:value> 3600*d10b5556SXylle * </cas:attribute> 3601*d10b5556SXylle * <cas:attribute> 3602*d10b5556SXylle * <cas:name>memberOf</cas:name> 3603*d10b5556SXylle * <cas:value>['CN=Staff,OU=Groups,DC=example,DC=edu', 'CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu']</cas:value> 3604*d10b5556SXylle * </cas:attribute> 3605*d10b5556SXylle * </cas:attributes> 3606*d10b5556SXylle * <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket> 3607*d10b5556SXylle * </cas:authenticationSuccess> 3608*d10b5556SXylle * </cas:serviceResponse> 3609*d10b5556SXylle * 3610*d10b5556SXylle * "RubyCAS Style" attributes 3611*d10b5556SXylle * 3612*d10b5556SXylle * <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> 3613*d10b5556SXylle * <cas:authenticationSuccess> 3614*d10b5556SXylle * <cas:user>jsmith</cas:user> 3615*d10b5556SXylle * 3616*d10b5556SXylle * <cas:attraStyle>RubyCAS</cas:attraStyle> 3617*d10b5556SXylle * <cas:surname>Smith</cas:surname> 3618*d10b5556SXylle * <cas:givenName>John</cas:givenName> 3619*d10b5556SXylle * <cas:memberOf>CN=Staff,OU=Groups,DC=example,DC=edu</cas:memberOf> 3620*d10b5556SXylle * <cas:memberOf>CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu</cas:memberOf> 3621*d10b5556SXylle * 3622*d10b5556SXylle * <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket> 3623*d10b5556SXylle * </cas:authenticationSuccess> 3624*d10b5556SXylle * </cas:serviceResponse> 3625*d10b5556SXylle * 3626*d10b5556SXylle * "Name-Value" attributes. 3627*d10b5556SXylle * 3628*d10b5556SXylle * Attribute format from these mailing list thread: 3629*d10b5556SXylle * http://jasig.275507.n4.nabble.com/CAS-attributes-and-how-they-appear-in-the-CAS-response-td264272.html 3630*d10b5556SXylle * Note: This is a less widely used format, but in use by at least two institutions. 3631*d10b5556SXylle * 3632*d10b5556SXylle * <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'> 3633*d10b5556SXylle * <cas:authenticationSuccess> 3634*d10b5556SXylle * <cas:user>jsmith</cas:user> 3635*d10b5556SXylle * 3636*d10b5556SXylle * <cas:attribute name='attraStyle' value='Name-Value' /> 3637*d10b5556SXylle * <cas:attribute name='surname' value='Smith' /> 3638*d10b5556SXylle * <cas:attribute name='givenName' value='John' /> 3639*d10b5556SXylle * <cas:attribute name='memberOf' value='CN=Staff,OU=Groups,DC=example,DC=edu' /> 3640*d10b5556SXylle * <cas:attribute name='memberOf' value='CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu' /> 3641*d10b5556SXylle * 3642*d10b5556SXylle * <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket> 3643*d10b5556SXylle * </cas:authenticationSuccess> 3644*d10b5556SXylle * </cas:serviceResponse> 3645*d10b5556SXylle * 3646*d10b5556SXylle * result: 3647*d10b5556SXylle * 3648*d10b5556SXylle * Array ( 3649*d10b5556SXylle * [surname] => Smith 3650*d10b5556SXylle * [givenName] => John 3651*d10b5556SXylle * [memberOf] => Array ( 3652*d10b5556SXylle * [0] => CN=Staff, OU=Groups, DC=example, DC=edu 3653*d10b5556SXylle * [1] => CN=Spanish Department, OU=Departments, OU=Groups, DC=example, DC=edu 3654*d10b5556SXylle * ) 3655*d10b5556SXylle * ) 3656*d10b5556SXylle */ 3657*d10b5556SXylle private function _xml_to_array($root, $namespace = "cas") 3658*d10b5556SXylle { 3659*d10b5556SXylle $result = array(); 3660*d10b5556SXylle if ($root->hasAttributes()) { 3661*d10b5556SXylle $attrs = $root->attributes; 3662*d10b5556SXylle $pair = array(); 3663*d10b5556SXylle foreach ($attrs as $attr) { 3664*d10b5556SXylle if ($attr->name === "name") { 3665*d10b5556SXylle $pair['name'] = $attr->value; 3666*d10b5556SXylle } elseif ($attr->name === "value") { 3667*d10b5556SXylle $pair['value'] = $attr->value; 3668*d10b5556SXylle } else { 3669*d10b5556SXylle $result[$attr->name] = $attr->value; 3670*d10b5556SXylle } 3671*d10b5556SXylle if (array_key_exists('name', $pair) && array_key_exists('value', $pair)) { 3672*d10b5556SXylle $result[$pair['name']] = $pair['value']; 3673*d10b5556SXylle } 3674*d10b5556SXylle } 3675*d10b5556SXylle } 3676*d10b5556SXylle if ($root->hasChildNodes()) { 3677*d10b5556SXylle $children = $root->childNodes; 3678*d10b5556SXylle if ($children->length == 1) { 3679*d10b5556SXylle $child = $children->item(0); 3680*d10b5556SXylle if ($child->nodeType == XML_TEXT_NODE) { 3681*d10b5556SXylle $result['_value'] = $child->nodeValue; 3682*d10b5556SXylle return (count($result) == 1) ? $result['_value'] : $result; 3683*d10b5556SXylle } 3684*d10b5556SXylle } 3685*d10b5556SXylle $groups = array(); 3686*d10b5556SXylle foreach ($children as $child) { 3687*d10b5556SXylle $child_nodeName = str_ireplace($namespace . ":", "", $child->nodeName); 3688*d10b5556SXylle if (in_array($child_nodeName, array("user", "proxies", "proxyGrantingTicket"))) { 3689*d10b5556SXylle continue; 3690*d10b5556SXylle } 3691*d10b5556SXylle if (!isset($result[$child_nodeName])) { 3692*d10b5556SXylle $res = $this->_xml_to_array($child, $namespace); 3693*d10b5556SXylle if (!empty($res)) { 3694*d10b5556SXylle $result[$child_nodeName] = $this->_xml_to_array($child, $namespace); 3695*d10b5556SXylle } 3696*d10b5556SXylle } else { 3697*d10b5556SXylle if (!isset($groups[$child_nodeName])) { 3698*d10b5556SXylle $result[$child_nodeName] = array($result[$child_nodeName]); 3699*d10b5556SXylle $groups[$child_nodeName] = 1; 3700*d10b5556SXylle } 3701*d10b5556SXylle $result[$child_nodeName][] = $this->_xml_to_array($child, $namespace); 3702*d10b5556SXylle } 3703*d10b5556SXylle } 3704*d10b5556SXylle } 3705*d10b5556SXylle return $result; 3706*d10b5556SXylle } 3707*d10b5556SXylle 3708*d10b5556SXylle /** 3709*d10b5556SXylle * This method parses a "JSON-like array" of strings 3710*d10b5556SXylle * into an array of strings 3711*d10b5556SXylle * 3712*d10b5556SXylle * @param string $json_value the json-like string: 3713*d10b5556SXylle * e.g.: 3714*d10b5556SXylle * ['CN=Staff,OU=Groups,DC=example,DC=edu', 'CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu'] 3715*d10b5556SXylle * 3716*d10b5556SXylle * @return array of strings Description 3717*d10b5556SXylle * e.g.: 3718*d10b5556SXylle * Array ( 3719*d10b5556SXylle * [0] => CN=Staff,OU=Groups,DC=example,DC=edu 3720*d10b5556SXylle * [1] => CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu 3721*d10b5556SXylle * ) 3722*d10b5556SXylle */ 3723*d10b5556SXylle private function _parse_json_like_array_value($json_value) 3724*d10b5556SXylle { 3725*d10b5556SXylle $parts = explode(",", trim($json_value, "[]")); 3726*d10b5556SXylle $out = array(); 3727*d10b5556SXylle $quote = ''; 3728*d10b5556SXylle foreach ($parts as $part) { 3729*d10b5556SXylle $part = trim($part); 3730*d10b5556SXylle if ($quote === '') { 3731*d10b5556SXylle $value = ""; 3732*d10b5556SXylle if ($this->_startsWith($part, '\'')) { 3733*d10b5556SXylle $quote = '\''; 3734*d10b5556SXylle } elseif ($this->_startsWith($part, '"')) { 3735*d10b5556SXylle $quote = '"'; 3736*d10b5556SXylle } else { 3737*d10b5556SXylle $out[] = $part; 3738*d10b5556SXylle } 3739*d10b5556SXylle $part = ltrim($part, $quote); 3740*d10b5556SXylle } 3741*d10b5556SXylle if ($quote !== '') { 3742*d10b5556SXylle $value .= $part; 3743*d10b5556SXylle if ($this->_endsWith($part, $quote)) { 3744*d10b5556SXylle $out[] = rtrim($value, $quote); 3745*d10b5556SXylle $quote = ''; 3746*d10b5556SXylle } else { 3747*d10b5556SXylle $value .= ", "; 3748*d10b5556SXylle }; 3749*d10b5556SXylle } 3750*d10b5556SXylle } 3751*d10b5556SXylle return $out; 3752*d10b5556SXylle } 3753*d10b5556SXylle 3754*d10b5556SXylle /** 3755*d10b5556SXylle * This method recursively removes unneccessary hirarchy levels in array-trees. 3756*d10b5556SXylle * into an array of strings 3757*d10b5556SXylle * 3758*d10b5556SXylle * @param array $arr the array to flatten 3759*d10b5556SXylle * e.g.: 3760*d10b5556SXylle * Array ( 3761*d10b5556SXylle * [attributes] => Array ( 3762*d10b5556SXylle * [attribute] => Array ( 3763*d10b5556SXylle * [0] => Array ( 3764*d10b5556SXylle * [name] => surname 3765*d10b5556SXylle * [value] => Smith 3766*d10b5556SXylle * ) 3767*d10b5556SXylle * [1] => Array ( 3768*d10b5556SXylle * [name] => givenName 3769*d10b5556SXylle * [value] => John 3770*d10b5556SXylle * ) 3771*d10b5556SXylle * [2] => Array ( 3772*d10b5556SXylle * [name] => memberOf 3773*d10b5556SXylle * [value] => ['CN=Staff,OU=Groups,DC=example,DC=edu', 'CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu'] 3774*d10b5556SXylle * ) 3775*d10b5556SXylle * ) 3776*d10b5556SXylle * ) 3777*d10b5556SXylle * ) 3778*d10b5556SXylle * 3779*d10b5556SXylle * @return array the flattened array 3780*d10b5556SXylle * e.g.: 3781*d10b5556SXylle * Array ( 3782*d10b5556SXylle * [attribute] => Array ( 3783*d10b5556SXylle * [surname] => Smith 3784*d10b5556SXylle * [givenName] => John 3785*d10b5556SXylle * [memberOf] => Array ( 3786*d10b5556SXylle * [0] => CN=Staff, OU=Groups, DC=example, DC=edu 3787*d10b5556SXylle * [1] => CN=Spanish Department, OU=Departments, OU=Groups, DC=example, DC=edu 3788*d10b5556SXylle * ) 3789*d10b5556SXylle * ) 3790*d10b5556SXylle * ) 3791*d10b5556SXylle */ 3792*d10b5556SXylle private function _flatten_array($arr) 3793*d10b5556SXylle { 3794*d10b5556SXylle if (!is_array($arr)) { 3795*d10b5556SXylle if ($this->_startsWith($arr, '[') && $this->_endsWith($arr, ']')) { 3796*d10b5556SXylle return $this->_parse_json_like_array_value($arr); 3797*d10b5556SXylle } else { 3798*d10b5556SXylle return $arr; 3799*d10b5556SXylle } 3800*d10b5556SXylle } 3801*d10b5556SXylle $out = array(); 3802*d10b5556SXylle foreach ($arr as $key => $val) { 3803*d10b5556SXylle if (!is_array($val)) { 3804*d10b5556SXylle $out[$key] = $val; 3805*d10b5556SXylle } else { 3806*d10b5556SXylle switch (count($val)) { 3807*d10b5556SXylle case 1 : { 3808*d10b5556SXylle $key = key($val); 3809*d10b5556SXylle if (array_key_exists($key, $out)) { 3810*d10b5556SXylle $value = $out[$key]; 3811*d10b5556SXylle if (!is_array($value)) { 3812*d10b5556SXylle $out[$key] = array(); 3813*d10b5556SXylle $out[$key][] = $value; 3814*d10b5556SXylle } 3815*d10b5556SXylle $out[$key][] = $this->_flatten_array($val[$key]); 3816*d10b5556SXylle } else { 3817*d10b5556SXylle $out[$key] = $this->_flatten_array($val[$key]); 3818*d10b5556SXylle }; 3819*d10b5556SXylle break; 3820*d10b5556SXylle }; 3821*d10b5556SXylle case 2 : { 3822*d10b5556SXylle if (array_key_exists("name", $val) && array_key_exists("value", $val)) { 3823*d10b5556SXylle $key = $val['name']; 3824*d10b5556SXylle if (array_key_exists($key, $out)) { 3825*d10b5556SXylle $value = $out[$key]; 3826*d10b5556SXylle if (!is_array($value)) { 3827*d10b5556SXylle $out[$key] = array(); 3828*d10b5556SXylle $out[$key][] = $value; 3829*d10b5556SXylle } 3830*d10b5556SXylle $out[$key][] = $this->_flatten_array($val['value']); 3831*d10b5556SXylle } else { 3832*d10b5556SXylle $out[$key] = $this->_flatten_array($val['value']); 3833*d10b5556SXylle }; 3834*d10b5556SXylle } else { 3835*d10b5556SXylle $out[$key] = $this->_flatten_array($val); 3836*d10b5556SXylle } 3837*d10b5556SXylle break; 3838*d10b5556SXylle }; 3839*d10b5556SXylle default: { 3840*d10b5556SXylle $out[$key] = $this->_flatten_array($val); 3841*d10b5556SXylle } 3842*d10b5556SXylle } 3843*d10b5556SXylle } 3844*d10b5556SXylle } 3845*d10b5556SXylle return $out; 3846*d10b5556SXylle } 3847*d10b5556SXylle 3848*d10b5556SXylle /** 3849*d10b5556SXylle * This method will parse the DOM and pull out the attributes from the XML 3850*d10b5556SXylle * payload and put them into an array, then put the array into the session. 3851*d10b5556SXylle * 3852*d10b5556SXylle * @param DOMNodeList $success_elements payload of the response 3853*d10b5556SXylle * 3854*d10b5556SXylle * @return bool true when successfull, halt otherwise by calling 3855*d10b5556SXylle * CAS_Client::_authError(). 3856*d10b5556SXylle */ 3857*d10b5556SXylle private function _readExtraAttributesCas20($success_elements) 3858*d10b5556SXylle { 3859*d10b5556SXylle phpCAS::traceBegin(); 3860*d10b5556SXylle 3861*d10b5556SXylle $extra_attributes = array(); 3862*d10b5556SXylle if ($this->_casAttributeParserCallbackFunction !== null 3863*d10b5556SXylle && is_callable($this->_casAttributeParserCallbackFunction) 3864*d10b5556SXylle ) { 3865*d10b5556SXylle array_unshift($this->_casAttributeParserCallbackArgs, $success_elements->item(0)); 3866*d10b5556SXylle phpCAS :: trace("Calling attritubeParser callback"); 3867*d10b5556SXylle $extra_attributes = call_user_func_array( 3868*d10b5556SXylle $this->_casAttributeParserCallbackFunction, 3869*d10b5556SXylle $this->_casAttributeParserCallbackArgs 3870*d10b5556SXylle ); 3871*d10b5556SXylle } else { 3872*d10b5556SXylle phpCAS :: trace("Parse extra attributes: "); 3873*d10b5556SXylle $attributes = $this->_xml_to_array($success_elements->item(0)); 3874*d10b5556SXylle phpCAS :: trace(print_r($attributes,true). "\nFLATTEN Array: "); 3875*d10b5556SXylle $extra_attributes = $this->_flatten_array($attributes); 3876*d10b5556SXylle phpCAS :: trace(print_r($extra_attributes, true)."\nFILTER : "); 3877*d10b5556SXylle if (array_key_exists("attribute", $extra_attributes)) { 3878*d10b5556SXylle $extra_attributes = $extra_attributes["attribute"]; 3879*d10b5556SXylle } elseif (array_key_exists("attributes", $extra_attributes)) { 3880*d10b5556SXylle $extra_attributes = $extra_attributes["attributes"]; 3881*d10b5556SXylle }; 3882*d10b5556SXylle phpCAS :: trace(print_r($extra_attributes, true)."return"); 3883*d10b5556SXylle } 3884*d10b5556SXylle $this->setAttributes($extra_attributes); 3885*d10b5556SXylle phpCAS::traceEnd(); 3886*d10b5556SXylle return true; 3887*d10b5556SXylle } 3888*d10b5556SXylle 3889*d10b5556SXylle /** 3890*d10b5556SXylle * Add an attribute value to an array of attributes. 3891*d10b5556SXylle * 3892*d10b5556SXylle * @param array &$attributeArray reference to array 3893*d10b5556SXylle * @param string $name name of attribute 3894*d10b5556SXylle * @param string $value value of attribute 3895*d10b5556SXylle * 3896*d10b5556SXylle * @return void 3897*d10b5556SXylle */ 3898*d10b5556SXylle private function _addAttributeToArray(array &$attributeArray, $name, $value) 3899*d10b5556SXylle { 3900*d10b5556SXylle // If multiple attributes exist, add as an array value 3901*d10b5556SXylle if (isset($attributeArray[$name])) { 3902*d10b5556SXylle // Initialize the array with the existing value 3903*d10b5556SXylle if (!is_array($attributeArray[$name])) { 3904*d10b5556SXylle $existingValue = $attributeArray[$name]; 3905*d10b5556SXylle $attributeArray[$name] = array($existingValue); 3906*d10b5556SXylle } 3907*d10b5556SXylle 3908*d10b5556SXylle $attributeArray[$name][] = trim($value); 3909*d10b5556SXylle } else { 3910*d10b5556SXylle $attributeArray[$name] = trim($value); 3911*d10b5556SXylle } 3912*d10b5556SXylle } 3913*d10b5556SXylle 3914*d10b5556SXylle /** @} */ 3915*d10b5556SXylle 3916*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 3917*d10b5556SXylle // XX XX 3918*d10b5556SXylle // XX MISC XX 3919*d10b5556SXylle // XX XX 3920*d10b5556SXylle // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 3921*d10b5556SXylle 3922*d10b5556SXylle /** 3923*d10b5556SXylle * @addtogroup internalMisc 3924*d10b5556SXylle * @{ 3925*d10b5556SXylle */ 3926*d10b5556SXylle 3927*d10b5556SXylle // ######################################################################## 3928*d10b5556SXylle // URL 3929*d10b5556SXylle // ######################################################################## 3930*d10b5556SXylle /** 3931*d10b5556SXylle * the URL of the current request (without any ticket CGI parameter). Written 3932*d10b5556SXylle * and read by CAS_Client::getURL(). 3933*d10b5556SXylle * 3934*d10b5556SXylle * @hideinitializer 3935*d10b5556SXylle */ 3936*d10b5556SXylle private $_url = ''; 3937*d10b5556SXylle 3938*d10b5556SXylle 3939*d10b5556SXylle /** 3940*d10b5556SXylle * This method sets the URL of the current request 3941*d10b5556SXylle * 3942*d10b5556SXylle * @param string $url url to set for service 3943*d10b5556SXylle * 3944*d10b5556SXylle * @return void 3945*d10b5556SXylle */ 3946*d10b5556SXylle public function setURL($url) 3947*d10b5556SXylle { 3948*d10b5556SXylle // Argument Validation 3949*d10b5556SXylle if (gettype($url) != 'string') 3950*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 3951*d10b5556SXylle 3952*d10b5556SXylle $this->_url = $url; 3953*d10b5556SXylle } 3954*d10b5556SXylle 3955*d10b5556SXylle /** 3956*d10b5556SXylle * This method returns the URL of the current request (without any ticket 3957*d10b5556SXylle * CGI parameter). 3958*d10b5556SXylle * 3959*d10b5556SXylle * @return string The URL 3960*d10b5556SXylle */ 3961*d10b5556SXylle public function getURL() 3962*d10b5556SXylle { 3963*d10b5556SXylle phpCAS::traceBegin(); 3964*d10b5556SXylle // the URL is built when needed only 3965*d10b5556SXylle if ( empty($this->_url) ) { 3966*d10b5556SXylle // remove the ticket if present in the URL 3967*d10b5556SXylle $final_uri = $this->getServiceBaseUrl()->get(); 3968*d10b5556SXylle $request_uri = explode('?', $_SERVER['REQUEST_URI'], 2); 3969*d10b5556SXylle $final_uri .= $request_uri[0]; 3970*d10b5556SXylle 3971*d10b5556SXylle if (isset($request_uri[1]) && $request_uri[1]) { 3972*d10b5556SXylle $query_string= $this->_removeParameterFromQueryString('ticket', $request_uri[1]); 3973*d10b5556SXylle 3974*d10b5556SXylle // If the query string still has anything left, 3975*d10b5556SXylle // append it to the final URI 3976*d10b5556SXylle if ($query_string !== '') { 3977*d10b5556SXylle $final_uri .= "?$query_string"; 3978*d10b5556SXylle } 3979*d10b5556SXylle } 3980*d10b5556SXylle 3981*d10b5556SXylle phpCAS::trace("Final URI: $final_uri"); 3982*d10b5556SXylle $this->setURL($final_uri); 3983*d10b5556SXylle } 3984*d10b5556SXylle phpCAS::traceEnd($this->_url); 3985*d10b5556SXylle return $this->_url; 3986*d10b5556SXylle } 3987*d10b5556SXylle 3988*d10b5556SXylle /** 3989*d10b5556SXylle * This method sets the base URL of the CAS server. 3990*d10b5556SXylle * 3991*d10b5556SXylle * @param string $url the base URL 3992*d10b5556SXylle * 3993*d10b5556SXylle * @return string base url 3994*d10b5556SXylle */ 3995*d10b5556SXylle public function setBaseURL($url) 3996*d10b5556SXylle { 3997*d10b5556SXylle // Argument Validation 3998*d10b5556SXylle if (gettype($url) != 'string') 3999*d10b5556SXylle throw new CAS_TypeMismatchException($url, '$url', 'string'); 4000*d10b5556SXylle 4001*d10b5556SXylle return $this->_server['base_url'] = $url; 4002*d10b5556SXylle } 4003*d10b5556SXylle 4004*d10b5556SXylle /** 4005*d10b5556SXylle * The ServiceBaseUrl object that provides base URL during service URL 4006*d10b5556SXylle * discovery process. 4007*d10b5556SXylle * 4008*d10b5556SXylle * @var CAS_ServiceBaseUrl_Interface 4009*d10b5556SXylle * 4010*d10b5556SXylle * @hideinitializer 4011*d10b5556SXylle */ 4012*d10b5556SXylle private $_serviceBaseUrl = null; 4013*d10b5556SXylle 4014*d10b5556SXylle /** 4015*d10b5556SXylle * Answer the CAS_ServiceBaseUrl_Interface object for this client. 4016*d10b5556SXylle * 4017*d10b5556SXylle * @return CAS_ServiceBaseUrl_Interface 4018*d10b5556SXylle */ 4019*d10b5556SXylle public function getServiceBaseUrl() 4020*d10b5556SXylle { 4021*d10b5556SXylle if (empty($this->_serviceBaseUrl)) { 4022*d10b5556SXylle phpCAS::error("ServiceBaseUrl object is not initialized"); 4023*d10b5556SXylle } 4024*d10b5556SXylle return $this->_serviceBaseUrl; 4025*d10b5556SXylle } 4026*d10b5556SXylle 4027*d10b5556SXylle /** 4028*d10b5556SXylle * This method sets the service base URL used during service URL discovery process. 4029*d10b5556SXylle * 4030*d10b5556SXylle * This is required since phpCAS 1.6.0 to protect the integrity of the authentication. 4031*d10b5556SXylle * 4032*d10b5556SXylle * @since phpCAS 1.6.0 4033*d10b5556SXylle * 4034*d10b5556SXylle * @param $name can be any of the following: 4035*d10b5556SXylle * - A base URL string. The service URL discovery will always use this (protocol, 4036*d10b5556SXylle * hostname and optional port number) without using any external host names. 4037*d10b5556SXylle * - An array of base URL strings. The service URL discovery will check against 4038*d10b5556SXylle * this list before using the auto discovered base URL. If there is no match, 4039*d10b5556SXylle * the first base URL in the array will be used as the default. This option is 4040*d10b5556SXylle * helpful if your PHP website is accessible through multiple domains without a 4041*d10b5556SXylle * canonical name, or through both HTTP and HTTPS. 4042*d10b5556SXylle * - A class that implements CAS_ServiceBaseUrl_Interface. If you need to customize 4043*d10b5556SXylle * the base URL discovery behavior, you can pass in a class that implements the 4044*d10b5556SXylle * interface. 4045*d10b5556SXylle * 4046*d10b5556SXylle * @return void 4047*d10b5556SXylle */ 4048*d10b5556SXylle private function _setServiceBaseUrl($name) 4049*d10b5556SXylle { 4050*d10b5556SXylle if (is_array($name)) { 4051*d10b5556SXylle $this->_serviceBaseUrl = new CAS_ServiceBaseUrl_AllowedListDiscovery($name); 4052*d10b5556SXylle } else if (is_string($name)) { 4053*d10b5556SXylle $this->_serviceBaseUrl = new CAS_ServiceBaseUrl_Static($name); 4054*d10b5556SXylle } else if ($name instanceof CAS_ServiceBaseUrl_Interface) { 4055*d10b5556SXylle $this->_serviceBaseUrl = $name; 4056*d10b5556SXylle } else { 4057*d10b5556SXylle throw new CAS_TypeMismatchException($name, '$name', 'array, string, or CAS_ServiceBaseUrl_Interface object'); 4058*d10b5556SXylle } 4059*d10b5556SXylle } 4060*d10b5556SXylle 4061*d10b5556SXylle /** 4062*d10b5556SXylle * Removes a parameter from a query string 4063*d10b5556SXylle * 4064*d10b5556SXylle * @param string $parameterName name of parameter 4065*d10b5556SXylle * @param string $queryString query string 4066*d10b5556SXylle * 4067*d10b5556SXylle * @return string new query string 4068*d10b5556SXylle * 4069*d10b5556SXylle * @link http://stackoverflow.com/questions/1842681/regular-expression-to-remove-one-parameter-from-query-string 4070*d10b5556SXylle */ 4071*d10b5556SXylle private function _removeParameterFromQueryString($parameterName, $queryString) 4072*d10b5556SXylle { 4073*d10b5556SXylle $parameterName = preg_quote($parameterName); 4074*d10b5556SXylle return preg_replace( 4075*d10b5556SXylle "/&$parameterName(=[^&]*)?|^$parameterName(=[^&]*)?&?/", 4076*d10b5556SXylle '', $queryString 4077*d10b5556SXylle ); 4078*d10b5556SXylle } 4079*d10b5556SXylle 4080*d10b5556SXylle /** 4081*d10b5556SXylle * This method is used to append query parameters to an url. Since the url 4082*d10b5556SXylle * might already contain parameter it has to be detected and to build a proper 4083*d10b5556SXylle * URL 4084*d10b5556SXylle * 4085*d10b5556SXylle * @param string $url base url to add the query params to 4086*d10b5556SXylle * @param string $query params in query form with & separated 4087*d10b5556SXylle * 4088*d10b5556SXylle * @return string url with query params 4089*d10b5556SXylle */ 4090*d10b5556SXylle private function _buildQueryUrl($url, $query) 4091*d10b5556SXylle { 4092*d10b5556SXylle $url .= (strstr($url, '?') === false) ? '?' : '&'; 4093*d10b5556SXylle $url .= $query; 4094*d10b5556SXylle return $url; 4095*d10b5556SXylle } 4096*d10b5556SXylle 4097*d10b5556SXylle /** 4098*d10b5556SXylle * This method tests if a string starts with a given character. 4099*d10b5556SXylle * 4100*d10b5556SXylle * @param string $text text to test 4101*d10b5556SXylle * @param string $char character to test for 4102*d10b5556SXylle * 4103*d10b5556SXylle * @return bool true if the $text starts with $char 4104*d10b5556SXylle */ 4105*d10b5556SXylle private function _startsWith($text, $char) 4106*d10b5556SXylle { 4107*d10b5556SXylle return (strpos($text, $char) === 0); 4108*d10b5556SXylle } 4109*d10b5556SXylle 4110*d10b5556SXylle /** 4111*d10b5556SXylle * This method tests if a string ends with a given character 4112*d10b5556SXylle * 4113*d10b5556SXylle * @param string $text text to test 4114*d10b5556SXylle * @param string $char character to test for 4115*d10b5556SXylle * 4116*d10b5556SXylle * @return bool true if the $text ends with $char 4117*d10b5556SXylle */ 4118*d10b5556SXylle private function _endsWith($text, $char) 4119*d10b5556SXylle { 4120*d10b5556SXylle return (strpos(strrev($text), $char) === 0); 4121*d10b5556SXylle } 4122*d10b5556SXylle 4123*d10b5556SXylle /** 4124*d10b5556SXylle * Answer a valid session-id given a CAS ticket. 4125*d10b5556SXylle * 4126*d10b5556SXylle * The output must be deterministic to allow single-log-out when presented with 4127*d10b5556SXylle * the ticket to log-out. 4128*d10b5556SXylle * 4129*d10b5556SXylle * 4130*d10b5556SXylle * @param string $ticket name of the ticket 4131*d10b5556SXylle * 4132*d10b5556SXylle * @return string 4133*d10b5556SXylle */ 4134*d10b5556SXylle private function _sessionIdForTicket($ticket) 4135*d10b5556SXylle { 4136*d10b5556SXylle // Hash the ticket to ensure that the value meets the PHP 7.1 requirement 4137*d10b5556SXylle // that session-ids have a length between 22 and 256 characters. 4138*d10b5556SXylle return hash('sha256', $this->_sessionIdSalt . $ticket); 4139*d10b5556SXylle } 4140*d10b5556SXylle 4141*d10b5556SXylle /** 4142*d10b5556SXylle * Set a salt/seed for the session-id hash to make it harder to guess. 4143*d10b5556SXylle * 4144*d10b5556SXylle * @var string $_sessionIdSalt 4145*d10b5556SXylle */ 4146*d10b5556SXylle private $_sessionIdSalt = ''; 4147*d10b5556SXylle 4148*d10b5556SXylle /** 4149*d10b5556SXylle * Set a salt/seed for the session-id hash to make it harder to guess. 4150*d10b5556SXylle * 4151*d10b5556SXylle * @param string $salt 4152*d10b5556SXylle * 4153*d10b5556SXylle * @return void 4154*d10b5556SXylle */ 4155*d10b5556SXylle public function setSessionIdSalt($salt) { 4156*d10b5556SXylle $this->_sessionIdSalt = (string)$salt; 4157*d10b5556SXylle } 4158*d10b5556SXylle 4159*d10b5556SXylle // ######################################################################## 4160*d10b5556SXylle // AUTHENTICATION ERROR HANDLING 4161*d10b5556SXylle // ######################################################################## 4162*d10b5556SXylle /** 4163*d10b5556SXylle * This method is used to print the HTML output when the user was not 4164*d10b5556SXylle * authenticated. 4165*d10b5556SXylle * 4166*d10b5556SXylle * @param string $failure the failure that occured 4167*d10b5556SXylle * @param string $cas_url the URL the CAS server was asked for 4168*d10b5556SXylle * @param bool $no_response the response from the CAS server (other 4169*d10b5556SXylle * parameters are ignored if true) 4170*d10b5556SXylle * @param bool $bad_response bad response from the CAS server ($err_code 4171*d10b5556SXylle * and $err_msg ignored if true) 4172*d10b5556SXylle * @param string $cas_response the response of the CAS server 4173*d10b5556SXylle * @param int $err_code the error code given by the CAS server 4174*d10b5556SXylle * @param string $err_msg the error message given by the CAS server 4175*d10b5556SXylle * 4176*d10b5556SXylle * @return void 4177*d10b5556SXylle */ 4178*d10b5556SXylle private function _authError( 4179*d10b5556SXylle $failure, 4180*d10b5556SXylle $cas_url, 4181*d10b5556SXylle $no_response=false, 4182*d10b5556SXylle $bad_response=false, 4183*d10b5556SXylle $cas_response='', 4184*d10b5556SXylle $err_code=-1, 4185*d10b5556SXylle $err_msg='' 4186*d10b5556SXylle ) { 4187*d10b5556SXylle phpCAS::traceBegin(); 4188*d10b5556SXylle $lang = $this->getLangObj(); 4189*d10b5556SXylle $this->printHTMLHeader($lang->getAuthenticationFailed()); 4190*d10b5556SXylle $this->printf( 4191*d10b5556SXylle $lang->getYouWereNotAuthenticated(), htmlentities($this->getURL()), 4192*d10b5556SXylle isset($_SERVER['SERVER_ADMIN']) ? $_SERVER['SERVER_ADMIN']:'' 4193*d10b5556SXylle ); 4194*d10b5556SXylle phpCAS::trace('CAS URL: '.$cas_url); 4195*d10b5556SXylle phpCAS::trace('Authentication failure: '.$failure); 4196*d10b5556SXylle if ( $no_response ) { 4197*d10b5556SXylle phpCAS::trace('Reason: no response from the CAS server'); 4198*d10b5556SXylle } else { 4199*d10b5556SXylle if ( $bad_response ) { 4200*d10b5556SXylle phpCAS::trace('Reason: bad response from the CAS server'); 4201*d10b5556SXylle } else { 4202*d10b5556SXylle switch ($this->getServerVersion()) { 4203*d10b5556SXylle case CAS_VERSION_1_0: 4204*d10b5556SXylle phpCAS::trace('Reason: CAS error'); 4205*d10b5556SXylle break; 4206*d10b5556SXylle case CAS_VERSION_2_0: 4207*d10b5556SXylle case CAS_VERSION_3_0: 4208*d10b5556SXylle if ( $err_code === -1 ) { 4209*d10b5556SXylle phpCAS::trace('Reason: no CAS error'); 4210*d10b5556SXylle } else { 4211*d10b5556SXylle phpCAS::trace( 4212*d10b5556SXylle 'Reason: ['.$err_code.'] CAS error: '.$err_msg 4213*d10b5556SXylle ); 4214*d10b5556SXylle } 4215*d10b5556SXylle break; 4216*d10b5556SXylle } 4217*d10b5556SXylle } 4218*d10b5556SXylle phpCAS::trace('CAS response: '.$cas_response); 4219*d10b5556SXylle } 4220*d10b5556SXylle $this->printHTMLFooter(); 4221*d10b5556SXylle phpCAS::traceExit(); 4222*d10b5556SXylle throw new CAS_GracefullTerminationException(); 4223*d10b5556SXylle } 4224*d10b5556SXylle 4225*d10b5556SXylle // ######################################################################## 4226*d10b5556SXylle // PGTIOU/PGTID and logoutRequest rebroadcasting 4227*d10b5556SXylle // ######################################################################## 4228*d10b5556SXylle 4229*d10b5556SXylle /** 4230*d10b5556SXylle * Boolean of whether to rebroadcast pgtIou/pgtId and logoutRequest, and 4231*d10b5556SXylle * array of the nodes. 4232*d10b5556SXylle */ 4233*d10b5556SXylle private $_rebroadcast = false; 4234*d10b5556SXylle private $_rebroadcast_nodes = array(); 4235*d10b5556SXylle 4236*d10b5556SXylle /** 4237*d10b5556SXylle * Constants used for determining rebroadcast node type. 4238*d10b5556SXylle */ 4239*d10b5556SXylle const HOSTNAME = 0; 4240*d10b5556SXylle const IP = 1; 4241*d10b5556SXylle 4242*d10b5556SXylle /** 4243*d10b5556SXylle * Determine the node type from the URL. 4244*d10b5556SXylle * 4245*d10b5556SXylle * @param String $nodeURL The node URL. 4246*d10b5556SXylle * 4247*d10b5556SXylle * @return int hostname 4248*d10b5556SXylle * 4249*d10b5556SXylle */ 4250*d10b5556SXylle private function _getNodeType($nodeURL) 4251*d10b5556SXylle { 4252*d10b5556SXylle phpCAS::traceBegin(); 4253*d10b5556SXylle if (preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $nodeURL)) { 4254*d10b5556SXylle phpCAS::traceEnd(self::IP); 4255*d10b5556SXylle return self::IP; 4256*d10b5556SXylle } else { 4257*d10b5556SXylle phpCAS::traceEnd(self::HOSTNAME); 4258*d10b5556SXylle return self::HOSTNAME; 4259*d10b5556SXylle } 4260*d10b5556SXylle } 4261*d10b5556SXylle 4262*d10b5556SXylle /** 4263*d10b5556SXylle * Store the rebroadcast node for pgtIou/pgtId and logout requests. 4264*d10b5556SXylle * 4265*d10b5556SXylle * @param string $rebroadcastNodeUrl The rebroadcast node URL. 4266*d10b5556SXylle * 4267*d10b5556SXylle * @return void 4268*d10b5556SXylle */ 4269*d10b5556SXylle public function addRebroadcastNode($rebroadcastNodeUrl) 4270*d10b5556SXylle { 4271*d10b5556SXylle // Argument validation 4272*d10b5556SXylle if ( !(bool)preg_match("/^(http|https):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i", $rebroadcastNodeUrl)) 4273*d10b5556SXylle throw new CAS_TypeMismatchException($rebroadcastNodeUrl, '$rebroadcastNodeUrl', 'url'); 4274*d10b5556SXylle 4275*d10b5556SXylle // Store the rebroadcast node and set flag 4276*d10b5556SXylle $this->_rebroadcast = true; 4277*d10b5556SXylle $this->_rebroadcast_nodes[] = $rebroadcastNodeUrl; 4278*d10b5556SXylle } 4279*d10b5556SXylle 4280*d10b5556SXylle /** 4281*d10b5556SXylle * An array to store extra rebroadcast curl options. 4282*d10b5556SXylle */ 4283*d10b5556SXylle private $_rebroadcast_headers = array(); 4284*d10b5556SXylle 4285*d10b5556SXylle /** 4286*d10b5556SXylle * This method is used to add header parameters when rebroadcasting 4287*d10b5556SXylle * pgtIou/pgtId or logoutRequest. 4288*d10b5556SXylle * 4289*d10b5556SXylle * @param string $header Header to send when rebroadcasting. 4290*d10b5556SXylle * 4291*d10b5556SXylle * @return void 4292*d10b5556SXylle */ 4293*d10b5556SXylle public function addRebroadcastHeader($header) 4294*d10b5556SXylle { 4295*d10b5556SXylle if (gettype($header) != 'string') 4296*d10b5556SXylle throw new CAS_TypeMismatchException($header, '$header', 'string'); 4297*d10b5556SXylle 4298*d10b5556SXylle $this->_rebroadcast_headers[] = $header; 4299*d10b5556SXylle } 4300*d10b5556SXylle 4301*d10b5556SXylle /** 4302*d10b5556SXylle * Constants used for determining rebroadcast type (logout or pgtIou/pgtId). 4303*d10b5556SXylle */ 4304*d10b5556SXylle const LOGOUT = 0; 4305*d10b5556SXylle const PGTIOU = 1; 4306*d10b5556SXylle 4307*d10b5556SXylle /** 4308*d10b5556SXylle * This method rebroadcasts logout/pgtIou requests. Can be LOGOUT,PGTIOU 4309*d10b5556SXylle * 4310*d10b5556SXylle * @param int $type type of rebroadcasting. 4311*d10b5556SXylle * 4312*d10b5556SXylle * @return void 4313*d10b5556SXylle */ 4314*d10b5556SXylle private function _rebroadcast($type) 4315*d10b5556SXylle { 4316*d10b5556SXylle phpCAS::traceBegin(); 4317*d10b5556SXylle 4318*d10b5556SXylle $rebroadcast_curl_options = array( 4319*d10b5556SXylle CURLOPT_FAILONERROR => 1, 4320*d10b5556SXylle CURLOPT_FOLLOWLOCATION => 1, 4321*d10b5556SXylle CURLOPT_RETURNTRANSFER => 1, 4322*d10b5556SXylle CURLOPT_CONNECTTIMEOUT => 1, 4323*d10b5556SXylle CURLOPT_TIMEOUT => 4); 4324*d10b5556SXylle 4325*d10b5556SXylle // Try to determine the IP address of the server 4326*d10b5556SXylle if (!empty($_SERVER['SERVER_ADDR'])) { 4327*d10b5556SXylle $ip = $_SERVER['SERVER_ADDR']; 4328*d10b5556SXylle } else if (!empty($_SERVER['LOCAL_ADDR'])) { 4329*d10b5556SXylle // IIS 7 4330*d10b5556SXylle $ip = $_SERVER['LOCAL_ADDR']; 4331*d10b5556SXylle } 4332*d10b5556SXylle // Try to determine the DNS name of the server 4333*d10b5556SXylle if (!empty($ip)) { 4334*d10b5556SXylle $dns = gethostbyaddr($ip); 4335*d10b5556SXylle } 4336*d10b5556SXylle $multiClassName = 'CAS_Request_CurlMultiRequest'; 4337*d10b5556SXylle $multiRequest = new $multiClassName(); 4338*d10b5556SXylle 4339*d10b5556SXylle for ($i = 0; $i < sizeof($this->_rebroadcast_nodes); $i++) { 4340*d10b5556SXylle if ((($this->_getNodeType($this->_rebroadcast_nodes[$i]) == self::HOSTNAME) && !empty($dns) && (stripos($this->_rebroadcast_nodes[$i], $dns) === false)) 4341*d10b5556SXylle || (($this->_getNodeType($this->_rebroadcast_nodes[$i]) == self::IP) && !empty($ip) && (stripos($this->_rebroadcast_nodes[$i], $ip) === false)) 4342*d10b5556SXylle ) { 4343*d10b5556SXylle phpCAS::trace( 4344*d10b5556SXylle 'Rebroadcast target URL: '.$this->_rebroadcast_nodes[$i] 4345*d10b5556SXylle .$_SERVER['REQUEST_URI'] 4346*d10b5556SXylle ); 4347*d10b5556SXylle $className = $this->_requestImplementation; 4348*d10b5556SXylle $request = new $className(); 4349*d10b5556SXylle 4350*d10b5556SXylle $url = $this->_rebroadcast_nodes[$i].$_SERVER['REQUEST_URI']; 4351*d10b5556SXylle $request->setUrl($url); 4352*d10b5556SXylle 4353*d10b5556SXylle if (count($this->_rebroadcast_headers)) { 4354*d10b5556SXylle $request->addHeaders($this->_rebroadcast_headers); 4355*d10b5556SXylle } 4356*d10b5556SXylle 4357*d10b5556SXylle $request->makePost(); 4358*d10b5556SXylle if ($type == self::LOGOUT) { 4359*d10b5556SXylle // Logout request 4360*d10b5556SXylle $request->setPostBody( 4361*d10b5556SXylle 'rebroadcast=false&logoutRequest='.$_POST['logoutRequest'] 4362*d10b5556SXylle ); 4363*d10b5556SXylle } else if ($type == self::PGTIOU) { 4364*d10b5556SXylle // pgtIou/pgtId rebroadcast 4365*d10b5556SXylle $request->setPostBody('rebroadcast=false'); 4366*d10b5556SXylle } 4367*d10b5556SXylle 4368*d10b5556SXylle $request->setCurlOptions($rebroadcast_curl_options); 4369*d10b5556SXylle 4370*d10b5556SXylle $multiRequest->addRequest($request); 4371*d10b5556SXylle } else { 4372*d10b5556SXylle phpCAS::trace( 4373*d10b5556SXylle 'Rebroadcast not sent to self: ' 4374*d10b5556SXylle .$this->_rebroadcast_nodes[$i].' == '.(!empty($ip)?$ip:'') 4375*d10b5556SXylle .'/'.(!empty($dns)?$dns:'') 4376*d10b5556SXylle ); 4377*d10b5556SXylle } 4378*d10b5556SXylle } 4379*d10b5556SXylle // We need at least 1 request 4380*d10b5556SXylle if ($multiRequest->getNumRequests() > 0) { 4381*d10b5556SXylle $multiRequest->send(); 4382*d10b5556SXylle } 4383*d10b5556SXylle phpCAS::traceEnd(); 4384*d10b5556SXylle } 4385*d10b5556SXylle 4386*d10b5556SXylle /** @} */ 4387*d10b5556SXylle} 4388