PageRenderTime 62ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/auth/cas/CAS/CAS/client.php

https://bitbucket.org/ceu/moodle_demo
PHP | 2776 lines | 1684 code | 196 blank | 896 comment | 207 complexity | 570a4a876336b914765dec972b205abe MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * Copyright Š 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. *
  9. * * Redistributions of source code must retain the above copyright notice,
  10. * this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. * * Neither the name of the ESUP-Portail consortium & the JA-SIG
  15. * Collaborative nor the names of its contributors may be used to endorse or
  16. * promote products derived from this software without specific prior
  17. * written permission.
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  22. * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  25. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. */
  29. /**
  30. * @file CAS/client.php
  31. * Main class of the phpCAS library
  32. */
  33. // include internationalization stuff
  34. include_once(dirname(__FILE__).'/languages/languages.php');
  35. // include PGT storage classes
  36. include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php');
  37. /**
  38. * @class CASClient
  39. * The CASClient class is a client interface that provides CAS authentication
  40. * to PHP applications.
  41. *
  42. * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
  43. */
  44. class CASClient
  45. {
  46. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  47. // XX XX
  48. // XX CONFIGURATION XX
  49. // XX XX
  50. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  51. // ########################################################################
  52. // HTML OUTPUT
  53. // ########################################################################
  54. /**
  55. * @addtogroup internalOutput
  56. * @{
  57. */
  58. /**
  59. * This method filters a string by replacing special tokens by appropriate values
  60. * and prints it. The corresponding tokens are taken into account:
  61. * - __CAS_VERSION__
  62. * - __PHPCAS_VERSION__
  63. * - __SERVER_BASE_URL__
  64. *
  65. * Used by CASClient::PrintHTMLHeader() and CASClient::printHTMLFooter().
  66. *
  67. * @param $str the string to filter and output
  68. *
  69. * @private
  70. */
  71. function HTMLFilterOutput($str)
  72. {
  73. $str = str_replace('__CAS_VERSION__',$this->getServerVersion(),$str);
  74. $str = str_replace('__PHPCAS_VERSION__',phpCAS::getVersion(),$str);
  75. $str = str_replace('__SERVER_BASE_URL__',$this->getServerBaseURL(),$str);
  76. echo $str;
  77. }
  78. /**
  79. * A string used to print the header of HTML pages. Written by CASClient::setHTMLHeader(),
  80. * read by CASClient::printHTMLHeader().
  81. *
  82. * @hideinitializer
  83. * @private
  84. * @see CASClient::setHTMLHeader, CASClient::printHTMLHeader()
  85. */
  86. var $_output_header = '';
  87. /**
  88. * This method prints the header of the HTML output (after filtering). If
  89. * CASClient::setHTMLHeader() was not used, a default header is output.
  90. *
  91. * @param $title the title of the page
  92. *
  93. * @see HTMLFilterOutput()
  94. * @private
  95. */
  96. function printHTMLHeader($title)
  97. {
  98. $this->HTMLFilterOutput(str_replace('__TITLE__',
  99. $title,
  100. (empty($this->_output_header)
  101. ? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
  102. : $this->_output_header)
  103. )
  104. );
  105. }
  106. /**
  107. * A string used to print the footer of HTML pages. Written by CASClient::setHTMLFooter(),
  108. * read by printHTMLFooter().
  109. *
  110. * @hideinitializer
  111. * @private
  112. * @see CASClient::setHTMLFooter, CASClient::printHTMLFooter()
  113. */
  114. var $_output_footer = '';
  115. /**
  116. * This method prints the footer of the HTML output (after filtering). If
  117. * CASClient::setHTMLFooter() was not used, a default footer is output.
  118. *
  119. * @see HTMLFilterOutput()
  120. * @private
  121. */
  122. function printHTMLFooter()
  123. {
  124. $this->HTMLFilterOutput(empty($this->_output_footer)
  125. ?('<hr><address>phpCAS __PHPCAS_VERSION__ '.$this->getString(CAS_STR_USING_SERVER).' <a href="__SERVER_BASE_URL__">__SERVER_BASE_URL__</a> (CAS __CAS_VERSION__)</a></address></body></html>')
  126. :$this->_output_footer);
  127. }
  128. /**
  129. * This method set the HTML header used for all outputs.
  130. *
  131. * @param $header the HTML header.
  132. *
  133. * @public
  134. */
  135. function setHTMLHeader($header)
  136. {
  137. $this->_output_header = $header;
  138. }
  139. /**
  140. * This method set the HTML footer used for all outputs.
  141. *
  142. * @param $footer the HTML footer.
  143. *
  144. * @public
  145. */
  146. function setHTMLFooter($footer)
  147. {
  148. $this->_output_footer = $footer;
  149. }
  150. /** @} */
  151. // ########################################################################
  152. // INTERNATIONALIZATION
  153. // ########################################################################
  154. /**
  155. * @addtogroup internalLang
  156. * @{
  157. */
  158. /**
  159. * A string corresponding to the language used by phpCAS. Written by
  160. * CASClient::setLang(), read by CASClient::getLang().
  161. * @note debugging information is always in english (debug purposes only).
  162. *
  163. * @hideinitializer
  164. * @private
  165. * @sa CASClient::_strings, CASClient::getString()
  166. */
  167. var $_lang = '';
  168. /**
  169. * This method returns the language used by phpCAS.
  170. *
  171. * @return a string representing the language
  172. *
  173. * @private
  174. */
  175. function getLang()
  176. {
  177. if ( empty($this->_lang) )
  178. $this->setLang(PHPCAS_LANG_DEFAULT);
  179. return $this->_lang;
  180. }
  181. /**
  182. * array containing the strings used by phpCAS. Written by CASClient::setLang(), read by
  183. * CASClient::getString() and used by CASClient::setLang().
  184. *
  185. * @note This array is filled by instructions in CAS/languages/<$this->_lang>.php
  186. *
  187. * @private
  188. * @see CASClient::_lang, CASClient::getString(), CASClient::setLang(), CASClient::getLang()
  189. */
  190. var $_strings;
  191. /**
  192. * This method returns a string depending on the language.
  193. *
  194. * @param $str the index of the string in $_string.
  195. *
  196. * @return the string corresponding to $index in $string.
  197. *
  198. * @private
  199. */
  200. function getString($str)
  201. {
  202. // call CASclient::getLang() to be sure the language is initialized
  203. $this->getLang();
  204. if ( !isset($this->_strings[$str]) ) {
  205. trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR);
  206. }
  207. return $this->_strings[$str];
  208. }
  209. /**
  210. * This method is used to set the language used by phpCAS.
  211. * @note Can be called only once.
  212. *
  213. * @param $lang a string representing the language.
  214. *
  215. * @public
  216. * @sa CAS_LANG_FRENCH, CAS_LANG_ENGLISH
  217. */
  218. function setLang($lang)
  219. {
  220. // include the corresponding language file
  221. include_once(dirname(__FILE__).'/languages/'.$lang.'.php');
  222. if ( !is_array($this->_strings) ) {
  223. trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR);
  224. }
  225. $this->_lang = $lang;
  226. }
  227. /** @} */
  228. // ########################################################################
  229. // CAS SERVER CONFIG
  230. // ########################################################################
  231. /**
  232. * @addtogroup internalConfig
  233. * @{
  234. */
  235. /**
  236. * a record to store information about the CAS server.
  237. * - $_server["version"]: the version of the CAS server
  238. * - $_server["hostname"]: the hostname of the CAS server
  239. * - $_server["port"]: the port the CAS server is running on
  240. * - $_server["uri"]: the base URI the CAS server is responding on
  241. * - $_server["base_url"]: the base URL of the CAS server
  242. * - $_server["login_url"]: the login URL of the CAS server
  243. * - $_server["service_validate_url"]: the service validating URL of the CAS server
  244. * - $_server["proxy_url"]: the proxy URL of the CAS server
  245. * - $_server["proxy_validate_url"]: the proxy validating URL of the CAS server
  246. * - $_server["logout_url"]: the logout URL of the CAS server
  247. *
  248. * $_server["version"], $_server["hostname"], $_server["port"] and $_server["uri"]
  249. * are written by CASClient::CASClient(), read by CASClient::getServerVersion(),
  250. * CASClient::getServerHostname(), CASClient::getServerPort() and CASClient::getServerURI().
  251. *
  252. * The other fields are written and read by CASClient::getServerBaseURL(),
  253. * CASClient::getServerLoginURL(), CASClient::getServerServiceValidateURL(),
  254. * CASClient::getServerProxyValidateURL() and CASClient::getServerLogoutURL().
  255. *
  256. * @hideinitializer
  257. * @private
  258. */
  259. var $_server = array(
  260. 'version' => -1,
  261. 'hostname' => 'none',
  262. 'port' => -1,
  263. 'uri' => 'none'
  264. );
  265. /**
  266. * This method is used to retrieve the version of the CAS server.
  267. * @return the version of the CAS server.
  268. * @private
  269. */
  270. function getServerVersion()
  271. {
  272. return $this->_server['version'];
  273. }
  274. /**
  275. * This method is used to retrieve the hostname of the CAS server.
  276. * @return the hostname of the CAS server.
  277. * @private
  278. */
  279. function getServerHostname()
  280. { return $this->_server['hostname']; }
  281. /**
  282. * This method is used to retrieve the port of the CAS server.
  283. * @return the port of the CAS server.
  284. * @private
  285. */
  286. function getServerPort()
  287. { return $this->_server['port']; }
  288. /**
  289. * This method is used to retrieve the URI of the CAS server.
  290. * @return a URI.
  291. * @private
  292. */
  293. function getServerURI()
  294. { return $this->_server['uri']; }
  295. /**
  296. * This method is used to retrieve the base URL of the CAS server.
  297. * @return a URL.
  298. * @private
  299. */
  300. function getServerBaseURL()
  301. {
  302. // the URL is build only when needed
  303. if ( empty($this->_server['base_url']) ) {
  304. $this->_server['base_url'] = 'https://' . $this->getServerHostname();
  305. if ($this->getServerPort()!=443) {
  306. $this->_server['base_url'] .= ':'
  307. .$this->getServerPort();
  308. }
  309. $this->_server['base_url'] .= $this->getServerURI();
  310. }
  311. return $this->_server['base_url'];
  312. }
  313. /**
  314. * This method is used to retrieve the login URL of the CAS server.
  315. * @param $gateway true to check authentication, false to force it
  316. * @param $renew true to force the authentication with the CAS server
  317. * NOTE : It is recommended that CAS implementations ignore the
  318. "gateway" parameter if "renew" is set
  319. * @return a URL.
  320. * @private
  321. */
  322. function getServerLoginURL($gateway=false,$renew=false) {
  323. phpCAS::traceBegin();
  324. // the URL is build only when needed
  325. if ( empty($this->_server['login_url']) ) {
  326. $this->_server['login_url'] = $this->getServerBaseURL();
  327. $this->_server['login_url'] .= 'login?service=';
  328. // $this->_server['login_url'] .= preg_replace('/&/','%26',$this->getURL());
  329. $this->_server['login_url'] .= urlencode($this->getURL());
  330. if($renew) {
  331. // It is recommended that when the "renew" parameter is set, its value be "true"
  332. $this->_server['login_url'] .= '&renew=true';
  333. } elseif ($gateway) {
  334. // It is recommended that when the "gateway" parameter is set, its value be "true"
  335. $this->_server['login_url'] .= '&gateway=true';
  336. }
  337. }
  338. phpCAS::traceEnd($this->_server['login_url']);
  339. return $this->_server['login_url'];
  340. }
  341. /**
  342. * This method sets the login URL of the CAS server.
  343. * @param $url the login URL
  344. * @private
  345. * @since 0.4.21 by Wyman Chan
  346. */
  347. function setServerLoginURL($url)
  348. {
  349. return $this->_server['login_url'] = $url;
  350. }
  351. /**
  352. * This method sets the serviceValidate URL of the CAS server.
  353. * @param $url the serviceValidate URL
  354. * @private
  355. * @since 1.1.0 by Joachim Fritschi
  356. */
  357. function setServerServiceValidateURL($url)
  358. {
  359. return $this->_server['service_validate_url'] = $url;
  360. }
  361. /**
  362. * This method sets the proxyValidate URL of the CAS server.
  363. * @param $url the proxyValidate URL
  364. * @private
  365. * @since 1.1.0 by Joachim Fritschi
  366. */
  367. function setServerProxyValidateURL($url)
  368. {
  369. return $this->_server['proxy_validate_url'] = $url;
  370. }
  371. /**
  372. * This method sets the samlValidate URL of the CAS server.
  373. * @param $url the samlValidate URL
  374. * @private
  375. * @since 1.1.0 by Joachim Fritschi
  376. */
  377. function setServerSamlValidateURL($url)
  378. {
  379. return $this->_server['saml_validate_url'] = $url;
  380. }
  381. /**
  382. * This method is used to retrieve the service validating URL of the CAS server.
  383. * @return a URL.
  384. * @private
  385. */
  386. function getServerServiceValidateURL()
  387. {
  388. // the URL is build only when needed
  389. if ( empty($this->_server['service_validate_url']) ) {
  390. switch ($this->getServerVersion()) {
  391. case CAS_VERSION_1_0:
  392. $this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
  393. break;
  394. case CAS_VERSION_2_0:
  395. $this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
  396. break;
  397. }
  398. }
  399. // return $this->_server['service_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL());
  400. return $this->_server['service_validate_url'].'?service='.urlencode($this->getURL());
  401. }
  402. /**
  403. * This method is used to retrieve the SAML validating URL of the CAS server.
  404. * @return a URL.
  405. * @private
  406. */
  407. function getServerSamlValidateURL()
  408. {
  409. phpCAS::traceBegin();
  410. // the URL is build only when needed
  411. if ( empty($this->_server['saml_validate_url']) ) {
  412. switch ($this->getServerVersion()) {
  413. case SAML_VERSION_1_1:
  414. $this->_server['saml_validate_url'] = $this->getServerBaseURL().'samlValidate';
  415. break;
  416. }
  417. }
  418. phpCAS::traceEnd($this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL()));
  419. return $this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL());
  420. }
  421. /**
  422. * This method is used to retrieve the proxy validating URL of the CAS server.
  423. * @return a URL.
  424. * @private
  425. */
  426. function getServerProxyValidateURL()
  427. {
  428. // the URL is build only when needed
  429. if ( empty($this->_server['proxy_validate_url']) ) {
  430. switch ($this->getServerVersion()) {
  431. case CAS_VERSION_1_0:
  432. $this->_server['proxy_validate_url'] = '';
  433. break;
  434. case CAS_VERSION_2_0:
  435. $this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
  436. break;
  437. }
  438. }
  439. // return $this->_server['proxy_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL());
  440. return $this->_server['proxy_validate_url'].'?service='.urlencode($this->getURL());
  441. }
  442. /**
  443. * This method is used to retrieve the proxy URL of the CAS server.
  444. * @return a URL.
  445. * @private
  446. */
  447. function getServerProxyURL()
  448. {
  449. // the URL is build only when needed
  450. if ( empty($this->_server['proxy_url']) ) {
  451. switch ($this->getServerVersion()) {
  452. case CAS_VERSION_1_0:
  453. $this->_server['proxy_url'] = '';
  454. break;
  455. case CAS_VERSION_2_0:
  456. $this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
  457. break;
  458. }
  459. }
  460. return $this->_server['proxy_url'];
  461. }
  462. /**
  463. * This method is used to retrieve the logout URL of the CAS server.
  464. * @return a URL.
  465. * @private
  466. */
  467. function getServerLogoutURL()
  468. {
  469. // the URL is build only when needed
  470. if ( empty($this->_server['logout_url']) ) {
  471. $this->_server['logout_url'] = $this->getServerBaseURL().'logout';
  472. }
  473. return $this->_server['logout_url'];
  474. }
  475. /**
  476. * This method sets the logout URL of the CAS server.
  477. * @param $url the logout URL
  478. * @private
  479. * @since 0.4.21 by Wyman Chan
  480. */
  481. function setServerLogoutURL($url)
  482. {
  483. return $this->_server['logout_url'] = $url;
  484. }
  485. /**
  486. * An array to store extra curl options.
  487. */
  488. var $_curl_options = array();
  489. /**
  490. * This method is used to set additional user curl options.
  491. */
  492. function setExtraCurlOption($key, $value)
  493. {
  494. $this->_curl_options[$key] = $value;
  495. }
  496. /**
  497. * This method checks to see if the request is secured via HTTPS
  498. * @return true if https, false otherwise
  499. * @private
  500. */
  501. function isHttps() {
  502. //if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ) {
  503. //0.4.24 by Hinnack
  504. if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
  505. return true;
  506. } else {
  507. return false;
  508. }
  509. }
  510. // ########################################################################
  511. // CONSTRUCTOR
  512. // ########################################################################
  513. /**
  514. * CASClient constructor.
  515. *
  516. * @param $server_version the version of the CAS server
  517. * @param $proxy TRUE if the CAS client is a CAS proxy, FALSE otherwise
  518. * @param $server_hostname the hostname of the CAS server
  519. * @param $server_port the port the CAS server is running on
  520. * @param $server_uri the URI the CAS server is responding on
  521. * @param $start_session Have phpCAS start PHP sessions (default true)
  522. *
  523. * @return a newly created CASClient object
  524. *
  525. * @public
  526. */
  527. function CASClient(
  528. $server_version,
  529. $proxy,
  530. $server_hostname,
  531. $server_port,
  532. $server_uri,
  533. $start_session = true) {
  534. phpCAS::traceBegin();
  535. // the redirect header() call and DOM parsing code from domxml-php4-php5.php won't work in PHP4 compatibility mode
  536. if (version_compare(PHP_VERSION,'5','>=') && ini_get('zend.ze1_compatibility_mode')) {
  537. phpCAS::error('phpCAS cannot support zend.ze1_compatibility_mode. Sorry.');
  538. }
  539. $this->_start_session = $start_session;
  540. if ($this->_start_session && session_id() !== "")
  541. {
  542. phpCAS :: error("Another session was started before phpcas. Either disable the session" .
  543. " handling for phpcas in the client() call or modify your application to leave" .
  544. " session handling to phpcas");
  545. }
  546. // skip Session Handling for logout requests and if don't want it'
  547. if ($start_session && !$this->isLogoutRequest())
  548. {
  549. phpCAS :: trace("Starting a new session");
  550. session_start();
  551. }
  552. // are we in proxy mode ?
  553. $this->_proxy = $proxy;
  554. //check version
  555. switch ($server_version) {
  556. case CAS_VERSION_1_0:
  557. if ( $this->isProxy() )
  558. phpCAS::error('CAS proxies are not supported in CAS '
  559. .$server_version);
  560. break;
  561. case CAS_VERSION_2_0:
  562. break;
  563. case SAML_VERSION_1_1:
  564. break;
  565. default:
  566. phpCAS::error('this version of CAS (`'
  567. .$server_version
  568. .'\') is not supported by phpCAS '
  569. .phpCAS::getVersion());
  570. }
  571. $this->_server['version'] = $server_version;
  572. // check hostname
  573. if ( empty($server_hostname)
  574. || !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
  575. phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
  576. }
  577. $this->_server['hostname'] = $server_hostname;
  578. // check port
  579. if ( $server_port == 0
  580. || !is_int($server_port) ) {
  581. phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
  582. }
  583. $this->_server['port'] = $server_port;
  584. // check URI
  585. if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
  586. phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
  587. }
  588. // add leading and trailing `/' and remove doubles
  589. $server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
  590. $this->_server['uri'] = $server_uri;
  591. // set to callback mode if PgtIou and PgtId CGI GET parameters are provided
  592. if ( $this->isProxy() ) {
  593. $this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
  594. }
  595. if ( $this->isCallbackMode() ) {
  596. //callback mode: check that phpCAS is secured
  597. if ( !$this->isHttps() ) {
  598. phpCAS::error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
  599. }
  600. } else {
  601. //normal mode: get ticket and remove it from CGI parameters for developpers
  602. $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
  603. switch ($this->getServerVersion()) {
  604. case CAS_VERSION_1_0: // check for a Service Ticket
  605. if( preg_match('/^ST-/',$ticket) ) {
  606. phpCAS::trace('ST \''.$ticket.'\' found');
  607. //ST present
  608. $this->setST($ticket);
  609. //ticket has been taken into account, unset it to hide it to applications
  610. unset($_GET['ticket']);
  611. } else if ( !empty($ticket) ) {
  612. //ill-formed ticket, halt
  613. phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
  614. }
  615. break;
  616. case CAS_VERSION_2_0: // check for a Service or Proxy Ticket
  617. if( preg_match('/^[SP]T-/',$ticket) ) {
  618. phpCAS::trace('ST or PT \''.$ticket.'\' found');
  619. $this->setPT($ticket);
  620. unset($_GET['ticket']);
  621. } else if ( !empty($ticket) ) {
  622. //ill-formed ticket, halt
  623. phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
  624. }
  625. break;
  626. case SAML_VERSION_1_1: // SAML just does Service Tickets
  627. if( preg_match('/^[SP]T-/',$ticket) ) {
  628. phpCAS::trace('SA \''.$ticket.'\' found');
  629. $this->setSA($ticket);
  630. unset($_GET['ticket']);
  631. } else if ( !empty($ticket) ) {
  632. //ill-formed ticket, halt
  633. phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
  634. }
  635. break;
  636. }
  637. }
  638. phpCAS::traceEnd();
  639. }
  640. /** @} */
  641. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  642. // XX XX
  643. // XX Session Handling XX
  644. // XX XX
  645. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  646. /**
  647. * A variable to whether phpcas will use its own session handling. Default = true
  648. * @hideinitializer
  649. * @private
  650. */
  651. var $_start_session = true;
  652. function setStartSession($session)
  653. {
  654. $this->_start_session = session;
  655. }
  656. function getStartSession($session)
  657. {
  658. $this->_start_session = session;
  659. }
  660. /**
  661. * Renaming the session
  662. */
  663. function renameSession($ticket)
  664. {
  665. phpCAS::traceBegin();
  666. if($this->_start_session){
  667. if (!empty ($this->_user))
  668. {
  669. $old_session = $_SESSION;
  670. session_destroy();
  671. // set up a new session, of name based on the ticket
  672. $session_id = preg_replace('/[^\w]/', '', $ticket);
  673. phpCAS :: trace("Session ID: ".$session_id);
  674. session_id($session_id);
  675. session_start();
  676. phpCAS :: trace("Restoring old session vars");
  677. $_SESSION = $old_session;
  678. } else
  679. {
  680. phpCAS :: error('Session should only be renamed after successfull authentication');
  681. }
  682. }else{
  683. phpCAS :: trace("Skipping session rename since phpCAS is not handling the session.");
  684. }
  685. phpCAS::traceEnd();
  686. }
  687. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  688. // XX XX
  689. // XX AUTHENTICATION XX
  690. // XX XX
  691. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  692. /**
  693. * @addtogroup internalAuthentication
  694. * @{
  695. */
  696. /**
  697. * The Authenticated user. Written by CASClient::setUser(), read by CASClient::getUser().
  698. * @attention client applications should use phpCAS::getUser().
  699. *
  700. * @hideinitializer
  701. * @private
  702. */
  703. var $_user = '';
  704. /**
  705. * This method sets the CAS user's login name.
  706. *
  707. * @param $user the login name of the authenticated user.
  708. *
  709. * @private
  710. */
  711. function setUser($user)
  712. {
  713. $this->_user = $user;
  714. }
  715. /**
  716. * This method returns the CAS user's login name.
  717. * @warning should be called only after CASClient::forceAuthentication() or
  718. * CASClient::isAuthenticated(), otherwise halt with an error.
  719. *
  720. * @return the login name of the authenticated user
  721. */
  722. function getUser()
  723. {
  724. if ( empty($this->_user) ) {
  725. phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
  726. }
  727. return $this->_user;
  728. }
  729. /***********************************************************************************************************************
  730. * Atrributes section
  731. *
  732. * @author Matthias Crauwels <matthias.crauwels@ugent.be>, Ghent University, Belgium
  733. *
  734. ***********************************************************************************************************************/
  735. /**
  736. * The Authenticated users attributes. Written by CASClient::setAttributes(), read by CASClient::getAttributes().
  737. * @attention client applications should use phpCAS::getAttributes().
  738. *
  739. * @hideinitializer
  740. * @private
  741. */
  742. var $_attributes = array();
  743. function setAttributes($attributes)
  744. { $this->_attributes = $attributes; }
  745. function getAttributes() {
  746. if ( empty($this->_user) ) { // if no user is set, there shouldn't be any attributes also...
  747. phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
  748. }
  749. return $this->_attributes;
  750. }
  751. function hasAttributes()
  752. { return !empty($this->_attributes); }
  753. function hasAttribute($key)
  754. { return (is_array($this->_attributes) && array_key_exists($key, $this->_attributes)); }
  755. function getAttribute($key) {
  756. if($this->hasAttribute($key)) {
  757. return $this->_attributes[$key];
  758. }
  759. }
  760. /**
  761. * This method is called to renew the authentication of the user
  762. * If the user is authenticated, renew the connection
  763. * If not, redirect to CAS
  764. * @public
  765. */
  766. function renewAuthentication(){
  767. phpCAS::traceBegin();
  768. // Either way, the user is authenticated by CAS
  769. if( isset( $_SESSION['phpCAS']['auth_checked'] ) )
  770. unset($_SESSION['phpCAS']['auth_checked']);
  771. if ( $this->isAuthenticated() ) {
  772. phpCAS::trace('user already authenticated; renew');
  773. $this->redirectToCas(false,true);
  774. } else {
  775. $this->redirectToCas();
  776. }
  777. phpCAS::traceEnd();
  778. }
  779. /**
  780. * This method is called to be sure that the user is authenticated. When not
  781. * authenticated, halt by redirecting to the CAS server; otherwise return TRUE.
  782. * @return TRUE when the user is authenticated; otherwise halt.
  783. * @public
  784. */
  785. function forceAuthentication()
  786. {
  787. phpCAS::traceBegin();
  788. if ( $this->isAuthenticated() ) {
  789. // the user is authenticated, nothing to be done.
  790. phpCAS::trace('no need to authenticate');
  791. $res = TRUE;
  792. } else {
  793. // the user is not authenticated, redirect to the CAS server
  794. if (isset($_SESSION['phpCAS']['auth_checked'])) {
  795. unset($_SESSION['phpCAS']['auth_checked']);
  796. }
  797. $this->redirectToCas(FALSE/* no gateway */);
  798. // never reached
  799. $res = FALSE;
  800. }
  801. phpCAS::traceEnd($res);
  802. return $res;
  803. }
  804. /**
  805. * An integer that gives the number of times authentication will be cached before rechecked.
  806. *
  807. * @hideinitializer
  808. * @private
  809. */
  810. var $_cache_times_for_auth_recheck = 0;
  811. /**
  812. * Set the number of times authentication will be cached before rechecked.
  813. *
  814. * @param $n an integer.
  815. *
  816. * @public
  817. */
  818. function setCacheTimesForAuthRecheck($n)
  819. {
  820. $this->_cache_times_for_auth_recheck = $n;
  821. }
  822. /**
  823. * This method is called to check whether the user is authenticated or not.
  824. * @return TRUE when the user is authenticated, FALSE otherwise.
  825. * @public
  826. */
  827. function checkAuthentication()
  828. {
  829. phpCAS::traceBegin();
  830. if ( $this->isAuthenticated() ) {
  831. phpCAS::trace('user is authenticated');
  832. $res = TRUE;
  833. } else if (isset($_SESSION['phpCAS']['auth_checked'])) {
  834. // the previous request has redirected the client to the CAS server with gateway=true
  835. unset($_SESSION['phpCAS']['auth_checked']);
  836. $res = FALSE;
  837. } else {
  838. // $_SESSION['phpCAS']['auth_checked'] = true;
  839. // $this->redirectToCas(TRUE/* gateway */);
  840. // // never reached
  841. // $res = FALSE;
  842. // avoid a check against CAS on every request
  843. if (! isset($_SESSION['phpCAS']['unauth_count']) )
  844. $_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized
  845. if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1)
  846. || ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck))
  847. {
  848. $res = FALSE;
  849. if ($this->_cache_times_for_auth_recheck != -1)
  850. {
  851. $_SESSION['phpCAS']['unauth_count']++;
  852. phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')');
  853. }
  854. else
  855. {
  856. phpCAS::trace('user is not authenticated (cached for until login pressed)');
  857. }
  858. }
  859. else
  860. {
  861. $_SESSION['phpCAS']['unauth_count'] = 0;
  862. $_SESSION['phpCAS']['auth_checked'] = true;
  863. phpCAS::trace('user is not authenticated (cache reset)');
  864. $this->redirectToCas(TRUE/* gateway */);
  865. // never reached
  866. $res = FALSE;
  867. }
  868. }
  869. phpCAS::traceEnd($res);
  870. return $res;
  871. }
  872. /**
  873. * This method is called to check if the user is authenticated (previously or by
  874. * tickets given in the URL).
  875. *
  876. * @return TRUE when the user is authenticated. Also may redirect to the same URL without the ticket.
  877. *
  878. * @public
  879. */
  880. function isAuthenticated()
  881. {
  882. phpCAS::traceBegin();
  883. $res = FALSE;
  884. $validate_url = '';
  885. if ( $this->wasPreviouslyAuthenticated() ) {
  886. if($this->hasST() || $this->hasPT() || $this->hasSA()){
  887. // User has a additional ticket but was already authenticated
  888. phpCAS::trace('ticket was present and will be discarded, use renewAuthenticate()');
  889. header('Location: '.$this->getURL());
  890. phpCAS::log( "Prepare redirect to remove ticket: ".$this->getURL() );
  891. phpCAS::traceExit();
  892. exit();
  893. }else{
  894. // the user has already (previously during the session) been
  895. // authenticated, nothing to be done.
  896. phpCAS::trace('user was already authenticated, no need to look for tickets');
  897. $res = TRUE;
  898. }
  899. }
  900. else {
  901. if ( $this->hasST() ) {
  902. // if a Service Ticket was given, validate it
  903. phpCAS::trace('ST `'.$this->getST().'\' is present');
  904. $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
  905. phpCAS::trace('ST `'.$this->getST().'\' was validated');
  906. if ( $this->isProxy() ) {
  907. $this->validatePGT($validate_url,$text_response,$tree_response); // idem
  908. phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
  909. $_SESSION['phpCAS']['pgt'] = $this->getPGT();
  910. }
  911. $_SESSION['phpCAS']['user'] = $this->getUser();
  912. $res = TRUE;
  913. }
  914. elseif ( $this->hasPT() ) {
  915. // if a Proxy Ticket was given, validate it
  916. phpCAS::trace('PT `'.$this->getPT().'\' is present');
  917. $this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts
  918. phpCAS::trace('PT `'.$this->getPT().'\' was validated');
  919. if ( $this->isProxy() ) {
  920. $this->validatePGT($validate_url,$text_response,$tree_response); // idem
  921. phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
  922. $_SESSION['phpCAS']['pgt'] = $this->getPGT();
  923. }
  924. $_SESSION['phpCAS']['user'] = $this->getUser();
  925. $res = TRUE;
  926. }
  927. elseif ( $this->hasSA() ) {
  928. // if we have a SAML ticket, validate it.
  929. phpCAS::trace('SA `'.$this->getSA().'\' is present');
  930. $this->validateSA($validate_url,$text_response,$tree_response); // if it fails, it halts
  931. phpCAS::trace('SA `'.$this->getSA().'\' was validated');
  932. $_SESSION['phpCAS']['user'] = $this->getUser();
  933. $_SESSION['phpCAS']['attributes'] = $this->getAttributes();
  934. $res = TRUE;
  935. }
  936. else {
  937. // no ticket given, not authenticated
  938. phpCAS::trace('no ticket found');
  939. }
  940. if ($res) {
  941. // if called with a ticket parameter, we need to redirect to the app without the ticket so that CAS-ification is transparent to the browser (for later POSTS)
  942. // most of the checks and errors should have been made now, so we're safe for redirect without masking error messages.
  943. // remove the ticket as a security precaution to prevent a ticket in the HTTP_REFERRER
  944. header('Location: '.$this->getURL());
  945. phpCAS::log( "Prepare redirect to : ".$this->getURL() );
  946. phpCAS::traceExit();
  947. exit();
  948. }
  949. }
  950. phpCAS::traceEnd($res);
  951. return $res;
  952. }
  953. /**
  954. * This method tells if the current session is authenticated.
  955. * @return true if authenticated based soley on $_SESSION variable
  956. * @since 0.4.22 by Brendan Arnold
  957. */
  958. function isSessionAuthenticated ()
  959. {
  960. return !empty($_SESSION['phpCAS']['user']);
  961. }
  962. /**
  963. * This method tells if the user has already been (previously) authenticated
  964. * by looking into the session variables.
  965. *
  966. * @note This function switches to callback mode when needed.
  967. *
  968. * @return TRUE when the user has already been authenticated; FALSE otherwise.
  969. *
  970. * @private
  971. */
  972. function wasPreviouslyAuthenticated()
  973. {
  974. phpCAS::traceBegin();
  975. if ( $this->isCallbackMode() ) {
  976. $this->callback();
  977. }
  978. $auth = FALSE;
  979. if ( $this->isProxy() ) {
  980. // CAS proxy: username and PGT must be present
  981. if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
  982. // authentication already done
  983. $this->setUser($_SESSION['phpCAS']['user']);
  984. $this->setPGT($_SESSION['phpCAS']['pgt']);
  985. phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\'');
  986. $auth = TRUE;
  987. } elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) {
  988. // these two variables should be empty or not empty at the same time
  989. phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
  990. // unset all tickets to enforce authentication
  991. unset($_SESSION['phpCAS']);
  992. $this->setST('');
  993. $this->setPT('');
  994. } elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
  995. // these two variables should be empty or not empty at the same time
  996. phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty');
  997. // unset all tickets to enforce authentication
  998. unset($_SESSION['phpCAS']);
  999. $this->setST('');
  1000. $this->setPT('');
  1001. } else {
  1002. phpCAS::trace('neither user not PGT found');
  1003. }
  1004. } else {
  1005. // `simple' CAS client (not a proxy): username must be present
  1006. if ( $this->isSessionAuthenticated() ) {
  1007. // authentication already done
  1008. $this->setUser($_SESSION['phpCAS']['user']);
  1009. if(isset($_SESSION['phpCAS']['attributes'])){
  1010. $this->setAttributes($_SESSION['phpCAS']['attributes']);
  1011. }
  1012. phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\'');
  1013. $auth = TRUE;
  1014. } else {
  1015. phpCAS::trace('no user found');
  1016. }
  1017. }
  1018. phpCAS::traceEnd($auth);
  1019. return $auth;
  1020. }
  1021. /**
  1022. * This method is used to redirect the client to the CAS server.
  1023. * It is used by CASClient::forceAuthentication() and CASClient::checkAuthentication().
  1024. * @param $gateway true to check authentication, false to force it
  1025. * @param $renew true to force the authentication with the CAS server
  1026. * @public
  1027. */
  1028. function redirectToCas($gateway=false,$renew=false){
  1029. phpCAS::traceBegin();
  1030. $cas_url = $this->getServerLoginURL($gateway,$renew);
  1031. header('Location: '.$cas_url);
  1032. phpCAS::log( "Redirect to : ".$cas_url );
  1033. $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
  1034. printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
  1035. $this->printHTMLFooter();
  1036. phpCAS::traceExit();
  1037. exit();
  1038. }
  1039. /**
  1040. * This method is used to logout from CAS.
  1041. * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server
  1042. * @public
  1043. */
  1044. function logout($params) {
  1045. phpCAS::traceBegin();
  1046. $cas_url = $this->getServerLogoutURL();
  1047. $paramSeparator = '?';
  1048. if (isset($params['url'])) {
  1049. $cas_url = $cas_url . $paramSeparator . "url=" . urlencode($params['url']);
  1050. $paramSeparator = '&';
  1051. }
  1052. if (isset($params['service'])) {
  1053. $cas_url = $cas_url . $paramSeparator . "service=" . urlencode($params['service']);
  1054. }
  1055. header('Location: '.$cas_url);
  1056. phpCAS::log( "Prepare redirect to : ".$cas_url );
  1057. session_unset();
  1058. session_destroy();
  1059. $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
  1060. printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
  1061. $this->printHTMLFooter();
  1062. phpCAS::traceExit();
  1063. exit();
  1064. }
  1065. /**
  1066. * @return true if the current request is a logout request.
  1067. * @private
  1068. */
  1069. function isLogoutRequest() {
  1070. return !empty($_POST['logoutRequest']);
  1071. }
  1072. /**
  1073. * @return true if a logout request is allowed.
  1074. * @private
  1075. */
  1076. function isLogoutRequestAllowed() {
  1077. }
  1078. /**
  1079. * This method handles logout requests.
  1080. * @param $check_client true to check the client bofore handling the request,
  1081. * false not to perform any access control. True by default.
  1082. * @param $allowed_clients an array of host names allowed to send logout requests.
  1083. * By default, only the CAs server (declared in the constructor) will be allowed.
  1084. * @public
  1085. */
  1086. function handleLogoutRequests($check_client=true, $allowed_clients=false) {
  1087. phpCAS::traceBegin();
  1088. if (!$this->isLogoutRequest()) {
  1089. phpCAS::log("Not a logout request");
  1090. phpCAS::traceEnd();
  1091. return;
  1092. }
  1093. if(!$this->_start_session){
  1094. phpCAS::log("phpCAS can't handle logout requests if it does not manage the session.");
  1095. }
  1096. phpCAS::log("Logout requested");
  1097. phpCAS::log("SAML REQUEST: ".$_POST['logoutRequest']);
  1098. if ($check_client) {
  1099. if (!$allowed_clients) {
  1100. $allowed_clients = array( $this->getServerHostname() );
  1101. }
  1102. $client_ip = $_SERVER['REMOTE_ADDR'];
  1103. $client = gethostbyaddr($client_ip);
  1104. phpCAS::log("Client: ".$client."/".$client_ip);
  1105. $allowed = false;
  1106. foreach ($allowed_clients as $allowed_client) {
  1107. if (($client == $allowed_client) or ($client_ip == $allowed_client)) {
  1108. phpCAS::log("Allowed client '".$allowed_client."' matches, logout request is allowed");
  1109. $allowed = true;
  1110. break;
  1111. } else {
  1112. phpCAS::log("Allowed client '".$allowed_client."' does not match");
  1113. }
  1114. }
  1115. if (!$allowed) {
  1116. phpCAS::error("Unauthorized logout request from client '".$client."'");
  1117. printf("Unauthorized!");
  1118. phpCAS::traceExit();
  1119. exit();
  1120. }
  1121. } else {
  1122. phpCAS::log("No access control set");
  1123. }
  1124. // Extract the ticket from the SAML Request
  1125. preg_match("|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|", $_POST['logoutRequest'], $tick, PREG_OFFSET_CAPTURE, 3);
  1126. $wrappedSamlSessionIndex = preg_replace('|<samlp:SessionIndex>|','',$tick[0][0]);
  1127. $ticket2logout = preg_replace('|</samlp:SessionIndex>|','',$wrappedSamlSessionIndex);
  1128. phpCAS::log("Ticket to logout: ".$ticket2logout);
  1129. $session_id = preg_replace('/[^\w]/','',$ticket2logout);
  1130. phpCAS::log("Session id: ".$session_id);
  1131. // destroy a possible application session created before phpcas
  1132. if(session_id() !== ""){
  1133. session_unset();
  1134. session_destroy();
  1135. }
  1136. // fix session ID
  1137. session_id($session_id);
  1138. $_COOKIE[session_name()]=$session_id;
  1139. $_GET[session_name()]=$session_id;
  1140. // Overwrite session
  1141. session_start();
  1142. session_unset();
  1143. session_destroy();
  1144. printf("Disconnected!");
  1145. phpCAS::traceExit();
  1146. exit();
  1147. }
  1148. /** @} */
  1149. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1150. // XX XX
  1151. // XX BASIC CLIENT FEATURES (CAS 1.0) XX
  1152. // XX XX
  1153. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1154. // ########################################################################
  1155. // ST
  1156. // ########################################################################
  1157. /**
  1158. * @addtogroup internalBasic
  1159. * @{
  1160. */
  1161. /**
  1162. * the Service Ticket provided in the URL of the request if present
  1163. * (empty otherwise). Written by CASClient::CASClient(), read by
  1164. * CASClient::getST() and CASClient::hasPGT().
  1165. *
  1166. * @hideinitializer
  1167. * @private
  1168. */
  1169. var $_st = '';
  1170. /**
  1171. * This method returns the Service Ticket provided in the URL of the request.
  1172. * @return The service ticket.
  1173. * @private
  1174. */
  1175. function getST()
  1176. { return $this->_st; }
  1177. /**
  1178. * This method stores the Service Ticket.
  1179. * @param $st The Service Ticket.
  1180. * @private
  1181. */
  1182. function setST($st)
  1183. { $this->_st = $st; }
  1184. /**
  1185. * This method tells if a Service Ticket was stored.
  1186. * @return TRUE if a Service Ticket has been stored.
  1187. * @private
  1188. */
  1189. function hasST()
  1190. { return !empty($this->_st); }
  1191. /** @} */
  1192. // ########################################################################
  1193. // ST VALIDATION
  1194. // ########################################################################
  1195. /**
  1196. * @addtogroup internalBasic
  1197. * @{
  1198. */
  1199. /**
  1200. * the certificate of the CAS server.
  1201. *
  1202. * @hideinitializer
  1203. * @private
  1204. */
  1205. var $_cas_server_cert = '';
  1206. /**
  1207. * the certificate of the CAS server CA.
  1208. *
  1209. * @hideinitializer
  1210. * @private
  1211. */
  1212. var $_cas_server_ca_cert = '';
  1213. /**
  1214. * Set to true not to validate the CAS server.
  1215. *
  1216. * @hideinitializer
  1217. * @private
  1218. */
  1219. var $_no_cas_server_validation = false;
  1220. /**
  1221. * Set the certificate of the CAS server.
  1222. *
  1223. * @param $cert the PEM certificate
  1224. */
  1225. function setCasServerCert($cert)
  1226. {
  1227. $this->_cas_server_cert = $cert;
  1228. }
  1229. /**
  1230. * Set the CA certificate of the CAS server.
  1231. *
  1232. * @param $cert the PEM certificate of the CA that emited the cert of the server
  1233. */
  1234. function setCasServerCACert($cert)
  1235. {
  1236. $this->_cas_server_ca_cert = $cert;
  1237. }
  1238. /**
  1239. * Set no SSL validation for the CAS server.
  1240. */
  1241. function setNoCasServerValidation()
  1242. {
  1243. $this->_no_cas_server_validation = true;
  1244. }
  1245. /**
  1246. * This method is used to validate a ST; halt on failure, and sets $validate_url,
  1247. * $text_reponse and $tree_response on success. These parameters are used later
  1248. * by CASClient::validatePGT() for CAS proxies.
  1249. * Used for all CAS 1.0 validations
  1250. * @param $validate_url the URL of the request to the CAS server.
  1251. * @param $text_response the response of the CAS server, as is (XML text).
  1252. * @param $tree_response the response of the CAS server, as a DOM XML tree.
  1253. *
  1254. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1255. *
  1256. * @private
  1257. */
  1258. function validateST($validate_url,&$text_response,&$tree_response)
  1259. {
  1260. phpCAS::traceBegin();
  1261. // build the URL to validate the ticket
  1262. $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
  1263. if ( $this->isProxy() ) {
  1264. // pass the callback url for CAS proxies
  1265. $validate_url .= '&pgtUrl='.urlencode($this->getCallbackURL());
  1266. }
  1267. // open and read the URL
  1268. if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
  1269. phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
  1270. $this->authError('ST not validated',
  1271. $validate_url,
  1272. TRUE/*$no_response*/);
  1273. }
  1274. // analyze the result depending on the version
  1275. switch ($this->getServerVersion()) {
  1276. case CAS_VERSION_1_0:
  1277. if (preg_match('/^no\n/',$text_response)) {
  1278. phpCAS::trace('ST has not been validated');
  1279. $this->authError('ST not validated',
  1280. $validate_url,
  1281. FALSE/*$no_response*/,
  1282. FALSE/*$bad_response*/,
  1283. $text_response);
  1284. }
  1285. if (!preg_match('/^yes\n/',$text_response)) {
  1286. phpCAS::trace('ill-formed response');
  1287. $this->authError('ST not validated',
  1288. $validate_url,
  1289. FALSE/*$no_response*/,
  1290. TRUE/*$bad_response*/,
  1291. $text_response);
  1292. }
  1293. // ST has been validated, extract the user name
  1294. $arr = preg_split('/\n/',$text_response);
  1295. $this->setUser(trim($arr[1]));
  1296. break;
  1297. case CAS_VERSION_2_0:
  1298. // read the response of the CAS server into a DOM object
  1299. if ( !($dom = domxml_open_mem($text_response))) {
  1300. phpCAS::trace('domxml_open_mem() failed');
  1301. $this->authError('ST not validated',
  1302. $validate_url,
  1303. FALSE/*$no_response*/,
  1304. TRUE/*$bad_response*/,
  1305. $text_response);
  1306. }
  1307. // read the root node of the XML tree
  1308. if ( !($tree_response = $dom->document_element()) ) {
  1309. phpCAS::trace('document_element() failed');
  1310. $this->authError('ST not validated',
  1311. $validate_url,
  1312. FALSE/*$no_response*/,
  1313. TRUE/*$bad_response*/,
  1314. $text_response);
  1315. }
  1316. // insure that tag name is 'serviceResponse'
  1317. if ( $tree_response->node_name() != 'serviceResponse' ) {
  1318. phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\'');
  1319. $this->authError('ST not validated',
  1320. $validate_url,
  1321. FALSE/*$no_response*/,
  1322. TRUE/*$bad_response*/,
  1323. $text_response);
  1324. }
  1325. if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
  1326. // authentication succeded, extract the user name
  1327. if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
  1328. phpCAS::trace('<authenticationSuccess> found, but no <user>');
  1329. $this->authError('ST not validated',
  1330. $validate_url,
  1331. FALSE/*$no_response*/,
  1332. TRUE/*$bad_response*/,
  1333. $text_response);
  1334. }
  1335. $user = trim($user_elements[0]->get_content());
  1336. phpCAS::trace('user = `'.$user);
  1337. $this->setUser($user);
  1338. } else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
  1339. phpCAS::trace('<authenticationFailure> found');
  1340. // authentication failed, extract the error code and message
  1341. $this->authError('ST not validated',
  1342. $validate_url,
  1343. FALSE/*$no_response*/,
  1344. FALSE/*$bad_response*/,
  1345. $text_response,
  1346. $failure_elements[0]->get_attribute('code')/*$err_code*/,
  1347. trim($failure_elements[0]->get_content())/*$err_msg*/);
  1348. } else {
  1349. phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
  1350. $this->authError('ST not validated',
  1351. $validate_url,
  1352. FALSE/*$no_response*/,
  1353. TRUE/*$bad_response*/,
  1354. $text_response);
  1355. }
  1356. break;
  1357. }
  1358. $this->renameSession($this->getST());
  1359. // at this step, ST has been validated and $this->_user has been set,
  1360. phpCAS::traceEnd(TRUE);
  1361. return TRUE;
  1362. }
  1363. // ########################################################################
  1364. // SAML VALIDATION
  1365. // ########################################################################
  1366. /**
  1367. * @addtogroup internalBasic
  1368. * @{
  1369. */
  1370. /**
  1371. * This method is used to validate a SAML TICKET; halt on failure, and sets $validate_url,
  1372. * $text_reponse and $tree_response on success. These parameters are used later
  1373. * by CASClient::validatePGT() for CAS proxies.
  1374. *
  1375. * @param $validate_url the URL of the request to the CAS server.
  1376. * @param $text_response the response of the CAS server, as is (XML text).
  1377. * @param $tree_response the response of the CAS server, as a DOM XML tree.
  1378. *
  1379. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1380. *
  1381. * @private
  1382. */
  1383. function validateSA($validate_url,&$text_response,&$tree_response)
  1384. {
  1385. phpCAS::traceBegin();
  1386. // build the URL to validate the ticket
  1387. $validate_url = $this->getServerSamlValidateURL();
  1388. // open and read the URL
  1389. if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
  1390. phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
  1391. $this->authError('SA not validated', $validate_url, TRUE/*$no_response*/);
  1392. }
  1393. phpCAS::trace('server version: '.$this->getServerVersion());
  1394. // analyze the result depending on the version
  1395. switch ($this->getServerVersion()) {
  1396. case SAML_VERSION_1_1:
  1397. // read the response of the CAS server into a DOM object
  1398. if ( !($dom = domxml_open_mem($text_response))) {
  1399. phpCAS::trace('domxml_open_mem() failed');
  1400. $this->authError('SA not validated',
  1401. $validate_url,
  1402. FALSE/*$no_response*/,
  1403. TRUE/*$bad_response*/,
  1404. $text_response);
  1405. }
  1406. // read the root node of the XML tree
  1407. if ( !($tree

Large files files are truncated, but you can click here to view the full file