client.php 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700
  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://'
  305. .$this->getServerHostname()
  306. .':'
  307. .$this->getServerPort()
  308. .$this->getServerURI();
  309. }
  310. return $this->_server['base_url'];
  311. }
  312. /**
  313. * This method is used to retrieve the login URL of the CAS server.
  314. * @param $gateway true to check authentication, false to force it
  315. * @param $renew true to force the authentication with the CAS server
  316. * NOTE : It is recommended that CAS implementations ignore the
  317. "gateway" parameter if "renew" is set
  318. * @return a URL.
  319. * @private
  320. */
  321. function getServerLoginURL($gateway=false,$renew=false) {
  322. phpCAS::traceBegin();
  323. // the URL is build only when needed
  324. if ( empty($this->_server['login_url']) ) {
  325. $this->_server['login_url'] = $this->getServerBaseURL();
  326. $this->_server['login_url'] .= 'login?service=';
  327. // $this->_server['login_url'] .= preg_replace('/&/','%26',$this->getURL());
  328. $this->_server['login_url'] .= urlencode($this->getURL());
  329. if($renew) {
  330. // It is recommended that when the "renew" parameter is set, its value be "true"
  331. $this->_server['login_url'] .= '&renew=true';
  332. } elseif ($gateway) {
  333. // It is recommended that when the "gateway" parameter is set, its value be "true"
  334. $this->_server['login_url'] .= '&gateway=true';
  335. }
  336. }
  337. phpCAS::traceEnd($this->_server['login_url']);
  338. return $this->_server['login_url'];
  339. }
  340. /**
  341. * This method sets the login URL of the CAS server.
  342. * @param $url the login URL
  343. * @private
  344. * @since 0.4.21 by Wyman Chan
  345. */
  346. function setServerLoginURL($url)
  347. {
  348. return $this->_server['login_url'] = $url;
  349. }
  350. /**
  351. * This method sets the serviceValidate URL of the CAS server.
  352. * @param $url the serviceValidate URL
  353. * @private
  354. * @since 1.1.0 by Joachim Fritschi
  355. */
  356. function setServerServiceValidateURL($url)
  357. {
  358. return $this->_server['service_validate_url'] = $url;
  359. }
  360. /**
  361. * This method sets the proxyValidate URL of the CAS server.
  362. * @param $url the proxyValidate URL
  363. * @private
  364. * @since 1.1.0 by Joachim Fritschi
  365. */
  366. function setServerProxyValidateURL($url)
  367. {
  368. return $this->_server['proxy_validate_url'] = $url;
  369. }
  370. /**
  371. * This method sets the samlValidate URL of the CAS server.
  372. * @param $url the samlValidate URL
  373. * @private
  374. * @since 1.1.0 by Joachim Fritschi
  375. */
  376. function setServerSamlValidateURL($url)
  377. {
  378. return $this->_server['saml_validate_url'] = $url;
  379. }
  380. /**
  381. * This method is used to retrieve the service validating URL of the CAS server.
  382. * @return a URL.
  383. * @private
  384. */
  385. function getServerServiceValidateURL()
  386. {
  387. // the URL is build only when needed
  388. if ( empty($this->_server['service_validate_url']) ) {
  389. switch ($this->getServerVersion()) {
  390. case CAS_VERSION_1_0:
  391. $this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
  392. break;
  393. case CAS_VERSION_2_0:
  394. $this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
  395. break;
  396. }
  397. }
  398. // return $this->_server['service_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL());
  399. return $this->_server['service_validate_url'].'?service='.urlencode($this->getURL());
  400. }
  401. /**
  402. * This method is used to retrieve the SAML validating URL of the CAS server.
  403. * @return a URL.
  404. * @private
  405. */
  406. function getServerSamlValidateURL()
  407. {
  408. phpCAS::traceBegin();
  409. // the URL is build only when needed
  410. if ( empty($this->_server['saml_validate_url']) ) {
  411. switch ($this->getServerVersion()) {
  412. case SAML_VERSION_1_1:
  413. $this->_server['saml_validate_url'] = $this->getServerBaseURL().'samlValidate';
  414. break;
  415. }
  416. }
  417. phpCAS::traceEnd($this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL()));
  418. return $this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL());
  419. }
  420. /**
  421. * This method is used to retrieve the proxy validating URL of the CAS server.
  422. * @return a URL.
  423. * @private
  424. */
  425. function getServerProxyValidateURL()
  426. {
  427. // the URL is build only when needed
  428. if ( empty($this->_server['proxy_validate_url']) ) {
  429. switch ($this->getServerVersion()) {
  430. case CAS_VERSION_1_0:
  431. $this->_server['proxy_validate_url'] = '';
  432. break;
  433. case CAS_VERSION_2_0:
  434. $this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
  435. break;
  436. }
  437. }
  438. // return $this->_server['proxy_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL());
  439. return $this->_server['proxy_validate_url'].'?service='.urlencode($this->getURL());
  440. }
  441. /**
  442. * This method is used to retrieve the proxy URL of the CAS server.
  443. * @return a URL.
  444. * @private
  445. */
  446. function getServerProxyURL()
  447. {
  448. // the URL is build only when needed
  449. if ( empty($this->_server['proxy_url']) ) {
  450. switch ($this->getServerVersion()) {
  451. case CAS_VERSION_1_0:
  452. $this->_server['proxy_url'] = '';
  453. break;
  454. case CAS_VERSION_2_0:
  455. $this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
  456. break;
  457. }
  458. }
  459. return $this->_server['proxy_url'];
  460. }
  461. /**
  462. * This method is used to retrieve the logout URL of the CAS server.
  463. * @return a URL.
  464. * @private
  465. */
  466. function getServerLogoutURL()
  467. {
  468. // the URL is build only when needed
  469. if ( empty($this->_server['logout_url']) ) {
  470. $this->_server['logout_url'] = $this->getServerBaseURL().'logout';
  471. }
  472. return $this->_server['logout_url'];
  473. }
  474. /**
  475. * This method sets the logout URL of the CAS server.
  476. * @param $url the logout URL
  477. * @private
  478. * @since 0.4.21 by Wyman Chan
  479. */
  480. function setServerLogoutURL($url)
  481. {
  482. return $this->_server['logout_url'] = $url;
  483. }
  484. /**
  485. * An array to store extra curl options.
  486. */
  487. var $_curl_options = array();
  488. /**
  489. * This method is used to set additional user curl options.
  490. */
  491. function setExtraCurlOption($key, $value)
  492. {
  493. $this->_curl_options[$key] = $value;
  494. }
  495. /**
  496. * This method checks to see if the request is secured via HTTPS
  497. * @return true if https, false otherwise
  498. * @private
  499. */
  500. function isHttps() {
  501. //if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ) {
  502. //0.4.24 by Hinnack
  503. if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
  504. return true;
  505. } else {
  506. return false;
  507. }
  508. }
  509. // ########################################################################
  510. // CONSTRUCTOR
  511. // ########################################################################
  512. /**
  513. * CASClient constructor.
  514. *
  515. * @param $server_version the version of the CAS server
  516. * @param $proxy TRUE if the CAS client is a CAS proxy, FALSE otherwise
  517. * @param $server_hostname the hostname of the CAS server
  518. * @param $server_port the port the CAS server is running on
  519. * @param $server_uri the URI the CAS server is responding on
  520. * @param $start_session Have phpCAS start PHP sessions (default true)
  521. *
  522. * @return a newly created CASClient object
  523. *
  524. * @public
  525. */
  526. function CASClient(
  527. $server_version,
  528. $proxy,
  529. $server_hostname,
  530. $server_port,
  531. $server_uri,
  532. $start_session = true) {
  533. phpCAS::traceBegin();
  534. // the redirect header() call and DOM parsing code from domxml-php4-php5.php won't work in PHP4 compatibility mode
  535. if (version_compare(PHP_VERSION,'5','>=') && ini_get('zend.ze1_compatibility_mode')) {
  536. phpCAS::error('phpCAS cannot support zend.ze1_compatibility_mode. Sorry.');
  537. }
  538. // skip Session Handling for logout requests and if don't want it'
  539. if ($start_session && !$this->isLogoutRequest()) {
  540. phpCAS::trace("Starting session handling");
  541. // Check for Tickets from the CAS server
  542. if (empty($_GET['ticket'])){
  543. phpCAS::trace("No ticket found");
  544. // only create a session if necessary
  545. if (!session_id()) {
  546. phpCAS::trace("No session found, creating new session");
  547. session_start();
  548. }
  549. }else{
  550. phpCAS::trace("Ticket found");
  551. // We have to copy any old data before renaming the session
  552. if (session_id()) {
  553. phpCAS::trace("Old active session found, saving old data and destroying session");
  554. $old_session = $_SESSION;
  555. session_destroy();
  556. }else{
  557. session_start();
  558. phpCAS::trace("Starting possible old session to copy variables");
  559. $old_session = $_SESSION;
  560. session_destroy();
  561. }
  562. // set up a new session, of name based on the ticket
  563. $session_id = preg_replace('/[^\w]/','',$_GET['ticket']);
  564. phpCAS::LOG("Session ID: " . $session_id);
  565. session_id($session_id);
  566. session_start();
  567. // restore old session vars
  568. if(isset($old_session)){
  569. phpCAS::trace("Restoring old session vars");
  570. $_SESSION = $old_session;
  571. }
  572. }
  573. }else{
  574. phpCAS::trace("Skipping session creation");
  575. }
  576. // are we in proxy mode ?
  577. $this->_proxy = $proxy;
  578. //check version
  579. switch ($server_version) {
  580. case CAS_VERSION_1_0:
  581. if ( $this->isProxy() )
  582. phpCAS::error('CAS proxies are not supported in CAS '
  583. .$server_version);
  584. break;
  585. case CAS_VERSION_2_0:
  586. break;
  587. case SAML_VERSION_1_1:
  588. break;
  589. default:
  590. phpCAS::error('this version of CAS (`'
  591. .$server_version
  592. .'\') is not supported by phpCAS '
  593. .phpCAS::getVersion());
  594. }
  595. $this->_server['version'] = $server_version;
  596. // check hostname
  597. if ( empty($server_hostname)
  598. || !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
  599. phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
  600. }
  601. $this->_server['hostname'] = $server_hostname;
  602. // check port
  603. if ( $server_port == 0
  604. || !is_int($server_port) ) {
  605. phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
  606. }
  607. $this->_server['port'] = $server_port;
  608. // check URI
  609. if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
  610. phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
  611. }
  612. // add leading and trailing `/' and remove doubles
  613. $server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
  614. $this->_server['uri'] = $server_uri;
  615. // set to callback mode if PgtIou and PgtId CGI GET parameters are provided
  616. if ( $this->isProxy() ) {
  617. $this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
  618. }
  619. if ( $this->isCallbackMode() ) {
  620. //callback mode: check that phpCAS is secured
  621. if ( !$this->isHttps() ) {
  622. phpCAS::error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
  623. }
  624. } else {
  625. //normal mode: get ticket and remove it from CGI parameters for developpers
  626. $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
  627. switch ($this->getServerVersion()) {
  628. case CAS_VERSION_1_0: // check for a Service Ticket
  629. if( preg_match('/^ST-/',$ticket) ) {
  630. phpCAS::trace('ST \''.$ticket.'\' found');
  631. //ST present
  632. $this->setST($ticket);
  633. //ticket has been taken into account, unset it to hide it to applications
  634. unset($_GET['ticket']);
  635. } else if ( !empty($ticket) ) {
  636. //ill-formed ticket, halt
  637. phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
  638. }
  639. break;
  640. case CAS_VERSION_2_0: // check for a Service or Proxy Ticket
  641. if( preg_match('/^[SP]T-/',$ticket) ) {
  642. phpCAS::trace('ST or PT \''.$ticket.'\' found');
  643. $this->setPT($ticket);
  644. unset($_GET['ticket']);
  645. } else if ( !empty($ticket) ) {
  646. //ill-formed ticket, halt
  647. phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
  648. }
  649. break;
  650. case SAML_VERSION_1_1: // SAML just does Service Tickets
  651. if( preg_match('/^[SP]T-/',$ticket) ) {
  652. phpCAS::trace('SA \''.$ticket.'\' found');
  653. $this->setSA($ticket);
  654. unset($_GET['ticket']);
  655. } else if ( !empty($ticket) ) {
  656. //ill-formed ticket, halt
  657. phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
  658. }
  659. break;
  660. }
  661. }
  662. phpCAS::traceEnd();
  663. }
  664. /** @} */
  665. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  666. // XX XX
  667. // XX AUTHENTICATION XX
  668. // XX XX
  669. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  670. /**
  671. * @addtogroup internalAuthentication
  672. * @{
  673. */
  674. /**
  675. * The Authenticated user. Written by CASClient::setUser(), read by CASClient::getUser().
  676. * @attention client applications should use phpCAS::getUser().
  677. *
  678. * @hideinitializer
  679. * @private
  680. */
  681. var $_user = '';
  682. /**
  683. * This method sets the CAS user's login name.
  684. *
  685. * @param $user the login name of the authenticated user.
  686. *
  687. * @private
  688. */
  689. function setUser($user)
  690. {
  691. $this->_user = $user;
  692. }
  693. /**
  694. * This method returns the CAS user's login name.
  695. * @warning should be called only after CASClient::forceAuthentication() or
  696. * CASClient::isAuthenticated(), otherwise halt with an error.
  697. *
  698. * @return the login name of the authenticated user
  699. */
  700. function getUser()
  701. {
  702. if ( empty($this->_user) ) {
  703. phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
  704. }
  705. return $this->_user;
  706. }
  707. /***********************************************************************************************************************
  708. * Atrributes section
  709. *
  710. * @author Matthias Crauwels <matthias.crauwels@ugent.be>, Ghent University, Belgium
  711. *
  712. ***********************************************************************************************************************/
  713. /**
  714. * The Authenticated users attributes. Written by CASClient::setAttributes(), read by CASClient::getAttributes().
  715. * @attention client applications should use phpCAS::getAttributes().
  716. *
  717. * @hideinitializer
  718. * @private
  719. */
  720. var $_attributes = array();
  721. function setAttributes($attributes)
  722. { $this->_attributes = $attributes; }
  723. function getAttributes() {
  724. if ( empty($this->_user) ) { // if no user is set, there shouldn't be any attributes also...
  725. phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
  726. }
  727. return $this->_attributes;
  728. }
  729. function hasAttributes()
  730. { return !empty($this->_attributes); }
  731. function hasAttribute($key)
  732. { return (is_array($this->_attributes) && array_key_exists($key, $this->_attributes)); }
  733. function getAttribute($key) {
  734. if($this->hasAttribute($key)) {
  735. return $this->_attributes[$key];
  736. }
  737. }
  738. /**
  739. * This method is called to renew the authentication of the user
  740. * If the user is authenticated, renew the connection
  741. * If not, redirect to CAS
  742. * @public
  743. */
  744. function renewAuthentication(){
  745. phpCAS::traceBegin();
  746. // Either way, the user is authenticated by CAS
  747. if( isset( $_SESSION['phpCAS']['auth_checked'] ) )
  748. unset($_SESSION['phpCAS']['auth_checked']);
  749. if ( $this->isAuthenticated() ) {
  750. phpCAS::trace('user already authenticated; renew');
  751. $this->redirectToCas(false,true);
  752. } else {
  753. $this->redirectToCas();
  754. }
  755. phpCAS::traceEnd();
  756. }
  757. /**
  758. * This method is called to be sure that the user is authenticated. When not
  759. * authenticated, halt by redirecting to the CAS server; otherwise return TRUE.
  760. * @return TRUE when the user is authenticated; otherwise halt.
  761. * @public
  762. */
  763. function forceAuthentication()
  764. {
  765. phpCAS::traceBegin();
  766. if ( $this->isAuthenticated() ) {
  767. // the user is authenticated, nothing to be done.
  768. phpCAS::trace('no need to authenticate');
  769. $res = TRUE;
  770. } else {
  771. // the user is not authenticated, redirect to the CAS server
  772. if (isset($_SESSION['phpCAS']['auth_checked'])) {
  773. unset($_SESSION['phpCAS']['auth_checked']);
  774. }
  775. $this->redirectToCas(FALSE/* no gateway */);
  776. // never reached
  777. $res = FALSE;
  778. }
  779. phpCAS::traceEnd($res);
  780. return $res;
  781. }
  782. /**
  783. * An integer that gives the number of times authentication will be cached before rechecked.
  784. *
  785. * @hideinitializer
  786. * @private
  787. */
  788. var $_cache_times_for_auth_recheck = 0;
  789. /**
  790. * Set the number of times authentication will be cached before rechecked.
  791. *
  792. * @param $n an integer.
  793. *
  794. * @public
  795. */
  796. function setCacheTimesForAuthRecheck($n)
  797. {
  798. $this->_cache_times_for_auth_recheck = $n;
  799. }
  800. /**
  801. * This method is called to check whether the user is authenticated or not.
  802. * @return TRUE when the user is authenticated, FALSE otherwise.
  803. * @public
  804. */
  805. function checkAuthentication()
  806. {
  807. phpCAS::traceBegin();
  808. if ( $this->isAuthenticated() ) {
  809. phpCAS::trace('user is authenticated');
  810. $res = TRUE;
  811. } else if (isset($_SESSION['phpCAS']['auth_checked'])) {
  812. // the previous request has redirected the client to the CAS server with gateway=true
  813. // comment line bellow to
  814. // unset($_SESSION['phpCAS']['auth_checked']);
  815. $res = FALSE;
  816. } else {
  817. // $_SESSION['phpCAS']['auth_checked'] = true;
  818. // $this->redirectToCas(TRUE/* gateway */);
  819. // // never reached
  820. // $res = FALSE;
  821. // avoid a check against CAS on every request
  822. if (! isset($_SESSION['phpCAS']['unauth_count']) )
  823. $_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized
  824. if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1)
  825. || ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck))
  826. {
  827. $res = FALSE;
  828. if ($this->_cache_times_for_auth_recheck != -1)
  829. {
  830. $_SESSION['phpCAS']['unauth_count']++;
  831. phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')');
  832. }
  833. else
  834. {
  835. phpCAS::trace('user is not authenticated (cached for until login pressed)');
  836. }
  837. }
  838. else
  839. {
  840. $_SESSION['phpCAS']['unauth_count'] = 0;
  841. $_SESSION['phpCAS']['auth_checked'] = true;
  842. phpCAS::trace('user is not authenticated (cache reset)');
  843. // $this->redirectToCas(TRUE/* gateway */);
  844. // never reached
  845. $res = FALSE;
  846. }
  847. }
  848. phpCAS::traceEnd($res);
  849. return $res;
  850. }
  851. /**
  852. * This method is called to check if the user is authenticated (previously or by
  853. * tickets given in the URL).
  854. *
  855. * @return TRUE when the user is authenticated. Also may redirect to the same URL without the ticket.
  856. *
  857. * @public
  858. */
  859. function isAuthenticated()
  860. {
  861. phpCAS::traceBegin();
  862. $res = FALSE;
  863. $validate_url = '';
  864. if ( $this->wasPreviouslyAuthenticated() ) {
  865. // the user has already (previously during the session) been
  866. // authenticated, nothing to be done.
  867. phpCAS::trace('user was already authenticated, no need to look for tickets');
  868. $res = TRUE;
  869. }
  870. else {
  871. if ( $this->hasST() ) {
  872. // if a Service Ticket was given, validate it
  873. phpCAS::trace('ST `'.$this->getST().'\' is present');
  874. $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
  875. phpCAS::trace('ST `'.$this->getST().'\' was validated');
  876. if ( $this->isProxy() ) {
  877. $this->validatePGT($validate_url,$text_response,$tree_response); // idem
  878. phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
  879. $_SESSION['phpCAS']['pgt'] = $this->getPGT();
  880. }
  881. $_SESSION['phpCAS']['user'] = $this->getUser();
  882. $res = TRUE;
  883. }
  884. elseif ( $this->hasPT() ) {
  885. // if a Proxy Ticket was given, validate it
  886. phpCAS::trace('PT `'.$this->getPT().'\' is present');
  887. $this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts
  888. phpCAS::trace('PT `'.$this->getPT().'\' was validated');
  889. if ( $this->isProxy() ) {
  890. $this->validatePGT($validate_url,$text_response,$tree_response); // idem
  891. phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
  892. $_SESSION['phpCAS']['pgt'] = $this->getPGT();
  893. }
  894. $_SESSION['phpCAS']['user'] = $this->getUser();
  895. $res = TRUE;
  896. }
  897. elseif ( $this->hasSA() ) {
  898. // if we have a SAML ticket, validate it.
  899. phpCAS::trace('SA `'.$this->getSA().'\' is present');
  900. $this->validateSA($validate_url,$text_response,$tree_response); // if it fails, it halts
  901. phpCAS::trace('SA `'.$this->getSA().'\' was validated');
  902. $_SESSION['phpCAS']['user'] = $this->getUser();
  903. $_SESSION['phpCAS']['attributes'] = $this->getAttributes();
  904. $res = TRUE;
  905. }
  906. else {
  907. // no ticket given, not authenticated
  908. phpCAS::trace('no ticket found');
  909. }
  910. if ($res) {
  911. // 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)
  912. // most of the checks and errors should have been made now, so we're safe for redirect without masking error messages.
  913. header('Location: '.$this->getURL());
  914. phpCAS::log( "Prepare redirect to : ".$this->getURL() );
  915. }
  916. }
  917. phpCAS::traceEnd($res);
  918. return $res;
  919. }
  920. /**
  921. * This method tells if the current session is authenticated.
  922. * @return true if authenticated based soley on $_SESSION variable
  923. * @since 0.4.22 by Brendan Arnold
  924. */
  925. function isSessionAuthenticated ()
  926. {
  927. return !empty($_SESSION['phpCAS']['user']);
  928. }
  929. /**
  930. * This method tells if the user has already been (previously) authenticated
  931. * by looking into the session variables.
  932. *
  933. * @note This function switches to callback mode when needed.
  934. *
  935. * @return TRUE when the user has already been authenticated; FALSE otherwise.
  936. *
  937. * @private
  938. */
  939. function wasPreviouslyAuthenticated()
  940. {
  941. phpCAS::traceBegin();
  942. if ( $this->isCallbackMode() ) {
  943. $this->callback();
  944. }
  945. $auth = FALSE;
  946. if ( $this->isProxy() ) {
  947. // CAS proxy: username and PGT must be present
  948. if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
  949. // authentication already done
  950. $this->setUser($_SESSION['phpCAS']['user']);
  951. $this->setPGT($_SESSION['phpCAS']['pgt']);
  952. phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\'');
  953. $auth = TRUE;
  954. } elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) {
  955. // these two variables should be empty or not empty at the same time
  956. phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
  957. // unset all tickets to enforce authentication
  958. unset($_SESSION['phpCAS']);
  959. $this->setST('');
  960. $this->setPT('');
  961. } elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
  962. // these two variables should be empty or not empty at the same time
  963. phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty');
  964. // unset all tickets to enforce authentication
  965. unset($_SESSION['phpCAS']);
  966. $this->setST('');
  967. $this->setPT('');
  968. } else {
  969. phpCAS::trace('neither user not PGT found');
  970. }
  971. } else {
  972. // `simple' CAS client (not a proxy): username must be present
  973. if ( $this->isSessionAuthenticated() ) {
  974. // authentication already done
  975. $this->setUser($_SESSION['phpCAS']['user']);
  976. if(isset($_SESSION['phpCAS']['attributes'])){
  977. $this->setAttributes($_SESSION['phpCAS']['attributes']);
  978. }
  979. phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\'');
  980. $auth = TRUE;
  981. } else {
  982. phpCAS::trace('no user found');
  983. }
  984. }
  985. phpCAS::traceEnd($auth);
  986. return $auth;
  987. }
  988. /**
  989. * This method is used to redirect the client to the CAS server.
  990. * It is used by CASClient::forceAuthentication() and CASClient::checkAuthentication().
  991. * @param $gateway true to check authentication, false to force it
  992. * @param $renew true to force the authentication with the CAS server
  993. * @public
  994. */
  995. function redirectToCas($gateway=false,$renew=false){
  996. phpCAS::traceBegin();
  997. $cas_url = $this->getServerLoginURL($gateway,$renew);
  998. header('Location: '.$cas_url);
  999. phpCAS::log( "Redirect to : ".$cas_url );
  1000. $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
  1001. printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
  1002. $this->printHTMLFooter();
  1003. phpCAS::traceExit();
  1004. exit();
  1005. }
  1006. /**
  1007. * This method is used to logout from CAS.
  1008. * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server
  1009. * @public
  1010. */
  1011. function logout($params) {
  1012. phpCAS::traceBegin();
  1013. $cas_url = $this->getServerLogoutURL();
  1014. $paramSeparator = '?';
  1015. if (isset($params['url'])) {
  1016. $cas_url = $cas_url . $paramSeparator . "url=" . urlencode($params['url']);
  1017. $paramSeparator = '&';
  1018. }
  1019. if (isset($params['service'])) {
  1020. $cas_url = $cas_url . $paramSeparator . "service=" . urlencode($params['service']);
  1021. }
  1022. header('Location: '.$cas_url);
  1023. phpCAS::log( "Prepare redirect to : ".$cas_url );
  1024. session_unset();
  1025. session_destroy();
  1026. $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
  1027. printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
  1028. $this->printHTMLFooter();
  1029. phpCAS::traceExit();
  1030. exit();
  1031. }
  1032. /**
  1033. * @return true if the current request is a logout request.
  1034. * @private
  1035. */
  1036. function isLogoutRequest() {
  1037. return !empty($_POST['logoutRequest']);
  1038. }
  1039. /**
  1040. * @return true if a logout request is allowed.
  1041. * @private
  1042. */
  1043. function isLogoutRequestAllowed() {
  1044. }
  1045. /**
  1046. * This method handles logout requests.
  1047. * @param $check_client true to check the client bofore handling the request,
  1048. * false not to perform any access control. True by default.
  1049. * @param $allowed_clients an array of host names allowed to send logout requests.
  1050. * By default, only the CAs server (declared in the constructor) will be allowed.
  1051. * @public
  1052. */
  1053. function handleLogoutRequests($check_client=true, $allowed_clients=false) {
  1054. phpCAS::traceBegin();
  1055. if (!$this->isLogoutRequest()) {
  1056. phpCAS::log("Not a logout request");
  1057. phpCAS::traceEnd();
  1058. return;
  1059. }
  1060. phpCAS::log("Logout requested");
  1061. phpCAS::log("SAML REQUEST: ".$_POST['logoutRequest']);
  1062. if ($check_client) {
  1063. if (!$allowed_clients) {
  1064. $allowed_clients = array( $this->getServerHostname() );
  1065. }
  1066. $client_ip = $_SERVER['REMOTE_ADDR'];
  1067. $client = gethostbyaddr($client_ip);
  1068. phpCAS::log("Client: ".$client."/".$client_ip);
  1069. $allowed = false;
  1070. foreach ($allowed_clients as $allowed_client) {
  1071. if (($client == $allowed_client) or ($client_ip == $allowed_client)) {
  1072. phpCAS::log("Allowed client '".$allowed_client."' matches, logout request is allowed");
  1073. $allowed = true;
  1074. break;
  1075. } else {
  1076. phpCAS::log("Allowed client '".$allowed_client."' does not match");
  1077. }
  1078. }
  1079. if (!$allowed) {
  1080. phpCAS::error("Unauthorized logout request from client '".$client."'");
  1081. printf("Unauthorized!");
  1082. phpCAS::traceExit();
  1083. exit();
  1084. }
  1085. } else {
  1086. phpCAS::log("No access control set");
  1087. }
  1088. // Extract the ticket from the SAML Request
  1089. preg_match("|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|", $_POST['logoutRequest'], $tick, PREG_OFFSET_CAPTURE, 3);
  1090. $wrappedSamlSessionIndex = preg_replace('|<samlp:SessionIndex>|','',$tick[0][0]);
  1091. $ticket2logout = preg_replace('|</samlp:SessionIndex>|','',$wrappedSamlSessionIndex);
  1092. phpCAS::log("Ticket to logout: ".$ticket2logout);
  1093. $session_id = preg_replace('/[^\w]/','',$ticket2logout);
  1094. phpCAS::log("Session id: ".$session_id);
  1095. // destroy a possible application session created before phpcas
  1096. if(session_id()){
  1097. session_unset();
  1098. session_destroy();
  1099. }
  1100. // fix session ID
  1101. session_id($session_id);
  1102. $_COOKIE[session_name()]=$session_id;
  1103. $_GET[session_name()]=$session_id;
  1104. // Overwrite session
  1105. session_start();
  1106. session_unset();
  1107. session_destroy();
  1108. printf("Disconnected!");
  1109. phpCAS::traceExit();
  1110. exit();
  1111. }
  1112. /** @} */
  1113. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1114. // XX XX
  1115. // XX BASIC CLIENT FEATURES (CAS 1.0) XX
  1116. // XX XX
  1117. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1118. // ########################################################################
  1119. // ST
  1120. // ########################################################################
  1121. /**
  1122. * @addtogroup internalBasic
  1123. * @{
  1124. */
  1125. /**
  1126. * the Service Ticket provided in the URL of the request if present
  1127. * (empty otherwise). Written by CASClient::CASClient(), read by
  1128. * CASClient::getST() and CASClient::hasPGT().
  1129. *
  1130. * @hideinitializer
  1131. * @private
  1132. */
  1133. var $_st = '';
  1134. /**
  1135. * This method returns the Service Ticket provided in the URL of the request.
  1136. * @return The service ticket.
  1137. * @private
  1138. */
  1139. function getST()
  1140. { return $this->_st; }
  1141. /**
  1142. * This method stores the Service Ticket.
  1143. * @param $st The Service Ticket.
  1144. * @private
  1145. */
  1146. function setST($st)
  1147. { $this->_st = $st; }
  1148. /**
  1149. * This method tells if a Service Ticket was stored.
  1150. * @return TRUE if a Service Ticket has been stored.
  1151. * @private
  1152. */
  1153. function hasST()
  1154. { return !empty($this->_st); }
  1155. /** @} */
  1156. // ########################################################################
  1157. // ST VALIDATION
  1158. // ########################################################################
  1159. /**
  1160. * @addtogroup internalBasic
  1161. * @{
  1162. */
  1163. /**
  1164. * the certificate of the CAS server.
  1165. *
  1166. * @hideinitializer
  1167. * @private
  1168. */
  1169. var $_cas_server_cert = '';
  1170. /**
  1171. * the certificate of the CAS server CA.
  1172. *
  1173. * @hideinitializer
  1174. * @private
  1175. */
  1176. var $_cas_server_ca_cert = '';
  1177. /**
  1178. * Set to true not to validate the CAS server.
  1179. *
  1180. * @hideinitializer
  1181. * @private
  1182. */
  1183. var $_no_cas_server_validation = false;
  1184. /**
  1185. * Set the certificate of the CAS server.
  1186. *
  1187. * @param $cert the PEM certificate
  1188. */
  1189. function setCasServerCert($cert)
  1190. {
  1191. $this->_cas_server_cert = $cert;
  1192. }
  1193. /**
  1194. * Set the CA certificate of the CAS server.
  1195. *
  1196. * @param $cert the PEM certificate of the CA that emited the cert of the server
  1197. */
  1198. function setCasServerCACert($cert)
  1199. {
  1200. $this->_cas_server_ca_cert = $cert;
  1201. }
  1202. /**
  1203. * Set no SSL validation for the CAS server.
  1204. */
  1205. function setNoCasServerValidation()
  1206. {
  1207. $this->_no_cas_server_validation = true;
  1208. }
  1209. /**
  1210. * This method is used to validate a ST; halt on failure, and sets $validate_url,
  1211. * $text_reponse and $tree_response on success. These parameters are used later
  1212. * by CASClient::validatePGT() for CAS proxies.
  1213. * Used for all CAS 1.0 validations
  1214. * @param $validate_url the URL of the request to the CAS server.
  1215. * @param $text_response the response of the CAS server, as is (XML text).
  1216. * @param $tree_response the response of the CAS server, as a DOM XML tree.
  1217. *
  1218. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1219. *
  1220. * @private
  1221. */
  1222. function validateST($validate_url,&$text_response,&$tree_response)
  1223. {
  1224. phpCAS::traceBegin();
  1225. // build the URL to validate the ticket
  1226. $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
  1227. if ( $this->isProxy() ) {
  1228. // pass the callback url for CAS proxies
  1229. $validate_url .= '&pgtUrl='.$this->getCallbackURL();
  1230. }
  1231. // open and read the URL
  1232. if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
  1233. phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
  1234. $this->authError('ST not validated',
  1235. $validate_url,
  1236. TRUE/*$no_response*/);
  1237. }
  1238. // analyze the result depending on the version
  1239. switch ($this->getServerVersion()) {
  1240. case CAS_VERSION_1_0:
  1241. if (preg_match('/^no\n/',$text_response)) {
  1242. phpCAS::trace('ST has not been validated');
  1243. $this->authError('ST not validated',
  1244. $validate_url,
  1245. FALSE/*$no_response*/,
  1246. FALSE/*$bad_response*/,
  1247. $text_response);
  1248. }
  1249. if (!preg_match('/^yes\n/',$text_response)) {
  1250. phpCAS::trace('ill-formed response');
  1251. $this->authError('ST not validated',
  1252. $validate_url,
  1253. FALSE/*$no_response*/,
  1254. TRUE/*$bad_response*/,
  1255. $text_response);
  1256. }
  1257. // ST has been validated, extract the user name
  1258. $arr = preg_split('/\n/',$text_response);
  1259. $this->setUser(trim($arr[1]));
  1260. break;
  1261. case CAS_VERSION_2_0:
  1262. // read the response of the CAS server into a DOM object
  1263. if ( !($dom = domxml_open_mem($text_response))) {
  1264. phpCAS::trace('domxml_open_mem() failed');
  1265. $this->authError('ST not validated',
  1266. $validate_url,
  1267. FALSE/*$no_response*/,
  1268. TRUE/*$bad_response*/,
  1269. $text_response);
  1270. }
  1271. // read the root node of the XML tree
  1272. if ( !($tree_response = $dom->document_element()) ) {
  1273. phpCAS::trace('document_element() failed');
  1274. $this->authError('ST not validated',
  1275. $validate_url,
  1276. FALSE/*$no_response*/,
  1277. TRUE/*$bad_response*/,
  1278. $text_response);
  1279. }
  1280. // insure that tag name is 'serviceResponse'
  1281. if ( $tree_response->node_name() != 'serviceResponse' ) {
  1282. phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\'');
  1283. $this->authError('ST not validated',
  1284. $validate_url,
  1285. FALSE/*$no_response*/,
  1286. TRUE/*$bad_response*/,
  1287. $text_response);
  1288. }
  1289. if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
  1290. // authentication succeded, extract the user name
  1291. if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
  1292. phpCAS::trace('<authenticationSuccess> found, but no <user>');
  1293. $this->authError('ST not validated',
  1294. $validate_url,
  1295. FALSE/*$no_response*/,
  1296. TRUE/*$bad_response*/,
  1297. $text_response);
  1298. }
  1299. $user = trim($user_elements[0]->get_content());
  1300. phpCAS::trace('user = `'.$user);
  1301. $this->setUser($user);
  1302. } else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
  1303. phpCAS::trace('<authenticationFailure> found');
  1304. // authentication failed, extract the error code and message
  1305. $this->authError('ST not validated',
  1306. $validate_url,
  1307. FALSE/*$no_response*/,
  1308. FALSE/*$bad_response*/,
  1309. $text_response,
  1310. $failure_elements[0]->get_attribute('code')/*$err_code*/,
  1311. trim($failure_elements[0]->get_content())/*$err_msg*/);
  1312. } else {
  1313. phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
  1314. $this->authError('ST not validated',
  1315. $validate_url,
  1316. FALSE/*$no_response*/,
  1317. TRUE/*$bad_response*/,
  1318. $text_response);
  1319. }
  1320. break;
  1321. }
  1322. // at this step, ST has been validated and $this->_user has been set,
  1323. phpCAS::traceEnd(TRUE);
  1324. return TRUE;
  1325. }
  1326. // ########################################################################
  1327. // SAML VALIDATION
  1328. // ########################################################################
  1329. /**
  1330. * @addtogroup internalBasic
  1331. * @{
  1332. */
  1333. /**
  1334. * This method is used to validate a SAML TICKET; halt on failure, and sets $validate_url,
  1335. * $text_reponse and $tree_response on success. These parameters are used later
  1336. * by CASClient::validatePGT() for CAS proxies.
  1337. *
  1338. * @param $validate_url the URL of the request to the CAS server.
  1339. * @param $text_response the response of the CAS server, as is (XML text).
  1340. * @param $tree_response the response of the CAS server, as a DOM XML tree.
  1341. *
  1342. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1343. *
  1344. * @private
  1345. */
  1346. function validateSA($validate_url,&$text_response,&$tree_response)
  1347. {
  1348. phpCAS::traceBegin();
  1349. // build the URL to validate the ticket
  1350. $validate_url = $this->getServerSamlValidateURL();
  1351. // open and read the URL
  1352. if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
  1353. phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
  1354. $this->authError('SA not validated', $validate_url, TRUE/*$no_response*/);
  1355. }
  1356. phpCAS::trace('server version: '.$this->getServerVersion());
  1357. // analyze the result depending on the version
  1358. switch ($this->getServerVersion()) {
  1359. case SAML_VERSION_1_1:
  1360. // read the response of the CAS server into a DOM object
  1361. if ( !($dom = domxml_open_mem($text_response))) {
  1362. phpCAS::trace('domxml_open_mem() failed');
  1363. $this->authError('SA not validated',
  1364. $validate_url,
  1365. FALSE/*$no_response*/,
  1366. TRUE/*$bad_response*/,
  1367. $text_response);
  1368. }
  1369. // read the root node of the XML tree
  1370. if ( !($tree_response = $dom->document_element()) ) {
  1371. phpCAS::trace('document_element() failed');
  1372. $this->authError('SA not validated',
  1373. $validate_url,
  1374. FALSE/*$no_response*/,
  1375. TRUE/*$bad_response*/,
  1376. $text_response);
  1377. }
  1378. // insure that tag name is 'Envelope'
  1379. if ( $tree_response->node_name() != 'Envelope' ) {
  1380. phpCAS::trace('bad XML root node (should be `Envelope\' instead of `'.$tree_response->node_name().'\'');
  1381. $this->authError('SA not validated',
  1382. $validate_url,
  1383. FALSE/*$no_response*/,
  1384. TRUE/*$bad_response*/,
  1385. $text_response);
  1386. }
  1387. // check for the NameIdentifier tag in the SAML response
  1388. if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("NameIdentifier")) != 0) {
  1389. phpCAS::trace('NameIdentifier found');
  1390. $user = trim($success_elements[0]->get_content());
  1391. phpCAS::trace('user = `'.$user.'`');
  1392. $this->setUser($user);
  1393. $this->setSessionAttributes($text_response);
  1394. } else {
  1395. phpCAS::trace('no <NameIdentifier> tag found in SAML payload');
  1396. $this->authError('SA not validated',
  1397. $validate_url,
  1398. FALSE/*$no_response*/,
  1399. TRUE/*$bad_response*/,
  1400. $text_response);
  1401. }
  1402. break;
  1403. }
  1404. // at this step, ST has been validated and $this->_user has been set,
  1405. phpCAS::traceEnd(TRUE);
  1406. return TRUE;
  1407. }
  1408. /**
  1409. * This method will parse the DOM and pull out the attributes from the SAML
  1410. * payload and put them into an array, then put the array into the session.
  1411. *
  1412. * @param $text_response the SAML payload.
  1413. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1414. *
  1415. * @private
  1416. */
  1417. function setSessionAttributes($text_response)
  1418. {
  1419. phpCAS::traceBegin();
  1420. $result = FALSE;
  1421. if (isset($_SESSION[SAML_ATTRIBUTES])) {
  1422. phpCAS::trace("session attrs already set."); //testbml - do we care?
  1423. }
  1424. $attr_array = array();
  1425. if (($dom = domxml_open_mem($text_response))) {
  1426. $xPath = $dom->xpath_new_context();
  1427. $xPath->xpath_register_ns('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
  1428. $xPath->xpath_register_ns('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
  1429. $nodelist = $xPath->xpath_eval("//saml:Attribute");
  1430. $attrs = $nodelist->nodeset;
  1431. phpCAS::trace($text_response);
  1432. foreach($attrs as $attr){
  1433. $xres = $xPath->xpath_eval("saml:AttributeValue", $attr);
  1434. $name = $attr->get_attribute("AttributeName");
  1435. $value_array = array();
  1436. foreach($xres->nodeset as $node){
  1437. $value_array[] = $node->get_content();
  1438. }
  1439. phpCAS::trace("* " . $name . "=" . $value_array);
  1440. $attr_array[$name] = $value_array;
  1441. }
  1442. $_SESSION[SAML_ATTRIBUTES] = $attr_array;
  1443. // UGent addition...
  1444. foreach($attr_array as $attr_key => $attr_value) {
  1445. if(count($attr_value) > 1) {
  1446. $this->_attributes[$attr_key] = $attr_value;
  1447. }
  1448. else {
  1449. $this->_attributes[$attr_key] = $attr_value[0];
  1450. }
  1451. }
  1452. $result = TRUE;
  1453. }
  1454. phpCAS::traceEnd($result);
  1455. return $result;
  1456. }
  1457. /** @} */
  1458. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1459. // XX XX
  1460. // XX PROXY FEATURES (CAS 2.0) XX
  1461. // XX XX
  1462. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1463. // ########################################################################
  1464. // PROXYING
  1465. // ########################################################################
  1466. /**
  1467. * @addtogroup internalProxy
  1468. * @{
  1469. */
  1470. /**
  1471. * A boolean telling if the client is a CAS proxy or not. Written by CASClient::CASClient(),
  1472. * read by CASClient::isProxy().
  1473. *
  1474. * @private
  1475. */
  1476. var $_proxy;
  1477. /**
  1478. * Tells if a CAS client is a CAS proxy or not
  1479. *
  1480. * @return TRUE when the CAS client is a CAs proxy, FALSE otherwise
  1481. *
  1482. * @private
  1483. */
  1484. function isProxy()
  1485. {
  1486. return $this->_proxy;
  1487. }
  1488. /** @} */
  1489. // ########################################################################
  1490. // PGT
  1491. // ########################################################################
  1492. /**
  1493. * @addtogroup internalProxy
  1494. * @{
  1495. */
  1496. /**
  1497. * the Proxy Grnting Ticket given by the CAS server (empty otherwise).
  1498. * Written by CASClient::setPGT(), read by CASClient::getPGT() and CASClient::hasPGT().
  1499. *
  1500. * @hideinitializer
  1501. * @private
  1502. */
  1503. var $_pgt = '';
  1504. /**
  1505. * This method returns the Proxy Granting Ticket given by the CAS server.
  1506. * @return The Proxy Granting Ticket.
  1507. * @private
  1508. */
  1509. function getPGT()
  1510. { return $this->_pgt; }
  1511. /**
  1512. * This method stores the Proxy Granting Ticket.
  1513. * @param $pgt The Proxy Granting Ticket.
  1514. * @private
  1515. */
  1516. function setPGT($pgt)
  1517. { $this->_pgt = $pgt; }
  1518. /**
  1519. * This method tells if a Proxy Granting Ticket was stored.
  1520. * @return TRUE if a Proxy Granting Ticket has been stored.
  1521. * @private
  1522. */
  1523. function hasPGT()
  1524. { return !empty($this->_pgt); }
  1525. /** @} */
  1526. // ########################################################################
  1527. // CALLBACK MODE
  1528. // ########################################################################
  1529. /**
  1530. * @addtogroup internalCallback
  1531. * @{
  1532. */
  1533. /**
  1534. * each PHP script using phpCAS in proxy mode is its own callback to get the
  1535. * PGT back from the CAS server. callback_mode is detected by the constructor
  1536. * thanks to the GET parameters.
  1537. */
  1538. /**
  1539. * a boolean to know if the CAS client is running in callback mode. Written by
  1540. * CASClient::setCallBackMode(), read by CASClient::isCallbackMode().
  1541. *
  1542. * @hideinitializer
  1543. * @private
  1544. */
  1545. var $_callback_mode = FALSE;
  1546. /**
  1547. * This method sets/unsets callback mode.
  1548. *
  1549. * @param $callback_mode TRUE to set callback mode, FALSE otherwise.
  1550. *
  1551. * @private
  1552. */
  1553. function setCallbackMode($callback_mode)
  1554. {
  1555. $this->_callback_mode = $callback_mode;
  1556. }
  1557. /**
  1558. * This method returns TRUE when the CAs client is running i callback mode,
  1559. * FALSE otherwise.
  1560. *
  1561. * @return A boolean.
  1562. *
  1563. * @private
  1564. */
  1565. function isCallbackMode()
  1566. {
  1567. return $this->_callback_mode;
  1568. }
  1569. /**
  1570. * the URL that should be used for the PGT callback (in fact the URL of the
  1571. * current request without any CGI parameter). Written and read by
  1572. * CASClient::getCallbackURL().
  1573. *
  1574. * @hideinitializer
  1575. * @private
  1576. */
  1577. var $_callback_url = '';
  1578. /**
  1579. * This method returns the URL that should be used for the PGT callback (in
  1580. * fact the URL of the current request without any CGI parameter, except if
  1581. * phpCAS::setFixedCallbackURL() was used).
  1582. *
  1583. * @return The callback URL
  1584. *
  1585. * @private
  1586. */
  1587. function getCallbackURL()
  1588. {
  1589. // the URL is built when needed only
  1590. if ( empty($this->_callback_url) ) {
  1591. $final_uri = '';
  1592. // remove the ticket if present in the URL
  1593. $final_uri = 'https://';
  1594. /* replaced by Julien Marchal - v0.4.6
  1595. * $this->uri .= $_SERVER['SERVER_NAME'];
  1596. */
  1597. if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
  1598. /* replaced by teedog - v0.4.12
  1599. * $final_uri .= $_SERVER['SERVER_NAME'];
  1600. */
  1601. if (empty($_SERVER['SERVER_NAME'])) {
  1602. $final_uri .= $_SERVER['HTTP_HOST'];
  1603. } else {
  1604. $final_uri .= $_SERVER['SERVER_NAME'];
  1605. }
  1606. } else {
  1607. $final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
  1608. }
  1609. if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
  1610. || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
  1611. $final_uri .= ':';
  1612. $final_uri .= $_SERVER['SERVER_PORT'];
  1613. }
  1614. $request_uri = $_SERVER['REQUEST_URI'];
  1615. $request_uri = preg_replace('/\?.*$/','',$request_uri);
  1616. $final_uri .= $request_uri;
  1617. $this->setCallbackURL($final_uri);
  1618. }
  1619. return $this->_callback_url;
  1620. }
  1621. /**
  1622. * This method sets the callback url.
  1623. *
  1624. * @param $callback_url url to set callback
  1625. *
  1626. * @private
  1627. */
  1628. function setCallbackURL($url)
  1629. {
  1630. return $this->_callback_url = $url;
  1631. }
  1632. /**
  1633. * This method is called by CASClient::CASClient() when running in callback
  1634. * mode. It stores the PGT and its PGT Iou, prints its output and halts.
  1635. *
  1636. * @private
  1637. */
  1638. function callback()
  1639. {
  1640. phpCAS::traceBegin();
  1641. $this->printHTMLHeader('phpCAS callback');
  1642. $pgt_iou = $_GET['pgtIou'];
  1643. $pgt = $_GET['pgtId'];
  1644. phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
  1645. echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
  1646. $this->storePGT($pgt,$pgt_iou);
  1647. $this->printHTMLFooter();
  1648. phpCAS::traceExit();
  1649. exit();
  1650. }
  1651. /** @} */
  1652. // ########################################################################
  1653. // PGT STORAGE
  1654. // ########################################################################
  1655. /**
  1656. * @addtogroup internalPGTStorage
  1657. * @{
  1658. */
  1659. /**
  1660. * an instance of a class inheriting of PGTStorage, used to deal with PGT
  1661. * storage. Created by CASClient::setPGTStorageFile() or CASClient::setPGTStorageDB(), used
  1662. * by CASClient::setPGTStorageFile(), CASClient::setPGTStorageDB() and CASClient::initPGTStorage().
  1663. *
  1664. * @hideinitializer
  1665. * @private
  1666. */
  1667. var $_pgt_storage = null;
  1668. /**
  1669. * This method is used to initialize the storage of PGT's.
  1670. * Halts on error.
  1671. *
  1672. * @private
  1673. */
  1674. function initPGTStorage()
  1675. {
  1676. // if no SetPGTStorageXxx() has been used, default to file
  1677. if ( !is_object($this->_pgt_storage) ) {
  1678. $this->setPGTStorageFile();
  1679. }
  1680. // initializes the storage
  1681. $this->_pgt_storage->init();
  1682. }
  1683. /**
  1684. * This method stores a PGT. Halts on error.
  1685. *
  1686. * @param $pgt the PGT to store
  1687. * @param $pgt_iou its corresponding Iou
  1688. *
  1689. * @private
  1690. */
  1691. function storePGT($pgt,$pgt_iou)
  1692. {
  1693. // ensure that storage is initialized
  1694. $this->initPGTStorage();
  1695. // writes the PGT
  1696. $this->_pgt_storage->write($pgt,$pgt_iou);
  1697. }
  1698. /**
  1699. * This method reads a PGT from its Iou and deletes the corresponding storage entry.
  1700. *
  1701. * @param $pgt_iou the PGT Iou
  1702. *
  1703. * @return The PGT corresponding to the Iou, FALSE when not found.
  1704. *
  1705. * @private
  1706. */
  1707. function loadPGT($pgt_iou)
  1708. {
  1709. // ensure that storage is initialized
  1710. $this->initPGTStorage();
  1711. // read the PGT
  1712. return $this->_pgt_storage->read($pgt_iou);
  1713. }
  1714. /**
  1715. * This method is used to tell phpCAS to store the response of the
  1716. * CAS server to PGT requests onto the filesystem.
  1717. *
  1718. * @param $format the format used to store the PGT's (`plain' and `xml' allowed)
  1719. * @param $path the path where the PGT's should be stored
  1720. *
  1721. * @public
  1722. */
  1723. function setPGTStorageFile($format='',
  1724. $path='')
  1725. {
  1726. // check that the storage has not already been set
  1727. if ( is_object($this->_pgt_storage) ) {
  1728. phpCAS::error('PGT storage already defined');
  1729. }
  1730. // create the storage object
  1731. $this->_pgt_storage = new PGTStorageFile($this,$format,$path);
  1732. }
  1733. /**
  1734. * This method is used to tell phpCAS to store the response of the
  1735. * CAS server to PGT requests into a database.
  1736. * @note The connection to the database is done only when needed.
  1737. * As a consequence, bad parameters are detected only when
  1738. * initializing PGT storage.
  1739. *
  1740. * @param $user the user to access the data with
  1741. * @param $password the user's password
  1742. * @param $database_type the type of the database hosting the data
  1743. * @param $hostname the server hosting the database
  1744. * @param $port the port the server is listening on
  1745. * @param $database the name of the database
  1746. * @param $table the name of the table storing the data
  1747. *
  1748. * @public
  1749. */
  1750. function setPGTStorageDB($user,
  1751. $password,
  1752. $database_type,
  1753. $hostname,
  1754. $port,
  1755. $database,
  1756. $table)
  1757. {
  1758. // check that the storage has not already been set
  1759. if ( is_object($this->_pgt_storage) ) {
  1760. phpCAS::error('PGT storage already defined');
  1761. }
  1762. // warn the user that he should use file storage...
  1763. trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING);
  1764. // create the storage object
  1765. $this->_pgt_storage = new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table);
  1766. }
  1767. // ########################################################################
  1768. // PGT VALIDATION
  1769. // ########################################################################
  1770. /**
  1771. * This method is used to validate a PGT; halt on failure.
  1772. *
  1773. * @param $validate_url the URL of the request to the CAS server.
  1774. * @param $text_response the response of the CAS server, as is (XML text); result
  1775. * of CASClient::validateST() or CASClient::validatePT().
  1776. * @param $tree_response the response of the CAS server, as a DOM XML tree; result
  1777. * of CASClient::validateST() or CASClient::validatePT().
  1778. *
  1779. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1780. *
  1781. * @private
  1782. */
  1783. function validatePGT(&$validate_url,$text_response,$tree_response)
  1784. {
  1785. // here cannot use phpCAS::traceBegin(); alongside domxml-php4-to-php5.php
  1786. phpCAS::log('start validatePGT()');
  1787. if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) {
  1788. phpCAS::trace('<proxyGrantingTicket> not found');
  1789. // authentication succeded, but no PGT Iou was transmitted
  1790. $this->authError('Ticket validated but no PGT Iou transmitted',
  1791. $validate_url,
  1792. FALSE/*$no_response*/,
  1793. FALSE/*$bad_response*/,
  1794. $text_response);
  1795. } else {
  1796. // PGT Iou transmitted, extract it
  1797. $pgt_iou = trim($arr[0]->get_content());
  1798. $pgt = $this->loadPGT($pgt_iou);
  1799. if ( $pgt == FALSE ) {
  1800. phpCAS::trace('could not load PGT');
  1801. $this->authError('PGT Iou was transmitted but PGT could not be retrieved',
  1802. $validate_url,
  1803. FALSE/*$no_response*/,
  1804. FALSE/*$bad_response*/,
  1805. $text_response);
  1806. }
  1807. $this->setPGT($pgt);
  1808. }
  1809. // here, cannot use phpCAS::traceEnd(TRUE); alongside domxml-php4-to-php5.php
  1810. phpCAS::log('end validatePGT()');
  1811. return TRUE;
  1812. }
  1813. // ########################################################################
  1814. // PGT VALIDATION
  1815. // ########################################################################
  1816. /**
  1817. * This method is used to retrieve PT's from the CAS server thanks to a PGT.
  1818. *
  1819. * @param $target_service the service to ask for with the PT.
  1820. * @param $err_code an error code (PHPCAS_SERVICE_OK on success).
  1821. * @param $err_msg an error message (empty on success).
  1822. *
  1823. * @return a Proxy Ticket, or FALSE on error.
  1824. *
  1825. * @private
  1826. */
  1827. function retrievePT($target_service,&$err_code,&$err_msg)
  1828. {
  1829. phpCAS::traceBegin();
  1830. // by default, $err_msg is set empty and $pt to TRUE. On error, $pt is
  1831. // set to false and $err_msg to an error message. At the end, if $pt is FALSE
  1832. // and $error_msg is still empty, it is set to 'invalid response' (the most
  1833. // commonly encountered error).
  1834. $err_msg = '';
  1835. // build the URL to retrieve the PT
  1836. // $cas_url = $this->getServerProxyURL().'?targetService='.preg_replace('/&/','%26',$target_service).'&pgt='.$this->getPGT();
  1837. $cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT();
  1838. // open and read the URL
  1839. if ( !$this->readURL($cas_url,''/*cookies*/,$headers,$cas_response,$err_msg) ) {
  1840. phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
  1841. $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
  1842. $err_msg = 'could not retrieve PT (no response from the CAS server)';
  1843. phpCAS::traceEnd(FALSE);
  1844. return FALSE;
  1845. }
  1846. $bad_response = FALSE;
  1847. if ( !$bad_response ) {
  1848. // read the response of the CAS server into a DOM object
  1849. if ( !($dom = @domxml_open_mem($cas_response))) {
  1850. phpCAS::trace('domxml_open_mem() failed');
  1851. // read failed
  1852. $bad_response = TRUE;
  1853. }
  1854. }
  1855. if ( !$bad_response ) {
  1856. // read the root node of the XML tree
  1857. if ( !($root = $dom->document_element()) ) {
  1858. phpCAS::trace('document_element() failed');
  1859. // read failed
  1860. $bad_response = TRUE;
  1861. }
  1862. }
  1863. if ( !$bad_response ) {
  1864. // insure that tag name is 'serviceResponse'
  1865. if ( $root->node_name() != 'serviceResponse' ) {
  1866. phpCAS::trace('node_name() failed');
  1867. // bad root node
  1868. $bad_response = TRUE;
  1869. }
  1870. }
  1871. if ( !$bad_response ) {
  1872. // look for a proxySuccess tag
  1873. if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) {
  1874. // authentication succeded, look for a proxyTicket tag
  1875. if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) {
  1876. $err_code = PHPCAS_SERVICE_OK;
  1877. $err_msg = '';
  1878. phpCAS::trace('original PT: '.trim($arr[0]->get_content()));
  1879. $pt = trim($arr[0]->get_content());
  1880. phpCAS::traceEnd($pt);
  1881. return $pt;
  1882. } else {
  1883. phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
  1884. }
  1885. }
  1886. // look for a proxyFailure tag
  1887. else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) {
  1888. // authentication failed, extract the error
  1889. $err_code = PHPCAS_SERVICE_PT_FAILURE;
  1890. $err_msg = 'PT retrieving failed (code=`'
  1891. .$arr[0]->get_attribute('code')
  1892. .'\', message=`'
  1893. .trim($arr[0]->get_content())
  1894. .'\')';
  1895. phpCAS::traceEnd(FALSE);
  1896. return FALSE;
  1897. } else {
  1898. phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
  1899. }
  1900. }
  1901. // at this step, we are sure that the response of the CAS server was ill-formed
  1902. $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
  1903. $err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
  1904. phpCAS::traceEnd(FALSE);
  1905. return FALSE;
  1906. }
  1907. // ########################################################################
  1908. // ACCESS TO EXTERNAL SERVICES
  1909. // ########################################################################
  1910. /**
  1911. * This method is used to acces a remote URL.
  1912. *
  1913. * @param $url the URL to access.
  1914. * @param $cookies an array containing cookies strings such as 'name=val'
  1915. * @param $headers an array containing the HTTP header lines of the response
  1916. * (an empty array on failure).
  1917. * @param $body the body of the response, as a string (empty on failure).
  1918. * @param $err_msg an error message, filled on failure.
  1919. *
  1920. * @return TRUE on success, FALSE otherwise (in this later case, $err_msg
  1921. * contains an error message).
  1922. *
  1923. * @private
  1924. */
  1925. function readURL($url,$cookies,&$headers,&$body,&$err_msg)
  1926. {
  1927. phpCAS::traceBegin();
  1928. $headers = '';
  1929. $body = '';
  1930. $err_msg = '';
  1931. $res = TRUE;
  1932. // initialize the CURL session
  1933. $ch = curl_init($url);
  1934. if (version_compare(PHP_VERSION,'5.1.3','>=')) {
  1935. //only avaible in php5
  1936. curl_setopt_array($ch, $this->_curl_options);
  1937. } else {
  1938. foreach ($this->_curl_options as $key => $value) {
  1939. curl_setopt($ch, $key, $value);
  1940. }
  1941. }
  1942. if ($this->_cas_server_cert == '' && $this->_cas_server_ca_cert == '' && !$this->_no_cas_server_validation) {
  1943. phpCAS::error('one of the methods phpCAS::setCasServerCert(), phpCAS::setCasServerCACert() or phpCAS::setNoCasServerValidation() must be called.');
  1944. }
  1945. if ($this->_cas_server_cert != '' && $this->_cas_server_ca_cert != '') {
  1946. // This branch added by IDMS. Seems phpCAS implementor got a bit confused about the curl options CURLOPT_SSLCERT and CURLOPT_CAINFO
  1947. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
  1948. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
  1949. curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
  1950. curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
  1951. curl_setopt($ch, CURLOPT_VERBOSE, '1');
  1952. phpCAS::trace('CURL: Set all required opts for mutual authentication ------');
  1953. } else if ($this->_cas_server_cert != '' ) {
  1954. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
  1955. curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
  1956. } else if ($this->_cas_server_ca_cert != '') {
  1957. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
  1958. curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
  1959. } else {
  1960. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
  1961. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  1962. }
  1963. // return the CURL output into a variable
  1964. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  1965. // get the HTTP header with a callback
  1966. $this->_curl_headers = array(); // empty the headers array
  1967. curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, '_curl_read_headers'));
  1968. // add cookies headers
  1969. if ( is_array($cookies) ) {
  1970. curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies));
  1971. }
  1972. // add extra stuff if SAML
  1973. if ($this->hasSA()) {
  1974. $more_headers = array ("soapaction: http://www.oasis-open.org/committees/security",
  1975. "cache-control: no-cache",
  1976. "pragma: no-cache",
  1977. "accept: text/xml",
  1978. "connection: keep-alive",
  1979. "content-type: text/xml");
  1980. curl_setopt($ch, CURLOPT_HTTPHEADER, $more_headers);
  1981. curl_setopt($ch, CURLOPT_POST, 1);
  1982. $data = $this->buildSAMLPayload();
  1983. //phpCAS::trace('SAML Payload: '.print_r($data, TRUE));
  1984. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  1985. }
  1986. // perform the query
  1987. $buf = curl_exec ($ch);
  1988. //phpCAS::trace('CURL: Call completed. Response body is: \''.$buf.'\'');
  1989. if ( $buf === FALSE ) {
  1990. phpCAS::trace('curl_exec() failed');
  1991. $err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch);
  1992. //phpCAS::trace('curl error: '.$err_msg);
  1993. // close the CURL session
  1994. curl_close ($ch);
  1995. $res = FALSE;
  1996. } else {
  1997. // close the CURL session
  1998. curl_close ($ch);
  1999. $headers = $this->_curl_headers;
  2000. $body = $buf;
  2001. }
  2002. phpCAS::traceEnd($res);
  2003. return $res;
  2004. }
  2005. /**
  2006. * This method is used to build the SAML POST body sent to /samlValidate URL.
  2007. *
  2008. * @return the SOAP-encased SAMLP artifact (the ticket).
  2009. *
  2010. * @private
  2011. */
  2012. function buildSAMLPayload()
  2013. {
  2014. phpCAS::traceBegin();
  2015. //get the ticket
  2016. $sa = $this->getSA();
  2017. //phpCAS::trace("SA: ".$sa);
  2018. $body=SAML_SOAP_ENV.SAML_SOAP_BODY.SAMLP_REQUEST.SAML_ASSERTION_ARTIFACT.$sa.SAML_ASSERTION_ARTIFACT_CLOSE.SAMLP_REQUEST_CLOSE.SAML_SOAP_BODY_CLOSE.SAML_SOAP_ENV_CLOSE;
  2019. phpCAS::traceEnd($body);
  2020. return ($body);
  2021. }
  2022. /**
  2023. * This method is the callback used by readURL method to request HTTP headers.
  2024. */
  2025. var $_curl_headers = array();
  2026. function _curl_read_headers($ch, $header)
  2027. {
  2028. $this->_curl_headers[] = $header;
  2029. return strlen($header);
  2030. }
  2031. /**
  2032. * This method is used to access an HTTP[S] service.
  2033. *
  2034. * @param $url the service to access.
  2035. * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
  2036. * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
  2037. * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
  2038. * @param $output the output of the service (also used to give an error
  2039. * message on failure).
  2040. *
  2041. * @return TRUE on success, FALSE otherwise (in this later case, $err_code
  2042. * gives the reason why it failed and $output contains an error message).
  2043. *
  2044. * @public
  2045. */
  2046. function serviceWeb($url,&$err_code,&$output)
  2047. {
  2048. phpCAS::traceBegin();
  2049. // at first retrieve a PT
  2050. $pt = $this->retrievePT($url,$err_code,$output);
  2051. $res = TRUE;
  2052. // test if PT was retrieved correctly
  2053. if ( !$pt ) {
  2054. // note: $err_code and $err_msg are filled by CASClient::retrievePT()
  2055. phpCAS::trace('PT was not retrieved correctly');
  2056. $res = FALSE;
  2057. } else {
  2058. // add cookies if necessary
  2059. if ( is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) {
  2060. foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) {
  2061. $cookies[] = $name.'='.$val;
  2062. }
  2063. }
  2064. // build the URL including the PT
  2065. if ( strstr($url,'?') === FALSE ) {
  2066. $service_url = $url.'?ticket='.$pt;
  2067. } else {
  2068. $service_url = $url.'&ticket='.$pt;
  2069. }
  2070. phpCAS::trace('reading URL`'.$service_url.'\'');
  2071. if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
  2072. phpCAS::trace('could not read URL`'.$service_url.'\'');
  2073. $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
  2074. // give an error message
  2075. $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
  2076. $service_url,
  2077. $err_msg);
  2078. $res = FALSE;
  2079. } else {
  2080. // URL has been fetched, extract the cookies
  2081. phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
  2082. foreach ( $headers as $header ) {
  2083. // test if the header is a cookie
  2084. if ( preg_match('/^Set-Cookie:/',$header) ) {
  2085. // the header is a cookie, remove the beginning
  2086. $header_val = preg_replace('/^Set-Cookie: */','',$header);
  2087. // extract interesting information
  2088. $name_val = strtok($header_val,'; ');
  2089. // extract the name and the value of the cookie
  2090. $cookie_name = strtok($name_val,'=');
  2091. $cookie_val = strtok('=');
  2092. // store the cookie
  2093. $_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val;
  2094. phpCAS::trace($cookie_name.' -> '.$cookie_val);
  2095. }
  2096. }
  2097. }
  2098. }
  2099. phpCAS::traceEnd($res);
  2100. return $res;
  2101. }
  2102. /**
  2103. * This method is used to access an IMAP/POP3/NNTP service.
  2104. *
  2105. * @param $url a string giving the URL of the service, including the mailing box
  2106. * for IMAP URLs, as accepted by imap_open().
  2107. * @param $service a string giving for CAS retrieve Proxy ticket
  2108. * @param $flags options given to imap_open().
  2109. * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
  2110. * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
  2111. * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
  2112. * @param $err_msg an error message on failure
  2113. * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
  2114. * on success, FALSE on error).
  2115. *
  2116. * @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
  2117. * gives the reason why it failed and $err_msg contains an error message).
  2118. *
  2119. * @public
  2120. */
  2121. function serviceMail($url,$service,$flags,&$err_code,&$err_msg,&$pt)
  2122. {
  2123. phpCAS::traceBegin();
  2124. // at first retrieve a PT
  2125. $pt = $this->retrievePT($service,$err_code,$output);
  2126. $stream = FALSE;
  2127. // test if PT was retrieved correctly
  2128. if ( !$pt ) {
  2129. // note: $err_code and $err_msg are filled by CASClient::retrievePT()
  2130. phpCAS::trace('PT was not retrieved correctly');
  2131. } else {
  2132. phpCAS::trace('opening IMAP URL `'.$url.'\'...');
  2133. $stream = @imap_open($url,$this->getUser(),$pt,$flags);
  2134. if ( !$stream ) {
  2135. phpCAS::trace('could not open URL');
  2136. $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
  2137. // give an error message
  2138. $err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
  2139. $service_url,
  2140. var_export(imap_errors(),TRUE));
  2141. $pt = FALSE;
  2142. $stream = FALSE;
  2143. } else {
  2144. phpCAS::trace('ok');
  2145. }
  2146. }
  2147. phpCAS::traceEnd($stream);
  2148. return $stream;
  2149. }
  2150. /** @} */
  2151. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2152. // XX XX
  2153. // XX PROXIED CLIENT FEATURES (CAS 2.0) XX
  2154. // XX XX
  2155. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2156. // ########################################################################
  2157. // PT
  2158. // ########################################################################
  2159. /**
  2160. * @addtogroup internalProxied
  2161. * @{
  2162. */
  2163. /**
  2164. * the Proxy Ticket provided in the URL of the request if present
  2165. * (empty otherwise). Written by CASClient::CASClient(), read by
  2166. * CASClient::getPT() and CASClient::hasPGT().
  2167. *
  2168. * @hideinitializer
  2169. * @private
  2170. */
  2171. var $_pt = '';
  2172. /**
  2173. * This method returns the Proxy Ticket provided in the URL of the request.
  2174. * @return The proxy ticket.
  2175. * @private
  2176. */
  2177. function getPT()
  2178. {
  2179. // return 'ST'.substr($this->_pt, 2);
  2180. return $this->_pt;
  2181. }
  2182. /**
  2183. * This method stores the Proxy Ticket.
  2184. * @param $pt The Proxy Ticket.
  2185. * @private
  2186. */
  2187. function setPT($pt)
  2188. { $this->_pt = $pt; }
  2189. /**
  2190. * This method tells if a Proxy Ticket was stored.
  2191. * @return TRUE if a Proxy Ticket has been stored.
  2192. * @private
  2193. */
  2194. function hasPT()
  2195. { return !empty($this->_pt); }
  2196. /**
  2197. * This method returns the SAML Ticket provided in the URL of the request.
  2198. * @return The SAML ticket.
  2199. * @private
  2200. */
  2201. function getSA()
  2202. { return 'ST'.substr($this->_sa, 2); }
  2203. /**
  2204. * This method stores the SAML Ticket.
  2205. * @param $sa The SAML Ticket.
  2206. * @private
  2207. */
  2208. function setSA($sa)
  2209. { $this->_sa = $sa; }
  2210. /**
  2211. * This method tells if a SAML Ticket was stored.
  2212. * @return TRUE if a SAML Ticket has been stored.
  2213. * @private
  2214. */
  2215. function hasSA()
  2216. { return !empty($this->_sa); }
  2217. /** @} */
  2218. // ########################################################################
  2219. // PT VALIDATION
  2220. // ########################################################################
  2221. /**
  2222. * @addtogroup internalProxied
  2223. * @{
  2224. */
  2225. /**
  2226. * This method is used to validate a ST or PT; halt on failure
  2227. * Used for all CAS 2.0 validations
  2228. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  2229. *
  2230. * @private
  2231. */
  2232. function validatePT(&$validate_url,&$text_response,&$tree_response)
  2233. {
  2234. phpCAS::traceBegin();
  2235. // build the URL to validate the ticket
  2236. $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
  2237. if ( $this->isProxy() ) {
  2238. // pass the callback url for CAS proxies
  2239. $validate_url .= '&pgtUrl='.$this->getCallbackURL();
  2240. }
  2241. // open and read the URL
  2242. if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
  2243. phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
  2244. $this->authError('PT not validated',
  2245. $validate_url,
  2246. TRUE/*$no_response*/);
  2247. }
  2248. // read the response of the CAS server into a DOM object
  2249. if ( !($dom = domxml_open_mem($text_response))) {
  2250. // read failed
  2251. $this->authError('PT not validated',
  2252. $validate_url,
  2253. FALSE/*$no_response*/,
  2254. TRUE/*$bad_response*/,
  2255. $text_response);
  2256. }
  2257. // read the root node of the XML tree
  2258. if ( !($tree_response = $dom->document_element()) ) {
  2259. // read failed
  2260. $this->authError('PT not validated',
  2261. $validate_url,
  2262. FALSE/*$no_response*/,
  2263. TRUE/*$bad_response*/,
  2264. $text_response);
  2265. }
  2266. // insure that tag name is 'serviceResponse'
  2267. if ( $tree_response->node_name() != 'serviceResponse' ) {
  2268. // bad root node
  2269. $this->authError('PT not validated',
  2270. $validate_url,
  2271. FALSE/*$no_response*/,
  2272. TRUE/*$bad_response*/,
  2273. $text_response);
  2274. }
  2275. if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
  2276. // authentication succeded, extract the user name
  2277. if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) {
  2278. // no user specified => error
  2279. $this->authError('PT not validated',
  2280. $validate_url,
  2281. FALSE/*$no_response*/,
  2282. TRUE/*$bad_response*/,
  2283. $text_response);
  2284. }
  2285. $this->setUser(trim($arr[0]->get_content()));
  2286. } else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
  2287. // authentication succeded, extract the error code and message
  2288. $this->authError('PT not validated',
  2289. $validate_url,
  2290. FALSE/*$no_response*/,
  2291. FALSE/*$bad_response*/,
  2292. $text_response,
  2293. $arr[0]->get_attribute('code')/*$err_code*/,
  2294. trim($arr[0]->get_content())/*$err_msg*/);
  2295. } else {
  2296. $this->authError('PT not validated',
  2297. $validate_url,
  2298. FALSE/*$no_response*/,
  2299. TRUE/*$bad_response*/,
  2300. $text_response);
  2301. }
  2302. // at this step, PT has been validated and $this->_user has been set,
  2303. phpCAS::traceEnd(TRUE);
  2304. return TRUE;
  2305. }
  2306. /** @} */
  2307. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2308. // XX XX
  2309. // XX MISC XX
  2310. // XX XX
  2311. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2312. /**
  2313. * @addtogroup internalMisc
  2314. * @{
  2315. */
  2316. // ########################################################################
  2317. // URL
  2318. // ########################################################################
  2319. /**
  2320. * the URL of the current request (without any ticket CGI parameter). Written
  2321. * and read by CASClient::getURL().
  2322. *
  2323. * @hideinitializer
  2324. * @private
  2325. */
  2326. var $_url = '';
  2327. /**
  2328. * This method returns the URL of the current request (without any ticket
  2329. * CGI parameter).
  2330. *
  2331. * @return The URL
  2332. *
  2333. * @private
  2334. */
  2335. function getURL()
  2336. {
  2337. phpCAS::traceBegin();
  2338. // the URL is built when needed only
  2339. if ( empty($this->_url) ) {
  2340. $final_uri = '';
  2341. // remove the ticket if present in the URL
  2342. $final_uri = ($this->isHttps()) ? 'https' : 'http';
  2343. $final_uri .= '://';
  2344. /* replaced by Julien Marchal - v0.4.6
  2345. * $this->_url .= $_SERVER['SERVER_NAME'];
  2346. */
  2347. if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
  2348. /* replaced by teedog - v0.4.12
  2349. * $this->_url .= $_SERVER['SERVER_NAME'];
  2350. */
  2351. if (empty($_SERVER['SERVER_NAME'])) {
  2352. $server_name = $_SERVER['HTTP_HOST'];
  2353. } else {
  2354. $server_name = $_SERVER['SERVER_NAME'];
  2355. }
  2356. } else {
  2357. $server_name = $_SERVER['HTTP_X_FORWARDED_SERVER'];
  2358. }
  2359. $final_uri .= $server_name;
  2360. if (!strpos($server_name, ':')) {
  2361. if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
  2362. || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
  2363. $final_uri .= ':';
  2364. $final_uri .= $_SERVER['SERVER_PORT'];
  2365. }
  2366. }
  2367. $request_uri = explode('?', $_SERVER['REQUEST_URI'], 2);
  2368. $final_uri .= $request_uri[0];
  2369. if (isset($request_uri[1]) && $request_uri[1])
  2370. {
  2371. $query_string = $this->removeParameterFromQueryString('ticket', $request_uri[1]);
  2372. // If the query string still has anything left, append it to the final URI
  2373. if ($query_string !== '')
  2374. $final_uri .= "?$query_string";
  2375. }
  2376. phpCAS::trace("Final URI: $final_uri");
  2377. $this->setURL($final_uri);
  2378. }
  2379. phpCAS::traceEnd($this->_url);
  2380. return $this->_url;
  2381. }
  2382. /**
  2383. * Removes a parameter from a query string
  2384. *
  2385. * @param string $parameterName
  2386. * @param string $queryString
  2387. * @return string
  2388. *
  2389. * @link http://stackoverflow.com/questions/1842681/regular-expression-to-remove-one-parameter-from-query-string
  2390. */
  2391. function removeParameterFromQueryString($parameterName, $queryString)
  2392. {
  2393. $parameterName = preg_quote($parameterName);
  2394. return preg_replace("/&$parameterName(=[^&]*)?|^$parameterName(=[^&]*)?&?/", '', $queryString);
  2395. }
  2396. /**
  2397. * This method sets the URL of the current request
  2398. *
  2399. * @param $url url to set for service
  2400. *
  2401. * @private
  2402. */
  2403. function setURL($url)
  2404. {
  2405. $this->_url = $url;
  2406. }
  2407. // ########################################################################
  2408. // AUTHENTICATION ERROR HANDLING
  2409. // ########################################################################
  2410. /**
  2411. * This method is used to print the HTML output when the user was not authenticated.
  2412. *
  2413. * @param $failure the failure that occured
  2414. * @param $cas_url the URL the CAS server was asked for
  2415. * @param $no_response the response from the CAS server (other
  2416. * parameters are ignored if TRUE)
  2417. * @param $bad_response bad response from the CAS server ($err_code
  2418. * and $err_msg ignored if TRUE)
  2419. * @param $cas_response the response of the CAS server
  2420. * @param $err_code the error code given by the CAS server
  2421. * @param $err_msg the error message given by the CAS server
  2422. *
  2423. * @private
  2424. */
  2425. function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
  2426. {
  2427. phpCAS::traceBegin();
  2428. $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
  2429. printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),htmlentities($this->getURL()),$_SERVER['SERVER_ADMIN']);
  2430. phpCAS::trace('CAS URL: '.$cas_url);
  2431. phpCAS::trace('Authentication failure: '.$failure);
  2432. if ( $no_response ) {
  2433. phpCAS::trace('Reason: no response from the CAS server');
  2434. } else {
  2435. if ( $bad_response ) {
  2436. phpCAS::trace('Reason: bad response from the CAS server');
  2437. } else {
  2438. switch ($this->getServerVersion()) {
  2439. case CAS_VERSION_1_0:
  2440. phpCAS::trace('Reason: CAS error');
  2441. break;
  2442. case CAS_VERSION_2_0:
  2443. if ( empty($err_code) )
  2444. phpCAS::trace('Reason: no CAS error');
  2445. else
  2446. phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
  2447. break;
  2448. }
  2449. }
  2450. phpCAS::trace('CAS response: '.$cas_response);
  2451. }
  2452. $this->printHTMLFooter();
  2453. phpCAS::traceExit();
  2454. exit();
  2455. }
  2456. /** @} */
  2457. }
  2458. ?>