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. unset($_SESSION['phpCAS']['auth_checked']);
  814. $res = FALSE;
  815. } else {
  816. // $_SESSION['phpCAS']['auth_checked'] = true;
  817. // $this->redirectToCas(TRUE/* gateway */);
  818. // // never reached
  819. // $res = FALSE;
  820. // avoid a check against CAS on every request
  821. if (! isset($_SESSION['phpCAS']['unauth_count']) )
  822. $_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized
  823. if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1)
  824. || ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck))
  825. {
  826. $res = FALSE;
  827. if ($this->_cache_times_for_auth_recheck != -1)
  828. {
  829. $_SESSION['phpCAS']['unauth_count']++;
  830. phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')');
  831. }
  832. else
  833. {
  834. phpCAS::trace('user is not authenticated (cached for until login pressed)');
  835. }
  836. }
  837. else
  838. {
  839. $_SESSION['phpCAS']['unauth_count'] = 0;
  840. $_SESSION['phpCAS']['auth_checked'] = true;
  841. phpCAS::trace('user is not authenticated (cache reset)');
  842. $this->redirectToCas(TRUE/* gateway */);
  843. // never reached
  844. $res = FALSE;
  845. }
  846. }
  847. phpCAS::traceEnd($res);
  848. return $res;
  849. }
  850. /**
  851. * This method is called to check if the user is authenticated (previously or by
  852. * tickets given in the URL).
  853. *
  854. * @return TRUE when the user is authenticated. Also may redirect to the same URL without the ticket.
  855. *
  856. * @public
  857. */
  858. function isAuthenticated()
  859. {
  860. phpCAS::traceBegin();
  861. $res = FALSE;
  862. $validate_url = '';
  863. if ( $this->wasPreviouslyAuthenticated() ) {
  864. // the user has already (previously during the session) been
  865. // authenticated, nothing to be done.
  866. phpCAS::trace('user was already authenticated, no need to look for tickets');
  867. $res = TRUE;
  868. }
  869. else {
  870. if ( $this->hasST() ) {
  871. // if a Service Ticket was given, validate it
  872. phpCAS::trace('ST `'.$this->getST().'\' is present');
  873. $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
  874. phpCAS::trace('ST `'.$this->getST().'\' was validated');
  875. if ( $this->isProxy() ) {
  876. $this->validatePGT($validate_url,$text_response,$tree_response); // idem
  877. phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
  878. $_SESSION['phpCAS']['pgt'] = $this->getPGT();
  879. }
  880. $_SESSION['phpCAS']['user'] = $this->getUser();
  881. $res = TRUE;
  882. }
  883. elseif ( $this->hasPT() ) {
  884. // if a Proxy Ticket was given, validate it
  885. phpCAS::trace('PT `'.$this->getPT().'\' is present');
  886. $this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts
  887. phpCAS::trace('PT `'.$this->getPT().'\' was validated');
  888. if ( $this->isProxy() ) {
  889. $this->validatePGT($validate_url,$text_response,$tree_response); // idem
  890. phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
  891. $_SESSION['phpCAS']['pgt'] = $this->getPGT();
  892. }
  893. $_SESSION['phpCAS']['user'] = $this->getUser();
  894. $res = TRUE;
  895. }
  896. elseif ( $this->hasSA() ) {
  897. // if we have a SAML ticket, validate it.
  898. phpCAS::trace('SA `'.$this->getSA().'\' is present');
  899. $this->validateSA($validate_url,$text_response,$tree_response); // if it fails, it halts
  900. phpCAS::trace('SA `'.$this->getSA().'\' was validated');
  901. $_SESSION['phpCAS']['user'] = $this->getUser();
  902. $_SESSION['phpCAS']['attributes'] = $this->getAttributes();
  903. $res = TRUE;
  904. }
  905. else {
  906. // no ticket given, not authenticated
  907. phpCAS::trace('no ticket found');
  908. }
  909. if ($res) {
  910. // 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)
  911. // most of the checks and errors should have been made now, so we're safe for redirect without masking error messages.
  912. header('Location: '.$this->getURL());
  913. phpCAS::log( "Prepare redirect to : ".$this->getURL() );
  914. }
  915. }
  916. phpCAS::traceEnd($res);
  917. return $res;
  918. }
  919. /**
  920. * This method tells if the current session is authenticated.
  921. * @return true if authenticated based soley on $_SESSION variable
  922. * @since 0.4.22 by Brendan Arnold
  923. */
  924. function isSessionAuthenticated ()
  925. {
  926. return !empty($_SESSION['phpCAS']['user']);
  927. }
  928. /**
  929. * This method tells if the user has already been (previously) authenticated
  930. * by looking into the session variables.
  931. *
  932. * @note This function switches to callback mode when needed.
  933. *
  934. * @return TRUE when the user has already been authenticated; FALSE otherwise.
  935. *
  936. * @private
  937. */
  938. function wasPreviouslyAuthenticated()
  939. {
  940. phpCAS::traceBegin();
  941. if ( $this->isCallbackMode() ) {
  942. $this->callback();
  943. }
  944. $auth = FALSE;
  945. if ( $this->isProxy() ) {
  946. // CAS proxy: username and PGT must be present
  947. if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
  948. // authentication already done
  949. $this->setUser($_SESSION['phpCAS']['user']);
  950. $this->setPGT($_SESSION['phpCAS']['pgt']);
  951. phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\'');
  952. $auth = TRUE;
  953. } elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) {
  954. // these two variables should be empty or not empty at the same time
  955. phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
  956. // unset all tickets to enforce authentication
  957. unset($_SESSION['phpCAS']);
  958. $this->setST('');
  959. $this->setPT('');
  960. } elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
  961. // these two variables should be empty or not empty at the same time
  962. phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty');
  963. // unset all tickets to enforce authentication
  964. unset($_SESSION['phpCAS']);
  965. $this->setST('');
  966. $this->setPT('');
  967. } else {
  968. phpCAS::trace('neither user not PGT found');
  969. }
  970. } else {
  971. // `simple' CAS client (not a proxy): username must be present
  972. if ( $this->isSessionAuthenticated() ) {
  973. // authentication already done
  974. $this->setUser($_SESSION['phpCAS']['user']);
  975. if(isset($_SESSION['phpCAS']['attributes'])){
  976. $this->setAttributes($_SESSION['phpCAS']['attributes']);
  977. }
  978. phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\'');
  979. $auth = TRUE;
  980. } else {
  981. phpCAS::trace('no user found');
  982. }
  983. }
  984. phpCAS::traceEnd($auth);
  985. return $auth;
  986. }
  987. /**
  988. * This method is used to redirect the client to the CAS server.
  989. * It is used by CASClient::forceAuthentication() and CASClient::checkAuthentication().
  990. * @param $gateway true to check authentication, false to force it
  991. * @param $renew true to force the authentication with the CAS server
  992. * @public
  993. */
  994. function redirectToCas($gateway=false,$renew=false){
  995. phpCAS::traceBegin();
  996. $cas_url = $this->getServerLoginURL($gateway,$renew);
  997. header('Location: '.$cas_url);
  998. phpCAS::log( "Redirect to : ".$cas_url );
  999. $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
  1000. printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
  1001. $this->printHTMLFooter();
  1002. phpCAS::traceExit();
  1003. exit();
  1004. }
  1005. /**
  1006. * This method is used to logout from CAS.
  1007. * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server
  1008. * @public
  1009. */
  1010. function logout($params) {
  1011. phpCAS::traceBegin();
  1012. $cas_url = $this->getServerLogoutURL();
  1013. $paramSeparator = '?';
  1014. if (isset($params['url'])) {
  1015. $cas_url = $cas_url . $paramSeparator . "url=" . urlencode($params['url']);
  1016. $paramSeparator = '&';
  1017. }
  1018. if (isset($params['service'])) {
  1019. $cas_url = $cas_url . $paramSeparator . "service=" . urlencode($params['service']);
  1020. }
  1021. header('Location: '.$cas_url);
  1022. phpCAS::log( "Prepare redirect to : ".$cas_url );
  1023. session_unset();
  1024. session_destroy();
  1025. $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
  1026. printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
  1027. $this->printHTMLFooter();
  1028. phpCAS::traceExit();
  1029. exit();
  1030. }
  1031. /**
  1032. * @return true if the current request is a logout request.
  1033. * @private
  1034. */
  1035. function isLogoutRequest() {
  1036. return !empty($_POST['logoutRequest']);
  1037. }
  1038. /**
  1039. * @return true if a logout request is allowed.
  1040. * @private
  1041. */
  1042. function isLogoutRequestAllowed() {
  1043. }
  1044. /**
  1045. * This method handles logout requests.
  1046. * @param $check_client true to check the client bofore handling the request,
  1047. * false not to perform any access control. True by default.
  1048. * @param $allowed_clients an array of host names allowed to send logout requests.
  1049. * By default, only the CAs server (declared in the constructor) will be allowed.
  1050. * @public
  1051. */
  1052. function handleLogoutRequests($check_client=true, $allowed_clients=false) {
  1053. phpCAS::traceBegin();
  1054. if (!$this->isLogoutRequest()) {
  1055. phpCAS::log("Not a logout request");
  1056. phpCAS::traceEnd();
  1057. return;
  1058. }
  1059. phpCAS::log("Logout requested");
  1060. phpCAS::log("SAML REQUEST: ".$_POST['logoutRequest']);
  1061. if ($check_client) {
  1062. if (!$allowed_clients) {
  1063. $allowed_clients = array( $this->getServerHostname() );
  1064. }
  1065. $client_ip = $_SERVER['REMOTE_ADDR'];
  1066. $client = gethostbyaddr($client_ip);
  1067. phpCAS::log("Client: ".$client."/".$client_ip);
  1068. $allowed = false;
  1069. foreach ($allowed_clients as $allowed_client) {
  1070. if (($client == $allowed_client) or ($client_ip == $allowed_client)) {
  1071. phpCAS::log("Allowed client '".$allowed_client."' matches, logout request is allowed");
  1072. $allowed = true;
  1073. break;
  1074. } else {
  1075. phpCAS::log("Allowed client '".$allowed_client."' does not match");
  1076. }
  1077. }
  1078. if (!$allowed) {
  1079. phpCAS::error("Unauthorized logout request from client '".$client."'");
  1080. printf("Unauthorized!");
  1081. phpCAS::traceExit();
  1082. exit();
  1083. }
  1084. } else {
  1085. phpCAS::log("No access control set");
  1086. }
  1087. // Extract the ticket from the SAML Request
  1088. preg_match("|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|", $_POST['logoutRequest'], $tick, PREG_OFFSET_CAPTURE, 3);
  1089. $wrappedSamlSessionIndex = preg_replace('|<samlp:SessionIndex>|','',$tick[0][0]);
  1090. $ticket2logout = preg_replace('|</samlp:SessionIndex>|','',$wrappedSamlSessionIndex);
  1091. phpCAS::log("Ticket to logout: ".$ticket2logout);
  1092. $session_id = preg_replace('/[^\w]/','',$ticket2logout);
  1093. phpCAS::log("Session id: ".$session_id);
  1094. // destroy a possible application session created before phpcas
  1095. if(session_id()){
  1096. session_unset();
  1097. session_destroy();
  1098. }
  1099. // fix session ID
  1100. session_id($session_id);
  1101. $_COOKIE[session_name()]=$session_id;
  1102. $_GET[session_name()]=$session_id;
  1103. // Overwrite session
  1104. session_start();
  1105. session_unset();
  1106. session_destroy();
  1107. printf("Disconnected!");
  1108. phpCAS::traceExit();
  1109. exit();
  1110. }
  1111. /** @} */
  1112. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1113. // XX XX
  1114. // XX BASIC CLIENT FEATURES (CAS 1.0) XX
  1115. // XX XX
  1116. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1117. // ########################################################################
  1118. // ST
  1119. // ########################################################################
  1120. /**
  1121. * @addtogroup internalBasic
  1122. * @{
  1123. */
  1124. /**
  1125. * the Service Ticket provided in the URL of the request if present
  1126. * (empty otherwise). Written by CASClient::CASClient(), read by
  1127. * CASClient::getST() and CASClient::hasPGT().
  1128. *
  1129. * @hideinitializer
  1130. * @private
  1131. */
  1132. var $_st = '';
  1133. /**
  1134. * This method returns the Service Ticket provided in the URL of the request.
  1135. * @return The service ticket.
  1136. * @private
  1137. */
  1138. function getST()
  1139. { return $this->_st; }
  1140. /**
  1141. * This method stores the Service Ticket.
  1142. * @param $st The Service Ticket.
  1143. * @private
  1144. */
  1145. function setST($st)
  1146. { $this->_st = $st; }
  1147. /**
  1148. * This method tells if a Service Ticket was stored.
  1149. * @return TRUE if a Service Ticket has been stored.
  1150. * @private
  1151. */
  1152. function hasST()
  1153. { return !empty($this->_st); }
  1154. /** @} */
  1155. // ########################################################################
  1156. // ST VALIDATION
  1157. // ########################################################################
  1158. /**
  1159. * @addtogroup internalBasic
  1160. * @{
  1161. */
  1162. /**
  1163. * the certificate of the CAS server.
  1164. *
  1165. * @hideinitializer
  1166. * @private
  1167. */
  1168. var $_cas_server_cert = '';
  1169. /**
  1170. * the certificate of the CAS server CA.
  1171. *
  1172. * @hideinitializer
  1173. * @private
  1174. */
  1175. var $_cas_server_ca_cert = '';
  1176. /**
  1177. * Set to true not to validate the CAS server.
  1178. *
  1179. * @hideinitializer
  1180. * @private
  1181. */
  1182. var $_no_cas_server_validation = false;
  1183. /**
  1184. * Set the certificate of the CAS server.
  1185. *
  1186. * @param $cert the PEM certificate
  1187. */
  1188. function setCasServerCert($cert)
  1189. {
  1190. $this->_cas_server_cert = $cert;
  1191. }
  1192. /**
  1193. * Set the CA certificate of the CAS server.
  1194. *
  1195. * @param $cert the PEM certificate of the CA that emited the cert of the server
  1196. */
  1197. function setCasServerCACert($cert)
  1198. {
  1199. $this->_cas_server_ca_cert = $cert;
  1200. }
  1201. /**
  1202. * Set no SSL validation for the CAS server.
  1203. */
  1204. function setNoCasServerValidation()
  1205. {
  1206. $this->_no_cas_server_validation = true;
  1207. }
  1208. /**
  1209. * This method is used to validate a ST; halt on failure, and sets $validate_url,
  1210. * $text_reponse and $tree_response on success. These parameters are used later
  1211. * by CASClient::validatePGT() for CAS proxies.
  1212. * Used for all CAS 1.0 validations
  1213. * @param $validate_url the URL of the request to the CAS server.
  1214. * @param $text_response the response of the CAS server, as is (XML text).
  1215. * @param $tree_response the response of the CAS server, as a DOM XML tree.
  1216. *
  1217. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1218. *
  1219. * @private
  1220. */
  1221. function validateST($validate_url,&$text_response,&$tree_response)
  1222. {
  1223. phpCAS::traceBegin();
  1224. // build the URL to validate the ticket
  1225. $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
  1226. if ( $this->isProxy() ) {
  1227. // pass the callback url for CAS proxies
  1228. $validate_url .= '&pgtUrl='.$this->getCallbackURL();
  1229. }
  1230. // open and read the URL
  1231. if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
  1232. phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
  1233. $this->authError('ST not validated',
  1234. $validate_url,
  1235. TRUE/*$no_response*/);
  1236. }
  1237. // analyze the result depending on the version
  1238. switch ($this->getServerVersion()) {
  1239. case CAS_VERSION_1_0:
  1240. if (preg_match('/^no\n/',$text_response)) {
  1241. phpCAS::trace('ST has not been validated');
  1242. $this->authError('ST not validated',
  1243. $validate_url,
  1244. FALSE/*$no_response*/,
  1245. FALSE/*$bad_response*/,
  1246. $text_response);
  1247. }
  1248. if (!preg_match('/^yes\n/',$text_response)) {
  1249. phpCAS::trace('ill-formed response');
  1250. $this->authError('ST not validated',
  1251. $validate_url,
  1252. FALSE/*$no_response*/,
  1253. TRUE/*$bad_response*/,
  1254. $text_response);
  1255. }
  1256. // ST has been validated, extract the user name
  1257. $arr = preg_split('/\n/',$text_response);
  1258. $this->setUser(trim($arr[1]));
  1259. break;
  1260. case CAS_VERSION_2_0:
  1261. // read the response of the CAS server into a DOM object
  1262. if ( !($dom = domxml_open_mem($text_response))) {
  1263. phpCAS::trace('domxml_open_mem() failed');
  1264. $this->authError('ST not validated',
  1265. $validate_url,
  1266. FALSE/*$no_response*/,
  1267. TRUE/*$bad_response*/,
  1268. $text_response);
  1269. }
  1270. // read the root node of the XML tree
  1271. if ( !($tree_response = $dom->document_element()) ) {
  1272. phpCAS::trace('document_element() failed');
  1273. $this->authError('ST not validated',
  1274. $validate_url,
  1275. FALSE/*$no_response*/,
  1276. TRUE/*$bad_response*/,
  1277. $text_response);
  1278. }
  1279. // insure that tag name is 'serviceResponse'
  1280. if ( $tree_response->node_name() != 'serviceResponse' ) {
  1281. phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\'');
  1282. $this->authError('ST not validated',
  1283. $validate_url,
  1284. FALSE/*$no_response*/,
  1285. TRUE/*$bad_response*/,
  1286. $text_response);
  1287. }
  1288. if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
  1289. // authentication succeded, extract the user name
  1290. if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
  1291. phpCAS::trace('<authenticationSuccess> found, but no <user>');
  1292. $this->authError('ST not validated',
  1293. $validate_url,
  1294. FALSE/*$no_response*/,
  1295. TRUE/*$bad_response*/,
  1296. $text_response);
  1297. }
  1298. $user = trim($user_elements[0]->get_content());
  1299. phpCAS::trace('user = `'.$user);
  1300. $this->setUser($user);
  1301. } else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
  1302. phpCAS::trace('<authenticationFailure> found');
  1303. // authentication failed, extract the error code and message
  1304. $this->authError('ST not validated',
  1305. $validate_url,
  1306. FALSE/*$no_response*/,
  1307. FALSE/*$bad_response*/,
  1308. $text_response,
  1309. $failure_elements[0]->get_attribute('code')/*$err_code*/,
  1310. trim($failure_elements[0]->get_content())/*$err_msg*/);
  1311. } else {
  1312. phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
  1313. $this->authError('ST not validated',
  1314. $validate_url,
  1315. FALSE/*$no_response*/,
  1316. TRUE/*$bad_response*/,
  1317. $text_response);
  1318. }
  1319. break;
  1320. }
  1321. // at this step, ST has been validated and $this->_user has been set,
  1322. phpCAS::traceEnd(TRUE);
  1323. return TRUE;
  1324. }
  1325. // ########################################################################
  1326. // SAML VALIDATION
  1327. // ########################################################################
  1328. /**
  1329. * @addtogroup internalBasic
  1330. * @{
  1331. */
  1332. /**
  1333. * This method is used to validate a SAML TICKET; halt on failure, and sets $validate_url,
  1334. * $text_reponse and $tree_response on success. These parameters are used later
  1335. * by CASClient::validatePGT() for CAS proxies.
  1336. *
  1337. * @param $validate_url the URL of the request to the CAS server.
  1338. * @param $text_response the response of the CAS server, as is (XML text).
  1339. * @param $tree_response the response of the CAS server, as a DOM XML tree.
  1340. *
  1341. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1342. *
  1343. * @private
  1344. */
  1345. function validateSA($validate_url,&$text_response,&$tree_response)
  1346. {
  1347. phpCAS::traceBegin();
  1348. // build the URL to validate the ticket
  1349. $validate_url = $this->getServerSamlValidateURL();
  1350. // open and read the URL
  1351. if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
  1352. phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
  1353. $this->authError('SA not validated', $validate_url, TRUE/*$no_response*/);
  1354. }
  1355. phpCAS::trace('server version: '.$this->getServerVersion());
  1356. // analyze the result depending on the version
  1357. switch ($this->getServerVersion()) {
  1358. case SAML_VERSION_1_1:
  1359. // read the response of the CAS server into a DOM object
  1360. if ( !($dom = domxml_open_mem($text_response))) {
  1361. phpCAS::trace('domxml_open_mem() failed');
  1362. $this->authError('SA not validated',
  1363. $validate_url,
  1364. FALSE/*$no_response*/,
  1365. TRUE/*$bad_response*/,
  1366. $text_response);
  1367. }
  1368. // read the root node of the XML tree
  1369. if ( !($tree_response = $dom->document_element()) ) {
  1370. phpCAS::trace('document_element() failed');
  1371. $this->authError('SA not validated',
  1372. $validate_url,
  1373. FALSE/*$no_response*/,
  1374. TRUE/*$bad_response*/,
  1375. $text_response);
  1376. }
  1377. // insure that tag name is 'Envelope'
  1378. if ( $tree_response->node_name() != 'Envelope' ) {
  1379. phpCAS::trace('bad XML root node (should be `Envelope\' instead of `'.$tree_response->node_name().'\'');
  1380. $this->authError('SA not validated',
  1381. $validate_url,
  1382. FALSE/*$no_response*/,
  1383. TRUE/*$bad_response*/,
  1384. $text_response);
  1385. }
  1386. // check for the NameIdentifier tag in the SAML response
  1387. if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("NameIdentifier")) != 0) {
  1388. phpCAS::trace('NameIdentifier found');
  1389. $user = trim($success_elements[0]->get_content());
  1390. phpCAS::trace('user = `'.$user.'`');
  1391. $this->setUser($user);
  1392. $this->setSessionAttributes($text_response);
  1393. } else {
  1394. phpCAS::trace('no <NameIdentifier> tag found in SAML payload');
  1395. $this->authError('SA not validated',
  1396. $validate_url,
  1397. FALSE/*$no_response*/,
  1398. TRUE/*$bad_response*/,
  1399. $text_response);
  1400. }
  1401. break;
  1402. }
  1403. // at this step, ST has been validated and $this->_user has been set,
  1404. phpCAS::traceEnd(TRUE);
  1405. return TRUE;
  1406. }
  1407. /**
  1408. * This method will parse the DOM and pull out the attributes from the SAML
  1409. * payload and put them into an array, then put the array into the session.
  1410. *
  1411. * @param $text_response the SAML payload.
  1412. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1413. *
  1414. * @private
  1415. */
  1416. function setSessionAttributes($text_response)
  1417. {
  1418. phpCAS::traceBegin();
  1419. $result = FALSE;
  1420. if (isset($_SESSION[SAML_ATTRIBUTES])) {
  1421. phpCAS::trace("session attrs already set."); //testbml - do we care?
  1422. }
  1423. $attr_array = array();
  1424. if (($dom = domxml_open_mem($text_response))) {
  1425. $xPath = $dom->xpath_new_context();
  1426. $xPath->xpath_register_ns('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
  1427. $xPath->xpath_register_ns('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
  1428. $nodelist = $xPath->xpath_eval("//saml:Attribute");
  1429. $attrs = $nodelist->nodeset;
  1430. phpCAS::trace($text_response);
  1431. foreach($attrs as $attr){
  1432. $xres = $xPath->xpath_eval("saml:AttributeValue", $attr);
  1433. $name = $attr->get_attribute("AttributeName");
  1434. $value_array = array();
  1435. foreach($xres->nodeset as $node){
  1436. $value_array[] = $node->get_content();
  1437. }
  1438. phpCAS::trace("* " . $name . "=" . $value_array);
  1439. $attr_array[$name] = $value_array;
  1440. }
  1441. $_SESSION[SAML_ATTRIBUTES] = $attr_array;
  1442. // UGent addition...
  1443. foreach($attr_array as $attr_key => $attr_value) {
  1444. if(count($attr_value) > 1) {
  1445. $this->_attributes[$attr_key] = $attr_value;
  1446. }
  1447. else {
  1448. $this->_attributes[$attr_key] = $attr_value[0];
  1449. }
  1450. }
  1451. $result = TRUE;
  1452. }
  1453. phpCAS::traceEnd($result);
  1454. return $result;
  1455. }
  1456. /** @} */
  1457. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1458. // XX XX
  1459. // XX PROXY FEATURES (CAS 2.0) XX
  1460. // XX XX
  1461. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  1462. // ########################################################################
  1463. // PROXYING
  1464. // ########################################################################
  1465. /**
  1466. * @addtogroup internalProxy
  1467. * @{
  1468. */
  1469. /**
  1470. * A boolean telling if the client is a CAS proxy or not. Written by CASClient::CASClient(),
  1471. * read by CASClient::isProxy().
  1472. *
  1473. * @private
  1474. */
  1475. var $_proxy;
  1476. /**
  1477. * Tells if a CAS client is a CAS proxy or not
  1478. *
  1479. * @return TRUE when the CAS client is a CAs proxy, FALSE otherwise
  1480. *
  1481. * @private
  1482. */
  1483. function isProxy()
  1484. {
  1485. return $this->_proxy;
  1486. }
  1487. /** @} */
  1488. // ########################################################################
  1489. // PGT
  1490. // ########################################################################
  1491. /**
  1492. * @addtogroup internalProxy
  1493. * @{
  1494. */
  1495. /**
  1496. * the Proxy Grnting Ticket given by the CAS server (empty otherwise).
  1497. * Written by CASClient::setPGT(), read by CASClient::getPGT() and CASClient::hasPGT().
  1498. *
  1499. * @hideinitializer
  1500. * @private
  1501. */
  1502. var $_pgt = '';
  1503. /**
  1504. * This method returns the Proxy Granting Ticket given by the CAS server.
  1505. * @return The Proxy Granting Ticket.
  1506. * @private
  1507. */
  1508. function getPGT()
  1509. { return $this->_pgt; }
  1510. /**
  1511. * This method stores the Proxy Granting Ticket.
  1512. * @param $pgt The Proxy Granting Ticket.
  1513. * @private
  1514. */
  1515. function setPGT($pgt)
  1516. { $this->_pgt = $pgt; }
  1517. /**
  1518. * This method tells if a Proxy Granting Ticket was stored.
  1519. * @return TRUE if a Proxy Granting Ticket has been stored.
  1520. * @private
  1521. */
  1522. function hasPGT()
  1523. { return !empty($this->_pgt); }
  1524. /** @} */
  1525. // ########################################################################
  1526. // CALLBACK MODE
  1527. // ########################################################################
  1528. /**
  1529. * @addtogroup internalCallback
  1530. * @{
  1531. */
  1532. /**
  1533. * each PHP script using phpCAS in proxy mode is its own callback to get the
  1534. * PGT back from the CAS server. callback_mode is detected by the constructor
  1535. * thanks to the GET parameters.
  1536. */
  1537. /**
  1538. * a boolean to know if the CAS client is running in callback mode. Written by
  1539. * CASClient::setCallBackMode(), read by CASClient::isCallbackMode().
  1540. *
  1541. * @hideinitializer
  1542. * @private
  1543. */
  1544. var $_callback_mode = FALSE;
  1545. /**
  1546. * This method sets/unsets callback mode.
  1547. *
  1548. * @param $callback_mode TRUE to set callback mode, FALSE otherwise.
  1549. *
  1550. * @private
  1551. */
  1552. function setCallbackMode($callback_mode)
  1553. {
  1554. $this->_callback_mode = $callback_mode;
  1555. }
  1556. /**
  1557. * This method returns TRUE when the CAs client is running i callback mode,
  1558. * FALSE otherwise.
  1559. *
  1560. * @return A boolean.
  1561. *
  1562. * @private
  1563. */
  1564. function isCallbackMode()
  1565. {
  1566. return $this->_callback_mode;
  1567. }
  1568. /**
  1569. * the URL that should be used for the PGT callback (in fact the URL of the
  1570. * current request without any CGI parameter). Written and read by
  1571. * CASClient::getCallbackURL().
  1572. *
  1573. * @hideinitializer
  1574. * @private
  1575. */
  1576. var $_callback_url = '';
  1577. /**
  1578. * This method returns the URL that should be used for the PGT callback (in
  1579. * fact the URL of the current request without any CGI parameter, except if
  1580. * phpCAS::setFixedCallbackURL() was used).
  1581. *
  1582. * @return The callback URL
  1583. *
  1584. * @private
  1585. */
  1586. function getCallbackURL()
  1587. {
  1588. // the URL is built when needed only
  1589. if ( empty($this->_callback_url) ) {
  1590. $final_uri = '';
  1591. // remove the ticket if present in the URL
  1592. $final_uri = 'https://';
  1593. /* replaced by Julien Marchal - v0.4.6
  1594. * $this->uri .= $_SERVER['SERVER_NAME'];
  1595. */
  1596. if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
  1597. /* replaced by teedog - v0.4.12
  1598. * $final_uri .= $_SERVER['SERVER_NAME'];
  1599. */
  1600. if (empty($_SERVER['SERVER_NAME'])) {
  1601. $final_uri .= $_SERVER['HTTP_HOST'];
  1602. } else {
  1603. $final_uri .= $_SERVER['SERVER_NAME'];
  1604. }
  1605. } else {
  1606. $final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
  1607. }
  1608. if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
  1609. || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
  1610. $final_uri .= ':';
  1611. $final_uri .= $_SERVER['SERVER_PORT'];
  1612. }
  1613. $request_uri = $_SERVER['REQUEST_URI'];
  1614. $request_uri = preg_replace('/\?.*$/','',$request_uri);
  1615. $final_uri .= $request_uri;
  1616. $this->setCallbackURL($final_uri);
  1617. }
  1618. return $this->_callback_url;
  1619. }
  1620. /**
  1621. * This method sets the callback url.
  1622. *
  1623. * @param $callback_url url to set callback
  1624. *
  1625. * @private
  1626. */
  1627. function setCallbackURL($url)
  1628. {
  1629. return $this->_callback_url = $url;
  1630. }
  1631. /**
  1632. * This method is called by CASClient::CASClient() when running in callback
  1633. * mode. It stores the PGT and its PGT Iou, prints its output and halts.
  1634. *
  1635. * @private
  1636. */
  1637. function callback()
  1638. {
  1639. phpCAS::traceBegin();
  1640. $this->printHTMLHeader('phpCAS callback');
  1641. $pgt_iou = $_GET['pgtIou'];
  1642. $pgt = $_GET['pgtId'];
  1643. phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
  1644. echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
  1645. $this->storePGT($pgt,$pgt_iou);
  1646. $this->printHTMLFooter();
  1647. phpCAS::traceExit();
  1648. exit();
  1649. }
  1650. /** @} */
  1651. // ########################################################################
  1652. // PGT STORAGE
  1653. // ########################################################################
  1654. /**
  1655. * @addtogroup internalPGTStorage
  1656. * @{
  1657. */
  1658. /**
  1659. * an instance of a class inheriting of PGTStorage, used to deal with PGT
  1660. * storage. Created by CASClient::setPGTStorageFile() or CASClient::setPGTStorageDB(), used
  1661. * by CASClient::setPGTStorageFile(), CASClient::setPGTStorageDB() and CASClient::initPGTStorage().
  1662. *
  1663. * @hideinitializer
  1664. * @private
  1665. */
  1666. var $_pgt_storage = null;
  1667. /**
  1668. * This method is used to initialize the storage of PGT's.
  1669. * Halts on error.
  1670. *
  1671. * @private
  1672. */
  1673. function initPGTStorage()
  1674. {
  1675. // if no SetPGTStorageXxx() has been used, default to file
  1676. if ( !is_object($this->_pgt_storage) ) {
  1677. $this->setPGTStorageFile();
  1678. }
  1679. // initializes the storage
  1680. $this->_pgt_storage->init();
  1681. }
  1682. /**
  1683. * This method stores a PGT. Halts on error.
  1684. *
  1685. * @param $pgt the PGT to store
  1686. * @param $pgt_iou its corresponding Iou
  1687. *
  1688. * @private
  1689. */
  1690. function storePGT($pgt,$pgt_iou)
  1691. {
  1692. // ensure that storage is initialized
  1693. $this->initPGTStorage();
  1694. // writes the PGT
  1695. $this->_pgt_storage->write($pgt,$pgt_iou);
  1696. }
  1697. /**
  1698. * This method reads a PGT from its Iou and deletes the corresponding storage entry.
  1699. *
  1700. * @param $pgt_iou the PGT Iou
  1701. *
  1702. * @return The PGT corresponding to the Iou, FALSE when not found.
  1703. *
  1704. * @private
  1705. */
  1706. function loadPGT($pgt_iou)
  1707. {
  1708. // ensure that storage is initialized
  1709. $this->initPGTStorage();
  1710. // read the PGT
  1711. return $this->_pgt_storage->read($pgt_iou);
  1712. }
  1713. /**
  1714. * This method is used to tell phpCAS to store the response of the
  1715. * CAS server to PGT requests onto the filesystem.
  1716. *
  1717. * @param $format the format used to store the PGT's (`plain' and `xml' allowed)
  1718. * @param $path the path where the PGT's should be stored
  1719. *
  1720. * @public
  1721. */
  1722. function setPGTStorageFile($format='',
  1723. $path='')
  1724. {
  1725. // check that the storage has not already been set
  1726. if ( is_object($this->_pgt_storage) ) {
  1727. phpCAS::error('PGT storage already defined');
  1728. }
  1729. // create the storage object
  1730. $this->_pgt_storage = new PGTStorageFile($this,$format,$path);
  1731. }
  1732. /**
  1733. * This method is used to tell phpCAS to store the response of the
  1734. * CAS server to PGT requests into a database.
  1735. * @note The connection to the database is done only when needed.
  1736. * As a consequence, bad parameters are detected only when
  1737. * initializing PGT storage.
  1738. *
  1739. * @param $user the user to access the data with
  1740. * @param $password the user's password
  1741. * @param $database_type the type of the database hosting the data
  1742. * @param $hostname the server hosting the database
  1743. * @param $port the port the server is listening on
  1744. * @param $database the name of the database
  1745. * @param $table the name of the table storing the data
  1746. *
  1747. * @public
  1748. */
  1749. function setPGTStorageDB($user,
  1750. $password,
  1751. $database_type,
  1752. $hostname,
  1753. $port,
  1754. $database,
  1755. $table)
  1756. {
  1757. // check that the storage has not already been set
  1758. if ( is_object($this->_pgt_storage) ) {
  1759. phpCAS::error('PGT storage already defined');
  1760. }
  1761. // warn the user that he should use file storage...
  1762. trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING);
  1763. // create the storage object
  1764. $this->_pgt_storage = new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table);
  1765. }
  1766. // ########################################################################
  1767. // PGT VALIDATION
  1768. // ########################################################################
  1769. /**
  1770. * This method is used to validate a PGT; halt on failure.
  1771. *
  1772. * @param $validate_url the URL of the request to the CAS server.
  1773. * @param $text_response the response of the CAS server, as is (XML text); result
  1774. * of CASClient::validateST() or CASClient::validatePT().
  1775. * @param $tree_response the response of the CAS server, as a DOM XML tree; result
  1776. * of CASClient::validateST() or CASClient::validatePT().
  1777. *
  1778. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  1779. *
  1780. * @private
  1781. */
  1782. function validatePGT(&$validate_url,$text_response,$tree_response)
  1783. {
  1784. // here cannot use phpCAS::traceBegin(); alongside domxml-php4-to-php5.php
  1785. phpCAS::log('start validatePGT()');
  1786. if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) {
  1787. phpCAS::trace('<proxyGrantingTicket> not found');
  1788. // authentication succeded, but no PGT Iou was transmitted
  1789. $this->authError('Ticket validated but no PGT Iou transmitted',
  1790. $validate_url,
  1791. FALSE/*$no_response*/,
  1792. FALSE/*$bad_response*/,
  1793. $text_response);
  1794. } else {
  1795. // PGT Iou transmitted, extract it
  1796. $pgt_iou = trim($arr[0]->get_content());
  1797. $pgt = $this->loadPGT($pgt_iou);
  1798. if ( $pgt == FALSE ) {
  1799. phpCAS::trace('could not load PGT');
  1800. $this->authError('PGT Iou was transmitted but PGT could not be retrieved',
  1801. $validate_url,
  1802. FALSE/*$no_response*/,
  1803. FALSE/*$bad_response*/,
  1804. $text_response);
  1805. }
  1806. $this->setPGT($pgt);
  1807. }
  1808. // here, cannot use phpCAS::traceEnd(TRUE); alongside domxml-php4-to-php5.php
  1809. phpCAS::log('end validatePGT()');
  1810. return TRUE;
  1811. }
  1812. // ########################################################################
  1813. // PGT VALIDATION
  1814. // ########################################################################
  1815. /**
  1816. * This method is used to retrieve PT's from the CAS server thanks to a PGT.
  1817. *
  1818. * @param $target_service the service to ask for with the PT.
  1819. * @param $err_code an error code (PHPCAS_SERVICE_OK on success).
  1820. * @param $err_msg an error message (empty on success).
  1821. *
  1822. * @return a Proxy Ticket, or FALSE on error.
  1823. *
  1824. * @private
  1825. */
  1826. function retrievePT($target_service,&$err_code,&$err_msg)
  1827. {
  1828. phpCAS::traceBegin();
  1829. // by default, $err_msg is set empty and $pt to TRUE. On error, $pt is
  1830. // set to false and $err_msg to an error message. At the end, if $pt is FALSE
  1831. // and $error_msg is still empty, it is set to 'invalid response' (the most
  1832. // commonly encountered error).
  1833. $err_msg = '';
  1834. // build the URL to retrieve the PT
  1835. // $cas_url = $this->getServerProxyURL().'?targetService='.preg_replace('/&/','%26',$target_service).'&pgt='.$this->getPGT();
  1836. $cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT();
  1837. // open and read the URL
  1838. if ( !$this->readURL($cas_url,''/*cookies*/,$headers,$cas_response,$err_msg) ) {
  1839. phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
  1840. $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
  1841. $err_msg = 'could not retrieve PT (no response from the CAS server)';
  1842. phpCAS::traceEnd(FALSE);
  1843. return FALSE;
  1844. }
  1845. $bad_response = FALSE;
  1846. if ( !$bad_response ) {
  1847. // read the response of the CAS server into a DOM object
  1848. if ( !($dom = @domxml_open_mem($cas_response))) {
  1849. phpCAS::trace('domxml_open_mem() failed');
  1850. // read failed
  1851. $bad_response = TRUE;
  1852. }
  1853. }
  1854. if ( !$bad_response ) {
  1855. // read the root node of the XML tree
  1856. if ( !($root = $dom->document_element()) ) {
  1857. phpCAS::trace('document_element() failed');
  1858. // read failed
  1859. $bad_response = TRUE;
  1860. }
  1861. }
  1862. if ( !$bad_response ) {
  1863. // insure that tag name is 'serviceResponse'
  1864. if ( $root->node_name() != 'serviceResponse' ) {
  1865. phpCAS::trace('node_name() failed');
  1866. // bad root node
  1867. $bad_response = TRUE;
  1868. }
  1869. }
  1870. if ( !$bad_response ) {
  1871. // look for a proxySuccess tag
  1872. if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) {
  1873. // authentication succeded, look for a proxyTicket tag
  1874. if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) {
  1875. $err_code = PHPCAS_SERVICE_OK;
  1876. $err_msg = '';
  1877. phpCAS::trace('original PT: '.trim($arr[0]->get_content()));
  1878. $pt = trim($arr[0]->get_content());
  1879. phpCAS::traceEnd($pt);
  1880. return $pt;
  1881. } else {
  1882. phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
  1883. }
  1884. }
  1885. // look for a proxyFailure tag
  1886. else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) {
  1887. // authentication failed, extract the error
  1888. $err_code = PHPCAS_SERVICE_PT_FAILURE;
  1889. $err_msg = 'PT retrieving failed (code=`'
  1890. .$arr[0]->get_attribute('code')
  1891. .'\', message=`'
  1892. .trim($arr[0]->get_content())
  1893. .'\')';
  1894. phpCAS::traceEnd(FALSE);
  1895. return FALSE;
  1896. } else {
  1897. phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
  1898. }
  1899. }
  1900. // at this step, we are sure that the response of the CAS server was ill-formed
  1901. $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
  1902. $err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
  1903. phpCAS::traceEnd(FALSE);
  1904. return FALSE;
  1905. }
  1906. // ########################################################################
  1907. // ACCESS TO EXTERNAL SERVICES
  1908. // ########################################################################
  1909. /**
  1910. * This method is used to acces a remote URL.
  1911. *
  1912. * @param $url the URL to access.
  1913. * @param $cookies an array containing cookies strings such as 'name=val'
  1914. * @param $headers an array containing the HTTP header lines of the response
  1915. * (an empty array on failure).
  1916. * @param $body the body of the response, as a string (empty on failure).
  1917. * @param $err_msg an error message, filled on failure.
  1918. *
  1919. * @return TRUE on success, FALSE otherwise (in this later case, $err_msg
  1920. * contains an error message).
  1921. *
  1922. * @private
  1923. */
  1924. function readURL($url,$cookies,&$headers,&$body,&$err_msg)
  1925. {
  1926. phpCAS::traceBegin();
  1927. $headers = '';
  1928. $body = '';
  1929. $err_msg = '';
  1930. $res = TRUE;
  1931. // initialize the CURL session
  1932. $ch = curl_init($url);
  1933. if (version_compare(PHP_VERSION,'5.1.3','>=')) {
  1934. //only avaible in php5
  1935. curl_setopt_array($ch, $this->_curl_options);
  1936. } else {
  1937. foreach ($this->_curl_options as $key => $value) {
  1938. curl_setopt($ch, $key, $value);
  1939. }
  1940. }
  1941. if ($this->_cas_server_cert == '' && $this->_cas_server_ca_cert == '' && !$this->_no_cas_server_validation) {
  1942. phpCAS::error('one of the methods phpCAS::setCasServerCert(), phpCAS::setCasServerCACert() or phpCAS::setNoCasServerValidation() must be called.');
  1943. }
  1944. if ($this->_cas_server_cert != '' && $this->_cas_server_ca_cert != '') {
  1945. // This branch added by IDMS. Seems phpCAS implementor got a bit confused about the curl options CURLOPT_SSLCERT and CURLOPT_CAINFO
  1946. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
  1947. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
  1948. curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
  1949. curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
  1950. curl_setopt($ch, CURLOPT_VERBOSE, '1');
  1951. phpCAS::trace('CURL: Set all required opts for mutual authentication ------');
  1952. } else if ($this->_cas_server_cert != '' ) {
  1953. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
  1954. curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
  1955. } else if ($this->_cas_server_ca_cert != '') {
  1956. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
  1957. curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
  1958. } else {
  1959. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
  1960. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  1961. }
  1962. // return the CURL output into a variable
  1963. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  1964. // get the HTTP header with a callback
  1965. $this->_curl_headers = array(); // empty the headers array
  1966. curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, '_curl_read_headers'));
  1967. // add cookies headers
  1968. if ( is_array($cookies) ) {
  1969. curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies));
  1970. }
  1971. // add extra stuff if SAML
  1972. if ($this->hasSA()) {
  1973. $more_headers = array ("soapaction: http://www.oasis-open.org/committees/security",
  1974. "cache-control: no-cache",
  1975. "pragma: no-cache",
  1976. "accept: text/xml",
  1977. "connection: keep-alive",
  1978. "content-type: text/xml");
  1979. curl_setopt($ch, CURLOPT_HTTPHEADER, $more_headers);
  1980. curl_setopt($ch, CURLOPT_POST, 1);
  1981. $data = $this->buildSAMLPayload();
  1982. //phpCAS::trace('SAML Payload: '.print_r($data, TRUE));
  1983. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  1984. }
  1985. // perform the query
  1986. $buf = curl_exec ($ch);
  1987. //phpCAS::trace('CURL: Call completed. Response body is: \''.$buf.'\'');
  1988. if ( $buf === FALSE ) {
  1989. phpCAS::trace('curl_exec() failed');
  1990. $err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch);
  1991. //phpCAS::trace('curl error: '.$err_msg);
  1992. // close the CURL session
  1993. curl_close ($ch);
  1994. $res = FALSE;
  1995. } else {
  1996. // close the CURL session
  1997. curl_close ($ch);
  1998. $headers = $this->_curl_headers;
  1999. $body = $buf;
  2000. }
  2001. phpCAS::traceEnd($res);
  2002. return $res;
  2003. }
  2004. /**
  2005. * This method is used to build the SAML POST body sent to /samlValidate URL.
  2006. *
  2007. * @return the SOAP-encased SAMLP artifact (the ticket).
  2008. *
  2009. * @private
  2010. */
  2011. function buildSAMLPayload()
  2012. {
  2013. phpCAS::traceBegin();
  2014. //get the ticket
  2015. $sa = $this->getSA();
  2016. //phpCAS::trace("SA: ".$sa);
  2017. $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;
  2018. phpCAS::traceEnd($body);
  2019. return ($body);
  2020. }
  2021. /**
  2022. * This method is the callback used by readURL method to request HTTP headers.
  2023. */
  2024. var $_curl_headers = array();
  2025. function _curl_read_headers($ch, $header)
  2026. {
  2027. $this->_curl_headers[] = $header;
  2028. return strlen($header);
  2029. }
  2030. /**
  2031. * This method is used to access an HTTP[S] service.
  2032. *
  2033. * @param $url the service to access.
  2034. * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
  2035. * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
  2036. * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
  2037. * @param $output the output of the service (also used to give an error
  2038. * message on failure).
  2039. *
  2040. * @return TRUE on success, FALSE otherwise (in this later case, $err_code
  2041. * gives the reason why it failed and $output contains an error message).
  2042. *
  2043. * @public
  2044. */
  2045. function serviceWeb($url,&$err_code,&$output)
  2046. {
  2047. phpCAS::traceBegin();
  2048. // at first retrieve a PT
  2049. $pt = $this->retrievePT($url,$err_code,$output);
  2050. $res = TRUE;
  2051. // test if PT was retrieved correctly
  2052. if ( !$pt ) {
  2053. // note: $err_code and $err_msg are filled by CASClient::retrievePT()
  2054. phpCAS::trace('PT was not retrieved correctly');
  2055. $res = FALSE;
  2056. } else {
  2057. // add cookies if necessary
  2058. if ( is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) {
  2059. foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) {
  2060. $cookies[] = $name.'='.$val;
  2061. }
  2062. }
  2063. // build the URL including the PT
  2064. if ( strstr($url,'?') === FALSE ) {
  2065. $service_url = $url.'?ticket='.$pt;
  2066. } else {
  2067. $service_url = $url.'&ticket='.$pt;
  2068. }
  2069. phpCAS::trace('reading URL`'.$service_url.'\'');
  2070. if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
  2071. phpCAS::trace('could not read URL`'.$service_url.'\'');
  2072. $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
  2073. // give an error message
  2074. $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
  2075. $service_url,
  2076. $err_msg);
  2077. $res = FALSE;
  2078. } else {
  2079. // URL has been fetched, extract the cookies
  2080. phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
  2081. foreach ( $headers as $header ) {
  2082. // test if the header is a cookie
  2083. if ( preg_match('/^Set-Cookie:/',$header) ) {
  2084. // the header is a cookie, remove the beginning
  2085. $header_val = preg_replace('/^Set-Cookie: */','',$header);
  2086. // extract interesting information
  2087. $name_val = strtok($header_val,'; ');
  2088. // extract the name and the value of the cookie
  2089. $cookie_name = strtok($name_val,'=');
  2090. $cookie_val = strtok('=');
  2091. // store the cookie
  2092. $_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val;
  2093. phpCAS::trace($cookie_name.' -> '.$cookie_val);
  2094. }
  2095. }
  2096. }
  2097. }
  2098. phpCAS::traceEnd($res);
  2099. return $res;
  2100. }
  2101. /**
  2102. * This method is used to access an IMAP/POP3/NNTP service.
  2103. *
  2104. * @param $url a string giving the URL of the service, including the mailing box
  2105. * for IMAP URLs, as accepted by imap_open().
  2106. * @param $service a string giving for CAS retrieve Proxy ticket
  2107. * @param $flags options given to imap_open().
  2108. * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
  2109. * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
  2110. * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
  2111. * @param $err_msg an error message on failure
  2112. * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
  2113. * on success, FALSE on error).
  2114. *
  2115. * @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
  2116. * gives the reason why it failed and $err_msg contains an error message).
  2117. *
  2118. * @public
  2119. */
  2120. function serviceMail($url,$service,$flags,&$err_code,&$err_msg,&$pt)
  2121. {
  2122. phpCAS::traceBegin();
  2123. // at first retrieve a PT
  2124. $pt = $this->retrievePT($service,$err_code,$output);
  2125. $stream = FALSE;
  2126. // test if PT was retrieved correctly
  2127. if ( !$pt ) {
  2128. // note: $err_code and $err_msg are filled by CASClient::retrievePT()
  2129. phpCAS::trace('PT was not retrieved correctly');
  2130. } else {
  2131. phpCAS::trace('opening IMAP URL `'.$url.'\'...');
  2132. $stream = @imap_open($url,$this->getUser(),$pt,$flags);
  2133. if ( !$stream ) {
  2134. phpCAS::trace('could not open URL');
  2135. $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
  2136. // give an error message
  2137. $err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
  2138. $service_url,
  2139. var_export(imap_errors(),TRUE));
  2140. $pt = FALSE;
  2141. $stream = FALSE;
  2142. } else {
  2143. phpCAS::trace('ok');
  2144. }
  2145. }
  2146. phpCAS::traceEnd($stream);
  2147. return $stream;
  2148. }
  2149. /** @} */
  2150. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2151. // XX XX
  2152. // XX PROXIED CLIENT FEATURES (CAS 2.0) XX
  2153. // XX XX
  2154. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2155. // ########################################################################
  2156. // PT
  2157. // ########################################################################
  2158. /**
  2159. * @addtogroup internalProxied
  2160. * @{
  2161. */
  2162. /**
  2163. * the Proxy Ticket provided in the URL of the request if present
  2164. * (empty otherwise). Written by CASClient::CASClient(), read by
  2165. * CASClient::getPT() and CASClient::hasPGT().
  2166. *
  2167. * @hideinitializer
  2168. * @private
  2169. */
  2170. var $_pt = '';
  2171. /**
  2172. * This method returns the Proxy Ticket provided in the URL of the request.
  2173. * @return The proxy ticket.
  2174. * @private
  2175. */
  2176. function getPT()
  2177. {
  2178. // return 'ST'.substr($this->_pt, 2);
  2179. return $this->_pt;
  2180. }
  2181. /**
  2182. * This method stores the Proxy Ticket.
  2183. * @param $pt The Proxy Ticket.
  2184. * @private
  2185. */
  2186. function setPT($pt)
  2187. { $this->_pt = $pt; }
  2188. /**
  2189. * This method tells if a Proxy Ticket was stored.
  2190. * @return TRUE if a Proxy Ticket has been stored.
  2191. * @private
  2192. */
  2193. function hasPT()
  2194. { return !empty($this->_pt); }
  2195. /**
  2196. * This method returns the SAML Ticket provided in the URL of the request.
  2197. * @return The SAML ticket.
  2198. * @private
  2199. */
  2200. function getSA()
  2201. { return 'ST'.substr($this->_sa, 2); }
  2202. /**
  2203. * This method stores the SAML Ticket.
  2204. * @param $sa The SAML Ticket.
  2205. * @private
  2206. */
  2207. function setSA($sa)
  2208. { $this->_sa = $sa; }
  2209. /**
  2210. * This method tells if a SAML Ticket was stored.
  2211. * @return TRUE if a SAML Ticket has been stored.
  2212. * @private
  2213. */
  2214. function hasSA()
  2215. { return !empty($this->_sa); }
  2216. /** @} */
  2217. // ########################################################################
  2218. // PT VALIDATION
  2219. // ########################################################################
  2220. /**
  2221. * @addtogroup internalProxied
  2222. * @{
  2223. */
  2224. /**
  2225. * This method is used to validate a ST or PT; halt on failure
  2226. * Used for all CAS 2.0 validations
  2227. * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
  2228. *
  2229. * @private
  2230. */
  2231. function validatePT(&$validate_url,&$text_response,&$tree_response)
  2232. {
  2233. phpCAS::traceBegin();
  2234. // build the URL to validate the ticket
  2235. $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
  2236. if ( $this->isProxy() ) {
  2237. // pass the callback url for CAS proxies
  2238. $validate_url .= '&pgtUrl='.$this->getCallbackURL();
  2239. }
  2240. // open and read the URL
  2241. if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
  2242. phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
  2243. $this->authError('PT not validated',
  2244. $validate_url,
  2245. TRUE/*$no_response*/);
  2246. }
  2247. // read the response of the CAS server into a DOM object
  2248. if ( !($dom = domxml_open_mem($text_response))) {
  2249. // read failed
  2250. $this->authError('PT not validated',
  2251. $validate_url,
  2252. FALSE/*$no_response*/,
  2253. TRUE/*$bad_response*/,
  2254. $text_response);
  2255. }
  2256. // read the root node of the XML tree
  2257. if ( !($tree_response = $dom->document_element()) ) {
  2258. // read failed
  2259. $this->authError('PT not validated',
  2260. $validate_url,
  2261. FALSE/*$no_response*/,
  2262. TRUE/*$bad_response*/,
  2263. $text_response);
  2264. }
  2265. // insure that tag name is 'serviceResponse'
  2266. if ( $tree_response->node_name() != 'serviceResponse' ) {
  2267. // bad root node
  2268. $this->authError('PT not validated',
  2269. $validate_url,
  2270. FALSE/*$no_response*/,
  2271. TRUE/*$bad_response*/,
  2272. $text_response);
  2273. }
  2274. if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
  2275. // authentication succeded, extract the user name
  2276. if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) {
  2277. // no user specified => error
  2278. $this->authError('PT not validated',
  2279. $validate_url,
  2280. FALSE/*$no_response*/,
  2281. TRUE/*$bad_response*/,
  2282. $text_response);
  2283. }
  2284. $this->setUser(trim($arr[0]->get_content()));
  2285. } else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
  2286. // authentication succeded, extract the error code and message
  2287. $this->authError('PT not validated',
  2288. $validate_url,
  2289. FALSE/*$no_response*/,
  2290. FALSE/*$bad_response*/,
  2291. $text_response,
  2292. $arr[0]->get_attribute('code')/*$err_code*/,
  2293. trim($arr[0]->get_content())/*$err_msg*/);
  2294. } else {
  2295. $this->authError('PT not validated',
  2296. $validate_url,
  2297. FALSE/*$no_response*/,
  2298. TRUE/*$bad_response*/,
  2299. $text_response);
  2300. }
  2301. // at this step, PT has been validated and $this->_user has been set,
  2302. phpCAS::traceEnd(TRUE);
  2303. return TRUE;
  2304. }
  2305. /** @} */
  2306. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2307. // XX XX
  2308. // XX MISC XX
  2309. // XX XX
  2310. // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  2311. /**
  2312. * @addtogroup internalMisc
  2313. * @{
  2314. */
  2315. // ########################################################################
  2316. // URL
  2317. // ########################################################################
  2318. /**
  2319. * the URL of the current request (without any ticket CGI parameter). Written
  2320. * and read by CASClient::getURL().
  2321. *
  2322. * @hideinitializer
  2323. * @private
  2324. */
  2325. var $_url = '';
  2326. /**
  2327. * This method returns the URL of the current request (without any ticket
  2328. * CGI parameter).
  2329. *
  2330. * @return The URL
  2331. *
  2332. * @private
  2333. */
  2334. function getURL()
  2335. {
  2336. phpCAS::traceBegin();
  2337. // the URL is built when needed only
  2338. if ( empty($this->_url) ) {
  2339. $final_uri = '';
  2340. // remove the ticket if present in the URL
  2341. $final_uri = ($this->isHttps()) ? 'https' : 'http';
  2342. $final_uri .= '://';
  2343. /* replaced by Julien Marchal - v0.4.6
  2344. * $this->_url .= $_SERVER['SERVER_NAME'];
  2345. */
  2346. if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
  2347. /* replaced by teedog - v0.4.12
  2348. * $this->_url .= $_SERVER['SERVER_NAME'];
  2349. */
  2350. if (empty($_SERVER['SERVER_NAME'])) {
  2351. $server_name = $_SERVER['HTTP_HOST'];
  2352. } else {
  2353. $server_name = $_SERVER['SERVER_NAME'];
  2354. }
  2355. } else {
  2356. $server_name = $_SERVER['HTTP_X_FORWARDED_SERVER'];
  2357. }
  2358. $final_uri .= $server_name;
  2359. if (!strpos($server_name, ':')) {
  2360. if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
  2361. || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
  2362. $final_uri .= ':';
  2363. $final_uri .= $_SERVER['SERVER_PORT'];
  2364. }
  2365. }
  2366. $request_uri = explode('?', $_SERVER['REQUEST_URI'], 2);
  2367. $final_uri .= $request_uri[0];
  2368. if (isset($request_uri[1]) && $request_uri[1])
  2369. {
  2370. $query_string = $this->removeParameterFromQueryString('ticket', $request_uri[1]);
  2371. // If the query string still has anything left, append it to the final URI
  2372. if ($query_string !== '')
  2373. $final_uri .= "?$query_string";
  2374. }
  2375. phpCAS::trace("Final URI: $final_uri");
  2376. $this->setURL($final_uri);
  2377. }
  2378. phpCAS::traceEnd($this->_url);
  2379. return $this->_url;
  2380. }
  2381. /**
  2382. * Removes a parameter from a query string
  2383. *
  2384. * @param string $parameterName
  2385. * @param string $queryString
  2386. * @return string
  2387. *
  2388. * @link http://stackoverflow.com/questions/1842681/regular-expression-to-remove-one-parameter-from-query-string
  2389. */
  2390. function removeParameterFromQueryString($parameterName, $queryString)
  2391. {
  2392. $parameterName = preg_quote($parameterName);
  2393. return preg_replace("/&$parameterName(=[^&]*)?|^$parameterName(=[^&]*)?&?/", '', $queryString);
  2394. }
  2395. /**
  2396. * This method sets the URL of the current request
  2397. *
  2398. * @param $url url to set for service
  2399. *
  2400. * @private
  2401. */
  2402. function setURL($url)
  2403. {
  2404. $this->_url = $url;
  2405. }
  2406. // ########################################################################
  2407. // AUTHENTICATION ERROR HANDLING
  2408. // ########################################################################
  2409. /**
  2410. * This method is used to print the HTML output when the user was not authenticated.
  2411. *
  2412. * @param $failure the failure that occured
  2413. * @param $cas_url the URL the CAS server was asked for
  2414. * @param $no_response the response from the CAS server (other
  2415. * parameters are ignored if TRUE)
  2416. * @param $bad_response bad response from the CAS server ($err_code
  2417. * and $err_msg ignored if TRUE)
  2418. * @param $cas_response the response of the CAS server
  2419. * @param $err_code the error code given by the CAS server
  2420. * @param $err_msg the error message given by the CAS server
  2421. *
  2422. * @private
  2423. */
  2424. function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
  2425. {
  2426. phpCAS::traceBegin();
  2427. $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
  2428. printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),htmlentities($this->getURL()),$_SERVER['SERVER_ADMIN']);
  2429. phpCAS::trace('CAS URL: '.$cas_url);
  2430. phpCAS::trace('Authentication failure: '.$failure);
  2431. if ( $no_response ) {
  2432. phpCAS::trace('Reason: no response from the CAS server');
  2433. } else {
  2434. if ( $bad_response ) {
  2435. phpCAS::trace('Reason: bad response from the CAS server');
  2436. } else {
  2437. switch ($this->getServerVersion()) {
  2438. case CAS_VERSION_1_0:
  2439. phpCAS::trace('Reason: CAS error');
  2440. break;
  2441. case CAS_VERSION_2_0:
  2442. if ( empty($err_code) )
  2443. phpCAS::trace('Reason: no CAS error');
  2444. else
  2445. phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
  2446. break;
  2447. }
  2448. }
  2449. phpCAS::trace('CAS response: '.$cas_response);
  2450. }
  2451. $this->printHTMLFooter();
  2452. phpCAS::traceExit();
  2453. exit();
  2454. }
  2455. /** @} */
  2456. }
  2457. ?>