1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348 |
- <?php
- /**
- * Licensed to Jasig under one or more contributor license
- * agreements. See the NOTICE file distributed with this work for
- * additional information regarding copyright ownership.
- *
- * Jasig licenses this file to you under the Apache License,
- * Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * PHP Version 5
- *
- * @file CAS/Client.php
- * @category Authentication
- * @package PhpCAS
- * @author Pascal Aubry <pascal.aubry@univ-rennes1.fr>
- * @author Olivier Berger <olivier.berger@it-sudparis.eu>
- * @author Brett Bieber <brett.bieber@gmail.com>
- * @author Joachim Fritschi <jfritschi@freenet.de>
- * @author Adam Franco <afranco@middlebury.edu>
- * @author Tobias Schiebeck <tobias.schiebeck@manchester.ac.uk>
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
- * @link https://wiki.jasig.org/display/CASC/phpCAS
- */
- /**
- * The CAS_Client class is a client interface that provides CAS authentication
- * to PHP applications.
- *
- * @class CAS_Client
- * @category Authentication
- * @package PhpCAS
- * @author Pascal Aubry <pascal.aubry@univ-rennes1.fr>
- * @author Olivier Berger <olivier.berger@it-sudparis.eu>
- * @author Brett Bieber <brett.bieber@gmail.com>
- * @author Joachim Fritschi <jfritschi@freenet.de>
- * @author Adam Franco <afranco@middlebury.edu>
- * @author Tobias Schiebeck <tobias.schiebeck@manchester.ac.uk>
- * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
- * @link https://wiki.jasig.org/display/CASC/phpCAS
- *
- */
- class CAS_Client
- {
- // ########################################################################
- // HTML OUTPUT
- // ########################################################################
- /**
- * @addtogroup internalOutput
- * @{
- */
- /**
- * This method filters a string by replacing special tokens by appropriate values
- * and prints it. The corresponding tokens are taken into account:
- * - __CAS_VERSION__
- * - __PHPCAS_VERSION__
- * - __SERVER_BASE_URL__
- *
- * Used by CAS_Client::PrintHTMLHeader() and CAS_Client::printHTMLFooter().
- *
- * @param string $str the string to filter and output
- *
- * @return void
- */
- private function _htmlFilterOutput($str)
- {
- $str = str_replace('__CAS_VERSION__', $this->getServerVersion(), $str);
- $str = str_replace('__PHPCAS_VERSION__', phpCAS::getVersion(), $str);
- $str = str_replace('__SERVER_BASE_URL__', $this->_getServerBaseURL(), $str);
- echo $str;
- }
- /**
- * A string used to print the header of HTML pages. Written by
- * CAS_Client::setHTMLHeader(), read by CAS_Client::printHTMLHeader().
- *
- * @hideinitializer
- * @see CAS_Client::setHTMLHeader, CAS_Client::printHTMLHeader()
- */
- private $_output_header = '';
- /**
- * This method prints the header of the HTML output (after filtering). If
- * CAS_Client::setHTMLHeader() was not used, a default header is output.
- *
- * @param string $title the title of the page
- *
- * @return void
- * @see _htmlFilterOutput()
- */
- public function printHTMLHeader($title)
- {
- $this->_htmlFilterOutput(
- str_replace(
- '__TITLE__', $title,
- (empty($this->_output_header)
- ? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
- : $this->_output_header)
- )
- );
- }
- /**
- * A string used to print the footer of HTML pages. Written by
- * CAS_Client::setHTMLFooter(), read by printHTMLFooter().
- *
- * @hideinitializer
- * @see CAS_Client::setHTMLFooter, CAS_Client::printHTMLFooter()
- */
- private $_output_footer = '';
- /**
- * This method prints the footer of the HTML output (after filtering). If
- * CAS_Client::setHTMLFooter() was not used, a default footer is output.
- *
- * @return void
- * @see _htmlFilterOutput()
- */
- public function printHTMLFooter()
- {
- $lang = $this->getLangObj();
- $this->_htmlFilterOutput(
- empty($this->_output_footer)?
- (phpCAS::getVerbose())?
- '<hr><address>phpCAS __PHPCAS_VERSION__ '
- .$lang->getUsingServer()
- .' <a href="__SERVER_BASE_URL__">__SERVER_BASE_URL__</a> (CAS __CAS_VERSION__)</a></address></body></html>'
- :'</body></html>'
- :$this->_output_footer
- );
- }
- /**
- * This method set the HTML header used for all outputs.
- *
- * @param string $header the HTML header.
- *
- * @return void
- */
- public function setHTMLHeader($header)
- {
- // Argument Validation
- if (gettype($header) != 'string')
- throw new CAS_TypeMismatchException($header, '$header', 'string');
- $this->_output_header = $header;
- }
- /**
- * This method set the HTML footer used for all outputs.
- *
- * @param string $footer the HTML footer.
- *
- * @return void
- */
- public function setHTMLFooter($footer)
- {
- // Argument Validation
- if (gettype($footer) != 'string')
- throw new CAS_TypeMismatchException($footer, '$footer', 'string');
- $this->_output_footer = $footer;
- }
- /** @} */
- // ########################################################################
- // INTERNATIONALIZATION
- // ########################################################################
- /**
- * @addtogroup internalLang
- * @{
- */
- /**
- * A string corresponding to the language used by phpCAS. Written by
- * CAS_Client::setLang(), read by CAS_Client::getLang().
- * @note debugging information is always in english (debug purposes only).
- */
- private $_lang = PHPCAS_LANG_DEFAULT;
- /**
- * This method is used to set the language used by phpCAS.
- *
- * @param string $lang representing the language.
- *
- * @return void
- */
- public function setLang($lang)
- {
- // Argument Validation
- if (gettype($lang) != 'string')
- throw new CAS_TypeMismatchException($lang, '$lang', 'string');
- phpCAS::traceBegin();
- $obj = new $lang();
- if (!($obj instanceof CAS_Languages_LanguageInterface)) {
- throw new CAS_InvalidArgumentException(
- '$className must implement the CAS_Languages_LanguageInterface'
- );
- }
- $this->_lang = $lang;
- phpCAS::traceEnd();
- }
- /**
- * Create the language
- *
- * @return CAS_Languages_LanguageInterface object implementing the class
- */
- public function getLangObj()
- {
- $classname = $this->_lang;
- return new $classname();
- }
- /** @} */
- // ########################################################################
- // CAS SERVER CONFIG
- // ########################################################################
- /**
- * @addtogroup internalConfig
- * @{
- */
- /**
- * a record to store information about the CAS server.
- * - $_server['version']: the version of the CAS server
- * - $_server['hostname']: the hostname of the CAS server
- * - $_server['port']: the port the CAS server is running on
- * - $_server['uri']: the base URI the CAS server is responding on
- * - $_server['base_url']: the base URL of the CAS server
- * - $_server['login_url']: the login URL of the CAS server
- * - $_server['service_validate_url']: the service validating URL of the
- * CAS server
- * - $_server['proxy_url']: the proxy URL of the CAS server
- * - $_server['proxy_validate_url']: the proxy validating URL of the CAS server
- * - $_server['logout_url']: the logout URL of the CAS server
- *
- * $_server['version'], $_server['hostname'], $_server['port'] and
- * $_server['uri'] are written by CAS_Client::CAS_Client(), read by
- * CAS_Client::getServerVersion(), CAS_Client::_getServerHostname(),
- * CAS_Client::_getServerPort() and CAS_Client::_getServerURI().
- *
- * The other fields are written and read by CAS_Client::_getServerBaseURL(),
- * CAS_Client::getServerLoginURL(), CAS_Client::getServerServiceValidateURL(),
- * CAS_Client::getServerProxyValidateURL() and CAS_Client::getServerLogoutURL().
- *
- * @hideinitializer
- */
- private $_server = array(
- 'version' => '',
- 'hostname' => 'none',
- 'port' => -1,
- 'uri' => 'none');
- /**
- * This method is used to retrieve the version of the CAS server.
- *
- * @return string the version of the CAS server.
- */
- public function getServerVersion()
- {
- return $this->_server['version'];
- }
- /**
- * This method is used to retrieve the hostname of the CAS server.
- *
- * @return string the hostname of the CAS server.
- */
- private function _getServerHostname()
- {
- return $this->_server['hostname'];
- }
- /**
- * This method is used to retrieve the port of the CAS server.
- *
- * @return int the port of the CAS server.
- */
- private function _getServerPort()
- {
- return $this->_server['port'];
- }
- /**
- * This method is used to retrieve the URI of the CAS server.
- *
- * @return string a URI.
- */
- private function _getServerURI()
- {
- return $this->_server['uri'];
- }
- /**
- * This method is used to retrieve the base URL of the CAS server.
- *
- * @return string a URL.
- */
- private function _getServerBaseURL()
- {
- // the URL is build only when needed
- if ( empty($this->_server['base_url']) ) {
- $this->_server['base_url'] = 'https://' . $this->_getServerHostname();
- if ($this->_getServerPort()!=443) {
- $this->_server['base_url'] .= ':'
- .$this->_getServerPort();
- }
- $this->_server['base_url'] .= $this->_getServerURI();
- }
- return $this->_server['base_url'];
- }
- /**
- * This method is used to retrieve the login URL of the CAS server.
- *
- * @param bool $gateway true to check authentication, false to force it
- * @param bool $renew true to force the authentication with the CAS server
- *
- * @return string a URL.
- * @note It is recommended that CAS implementations ignore the "gateway"
- * parameter if "renew" is set
- */
- public function getServerLoginURL($gateway=false,$renew=false)
- {
- phpCAS::traceBegin();
- // the URL is build only when needed
- if ( empty($this->_server['login_url']) ) {
- $this->_server['login_url'] = $this->_buildQueryUrl($this->_getServerBaseURL().'login','service='.urlencode($this->getURL()));
- }
- $url = $this->_server['login_url'];
- if ($renew) {
- // It is recommended that when the "renew" parameter is set, its
- // value be "true"
- $url = $this->_buildQueryUrl($url, 'renew=true');
- } elseif ($gateway) {
- // It is recommended that when the "gateway" parameter is set, its
- // value be "true"
- $url = $this->_buildQueryUrl($url, 'gateway=true');
- }
- phpCAS::traceEnd($url);
- return $url;
- }
- /**
- * This method sets the login URL of the CAS server.
- *
- * @param string $url the login URL
- *
- * @return string login url
- */
- public function setServerLoginURL($url)
- {
- // Argument Validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- return $this->_server['login_url'] = $url;
- }
- /**
- * This method sets the serviceValidate URL of the CAS server.
- *
- * @param string $url the serviceValidate URL
- *
- * @return string serviceValidate URL
- */
- public function setServerServiceValidateURL($url)
- {
- // Argument Validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- return $this->_server['service_validate_url'] = $url;
- }
- /**
- * This method sets the proxyValidate URL of the CAS server.
- *
- * @param string $url the proxyValidate URL
- *
- * @return string proxyValidate URL
- */
- public function setServerProxyValidateURL($url)
- {
- // Argument Validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- return $this->_server['proxy_validate_url'] = $url;
- }
- /**
- * This method sets the samlValidate URL of the CAS server.
- *
- * @param string $url the samlValidate URL
- *
- * @return string samlValidate URL
- */
- public function setServerSamlValidateURL($url)
- {
- // Argument Validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- return $this->_server['saml_validate_url'] = $url;
- }
- /**
- * This method is used to retrieve the service validating URL of the CAS server.
- *
- * @return string serviceValidate URL.
- */
- public function getServerServiceValidateURL()
- {
- phpCAS::traceBegin();
- // the URL is build only when needed
- if ( empty($this->_server['service_validate_url']) ) {
- switch ($this->getServerVersion()) {
- case CAS_VERSION_1_0:
- $this->_server['service_validate_url'] = $this->_getServerBaseURL()
- .'validate';
- break;
- case CAS_VERSION_2_0:
- $this->_server['service_validate_url'] = $this->_getServerBaseURL()
- .'serviceValidate';
- break;
- case CAS_VERSION_3_0:
- $this->_server['service_validate_url'] = $this->_getServerBaseURL()
- .'p3/serviceValidate';
- break;
- }
- }
- $url = $this->_buildQueryUrl(
- $this->_server['service_validate_url'],
- 'service='.urlencode($this->getURL())
- );
- phpCAS::traceEnd($url);
- return $url;
- }
- /**
- * This method is used to retrieve the SAML validating URL of the CAS server.
- *
- * @return string samlValidate URL.
- */
- public function getServerSamlValidateURL()
- {
- phpCAS::traceBegin();
- // the URL is build only when needed
- if ( empty($this->_server['saml_validate_url']) ) {
- switch ($this->getServerVersion()) {
- case SAML_VERSION_1_1:
- $this->_server['saml_validate_url'] = $this->_getServerBaseURL().'samlValidate';
- break;
- }
- }
- $url = $this->_buildQueryUrl(
- $this->_server['saml_validate_url'],
- 'TARGET='.urlencode($this->getURL())
- );
- phpCAS::traceEnd($url);
- return $url;
- }
- /**
- * This method is used to retrieve the proxy validating URL of the CAS server.
- *
- * @return string proxyValidate URL.
- */
- public function getServerProxyValidateURL()
- {
- phpCAS::traceBegin();
- // the URL is build only when needed
- if ( empty($this->_server['proxy_validate_url']) ) {
- switch ($this->getServerVersion()) {
- case CAS_VERSION_1_0:
- $this->_server['proxy_validate_url'] = '';
- break;
- case CAS_VERSION_2_0:
- $this->_server['proxy_validate_url'] = $this->_getServerBaseURL().'proxyValidate';
- break;
- case CAS_VERSION_3_0:
- $this->_server['proxy_validate_url'] = $this->_getServerBaseURL().'p3/proxyValidate';
- break;
- }
- }
- $url = $this->_buildQueryUrl(
- $this->_server['proxy_validate_url'],
- 'service='.urlencode($this->getURL())
- );
- phpCAS::traceEnd($url);
- return $url;
- }
- /**
- * This method is used to retrieve the proxy URL of the CAS server.
- *
- * @return string proxy URL.
- */
- public function getServerProxyURL()
- {
- // the URL is build only when needed
- if ( empty($this->_server['proxy_url']) ) {
- switch ($this->getServerVersion()) {
- case CAS_VERSION_1_0:
- $this->_server['proxy_url'] = '';
- break;
- case CAS_VERSION_2_0:
- case CAS_VERSION_3_0:
- $this->_server['proxy_url'] = $this->_getServerBaseURL().'proxy';
- break;
- }
- }
- return $this->_server['proxy_url'];
- }
- /**
- * This method is used to retrieve the logout URL of the CAS server.
- *
- * @return string logout URL.
- */
- public function getServerLogoutURL()
- {
- // the URL is build only when needed
- if ( empty($this->_server['logout_url']) ) {
- $this->_server['logout_url'] = $this->_getServerBaseURL().'logout';
- }
- return $this->_server['logout_url'];
- }
- /**
- * This method sets the logout URL of the CAS server.
- *
- * @param string $url the logout URL
- *
- * @return string logout url
- */
- public function setServerLogoutURL($url)
- {
- // Argument Validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- return $this->_server['logout_url'] = $url;
- }
- /**
- * An array to store extra curl options.
- */
- private $_curl_options = array();
- /**
- * This method is used to set additional user curl options.
- *
- * @param string $key name of the curl option
- * @param string $value value of the curl option
- *
- * @return void
- */
- public function setExtraCurlOption($key, $value)
- {
- $this->_curl_options[$key] = $value;
- }
- /** @} */
- // ########################################################################
- // Change the internal behaviour of phpcas
- // ########################################################################
- /**
- * @addtogroup internalBehave
- * @{
- */
- /**
- * The class to instantiate for making web requests in readUrl().
- * The class specified must implement the CAS_Request_RequestInterface.
- * By default CAS_Request_CurlRequest is used, but this may be overridden to
- * supply alternate request mechanisms for testing.
- */
- private $_requestImplementation = 'CAS_Request_CurlRequest';
- /**
- * Override the default implementation used to make web requests in readUrl().
- * This class must implement the CAS_Request_RequestInterface.
- *
- * @param string $className name of the RequestImplementation class
- *
- * @return void
- */
- public function setRequestImplementation ($className)
- {
- $obj = new $className;
- if (!($obj instanceof CAS_Request_RequestInterface)) {
- throw new CAS_InvalidArgumentException(
- '$className must implement the CAS_Request_RequestInterface'
- );
- }
- $this->_requestImplementation = $className;
- }
- /**
- * @var boolean $_clearTicketsFromUrl; If true, phpCAS will clear session
- * tickets from the URL after a successful authentication.
- */
- private $_clearTicketsFromUrl = true;
- /**
- * Configure the client to not send redirect headers and call exit() on
- * authentication success. The normal redirect is used to remove the service
- * ticket from the client's URL, but for running unit tests we need to
- * continue without exiting.
- *
- * Needed for testing authentication
- *
- * @return void
- */
- public function setNoClearTicketsFromUrl ()
- {
- $this->_clearTicketsFromUrl = false;
- }
- /**
- * @var callback $_attributeParserCallbackFunction;
- */
- private $_casAttributeParserCallbackFunction = null;
- /**
- * @var array $_attributeParserCallbackArgs;
- */
- private $_casAttributeParserCallbackArgs = array();
- /**
- * Set a callback function to be run when parsing CAS attributes
- *
- * The callback function will be passed a XMLNode as its first parameter,
- * followed by any $additionalArgs you pass.
- *
- * @param string $function callback function to call
- * @param array $additionalArgs optional array of arguments
- *
- * @return void
- */
- public function setCasAttributeParserCallback($function, array $additionalArgs = array())
- {
- $this->_casAttributeParserCallbackFunction = $function;
- $this->_casAttributeParserCallbackArgs = $additionalArgs;
- }
- /** @var callable $_postAuthenticateCallbackFunction;
- */
- private $_postAuthenticateCallbackFunction = null;
- /**
- * @var array $_postAuthenticateCallbackArgs;
- */
- private $_postAuthenticateCallbackArgs = array();
- /**
- * Set a callback function to be run when a user authenticates.
- *
- * The callback function will be passed a $logoutTicket as its first parameter,
- * followed by any $additionalArgs you pass. The $logoutTicket parameter is an
- * opaque string that can be used to map a session-id to the logout request
- * in order to support single-signout in applications that manage their own
- * sessions (rather than letting phpCAS start the session).
- *
- * phpCAS::forceAuthentication() will always exit and forward client unless
- * they are already authenticated. To perform an action at the moment the user
- * logs in (such as registering an account, performing logging, etc), register
- * a callback function here.
- *
- * @param callable $function callback function to call
- * @param array $additionalArgs optional array of arguments
- *
- * @return void
- */
- public function setPostAuthenticateCallback ($function, array $additionalArgs = array())
- {
- $this->_postAuthenticateCallbackFunction = $function;
- $this->_postAuthenticateCallbackArgs = $additionalArgs;
- }
- /**
- * @var callable $_signoutCallbackFunction;
- */
- private $_signoutCallbackFunction = null;
- /**
- * @var array $_signoutCallbackArgs;
- */
- private $_signoutCallbackArgs = array();
- /**
- * Set a callback function to be run when a single-signout request is received.
- *
- * The callback function will be passed a $logoutTicket as its first parameter,
- * followed by any $additionalArgs you pass. The $logoutTicket parameter is an
- * opaque string that can be used to map a session-id to the logout request in
- * order to support single-signout in applications that manage their own sessions
- * (rather than letting phpCAS start and destroy the session).
- *
- * @param callable $function callback function to call
- * @param array $additionalArgs optional array of arguments
- *
- * @return void
- */
- public function setSingleSignoutCallback ($function, array $additionalArgs = array())
- {
- $this->_signoutCallbackFunction = $function;
- $this->_signoutCallbackArgs = $additionalArgs;
- }
- // ########################################################################
- // Methods for supplying code-flow feedback to integrators.
- // ########################################################################
- /**
- * Ensure that this is actually a proxy object or fail with an exception
- *
- * @throws CAS_OutOfSequenceBeforeProxyException
- *
- * @return void
- */
- public function ensureIsProxy()
- {
- if (!$this->isProxy()) {
- throw new CAS_OutOfSequenceBeforeProxyException();
- }
- }
- /**
- * Mark the caller of authentication. This will help client integraters determine
- * problems with their code flow if they call a function such as getUser() before
- * authentication has occurred.
- *
- * @param bool $auth True if authentication was successful, false otherwise.
- *
- * @return null
- */
- public function markAuthenticationCall ($auth)
- {
- // store where the authentication has been checked and the result
- $dbg = debug_backtrace();
- $this->_authentication_caller = array (
- 'file' => $dbg[1]['file'],
- 'line' => $dbg[1]['line'],
- 'method' => $dbg[1]['class'] . '::' . $dbg[1]['function'],
- 'result' => (boolean)$auth
- );
- }
- private $_authentication_caller;
- /**
- * Answer true if authentication has been checked.
- *
- * @return bool
- */
- public function wasAuthenticationCalled ()
- {
- return !empty($this->_authentication_caller);
- }
- /**
- * Ensure that authentication was checked. Terminate with exception if no
- * authentication was performed
- *
- * @throws CAS_OutOfSequenceBeforeAuthenticationCallException
- *
- * @return void
- */
- private function _ensureAuthenticationCalled()
- {
- if (!$this->wasAuthenticationCalled()) {
- throw new CAS_OutOfSequenceBeforeAuthenticationCallException();
- }
- }
- /**
- * Answer the result of the authentication call.
- *
- * Throws a CAS_OutOfSequenceException if wasAuthenticationCalled() is false
- * and markAuthenticationCall() didn't happen.
- *
- * @return bool
- */
- public function wasAuthenticationCallSuccessful ()
- {
- $this->_ensureAuthenticationCalled();
- return $this->_authentication_caller['result'];
- }
- /**
- * Ensure that authentication was checked. Terminate with exception if no
- * authentication was performed
- *
- * @throws CAS_OutOfSequenceException
- *
- * @return void
- */
- public function ensureAuthenticationCallSuccessful()
- {
- $this->_ensureAuthenticationCalled();
- if (!$this->_authentication_caller['result']) {
- throw new CAS_OutOfSequenceException(
- 'authentication was checked (by '
- . $this->getAuthenticationCallerMethod()
- . '() at ' . $this->getAuthenticationCallerFile()
- . ':' . $this->getAuthenticationCallerLine()
- . ') but the method returned false'
- );
- }
- }
- /**
- * Answer information about the authentication caller.
- *
- * Throws a CAS_OutOfSequenceException if wasAuthenticationCalled() is false
- * and markAuthenticationCall() didn't happen.
- *
- * @return string the file that called authentication
- */
- public function getAuthenticationCallerFile ()
- {
- $this->_ensureAuthenticationCalled();
- return $this->_authentication_caller['file'];
- }
- /**
- * Answer information about the authentication caller.
- *
- * Throws a CAS_OutOfSequenceException if wasAuthenticationCalled() is false
- * and markAuthenticationCall() didn't happen.
- *
- * @return int the line that called authentication
- */
- public function getAuthenticationCallerLine ()
- {
- $this->_ensureAuthenticationCalled();
- return $this->_authentication_caller['line'];
- }
- /**
- * Answer information about the authentication caller.
- *
- * Throws a CAS_OutOfSequenceException if wasAuthenticationCalled() is false
- * and markAuthenticationCall() didn't happen.
- *
- * @return string the method that called authentication
- */
- public function getAuthenticationCallerMethod ()
- {
- $this->_ensureAuthenticationCalled();
- return $this->_authentication_caller['method'];
- }
- /** @} */
- // ########################################################################
- // CONSTRUCTOR
- // ########################################################################
- /**
- * @addtogroup internalConfig
- * @{
- */
- /**
- * CAS_Client constructor.
- *
- * @param string $server_version the version of the CAS server
- * @param bool $proxy true if the CAS client is a CAS proxy
- * @param string $server_hostname the hostname of the CAS server
- * @param int $server_port the port the CAS server is running on
- * @param string $server_uri the URI the CAS server is responding on
- * @param bool $changeSessionID Allow phpCAS to change the session_id
- * (Single Sign Out/handleLogoutRequests
- * is based on that change)
- * @param \SessionHandlerInterface $sessionHandler the session handler
- *
- * @return self a newly created CAS_Client object
- */
- public function __construct(
- $server_version,
- $proxy,
- $server_hostname,
- $server_port,
- $server_uri,
- $changeSessionID = true,
- \SessionHandlerInterface $sessionHandler = null
- ) {
- // Argument validation
- if (gettype($server_version) != 'string')
- throw new CAS_TypeMismatchException($server_version, '$server_version', 'string');
- if (gettype($proxy) != 'boolean')
- throw new CAS_TypeMismatchException($proxy, '$proxy', 'boolean');
- if (gettype($server_hostname) != 'string')
- throw new CAS_TypeMismatchException($server_hostname, '$server_hostname', 'string');
- if (gettype($server_port) != 'integer')
- throw new CAS_TypeMismatchException($server_port, '$server_port', 'integer');
- if (gettype($server_uri) != 'string')
- throw new CAS_TypeMismatchException($server_uri, '$server_uri', 'string');
- if (gettype($changeSessionID) != 'boolean')
- throw new CAS_TypeMismatchException($changeSessionID, '$changeSessionID', 'boolean');
- if (empty($sessionHandler)) {
- $sessionHandler = new CAS_Session_PhpSession;
- }
- phpCAS::traceBegin();
- // true : allow to change the session_id(), false session_id won't be
- // changed and logout won't be handled because of that
- $this->_setChangeSessionID($changeSessionID);
- $this->setSessionHandler($sessionHandler);
- if (!$this->_isLogoutRequest()) {
- if (session_id() === "") {
- // skip Session Handling for logout requests and if don't want it
- session_start();
- phpCAS :: trace("Starting a new session " . session_id());
- }
- // init phpCAS session array
- if (!isset($_SESSION[static::PHPCAS_SESSION_PREFIX])
- || !is_array($_SESSION[static::PHPCAS_SESSION_PREFIX])) {
- $_SESSION[static::PHPCAS_SESSION_PREFIX] = array();
- }
- }
- // Only for debug purposes
- if ($this->isSessionAuthenticated()){
- phpCAS :: trace("Session is authenticated as: " . $this->getSessionValue('user'));
- } else {
- phpCAS :: trace("Session is not authenticated");
- }
- // are we in proxy mode ?
- $this->_proxy = $proxy;
- // Make cookie handling available.
- if ($this->isProxy()) {
- if (!$this->hasSessionValue('service_cookies')) {
- $this->setSessionValue('service_cookies', array());
- }
- // TODO remove explicit call to $_SESSION
- $this->_serviceCookieJar = new CAS_CookieJar(
- $_SESSION[static::PHPCAS_SESSION_PREFIX]['service_cookies']
- );
- }
- // check version
- $supportedProtocols = phpCAS::getSupportedProtocols();
- if (isset($supportedProtocols[$server_version]) === false) {
- phpCAS::error(
- 'this version of CAS (`'.$server_version
- .'\') is not supported by phpCAS '.phpCAS::getVersion()
- );
- }
- if ($server_version === CAS_VERSION_1_0 && $this->isProxy()) {
- phpCAS::error(
- 'CAS proxies are not supported in CAS '.$server_version
- );
- }
- $this->_server['version'] = $server_version;
- // check hostname
- if ( empty($server_hostname)
- || !preg_match('/[\.\d\-a-z]*/', $server_hostname)
- ) {
- phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
- }
- $this->_server['hostname'] = $server_hostname;
- // check port
- if ( $server_port == 0
- || !is_int($server_port)
- ) {
- phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
- }
- $this->_server['port'] = $server_port;
- // check URI
- if ( !preg_match('/[\.\d\-_a-z\/]*/', $server_uri) ) {
- phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
- }
- // add leading and trailing `/' and remove doubles
- if(strstr($server_uri, '?') === false) $server_uri .= '/';
- $server_uri = preg_replace('/\/\//', '/', '/'.$server_uri);
- $this->_server['uri'] = $server_uri;
- // set to callback mode if PgtIou and PgtId CGI GET parameters are provided
- if ( $this->isProxy() ) {
- if(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId'])) {
- $this->_setCallbackMode(true);
- $this->_setCallbackModeUsingPost(false);
- } elseif (!empty($_POST['pgtIou'])&&!empty($_POST['pgtId'])) {
- $this->_setCallbackMode(true);
- $this->_setCallbackModeUsingPost(true);
- } else {
- $this->_setCallbackMode(false);
- $this->_setCallbackModeUsingPost(false);
- }
- }
- if ( $this->_isCallbackMode() ) {
- //callback mode: check that phpCAS is secured
- if ( !$this->_isHttps() ) {
- phpCAS::error(
- 'CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server'
- );
- }
- } else {
- //normal mode: get ticket and remove it from CGI parameters for
- // developers
- $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
- if (preg_match('/^[SP]T-/', $ticket) ) {
- phpCAS::trace('Ticket \''.$ticket.'\' found');
- $this->setTicket($ticket);
- unset($_GET['ticket']);
- } else if ( !empty($ticket) ) {
- //ill-formed ticket, halt
- phpCAS::error(
- 'ill-formed ticket found in the URL (ticket=`'
- .htmlentities($ticket).'\')'
- );
- }
- }
- phpCAS::traceEnd();
- }
- /** @} */
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // XX XX
- // XX Session Handling XX
- // XX XX
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- /**
- * @addtogroup internalConfig
- * @{
- */
- /** The session prefix for phpCAS values */
- const PHPCAS_SESSION_PREFIX = 'phpCAS';
- /**
- * @var bool A variable to whether phpcas will use its own session handling. Default = true
- * @hideinitializer
- */
- private $_change_session_id = true;
- /**
- * @var SessionHandlerInterface
- */
- private $_sessionHandler;
- /**
- * Set a parameter whether to allow phpCAS to change session_id
- *
- * @param bool $allowed allow phpCAS to change session_id
- *
- * @return void
- */
- private function _setChangeSessionID($allowed)
- {
- $this->_change_session_id = $allowed;
- }
- /**
- * Get whether phpCAS is allowed to change session_id
- *
- * @return bool
- */
- public function getChangeSessionID()
- {
- return $this->_change_session_id;
- }
- /**
- * Set the session handler.
- *
- * @param \SessionHandlerInterface $sessionHandler
- *
- * @return bool
- */
- public function setSessionHandler(\SessionHandlerInterface $sessionHandler)
- {
- $this->_sessionHandler = $sessionHandler;
- if (session_status() !== PHP_SESSION_ACTIVE) {
- return session_set_save_handler($this->_sessionHandler, true);
- }
- return true;
- }
- /**
- * Get a session value using the given key.
- *
- * @param string $key
- * @param mixed $default default value if the key is not set
- *
- * @return mixed
- */
- protected function getSessionValue($key, $default = null)
- {
- $this->validateSession($key);
- if (isset($_SESSION[static::PHPCAS_SESSION_PREFIX][$key])) {
- return $_SESSION[static::PHPCAS_SESSION_PREFIX][$key];
- }
- return $default;
- }
- /**
- * Determine whether a session value is set or not.
- *
- * To check if a session value is empty or not please use
- * !!(getSessionValue($key)).
- *
- * @param string $key
- *
- * @return bool
- */
- protected function hasSessionValue($key)
- {
- $this->validateSession($key);
- return isset($_SESSION[static::PHPCAS_SESSION_PREFIX][$key]);
- }
- /**
- * Set a session value using the given key and value.
- *
- * @param string $key
- * @param mixed $value
- *
- * @return string
- */
- protected function setSessionValue($key, $value)
- {
- $this->validateSession($key);
- $_SESSION[static::PHPCAS_SESSION_PREFIX][$key] = $value;
- }
- /**
- * Remove a session value with the given key.
- *
- * @param string $key
- */
- protected function removeSessionValue($key)
- {
- $this->validateSession($key);
- if (isset($_SESSION[static::PHPCAS_SESSION_PREFIX][$key])) {
- unset($_SESSION[static::PHPCAS_SESSION_PREFIX][$key]);
- return true;
- }
- return false;
- }
- /**
- * Remove all phpCAS session values.
- */
- protected function clearSessionValues()
- {
- unset($_SESSION[static::PHPCAS_SESSION_PREFIX]);
- }
- /**
- * Ensure $key is a string for session utils input
- *
- * @param string $key
- *
- * @return bool
- */
- protected function validateSession($key)
- {
- if (!is_string($key)) {
- throw new InvalidArgumentException('Session key must be a string.');
- }
- return true;
- }
- /**
- * Renaming the session
- *
- * @param string $ticket name of the ticket
- *
- * @return void
- */
- protected function _renameSession($ticket)
- {
- phpCAS::traceBegin();
- if ($this->getChangeSessionID()) {
- if (!empty($this->_user)) {
- $old_session = $_SESSION;
- phpCAS :: trace("Killing session: ". session_id());
- session_destroy();
- // set up a new session, of name based on the ticket
- $session_id = $this->_sessionIdForTicket($ticket);
- phpCAS :: trace("Starting session: ". $session_id);
- session_id($session_id);
- session_start();
- phpCAS :: trace("Restoring old session vars");
- $_SESSION = $old_session;
- } else {
- phpCAS :: trace (
- 'Session should only be renamed after successfull authentication'
- );
- }
- } else {
- phpCAS :: trace(
- "Skipping session rename since phpCAS is not handling the session."
- );
- }
- phpCAS::traceEnd();
- }
- /** @} */
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // XX XX
- // XX AUTHENTICATION XX
- // XX XX
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- /**
- * @addtogroup internalAuthentication
- * @{
- */
- /**
- * The Authenticated user. Written by CAS_Client::_setUser(), read by
- * CAS_Client::getUser().
- *
- * @hideinitializer
- */
- private $_user = '';
- /**
- * This method sets the CAS user's login name.
- *
- * @param string $user the login name of the authenticated user.
- *
- * @return void
- */
- private function _setUser($user)
- {
- $this->_user = $user;
- }
- /**
- * This method returns the CAS user's login name.
- *
- * @return string the login name of the authenticated user
- *
- * @warning should be called only after CAS_Client::forceAuthentication() or
- * CAS_Client::isAuthenticated(), otherwise halt with an error.
- */
- public function getUser()
- {
- // Sequence validation
- $this->ensureAuthenticationCallSuccessful();
- return $this->_getUser();
- }
- /**
- * This method returns the CAS user's login name.
- *
- * @return string the login name of the authenticated user
- *
- * @warning should be called only after CAS_Client::forceAuthentication() or
- * CAS_Client::isAuthenticated(), otherwise halt with an error.
- */
- private function _getUser()
- {
- // This is likely a duplicate check that could be removed....
- if ( empty($this->_user) ) {
- phpCAS::error(
- 'this method should be used only after '.__CLASS__
- .'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()'
- );
- }
- return $this->_user;
- }
- /**
- * The Authenticated users attributes. Written by
- * CAS_Client::setAttributes(), read by CAS_Client::getAttributes().
- * @attention client applications should use phpCAS::getAttributes().
- *
- * @hideinitializer
- */
- private $_attributes = array();
- /**
- * Set an array of attributes
- *
- * @param array $attributes a key value array of attributes
- *
- * @return void
- */
- public function setAttributes($attributes)
- {
- $this->_attributes = $attributes;
- }
- /**
- * Get an key values arry of attributes
- *
- * @return array of attributes
- */
- public function getAttributes()
- {
- // Sequence validation
- $this->ensureAuthenticationCallSuccessful();
- // This is likely a duplicate check that could be removed....
- if ( empty($this->_user) ) {
- // if no user is set, there shouldn't be any attributes also...
- phpCAS::error(
- 'this method should be used only after '.__CLASS__
- .'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()'
- );
- }
- return $this->_attributes;
- }
- /**
- * Check whether attributes are available
- *
- * @return bool attributes available
- */
- public function hasAttributes()
- {
- // Sequence validation
- $this->ensureAuthenticationCallSuccessful();
- return !empty($this->_attributes);
- }
- /**
- * Check whether a specific attribute with a name is available
- *
- * @param string $key name of attribute
- *
- * @return bool is attribute available
- */
- public function hasAttribute($key)
- {
- // Sequence validation
- $this->ensureAuthenticationCallSuccessful();
- return $this->_hasAttribute($key);
- }
- /**
- * Check whether a specific attribute with a name is available
- *
- * @param string $key name of attribute
- *
- * @return bool is attribute available
- */
- private function _hasAttribute($key)
- {
- return (is_array($this->_attributes)
- && array_key_exists($key, $this->_attributes));
- }
- /**
- * Get a specific attribute by name
- *
- * @param string $key name of attribute
- *
- * @return string attribute values
- */
- public function getAttribute($key)
- {
- // Sequence validation
- $this->ensureAuthenticationCallSuccessful();
- if ($this->_hasAttribute($key)) {
- return $this->_attributes[$key];
- }
- }
- /**
- * This method is called to renew the authentication of the user
- * If the user is authenticated, renew the connection
- * If not, redirect to CAS
- *
- * @return bool true when the user is authenticated; otherwise halt.
- */
- public function renewAuthentication()
- {
- phpCAS::traceBegin();
- // Either way, the user is authenticated by CAS
- $this->removeSessionValue('auth_checked');
- if ( $this->isAuthenticated(true) ) {
- phpCAS::trace('user already authenticated');
- $res = true;
- } else {
- $this->redirectToCas(false, true);
- // never reached
- $res = false;
- }
- phpCAS::traceEnd();
- return $res;
- }
- /**
- * This method is called to be sure that the user is authenticated. When not
- * authenticated, halt by redirecting to the CAS server; otherwise return true.
- *
- * @return bool true when the user is authenticated; otherwise halt.
- */
- public function forceAuthentication()
- {
- phpCAS::traceBegin();
- if ( $this->isAuthenticated() ) {
- // the user is authenticated, nothing to be done.
- phpCAS::trace('no need to authenticate');
- $res = true;
- } else {
- // the user is not authenticated, redirect to the CAS server
- $this->removeSessionValue('auth_checked');
- $this->redirectToCas(false/* no gateway */);
- // never reached
- $res = false;
- }
- phpCAS::traceEnd($res);
- return $res;
- }
- /**
- * An integer that gives the number of times authentication will be cached
- * before rechecked.
- *
- * @hideinitializer
- */
- private $_cache_times_for_auth_recheck = 0;
- /**
- * Set the number of times authentication will be cached before rechecked.
- *
- * @param int $n number of times to wait for a recheck
- *
- * @return void
- */
- public function setCacheTimesForAuthRecheck($n)
- {
- if (gettype($n) != 'integer')
- throw new CAS_TypeMismatchException($n, '$n', 'string');
- $this->_cache_times_for_auth_recheck = $n;
- }
- /**
- * This method is called to check whether the user is authenticated or not.
- *
- * @return bool true when the user is authenticated, false when a previous
- * gateway login failed or the function will not return if the user is
- * redirected to the cas server for a gateway login attempt
- */
- public function checkAuthentication()
- {
- phpCAS::traceBegin();
- $res = false; // default
- if ( $this->isAuthenticated() ) {
- phpCAS::trace('user is authenticated');
- /* The 'auth_checked' variable is removed just in case it's set. */
- $this->removeSessionValue('auth_checked');
- $res = true;
- } else if ($this->getSessionValue('auth_checked')) {
- // the previous request has redirected the client to the CAS server
- // with gateway=true
- $this->removeSessionValue('auth_checked');
- } else {
- // avoid a check against CAS on every request
- // we need to write this back to session later
- $unauth_count = $this->getSessionValue('unauth_count', -2);
- if (($unauth_count != -2
- && $this->_cache_times_for_auth_recheck == -1)
- || ($unauth_count >= 0
- && $unauth_count < $this->_cache_times_for_auth_recheck)
- ) {
- if ($this->_cache_times_for_auth_recheck != -1) {
- $unauth_count++;
- phpCAS::trace(
- 'user is not authenticated (cached for '
- .$unauth_count.' times of '
- .$this->_cache_times_for_auth_recheck.')'
- );
- } else {
- phpCAS::trace(
- 'user is not authenticated (cached for until login pressed)'
- );
- }
- $this->setSessionValue('unauth_count', $unauth_count);
- } else {
- $this->setSessionValue('unauth_count', 0);
- $this->setSessionValue('auth_checked', true);
- phpCAS::trace('user is not authenticated (cache reset)');
- $this->redirectToCas(true/* gateway */);
- // never reached
- }
- }
- phpCAS::traceEnd($res);
- return $res;
- }
- /**
- * This method is called to check if the user is authenticated (previously or by
- * tickets given in the URL).
- *
- * @param bool $renew true to force the authentication with the CAS server
- *
- * @return bool true when the user is authenticated. Also may redirect to the
- * same URL without the ticket.
- */
- public function isAuthenticated($renew=false)
- {
- phpCAS::traceBegin();
- $res = false;
- $validate_url = '';
- if ( $this->_wasPreviouslyAuthenticated() ) {
- if ($this->hasTicket()) {
- // User has a additional ticket but was already authenticated
- phpCAS::trace(
- 'ticket was present and will be discarded, use renewAuthenticate()'
- );
- if ($this->_clearTicketsFromUrl) {
- phpCAS::trace("Prepare redirect to : ".$this->getURL());
- session_write_close();
- header('Location: '.$this->getURL());
- flush();
- phpCAS::traceExit();
- throw new CAS_GracefullTerminationException();
- } else {
- phpCAS::trace(
- 'Already authenticated, but skipping ticket clearing since setNoClearTicketsFromUrl() was used.'
- );
- $res = true;
- }
- } else {
- // the user has already (previously during the session) been
- // authenticated, nothing to be done.
- phpCAS::trace(
- 'user was already authenticated, no need to look for tickets'
- );
- $res = true;
- }
- // Mark the auth-check as complete to allow post-authentication
- // callbacks to make use of phpCAS::getUser() and similar methods
- $this->markAuthenticationCall($res);
- } else {
- if ($this->hasTicket()) {
- switch ($this->getServerVersion()) {
- case CAS_VERSION_1_0:
- // if a Service Ticket was given, validate it
- phpCAS::trace(
- 'CAS 1.0 ticket `'.$this->getTicket().'\' is present'
- );
- $this->validateCAS10(
- $validate_url, $text_response, $tree_response, $renew
- ); // if it fails, it halts
- phpCAS::trace(
- 'CAS 1.0 ticket `'.$this->getTicket().'\' was validated'
- );
- $this->setSessionValue('user', $this->_getUser());
- $res = true;
- $logoutTicket = $this->getTicket();
- break;
- case CAS_VERSION_2_0:
- case CAS_VERSION_3_0:
- // if a Proxy Ticket was given, validate it
- phpCAS::trace(
- 'CAS '.$this->getServerVersion().' ticket `'.$this->getTicket().'\' is present'
- );
- $this->validateCAS20(
- $validate_url, $text_response, $tree_response, $renew
- ); // note: if it fails, it halts
- phpCAS::trace(
- 'CAS '.$this->getServerVersion().' ticket `'.$this->getTicket().'\' was validated'
- );
- if ( $this->isProxy() ) {
- $this->_validatePGT(
- $validate_url, $text_response, $tree_response
- ); // idem
- phpCAS::trace('PGT `'.$this->_getPGT().'\' was validated');
- $this->setSessionValue('pgt', $this->_getPGT());
- }
- $this->setSessionValue('user', $this->_getUser());
- if (!empty($this->_attributes)) {
- $this->setSessionValue('attributes', $this->_attributes);
- }
- $proxies = $this->getProxies();
- if (!empty($proxies)) {
- $this->setSessionValue('proxies', $this->getProxies());
- }
- $res = true;
- $logoutTicket = $this->getTicket();
- break;
- case SAML_VERSION_1_1:
- // if we have a SAML ticket, validate it.
- phpCAS::trace(
- 'SAML 1.1 ticket `'.$this->getTicket().'\' is present'
- );
- $this->validateSA(
- $validate_url, $text_response, $tree_response, $renew
- ); // if it fails, it halts
- phpCAS::trace(
- 'SAML 1.1 ticket `'.$this->getTicket().'\' was validated'
- );
- $this->setSessionValue('user', $this->_getUser());
- $this->setSessionValue('attributes', $this->_attributes);
- $res = true;
- $logoutTicket = $this->getTicket();
- break;
- default:
- phpCAS::trace('Protocoll error');
- break;
- }
- } else {
- // no ticket given, not authenticated
- phpCAS::trace('no ticket found');
- }
- // Mark the auth-check as complete to allow post-authentication
- // callbacks to make use of phpCAS::getUser() and similar methods
- $this->markAuthenticationCall($res);
- if ($res) {
- // call the post-authenticate callback if registered.
- if ($this->_postAuthenticateCallbackFunction) {
- $args = $this->_postAuthenticateCallbackArgs;
- array_unshift($args, $logoutTicket);
- call_user_func_array(
- $this->_postAuthenticateCallbackFunction, $args
- );
- }
- // 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) most of the checks and
- // errors should have been made now, so we're safe for redirect
- // without masking error messages. remove the ticket as a
- // security precaution to prevent a ticket in the HTTP_REFERRER
- if ($this->_clearTicketsFromUrl) {
- phpCAS::trace("Prepare redirect to : ".$this->getURL());
- session_write_close();
- header('Location: '.$this->getURL());
- flush();
- phpCAS::traceExit();
- throw new CAS_GracefullTerminationException();
- }
- }
- }
- phpCAS::traceEnd($res);
- return $res;
- }
- /**
- * This method tells if the current session is authenticated.
- *
- * @return bool true if authenticated based soley on $_SESSION variable
- */
- public function isSessionAuthenticated ()
- {
- return !!$this->getSessionValue('user');
- }
- /**
- * This method tells if the user has already been (previously) authenticated
- * by looking into the session variables.
- *
- * @note This function switches to callback mode when needed.
- *
- * @return bool true when the user has already been authenticated; false otherwise.
- */
- private function _wasPreviouslyAuthenticated()
- {
- phpCAS::traceBegin();
- if ( $this->_isCallbackMode() ) {
- // Rebroadcast the pgtIou and pgtId to all nodes
- if ($this->_rebroadcast&&!isset($_POST['rebroadcast'])) {
- $this->_rebroadcast(self::PGTIOU);
- }
- $this->_callback();
- }
- $auth = false;
- if ( $this->isProxy() ) {
- // CAS proxy: username and PGT must be present
- if ( $this->isSessionAuthenticated()
- && $this->getSessionValue('pgt')
- ) {
- // authentication already done
- $this->_setUser($this->getSessionValue('user'));
- if ($this->hasSessionValue('attributes')) {
- $this->setAttributes($this->getSessionValue('attributes'));
- }
- $this->_setPGT($this->getSessionValue('pgt'));
- phpCAS::trace(
- 'user = `'.$this->getSessionValue('user').'\', PGT = `'
- .$this->getSessionValue('pgt').'\''
- );
- // Include the list of proxies
- if ($this->hasSessionValue('proxies')) {
- $this->_setProxies($this->getSessionValue('proxies'));
- phpCAS::trace(
- 'proxies = "'
- .implode('", "', $this->getSessionValue('proxies')).'"'
- );
- }
- $auth = true;
- } elseif ( $this->isSessionAuthenticated()
- && !$this->getSessionValue('pgt')
- ) {
- // these two variables should be empty or not empty at the same time
- phpCAS::trace(
- 'username found (`'.$this->getSessionValue('user')
- .'\') but PGT is empty'
- );
- // unset all tickets to enforce authentication
- $this->clearSessionValues();
- $this->setTicket('');
- } elseif ( !$this->isSessionAuthenticated()
- && $this->getSessionValue('pgt')
- ) {
- // these two variables should be empty or not empty at the same time
- phpCAS::trace(
- 'PGT found (`'.$this->getSessionValue('pgt')
- .'\') but username is empty'
- );
- // unset all tickets to enforce authentication
- $this->clearSessionValues();
- $this->setTicket('');
- } else {
- phpCAS::trace('neither user nor PGT found');
- }
- } else {
- // `simple' CAS client (not a proxy): username must be present
- if ( $this->isSessionAuthenticated() ) {
- // authentication already done
- $this->_setUser($this->getSessionValue('user'));
- if ($this->hasSessionValue('attributes')) {
- $this->setAttributes($this->getSessionValue('attributes'));
- }
- phpCAS::trace('user = `'.$this->getSessionValue('user').'\'');
- // Include the list of proxies
- if ($this->hasSessionValue('proxies')) {
- $this->_setProxies($this->getSessionValue('proxies'));
- phpCAS::trace(
- 'proxies = "'
- .implode('", "', $this->getSessionValue('proxies')).'"'
- );
- }
- $auth = true;
- } else {
- phpCAS::trace('no user found');
- }
- }
- phpCAS::traceEnd($auth);
- return $auth;
- }
- /**
- * This method is used to redirect the client to the CAS server.
- * It is used by CAS_Client::forceAuthentication() and
- * CAS_Client::checkAuthentication().
- *
- * @param bool $gateway true to check authentication, false to force it
- * @param bool $renew true to force the authentication with the CAS server
- *
- * @return void
- */
- public function redirectToCas($gateway=false,$renew=false)
- {
- phpCAS::traceBegin();
- $cas_url = $this->getServerLoginURL($gateway, $renew);
- session_write_close();
- if (php_sapi_name() === 'cli') {
- @header('Location: '.$cas_url);
- } else {
- header('Location: '.$cas_url);
- }
- phpCAS::trace("Redirect to : ".$cas_url);
- $lang = $this->getLangObj();
- $this->printHTMLHeader($lang->getAuthenticationWanted());
- printf('<p>'. $lang->getShouldHaveBeenRedirected(). '</p>', $cas_url);
- $this->printHTMLFooter();
- phpCAS::traceExit();
- throw new CAS_GracefullTerminationException();
- }
- /**
- * This method is used to logout from CAS.
- *
- * @param array $params an array that contains the optional url and service
- * parameters that will be passed to the CAS server
- *
- * @return void
- */
- public function logout($params)
- {
- phpCAS::traceBegin();
- $cas_url = $this->getServerLogoutURL();
- $paramSeparator = '?';
- if (isset($params['url'])) {
- $cas_url = $cas_url . $paramSeparator . "url="
- . urlencode($params['url']);
- $paramSeparator = '&';
- }
- if (isset($params['service'])) {
- $cas_url = $cas_url . $paramSeparator . "service="
- . urlencode($params['service']);
- }
- header('Location: '.$cas_url);
- phpCAS::trace("Prepare redirect to : ".$cas_url);
- phpCAS::trace("Destroying session : ".session_id());
- session_unset();
- session_destroy();
- if (session_status() === PHP_SESSION_NONE) {
- phpCAS::trace("Session terminated");
- } else {
- phpCAS::error("Session was not terminated");
- phpCAS::trace("Session was not terminated");
- }
- $lang = $this->getLangObj();
- $this->printHTMLHeader($lang->getLogout());
- printf('<p>'.$lang->getShouldHaveBeenRedirected(). '</p>', $cas_url);
- $this->printHTMLFooter();
- phpCAS::traceExit();
- throw new CAS_GracefullTerminationException();
- }
- /**
- * Check of the current request is a logout request
- *
- * @return bool is logout request.
- */
- private function _isLogoutRequest()
- {
- return !empty($_POST['logoutRequest']);
- }
- /**
- * This method handles logout requests.
- *
- * @param bool $check_client true to check the client bofore handling
- * the request, false not to perform any access control. True by default.
- * @param array $allowed_clients an array of host names allowed to send
- * logout requests.
- *
- * @return void
- */
- public function handleLogoutRequests($check_client=true, $allowed_clients=array())
- {
- phpCAS::traceBegin();
- if (!$this->_isLogoutRequest()) {
- phpCAS::trace("Not a logout request");
- phpCAS::traceEnd();
- return;
- }
- if (!$this->getChangeSessionID()
- && is_null($this->_signoutCallbackFunction)
- ) {
- phpCAS::trace(
- "phpCAS can't handle logout requests if it is not allowed to change session_id."
- );
- }
- phpCAS::trace("Logout requested");
- $decoded_logout_rq = urldecode($_POST['logoutRequest']);
- phpCAS::trace("SAML REQUEST: ".$decoded_logout_rq);
- $allowed = false;
- if ($check_client) {
- if ($allowed_clients === array()) {
- $allowed_clients = array( $this->_getServerHostname() );
- }
- $client_ip = $_SERVER['REMOTE_ADDR'];
- $client = gethostbyaddr($client_ip);
- phpCAS::trace("Client: ".$client."/".$client_ip);
- foreach ($allowed_clients as $allowed_client) {
- if (($client == $allowed_client)
- || ($client_ip == $allowed_client)
- ) {
- phpCAS::trace(
- "Allowed client '".$allowed_client
- ."' matches, logout request is allowed"
- );
- $allowed = true;
- break;
- } else {
- phpCAS::trace(
- "Allowed client '".$allowed_client."' does not match"
- );
- }
- }
- } else {
- phpCAS::trace("No access control set");
- $allowed = true;
- }
- // If Logout command is permitted proceed with the logout
- if ($allowed) {
- phpCAS::trace("Logout command allowed");
- // Rebroadcast the logout request
- if ($this->_rebroadcast && !isset($_POST['rebroadcast'])) {
- $this->_rebroadcast(self::LOGOUT);
- }
- // Extract the ticket from the SAML Request
- preg_match(
- "|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|",
- $decoded_logout_rq, $tick, PREG_OFFSET_CAPTURE, 3
- );
- $wrappedSamlSessionIndex = preg_replace(
- '|<samlp:SessionIndex>|', '', $tick[0][0]
- );
- $ticket2logout = preg_replace(
- '|</samlp:SessionIndex>|', '', $wrappedSamlSessionIndex
- );
- phpCAS::trace("Ticket to logout: ".$ticket2logout);
- // call the post-authenticate callback if registered.
- if ($this->_signoutCallbackFunction) {
- $args = $this->_signoutCallbackArgs;
- array_unshift($args, $ticket2logout);
- call_user_func_array($this->_signoutCallbackFunction, $args);
- }
- // If phpCAS is managing the session_id, destroy session thanks to
- // session_id.
- if ($this->getChangeSessionID()) {
- $session_id = $this->_sessionIdForTicket($ticket2logout);
- phpCAS::trace("Session id: ".$session_id);
- // destroy a possible application session created before phpcas
- if (session_id() !== "") {
- session_unset();
- session_destroy();
- }
- // fix session ID
- session_id($session_id);
- $_COOKIE[session_name()]=$session_id;
- $_GET[session_name()]=$session_id;
- // Overwrite session
- session_start();
- session_unset();
- session_destroy();
- phpCAS::trace("Session ". $session_id . " destroyed");
- }
- } else {
- phpCAS::error("Unauthorized logout request from client '".$client."'");
- phpCAS::trace("Unauthorized logout request from client '".$client."'");
- }
- flush();
- phpCAS::traceExit();
- throw new CAS_GracefullTerminationException();
- }
- /** @} */
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // XX XX
- // XX BASIC CLIENT FEATURES (CAS 1.0) XX
- // XX XX
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // ########################################################################
- // ST
- // ########################################################################
- /**
- * @addtogroup internalBasic
- * @{
- */
- /**
- * The Ticket provided in the URL of the request if present
- * (empty otherwise). Written by CAS_Client::CAS_Client(), read by
- * CAS_Client::getTicket() and CAS_Client::_hasPGT().
- *
- * @hideinitializer
- */
- private $_ticket = '';
- /**
- * This method returns the Service Ticket provided in the URL of the request.
- *
- * @return string service ticket.
- */
- public function getTicket()
- {
- return $this->_ticket;
- }
- /**
- * This method stores the Service Ticket.
- *
- * @param string $st The Service Ticket.
- *
- * @return void
- */
- public function setTicket($st)
- {
- $this->_ticket = $st;
- }
- /**
- * This method tells if a Service Ticket was stored.
- *
- * @return bool if a Service Ticket has been stored.
- */
- public function hasTicket()
- {
- return !empty($this->_ticket);
- }
- /** @} */
- // ########################################################################
- // ST VALIDATION
- // ########################################################################
- /**
- * @addtogroup internalBasic
- * @{
- */
- /**
- * @var string the certificate of the CAS server CA.
- *
- * @hideinitializer
- */
- private $_cas_server_ca_cert = null;
- /**
- * validate CN of the CAS server certificate
- *
- * @hideinitializer
- */
- private $_cas_server_cn_validate = true;
- /**
- * Set to true not to validate the CAS server.
- *
- * @hideinitializer
- */
- private $_no_cas_server_validation = false;
- /**
- * Set the CA certificate of the CAS server.
- *
- * @param string $cert the PEM certificate file name of the CA that emited
- * the cert of the server
- * @param bool $validate_cn valiate CN of the CAS server certificate
- *
- * @return void
- */
- public function setCasServerCACert($cert, $validate_cn)
- {
- // Argument validation
- if (gettype($cert) != 'string') {
- throw new CAS_TypeMismatchException($cert, '$cert', 'string');
- }
- if (gettype($validate_cn) != 'boolean') {
- throw new CAS_TypeMismatchException($validate_cn, '$validate_cn', 'boolean');
- }
- if (!file_exists($cert)) {
- throw new CAS_InvalidArgumentException("Certificate file does not exist " . $this->_requestImplementation);
- }
- $this->_cas_server_ca_cert = $cert;
- $this->_cas_server_cn_validate = $validate_cn;
- }
- /**
- * Set no SSL validation for the CAS server.
- *
- * @return void
- */
- public function setNoCasServerValidation()
- {
- $this->_no_cas_server_validation = true;
- }
- /**
- * This method is used to validate a CAS 1,0 ticket; halt on failure, and
- * sets $validate_url, $text_reponse and $tree_response on success.
- *
- * @param string &$validate_url reference to the the URL of the request to
- * the CAS server.
- * @param string &$text_response reference to the response of the CAS
- * server, as is (XML text).
- * @param string &$tree_response reference to the response of the CAS
- * server, as a DOM XML tree.
- * @param bool $renew true to force the authentication with the CAS server
- *
- * @return bool true when successfull and issue a CAS_AuthenticationException
- * and false on an error
- * @throws CAS_AuthenticationException
- */
- public function validateCAS10(&$validate_url,&$text_response,&$tree_response,$renew=false)
- {
- phpCAS::traceBegin();
- // build the URL to validate the ticket
- $validate_url = $this->getServerServiceValidateURL()
- .'&ticket='.urlencode($this->getTicket());
- if ( $renew ) {
- // pass the renew
- $validate_url .= '&renew=true';
- }
- // open and read the URL
- if ( !$this->_readURL($validate_url, $headers, $text_response, $err_msg) ) {
- phpCAS::trace(
- 'could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')'
- );
- throw new CAS_AuthenticationException(
- $this, 'CAS 1.0 ticket not validated', $validate_url,
- true/*$no_response*/
- );
- }
- if (preg_match('/^no\n/', $text_response)) {
- phpCAS::trace('Ticket has not been validated');
- throw new CAS_AuthenticationException(
- $this, 'ST not validated', $validate_url, false/*$no_response*/,
- false/*$bad_response*/, $text_response
- );
- } else if (!preg_match('/^yes\n/', $text_response)) {
- phpCAS::trace('ill-formed response');
- throw new CAS_AuthenticationException(
- $this, 'Ticket not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/, $text_response
- );
- }
- // ticket has been validated, extract the user name
- $arr = preg_split('/\n/', $text_response);
- $this->_setUser(trim($arr[1]));
- $this->_renameSession($this->getTicket());
- // at this step, ticket has been validated and $this->_user has been set,
- phpCAS::traceEnd(true);
- return true;
- }
- /** @} */
- // ########################################################################
- // SAML VALIDATION
- // ########################################################################
- /**
- * @addtogroup internalSAML
- * @{
- */
- /**
- * This method is used to validate a SAML TICKET; halt on failure, and sets
- * $validate_url, $text_reponse and $tree_response on success. These
- * parameters are used later by CAS_Client::_validatePGT() for CAS proxies.
- *
- * @param string &$validate_url reference to the the URL of the request to
- * the CAS server.
- * @param string &$text_response reference to the response of the CAS
- * server, as is (XML text).
- * @param string &$tree_response reference to the response of the CAS
- * server, as a DOM XML tree.
- * @param bool $renew true to force the authentication with the CAS server
- *
- * @return bool true when successfull and issue a CAS_AuthenticationException
- * and false on an error
- *
- * @throws CAS_AuthenticationException
- */
- public function validateSA(&$validate_url,&$text_response,&$tree_response,$renew=false)
- {
- phpCAS::traceBegin();
- $result = false;
- // build the URL to validate the ticket
- $validate_url = $this->getServerSamlValidateURL();
- if ( $renew ) {
- // pass the renew
- $validate_url .= '&renew=true';
- }
- // open and read the URL
- if ( !$this->_readURL($validate_url, $headers, $text_response, $err_msg) ) {
- phpCAS::trace(
- 'could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')'
- );
- throw new CAS_AuthenticationException(
- $this, 'SA not validated', $validate_url, true/*$no_response*/
- );
- }
- phpCAS::trace('server version: '.$this->getServerVersion());
- // analyze the result depending on the version
- switch ($this->getServerVersion()) {
- case SAML_VERSION_1_1:
- // create new DOMDocument Object
- $dom = new DOMDocument();
- // Fix possible whitspace problems
- $dom->preserveWhiteSpace = false;
- // read the response of the CAS server into a DOM object
- if (!($dom->loadXML($text_response))) {
- phpCAS::trace('dom->loadXML() failed');
- throw new CAS_AuthenticationException(
- $this, 'SA not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/,
- $text_response
- );
- }
- // read the root node of the XML tree
- if (!($tree_response = $dom->documentElement)) {
- phpCAS::trace('documentElement() failed');
- throw new CAS_AuthenticationException(
- $this, 'SA not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/,
- $text_response
- );
- } else if ( $tree_response->localName != 'Envelope' ) {
- // insure that tag name is 'Envelope'
- phpCAS::trace(
- 'bad XML root node (should be `Envelope\' instead of `'
- .$tree_response->localName.'\''
- );
- throw new CAS_AuthenticationException(
- $this, 'SA not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/,
- $text_response
- );
- } else if ($tree_response->getElementsByTagName("NameIdentifier")->length != 0) {
- // check for the NameIdentifier tag in the SAML response
- $success_elements = $tree_response->getElementsByTagName("NameIdentifier");
- phpCAS::trace('NameIdentifier found');
- $user = trim($success_elements->item(0)->nodeValue);
- phpCAS::trace('user = `'.$user.'`');
- $this->_setUser($user);
- $this->_setSessionAttributes($text_response);
- $result = true;
- } else {
- phpCAS::trace('no <NameIdentifier> tag found in SAML payload');
- throw new CAS_AuthenticationException(
- $this, 'SA not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/,
- $text_response
- );
- }
- }
- if ($result) {
- $this->_renameSession($this->getTicket());
- }
- // at this step, ST has been validated and $this->_user has been set,
- phpCAS::traceEnd($result);
- return $result;
- }
- /**
- * This method will parse the DOM and pull out the attributes from the SAML
- * payload and put them into an array, then put the array into the session.
- *
- * @param string $text_response the SAML payload.
- *
- * @return bool true when successfull and false if no attributes a found
- */
- private function _setSessionAttributes($text_response)
- {
- phpCAS::traceBegin();
- $result = false;
- $attr_array = array();
- // create new DOMDocument Object
- $dom = new DOMDocument();
- // Fix possible whitspace problems
- $dom->preserveWhiteSpace = false;
- if (($dom->loadXML($text_response))) {
- $xPath = new DOMXPath($dom);
- $xPath->registerNamespace('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
- $xPath->registerNamespace('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
- $nodelist = $xPath->query("//saml:Attribute");
- if ($nodelist) {
- foreach ($nodelist as $node) {
- $xres = $xPath->query("saml:AttributeValue", $node);
- $name = $node->getAttribute("AttributeName");
- $value_array = array();
- foreach ($xres as $node2) {
- $value_array[] = $node2->nodeValue;
- }
- $attr_array[$name] = $value_array;
- }
- // UGent addition...
- foreach ($attr_array as $attr_key => $attr_value) {
- if (count($attr_value) > 1) {
- $this->_attributes[$attr_key] = $attr_value;
- phpCAS::trace("* " . $attr_key . "=" . print_r($attr_value, true));
- } else {
- $this->_attributes[$attr_key] = $attr_value[0];
- phpCAS::trace("* " . $attr_key . "=" . $attr_value[0]);
- }
- }
- $result = true;
- } else {
- phpCAS::trace("SAML Attributes are empty");
- $result = false;
- }
- }
- phpCAS::traceEnd($result);
- return $result;
- }
- /** @} */
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // XX XX
- // XX PROXY FEATURES (CAS 2.0) XX
- // XX XX
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // ########################################################################
- // PROXYING
- // ########################################################################
- /**
- * @addtogroup internalProxy
- * @{
- */
- /**
- * @var bool is the client a proxy
- * A boolean telling if the client is a CAS proxy or not. Written by
- * CAS_Client::CAS_Client(), read by CAS_Client::isProxy().
- */
- private $_proxy;
- /**
- * @var CAS_CookieJar Handler for managing service cookies.
- */
- private $_serviceCookieJar;
- /**
- * Tells if a CAS client is a CAS proxy or not
- *
- * @return bool true when the CAS client is a CAS proxy, false otherwise
- */
- public function isProxy()
- {
- return $this->_proxy;
- }
- /** @} */
- // ########################################################################
- // PGT
- // ########################################################################
- /**
- * @addtogroup internalProxy
- * @{
- */
- /**
- * the Proxy Grnting Ticket given by the CAS server (empty otherwise).
- * Written by CAS_Client::_setPGT(), read by CAS_Client::_getPGT() and
- * CAS_Client::_hasPGT().
- *
- * @hideinitializer
- */
- private $_pgt = '';
- /**
- * This method returns the Proxy Granting Ticket given by the CAS server.
- *
- * @return string the Proxy Granting Ticket.
- */
- private function _getPGT()
- {
- return $this->_pgt;
- }
- /**
- * This method stores the Proxy Granting Ticket.
- *
- * @param string $pgt The Proxy Granting Ticket.
- *
- * @return void
- */
- private function _setPGT($pgt)
- {
- $this->_pgt = $pgt;
- }
- /**
- * This method tells if a Proxy Granting Ticket was stored.
- *
- * @return bool true if a Proxy Granting Ticket has been stored.
- */
- private function _hasPGT()
- {
- return !empty($this->_pgt);
- }
- /** @} */
- // ########################################################################
- // CALLBACK MODE
- // ########################################################################
- /**
- * @addtogroup internalCallback
- * @{
- */
- /**
- * each PHP script using phpCAS in proxy mode is its own callback to get the
- * PGT back from the CAS server. callback_mode is detected by the constructor
- * thanks to the GET parameters.
- */
- /**
- * @var bool a boolean to know if the CAS client is running in callback mode. Written by
- * CAS_Client::setCallBackMode(), read by CAS_Client::_isCallbackMode().
- *
- * @hideinitializer
- */
- private $_callback_mode = false;
- /**
- * This method sets/unsets callback mode.
- *
- * @param bool $callback_mode true to set callback mode, false otherwise.
- *
- * @return void
- */
- private function _setCallbackMode($callback_mode)
- {
- $this->_callback_mode = $callback_mode;
- }
- /**
- * This method returns true when the CAS client is running in callback mode,
- * false otherwise.
- *
- * @return bool A boolean.
- */
- private function _isCallbackMode()
- {
- return $this->_callback_mode;
- }
- /**
- * @var bool a boolean to know if the CAS client is using POST parameters when in callback mode.
- * Written by CAS_Client::_setCallbackModeUsingPost(), read by CAS_Client::_isCallbackModeUsingPost().
- *
- * @hideinitializer
- */
- private $_callback_mode_using_post = false;
- /**
- * This method sets/unsets usage of POST parameters in callback mode (default/false is GET parameters)
- *
- * @param bool $callback_mode_using_post true to use POST, false to use GET (default).
- *
- * @return void
- */
- private function _setCallbackModeUsingPost($callback_mode_using_post)
- {
- $this->_callback_mode_using_post = $callback_mode_using_post;
- }
- /**
- * This method returns true when the callback mode is using POST, false otherwise.
- *
- * @return bool A boolean.
- */
- private function _isCallbackModeUsingPost()
- {
- return $this->_callback_mode_using_post;
- }
- /**
- * the URL that should be used for the PGT callback (in fact the URL of the
- * current request without any CGI parameter). Written and read by
- * CAS_Client::_getCallbackURL().
- *
- * @hideinitializer
- */
- private $_callback_url = '';
- /**
- * This method returns the URL that should be used for the PGT callback (in
- * fact the URL of the current request without any CGI parameter, except if
- * phpCAS::setFixedCallbackURL() was used).
- *
- * @return string The callback URL
- */
- private function _getCallbackURL()
- {
- // the URL is built when needed only
- if ( empty($this->_callback_url) ) {
- // remove the ticket if present in the URL
- $final_uri = 'https://';
- $final_uri .= $this->_getClientUrl();
- $request_uri = $_SERVER['REQUEST_URI'];
- $request_uri = preg_replace('/\?.*$/', '', $request_uri);
- $final_uri .= $request_uri;
- $this->_callback_url = $final_uri;
- }
- return $this->_callback_url;
- }
- /**
- * This method sets the callback url.
- *
- * @param string $url url to set callback
- *
- * @return string the callback url
- */
- public function setCallbackURL($url)
- {
- // Sequence validation
- $this->ensureIsProxy();
- // Argument Validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- return $this->_callback_url = $url;
- }
- /**
- * This method is called by CAS_Client::CAS_Client() when running in callback
- * mode. It stores the PGT and its PGT Iou, prints its output and halts.
- *
- * @return void
- */
- private function _callback()
- {
- phpCAS::traceBegin();
- if ($this->_isCallbackModeUsingPost()) {
- $pgtId = $_POST['pgtId'];
- $pgtIou = $_POST['pgtIou'];
- } else {
- $pgtId = $_GET['pgtId'];
- $pgtIou = $_GET['pgtIou'];
- }
- if (preg_match('/^PGTIOU-[\.\-\w]+$/', $pgtIou)) {
- if (preg_match('/^[PT]GT-[\.\-\w]+$/', $pgtId)) {
- phpCAS::trace('Storing PGT `'.$pgtId.'\' (id=`'.$pgtIou.'\')');
- $this->_storePGT($pgtId, $pgtIou);
- if ($this->isXmlResponse()) {
- echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n";
- echo '<proxySuccess xmlns="http://www.yale.edu/tp/cas" />';
- phpCAS::traceExit("XML response sent");
- } else {
- $this->printHTMLHeader('phpCAS callback');
- echo '<p>Storing PGT `'.$pgtId.'\' (id=`'.$pgtIou.'\').</p>';
- $this->printHTMLFooter();
- phpCAS::traceExit("HTML response sent");
- }
- phpCAS::traceExit("Successfull Callback");
- } else {
- phpCAS::error('PGT format invalid' . $pgtId);
- phpCAS::traceExit('PGT format invalid' . $pgtId);
- }
- } else {
- phpCAS::error('PGTiou format invalid' . $pgtIou);
- phpCAS::traceExit('PGTiou format invalid' . $pgtIou);
- }
- // Flush the buffer to prevent from sending anything other then a 200
- // Success Status back to the CAS Server. The Exception would normally
- // report as a 500 error.
- flush();
- throw new CAS_GracefullTerminationException();
- }
- /**
- * Check if application/xml or text/xml is pressent in HTTP_ACCEPT header values
- * when return value is complex and contains attached q parameters.
- * Example: HTTP_ACCEPT = text/html,application/xhtml+xml,application/xml;q=0.9
- * @return bool
- */
- private function isXmlResponse()
- {
- if (!array_key_exists('HTTP_ACCEPT', $_SERVER)) {
- return false;
- }
- if (strpos($_SERVER['HTTP_ACCEPT'], 'application/xml') === false && strpos($_SERVER['HTTP_ACCEPT'], 'text/xml') === false) {
- return false;
- }
- return true;
- }
- /** @} */
- // ########################################################################
- // PGT STORAGE
- // ########################################################################
- /**
- * @addtogroup internalPGTStorage
- * @{
- */
- /**
- * @var CAS_PGTStorage_AbstractStorage
- * an instance of a class inheriting of PGTStorage, used to deal with PGT
- * storage. Created by CAS_Client::setPGTStorageFile(), used
- * by CAS_Client::setPGTStorageFile() and CAS_Client::_initPGTStorage().
- *
- * @hideinitializer
- */
- private $_pgt_storage = null;
- /**
- * This method is used to initialize the storage of PGT's.
- * Halts on error.
- *
- * @return void
- */
- private function _initPGTStorage()
- {
- // if no SetPGTStorageXxx() has been used, default to file
- if ( !is_object($this->_pgt_storage) ) {
- $this->setPGTStorageFile();
- }
- // initializes the storage
- $this->_pgt_storage->init();
- }
- /**
- * This method stores a PGT. Halts on error.
- *
- * @param string $pgt the PGT to store
- * @param string $pgt_iou its corresponding Iou
- *
- * @return void
- */
- private function _storePGT($pgt,$pgt_iou)
- {
- // ensure that storage is initialized
- $this->_initPGTStorage();
- // writes the PGT
- $this->_pgt_storage->write($pgt, $pgt_iou);
- }
- /**
- * This method reads a PGT from its Iou and deletes the corresponding
- * storage entry.
- *
- * @param string $pgt_iou the PGT Iou
- *
- * @return string mul The PGT corresponding to the Iou, false when not found.
- */
- private function _loadPGT($pgt_iou)
- {
- // ensure that storage is initialized
- $this->_initPGTStorage();
- // read the PGT
- return $this->_pgt_storage->read($pgt_iou);
- }
- /**
- * This method can be used to set a custom PGT storage object.
- *
- * @param CAS_PGTStorage_AbstractStorage $storage a PGT storage object that
- * inherits from the CAS_PGTStorage_AbstractStorage class
- *
- * @return void
- */
- public function setPGTStorage($storage)
- {
- // Sequence validation
- $this->ensureIsProxy();
- // check that the storage has not already been set
- if ( is_object($this->_pgt_storage) ) {
- phpCAS::error('PGT storage already defined');
- }
- // check to make sure a valid storage object was specified
- if ( !($storage instanceof CAS_PGTStorage_AbstractStorage) )
- throw new CAS_TypeMismatchException($storage, '$storage', 'CAS_PGTStorage_AbstractStorage object');
- // store the PGTStorage object
- $this->_pgt_storage = $storage;
- }
- /**
- * This method is used to tell phpCAS to store the response of the
- * CAS server to PGT requests in a database.
- *
- * @param string|PDO $dsn_or_pdo a dsn string to use for creating a PDO
- * object or a PDO object
- * @param string $username the username to use when connecting to the
- * database
- * @param string $password the password to use when connecting to the
- * database
- * @param string $table the table to use for storing and retrieving
- * PGTs
- * @param string $driver_options any driver options to use when connecting
- * to the database
- *
- * @return void
- */
- public function setPGTStorageDb(
- $dsn_or_pdo, $username='', $password='', $table='', $driver_options=null
- ) {
- // Sequence validation
- $this->ensureIsProxy();
- // Argument validation
- if (!(is_object($dsn_or_pdo) && $dsn_or_pdo instanceof PDO) && !is_string($dsn_or_pdo))
- throw new CAS_TypeMismatchException($dsn_or_pdo, '$dsn_or_pdo', 'string or PDO object');
- if (gettype($username) != 'string')
- throw new CAS_TypeMismatchException($username, '$username', 'string');
- if (gettype($password) != 'string')
- throw new CAS_TypeMismatchException($password, '$password', 'string');
- if (gettype($table) != 'string')
- throw new CAS_TypeMismatchException($table, '$password', 'string');
- // create the storage object
- $this->setPGTStorage(
- new CAS_PGTStorage_Db(
- $this, $dsn_or_pdo, $username, $password, $table, $driver_options
- )
- );
- }
- /**
- * This method is used to tell phpCAS to store the response of the
- * CAS server to PGT requests onto the filesystem.
- *
- * @param string $path the path where the PGT's should be stored
- *
- * @return void
- */
- public function setPGTStorageFile($path='')
- {
- // Sequence validation
- $this->ensureIsProxy();
- // Argument validation
- if (gettype($path) != 'string')
- throw new CAS_TypeMismatchException($path, '$path', 'string');
- // create the storage object
- $this->setPGTStorage(new CAS_PGTStorage_File($this, $path));
- }
- // ########################################################################
- // PGT VALIDATION
- // ########################################################################
- /**
- * This method is used to validate a PGT; halt on failure.
- *
- * @param string &$validate_url the URL of the request to the CAS server.
- * @param string $text_response the response of the CAS server, as is
- * (XML text); result of
- * CAS_Client::validateCAS10() or
- * CAS_Client::validateCAS20().
- * @param DOMElement $tree_response the response of the CAS server, as a DOM XML
- * tree; result of CAS_Client::validateCAS10() or CAS_Client::validateCAS20().
- *
- * @return bool true when successfull and issue a CAS_AuthenticationException
- * and false on an error
- *
- * @throws CAS_AuthenticationException
- */
- private function _validatePGT(&$validate_url,$text_response,$tree_response)
- {
- phpCAS::traceBegin();
- if ( $tree_response->getElementsByTagName("proxyGrantingTicket")->length == 0) {
- phpCAS::trace('<proxyGrantingTicket> not found');
- // authentication succeded, but no PGT Iou was transmitted
- throw new CAS_AuthenticationException(
- $this, 'Ticket validated but no PGT Iou transmitted',
- $validate_url, false/*$no_response*/, false/*$bad_response*/,
- $text_response
- );
- } else {
- // PGT Iou transmitted, extract it
- $pgt_iou = trim(
- $tree_response->getElementsByTagName("proxyGrantingTicket")->item(0)->nodeValue
- );
- if (preg_match('/^PGTIOU-[\.\-\w]+$/', $pgt_iou)) {
- $pgt = $this->_loadPGT($pgt_iou);
- if ( $pgt == false ) {
- phpCAS::trace('could not load PGT');
- throw new CAS_AuthenticationException(
- $this,
- 'PGT Iou was transmitted but PGT could not be retrieved',
- $validate_url, false/*$no_response*/,
- false/*$bad_response*/, $text_response
- );
- }
- $this->_setPGT($pgt);
- } else {
- phpCAS::trace('PGTiou format error');
- throw new CAS_AuthenticationException(
- $this, 'PGT Iou was transmitted but has wrong format',
- $validate_url, false/*$no_response*/, false/*$bad_response*/,
- $text_response
- );
- }
- }
- phpCAS::traceEnd(true);
- return true;
- }
- // ########################################################################
- // PGT VALIDATION
- // ########################################################################
- /**
- * This method is used to retrieve PT's from the CAS server thanks to a PGT.
- *
- * @param string $target_service the service to ask for with the PT.
- * @param int &$err_code an error code (PHPCAS_SERVICE_OK on success).
- * @param string &$err_msg an error message (empty on success).
- *
- * @return string|false a Proxy Ticket, or false on error.
- */
- public function retrievePT($target_service,&$err_code,&$err_msg)
- {
- // Argument validation
- if (gettype($target_service) != 'string')
- throw new CAS_TypeMismatchException($target_service, '$target_service', 'string');
- phpCAS::traceBegin();
- // by default, $err_msg is set empty and $pt to true. On error, $pt is
- // set to false and $err_msg to an error message. At the end, if $pt is false
- // and $error_msg is still empty, it is set to 'invalid response' (the most
- // commonly encountered error).
- $err_msg = '';
- // build the URL to retrieve the PT
- $cas_url = $this->getServerProxyURL().'?targetService='
- .urlencode($target_service).'&pgt='.$this->_getPGT();
- // open and read the URL
- if ( !$this->_readURL($cas_url, $headers, $cas_response, $err_msg) ) {
- phpCAS::trace(
- 'could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')'
- );
- $err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
- $err_msg = 'could not retrieve PT (no response from the CAS server)';
- phpCAS::traceEnd(false);
- return false;
- }
- $bad_response = false;
- // create new DOMDocument object
- $dom = new DOMDocument();
- // Fix possible whitspace problems
- $dom->preserveWhiteSpace = false;
- // read the response of the CAS server into a DOM object
- if ( !($dom->loadXML($cas_response))) {
- phpCAS::trace('dom->loadXML() failed');
- // read failed
- $bad_response = true;
- }
- if ( !$bad_response ) {
- // read the root node of the XML tree
- if ( !($root = $dom->documentElement) ) {
- phpCAS::trace('documentElement failed');
- // read failed
- $bad_response = true;
- }
- }
- if ( !$bad_response ) {
- // insure that tag name is 'serviceResponse'
- if ( $root->localName != 'serviceResponse' ) {
- phpCAS::trace('localName failed');
- // bad root node
- $bad_response = true;
- }
- }
- if ( !$bad_response ) {
- // look for a proxySuccess tag
- if ( $root->getElementsByTagName("proxySuccess")->length != 0) {
- $proxy_success_list = $root->getElementsByTagName("proxySuccess");
- // authentication succeded, look for a proxyTicket tag
- if ( $proxy_success_list->item(0)->getElementsByTagName("proxyTicket")->length != 0) {
- $err_code = PHPCAS_SERVICE_OK;
- $err_msg = '';
- $pt = trim(
- $proxy_success_list->item(0)->getElementsByTagName("proxyTicket")->item(0)->nodeValue
- );
- phpCAS::trace('original PT: '.trim($pt));
- phpCAS::traceEnd($pt);
- return $pt;
- } else {
- phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
- }
- } else if ($root->getElementsByTagName("proxyFailure")->length != 0) {
- // look for a proxyFailure tag
- $proxy_failure_list = $root->getElementsByTagName("proxyFailure");
- // authentication failed, extract the error
- $err_code = PHPCAS_SERVICE_PT_FAILURE;
- $err_msg = 'PT retrieving failed (code=`'
- .$proxy_failure_list->item(0)->getAttribute('code')
- .'\', message=`'
- .trim($proxy_failure_list->item(0)->nodeValue)
- .'\')';
- phpCAS::traceEnd(false);
- return false;
- } else {
- phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
- }
- }
- // at this step, we are sure that the response of the CAS server was
- // illformed
- $err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
- $err_msg = 'Invalid response from the CAS server (response=`'
- .$cas_response.'\')';
- phpCAS::traceEnd(false);
- return false;
- }
- /** @} */
- // ########################################################################
- // READ CAS SERVER ANSWERS
- // ########################################################################
- /**
- * @addtogroup internalMisc
- * @{
- */
- /**
- * This method is used to acces a remote URL.
- *
- * @param string $url the URL to access.
- * @param string &$headers an array containing the HTTP header lines of the
- * response (an empty array on failure).
- * @param string &$body the body of the response, as a string (empty on
- * failure).
- * @param string &$err_msg an error message, filled on failure.
- *
- * @return bool true on success, false otherwise (in this later case, $err_msg
- * contains an error message).
- */
- private function _readURL($url, &$headers, &$body, &$err_msg)
- {
- phpCAS::traceBegin();
- $className = $this->_requestImplementation;
- $request = new $className();
- if (count($this->_curl_options)) {
- $request->setCurlOptions($this->_curl_options);
- }
- $request->setUrl($url);
- if (empty($this->_cas_server_ca_cert) && !$this->_no_cas_server_validation) {
- phpCAS::error(
- 'one of the methods phpCAS::setCasServerCACert() or phpCAS::setNoCasServerValidation() must be called.'
- );
- }
- if ($this->_cas_server_ca_cert != '') {
- $request->setSslCaCert(
- $this->_cas_server_ca_cert, $this->_cas_server_cn_validate
- );
- }
- // add extra stuff if SAML
- if ($this->getServerVersion() == SAML_VERSION_1_1) {
- $request->addHeader("soapaction: http://www.oasis-open.org/committees/security");
- $request->addHeader("cache-control: no-cache");
- $request->addHeader("pragma: no-cache");
- $request->addHeader("accept: text/xml");
- $request->addHeader("connection: keep-alive");
- $request->addHeader("content-type: text/xml");
- $request->makePost();
- $request->setPostBody($this->_buildSAMLPayload());
- }
- if ($request->send()) {
- $headers = $request->getResponseHeaders();
- $body = $request->getResponseBody();
- $err_msg = '';
- phpCAS::traceEnd(true);
- return true;
- } else {
- $headers = '';
- $body = '';
- $err_msg = $request->getErrorMessage();
- phpCAS::traceEnd(false);
- return false;
- }
- }
- /**
- * This method is used to build the SAML POST body sent to /samlValidate URL.
- *
- * @return string the SOAP-encased SAMLP artifact (the ticket).
- */
- private function _buildSAMLPayload()
- {
- phpCAS::traceBegin();
- //get the ticket
- $sa = urlencode($this->getTicket());
- $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;
- phpCAS::traceEnd($body);
- return ($body);
- }
- /** @} **/
- // ########################################################################
- // ACCESS TO EXTERNAL SERVICES
- // ########################################################################
- /**
- * @addtogroup internalProxyServices
- * @{
- */
- /**
- * Answer a proxy-authenticated service handler.
- *
- * @param string $type The service type. One of:
- * PHPCAS_PROXIED_SERVICE_HTTP_GET, PHPCAS_PROXIED_SERVICE_HTTP_POST,
- * PHPCAS_PROXIED_SERVICE_IMAP
- *
- * @return CAS_ProxiedService
- * @throws InvalidArgumentException If the service type is unknown.
- */
- public function getProxiedService ($type)
- {
- // Sequence validation
- $this->ensureIsProxy();
- $this->ensureAuthenticationCallSuccessful();
- // Argument validation
- if (gettype($type) != 'string')
- throw new CAS_TypeMismatchException($type, '$type', 'string');
- switch ($type) {
- case PHPCAS_PROXIED_SERVICE_HTTP_GET:
- case PHPCAS_PROXIED_SERVICE_HTTP_POST:
- $requestClass = $this->_requestImplementation;
- $request = new $requestClass();
- if (count($this->_curl_options)) {
- $request->setCurlOptions($this->_curl_options);
- }
- $proxiedService = new $type($request, $this->_serviceCookieJar);
- if ($proxiedService instanceof CAS_ProxiedService_Testable) {
- $proxiedService->setCasClient($this);
- }
- return $proxiedService;
- case PHPCAS_PROXIED_SERVICE_IMAP;
- $proxiedService = new CAS_ProxiedService_Imap($this->_getUser());
- if ($proxiedService instanceof CAS_ProxiedService_Testable) {
- $proxiedService->setCasClient($this);
- }
- return $proxiedService;
- default:
- throw new CAS_InvalidArgumentException(
- "Unknown proxied-service type, $type."
- );
- }
- }
- /**
- * Initialize a proxied-service handler with the proxy-ticket it should use.
- *
- * @param CAS_ProxiedService $proxiedService service handler
- *
- * @return void
- *
- * @throws CAS_ProxyTicketException If there is a proxy-ticket failure.
- * The code of the Exception will be one of:
- * PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE
- * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE
- * PHPCAS_SERVICE_PT_FAILURE
- * @throws CAS_ProxiedService_Exception If there is a failure getting the
- * url from the proxied service.
- */
- public function initializeProxiedService (CAS_ProxiedService $proxiedService)
- {
- // Sequence validation
- $this->ensureIsProxy();
- $this->ensureAuthenticationCallSuccessful();
- $url = $proxiedService->getServiceUrl();
- if (!is_string($url)) {
- throw new CAS_ProxiedService_Exception(
- "Proxied Service ".get_class($proxiedService)
- ."->getServiceUrl() should have returned a string, returned a "
- .gettype($url)." instead."
- );
- }
- $pt = $this->retrievePT($url, $err_code, $err_msg);
- if (!$pt) {
- throw new CAS_ProxyTicketException($err_msg, $err_code);
- }
- $proxiedService->setProxyTicket($pt);
- }
- /**
- * This method is used to access an HTTP[S] service.
- *
- * @param string $url the service to access.
- * @param int &$err_code an error code Possible values are
- * PHPCAS_SERVICE_OK (on success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE,
- * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE, PHPCAS_SERVICE_PT_FAILURE,
- * PHPCAS_SERVICE_NOT_AVAILABLE.
- * @param string &$output the output of the service (also used to give an error
- * message on failure).
- *
- * @return bool true on success, false otherwise (in this later case, $err_code
- * gives the reason why it failed and $output contains an error message).
- */
- public function serviceWeb($url,&$err_code,&$output)
- {
- // Sequence validation
- $this->ensureIsProxy();
- $this->ensureAuthenticationCallSuccessful();
- // Argument validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- try {
- $service = $this->getProxiedService(PHPCAS_PROXIED_SERVICE_HTTP_GET);
- $service->setUrl($url);
- $service->send();
- $output = $service->getResponseBody();
- $err_code = PHPCAS_SERVICE_OK;
- return true;
- } catch (CAS_ProxyTicketException $e) {
- $err_code = $e->getCode();
- $output = $e->getMessage();
- return false;
- } catch (CAS_ProxiedService_Exception $e) {
- $lang = $this->getLangObj();
- $output = sprintf(
- $lang->getServiceUnavailable(), $url, $e->getMessage()
- );
- $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
- return false;
- }
- }
- /**
- * This method is used to access an IMAP/POP3/NNTP service.
- *
- * @param string $url a string giving the URL of the service, including
- * the mailing box for IMAP URLs, as accepted by imap_open().
- * @param string $serviceUrl a string giving for CAS retrieve Proxy ticket
- * @param string $flags options given to imap_open().
- * @param int &$err_code an error code Possible values are
- * PHPCAS_SERVICE_OK (on success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE,
- * PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE, PHPCAS_SERVICE_PT_FAILURE,
- * PHPCAS_SERVICE_NOT_AVAILABLE.
- * @param string &$err_msg an error message on failure
- * @param string &$pt the Proxy Ticket (PT) retrieved from the CAS
- * server to access the URL on success, false on error).
- *
- * @return object|false an IMAP stream on success, false otherwise (in this later
- * case, $err_code gives the reason why it failed and $err_msg contains an
- * error message).
- */
- public function serviceMail($url,$serviceUrl,$flags,&$err_code,&$err_msg,&$pt)
- {
- // Sequence validation
- $this->ensureIsProxy();
- $this->ensureAuthenticationCallSuccessful();
- // Argument validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- if (gettype($serviceUrl) != 'string')
- throw new CAS_TypeMismatchException($serviceUrl, '$serviceUrl', 'string');
- if (gettype($flags) != 'integer')
- throw new CAS_TypeMismatchException($flags, '$flags', 'string');
- try {
- $service = $this->getProxiedService(PHPCAS_PROXIED_SERVICE_IMAP);
- $service->setServiceUrl($serviceUrl);
- $service->setMailbox($url);
- $service->setOptions($flags);
- $stream = $service->open();
- $err_code = PHPCAS_SERVICE_OK;
- $pt = $service->getImapProxyTicket();
- return $stream;
- } catch (CAS_ProxyTicketException $e) {
- $err_msg = $e->getMessage();
- $err_code = $e->getCode();
- $pt = false;
- return false;
- } catch (CAS_ProxiedService_Exception $e) {
- $lang = $this->getLangObj();
- $err_msg = sprintf(
- $lang->getServiceUnavailable(),
- $url,
- $e->getMessage()
- );
- $err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
- $pt = false;
- return false;
- }
- }
- /** @} **/
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // XX XX
- // XX PROXIED CLIENT FEATURES (CAS 2.0) XX
- // XX XX
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // ########################################################################
- // PT
- // ########################################################################
- /**
- * @addtogroup internalService
- * @{
- */
- /**
- * This array will store a list of proxies in front of this application. This
- * property will only be populated if this script is being proxied rather than
- * accessed directly.
- *
- * It is set in CAS_Client::validateCAS20() and can be read by
- * CAS_Client::getProxies()
- *
- * @access private
- */
- private $_proxies = array();
- /**
- * Answer an array of proxies that are sitting in front of this application.
- *
- * This method will only return a non-empty array if we have received and
- * validated a Proxy Ticket.
- *
- * @return array
- * @access public
- */
- public function getProxies()
- {
- return $this->_proxies;
- }
- /**
- * Set the Proxy array, probably from persistant storage.
- *
- * @param array $proxies An array of proxies
- *
- * @return void
- * @access private
- */
- private function _setProxies($proxies)
- {
- $this->_proxies = $proxies;
- if (!empty($proxies)) {
- // For proxy-authenticated requests people are not viewing the URL
- // directly since the client is another application making a
- // web-service call.
- // Because of this, stripping the ticket from the URL is unnecessary
- // and causes another web-service request to be performed. Additionally,
- // if session handling on either the client or the server malfunctions
- // then the subsequent request will not complete successfully.
- $this->setNoClearTicketsFromUrl();
- }
- }
- /**
- * A container of patterns to be allowed as proxies in front of the cas client.
- *
- * @var CAS_ProxyChain_AllowedList
- */
- private $_allowed_proxy_chains;
- /**
- * Answer the CAS_ProxyChain_AllowedList object for this client.
- *
- * @return CAS_ProxyChain_AllowedList
- */
- public function getAllowedProxyChains ()
- {
- if (empty($this->_allowed_proxy_chains)) {
- $this->_allowed_proxy_chains = new CAS_ProxyChain_AllowedList();
- }
- return $this->_allowed_proxy_chains;
- }
- /** @} */
- // ########################################################################
- // PT VALIDATION
- // ########################################################################
- /**
- * @addtogroup internalProxied
- * @{
- */
- /**
- * This method is used to validate a cas 2.0 ST or PT; halt on failure
- * Used for all CAS 2.0 validations
- *
- * @param string &$validate_url the url of the reponse
- * @param string &$text_response the text of the repsones
- * @param DOMElement &$tree_response the domxml tree of the respones
- * @param bool $renew true to force the authentication with the CAS server
- *
- * @return bool true when successfull and issue a CAS_AuthenticationException
- * and false on an error
- *
- * @throws CAS_AuthenticationException
- */
- public function validateCAS20(&$validate_url,&$text_response,&$tree_response, $renew=false)
- {
- phpCAS::traceBegin();
- phpCAS::trace($text_response);
- // build the URL to validate the ticket
- if ($this->getAllowedProxyChains()->isProxyingAllowed()) {
- $validate_url = $this->getServerProxyValidateURL().'&ticket='
- .urlencode($this->getTicket());
- } else {
- $validate_url = $this->getServerServiceValidateURL().'&ticket='
- .urlencode($this->getTicket());
- }
- if ( $this->isProxy() ) {
- // pass the callback url for CAS proxies
- $validate_url .= '&pgtUrl='.urlencode($this->_getCallbackURL());
- }
- if ( $renew ) {
- // pass the renew
- $validate_url .= '&renew=true';
- }
- // open and read the URL
- if ( !$this->_readURL($validate_url, $headers, $text_response, $err_msg) ) {
- phpCAS::trace(
- 'could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')'
- );
- throw new CAS_AuthenticationException(
- $this, 'Ticket not validated', $validate_url,
- true/*$no_response*/
- );
- }
- // create new DOMDocument object
- $dom = new DOMDocument();
- // Fix possible whitspace problems
- $dom->preserveWhiteSpace = false;
- // CAS servers should only return data in utf-8
- $dom->encoding = "utf-8";
- // read the response of the CAS server into a DOMDocument object
- if ( !($dom->loadXML($text_response))) {
- // read failed
- throw new CAS_AuthenticationException(
- $this, 'Ticket not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/, $text_response
- );
- } else if ( !($tree_response = $dom->documentElement) ) {
- // read the root node of the XML tree
- // read failed
- throw new CAS_AuthenticationException(
- $this, 'Ticket not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/, $text_response
- );
- } else if ($tree_response->localName != 'serviceResponse') {
- // insure that tag name is 'serviceResponse'
- // bad root node
- throw new CAS_AuthenticationException(
- $this, 'Ticket not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/, $text_response
- );
- } else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) {
- // authentication failed, extract the error code and message and throw exception
- $auth_fail_list = $tree_response
- ->getElementsByTagName("authenticationFailure");
- throw new CAS_AuthenticationException(
- $this, 'Ticket not validated', $validate_url,
- false/*$no_response*/, false/*$bad_response*/,
- $text_response,
- $auth_fail_list->item(0)->getAttribute('code')/*$err_code*/,
- trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/
- );
- } else if ($tree_response->getElementsByTagName("authenticationSuccess")->length != 0) {
- // authentication succeded, extract the user name
- $success_elements = $tree_response
- ->getElementsByTagName("authenticationSuccess");
- if ( $success_elements->item(0)->getElementsByTagName("user")->length == 0) {
- // no user specified => error
- throw new CAS_AuthenticationException(
- $this, 'Ticket not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/, $text_response
- );
- } else {
- $this->_setUser(
- trim(
- $success_elements->item(0)->getElementsByTagName("user")->item(0)->nodeValue
- )
- );
- $this->_readExtraAttributesCas20($success_elements);
- // Store the proxies we are sitting behind for authorization checking
- $proxyList = array();
- if ( sizeof($arr = $success_elements->item(0)->getElementsByTagName("proxy")) > 0) {
- foreach ($arr as $proxyElem) {
- phpCAS::trace("Found Proxy: ".$proxyElem->nodeValue);
- $proxyList[] = trim($proxyElem->nodeValue);
- }
- $this->_setProxies($proxyList);
- phpCAS::trace("Storing Proxy List");
- }
- // Check if the proxies in front of us are allowed
- if (!$this->getAllowedProxyChains()->isProxyListAllowed($proxyList)) {
- throw new CAS_AuthenticationException(
- $this, 'Proxy not allowed', $validate_url,
- false/*$no_response*/, true/*$bad_response*/,
- $text_response
- );
- } else {
- $result = true;
- }
- }
- } else {
- throw new CAS_AuthenticationException(
- $this, 'Ticket not validated', $validate_url,
- false/*$no_response*/, true/*$bad_response*/,
- $text_response
- );
- }
- $this->_renameSession($this->getTicket());
- // at this step, Ticket has been validated and $this->_user has been set,
- phpCAS::traceEnd($result);
- return $result;
- }
- /**
- * This method recursively parses the attribute XML.
- * It also collapses name-value pairs into a single
- * array entry. It parses all common formats of
- * attributes and well formed XML files.
- *
- * @param string $root the DOM root element to be parsed
- * @param string $namespace namespace of the elements
- *
- * @return an array of the parsed XML elements
- *
- * Formats tested:
- *
- * "Jasig Style" Attributes:
- *
- * <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
- * <cas:authenticationSuccess>
- * <cas:user>jsmith</cas:user>
- * <cas:attributes>
- * <cas:attraStyle>RubyCAS</cas:attraStyle>
- * <cas:surname>Smith</cas:surname>
- * <cas:givenName>John</cas:givenName>
- * <cas:memberOf>CN=Staff,OU=Groups,DC=example,DC=edu</cas:memberOf>
- * <cas:memberOf>CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu</cas:memberOf>
- * </cas:attributes>
- * <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket>
- * </cas:authenticationSuccess>
- * </cas:serviceResponse>
- *
- * "Jasig Style" Attributes (longer version):
- *
- * <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
- * <cas:authenticationSuccess>
- * <cas:user>jsmith</cas:user>
- * <cas:attributes>
- * <cas:attribute>
- * <cas:name>surname</cas:name>
- * <cas:value>Smith</cas:value>
- * </cas:attribute>
- * <cas:attribute>
- * <cas:name>givenName</cas:name>
- * <cas:value>John</cas:value>
- * </cas:attribute>
- * <cas:attribute>
- * <cas:name>memberOf</cas:name>
- * <cas:value>['CN=Staff,OU=Groups,DC=example,DC=edu', 'CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu']</cas:value>
- * </cas:attribute>
- * </cas:attributes>
- * <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket>
- * </cas:authenticationSuccess>
- * </cas:serviceResponse>
- *
- * "RubyCAS Style" attributes
- *
- * <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
- * <cas:authenticationSuccess>
- * <cas:user>jsmith</cas:user>
- *
- * <cas:attraStyle>RubyCAS</cas:attraStyle>
- * <cas:surname>Smith</cas:surname>
- * <cas:givenName>John</cas:givenName>
- * <cas:memberOf>CN=Staff,OU=Groups,DC=example,DC=edu</cas:memberOf>
- * <cas:memberOf>CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu</cas:memberOf>
- *
- * <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket>
- * </cas:authenticationSuccess>
- * </cas:serviceResponse>
- *
- * "Name-Value" attributes.
- *
- * Attribute format from these mailing list thread:
- * http://jasig.275507.n4.nabble.com/CAS-attributes-and-how-they-appear-in-the-CAS-response-td264272.html
- * Note: This is a less widely used format, but in use by at least two institutions.
- *
- * <cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
- * <cas:authenticationSuccess>
- * <cas:user>jsmith</cas:user>
- *
- * <cas:attribute name='attraStyle' value='Name-Value' />
- * <cas:attribute name='surname' value='Smith' />
- * <cas:attribute name='givenName' value='John' />
- * <cas:attribute name='memberOf' value='CN=Staff,OU=Groups,DC=example,DC=edu' />
- * <cas:attribute name='memberOf' value='CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu' />
- *
- * <cas:proxyGrantingTicket>PGTIOU-84678-8a9d2sfa23casd</cas:proxyGrantingTicket>
- * </cas:authenticationSuccess>
- * </cas:serviceResponse>
- *
- * result:
- *
- * Array (
- * [surname] => Smith
- * [givenName] => John
- * [memberOf] => Array (
- * [0] => CN=Staff, OU=Groups, DC=example, DC=edu
- * [1] => CN=Spanish Department, OU=Departments, OU=Groups, DC=example, DC=edu
- * )
- * )
- */
- private function _xml_to_array($root, $namespace = "cas")
- {
- $result = array();
- if ($root->hasAttributes()) {
- $attrs = $root->attributes;
- $pair = array();
- foreach ($attrs as $attr) {
- if ($attr->name === "name") {
- $pair['name'] = $attr->value;
- } elseif ($attr->name === "value") {
- $pair['value'] = $attr->value;
- } else {
- $result[$attr->name] = $attr->value;
- }
- if (array_key_exists('name', $pair) && array_key_exists('value', $pair)) {
- $result[$pair['name']] = $pair['value'];
- }
- }
- }
- if ($root->hasChildNodes()) {
- $children = $root->childNodes;
- if ($children->length == 1) {
- $child = $children->item(0);
- if ($child->nodeType == XML_TEXT_NODE) {
- $result['_value'] = $child->nodeValue;
- return (count($result) == 1) ? $result['_value'] : $result;
- }
- }
- $groups = array();
- foreach ($children as $child) {
- $child_nodeName = str_ireplace($namespace . ":", "", $child->nodeName);
- if (in_array($child_nodeName, array("user", "proxies", "proxyGrantingTicket"))) {
- continue;
- }
- if (!isset($result[$child_nodeName])) {
- $res = $this->_xml_to_array($child, $namespace);
- if (!empty($res)) {
- $result[$child_nodeName] = $this->_xml_to_array($child, $namespace);
- }
- } else {
- if (!isset($groups[$child_nodeName])) {
- $result[$child_nodeName] = array($result[$child_nodeName]);
- $groups[$child_nodeName] = 1;
- }
- $result[$child_nodeName][] = $this->_xml_to_array($child, $namespace);
- }
- }
- }
- return $result;
- }
- /**
- * This method parses a "JSON-like array" of strings
- * into an array of strings
- *
- * @param string $json_value the json-like string:
- * e.g.:
- * ['CN=Staff,OU=Groups,DC=example,DC=edu', 'CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu']
- *
- * @return array of strings Description
- * e.g.:
- * Array (
- * [0] => CN=Staff,OU=Groups,DC=example,DC=edu
- * [1] => CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu
- * )
- */
- private function _parse_json_like_array_value($json_value)
- {
- $parts = explode(",", trim($json_value, "[]"));
- $out = array();
- $quote = '';
- foreach ($parts as $part) {
- $part = trim($part);
- if ($quote === '') {
- $value = "";
- if ($this->_startsWith($part, '\'')) {
- $quote = '\'';
- } elseif ($this->_startsWith($part, '"')) {
- $quote = '"';
- } else {
- $out[] = $part;
- }
- $part = ltrim($part, $quote);
- }
- if ($quote !== '') {
- $value .= $part;
- if ($this->_endsWith($part, $quote)) {
- $out[] = rtrim($value, $quote);
- $quote = '';
- } else {
- $value .= ", ";
- };
- }
- }
- return $out;
- }
- /**
- * This method recursively removes unneccessary hirarchy levels in array-trees.
- * into an array of strings
- *
- * @param array $arr the array to flatten
- * e.g.:
- * Array (
- * [attributes] => Array (
- * [attribute] => Array (
- * [0] => Array (
- * [name] => surname
- * [value] => Smith
- * )
- * [1] => Array (
- * [name] => givenName
- * [value] => John
- * )
- * [2] => Array (
- * [name] => memberOf
- * [value] => ['CN=Staff,OU=Groups,DC=example,DC=edu', 'CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu']
- * )
- * )
- * )
- * )
- *
- * @return array the flattened array
- * e.g.:
- * Array (
- * [attribute] => Array (
- * [surname] => Smith
- * [givenName] => John
- * [memberOf] => Array (
- * [0] => CN=Staff, OU=Groups, DC=example, DC=edu
- * [1] => CN=Spanish Department, OU=Departments, OU=Groups, DC=example, DC=edu
- * )
- * )
- * )
- */
- private function _flatten_array($arr)
- {
- if (!is_array($arr)) {
- if ($this->_startsWith($arr, '[') && $this->_endsWith($arr, ']')) {
- return $this->_parse_json_like_array_value($arr);
- } else {
- return $arr;
- }
- }
- $out = array();
- foreach ($arr as $key => $val) {
- if (!is_array($val)) {
- $out[$key] = $val;
- } else {
- switch (count($val)) {
- case 1 : {
- $key = key($val);
- if (array_key_exists($key, $out)) {
- $value = $out[$key];
- if (!is_array($value)) {
- $out[$key] = array();
- $out[$key][] = $value;
- }
- $out[$key][] = $this->_flatten_array($val[$key]);
- } else {
- $out[$key] = $this->_flatten_array($val[$key]);
- };
- break;
- };
- case 2 : {
- if (array_key_exists("name", $val) && array_key_exists("value", $val)) {
- $key = $val['name'];
- if (array_key_exists($key, $out)) {
- $value = $out[$key];
- if (!is_array($value)) {
- $out[$key] = array();
- $out[$key][] = $value;
- }
- $out[$key][] = $this->_flatten_array($val['value']);
- } else {
- $out[$key] = $this->_flatten_array($val['value']);
- };
- } else {
- $out[$key] = $this->_flatten_array($val);
- }
- break;
- };
- default: {
- $out[$key] = $this->_flatten_array($val);
- }
- }
- }
- }
- return $out;
- }
- /**
- * This method will parse the DOM and pull out the attributes from the XML
- * payload and put them into an array, then put the array into the session.
- *
- * @param DOMNodeList $success_elements payload of the response
- *
- * @return bool true when successfull, halt otherwise by calling
- * CAS_Client::_authError().
- */
- private function _readExtraAttributesCas20($success_elements)
- {
- phpCAS::traceBegin();
- $extra_attributes = array();
- if ($this->_casAttributeParserCallbackFunction !== null
- && is_callable($this->_casAttributeParserCallbackFunction)
- ) {
- array_unshift($this->_casAttributeParserCallbackArgs, $success_elements->item(0));
- phpCAS :: trace("Calling attritubeParser callback");
- $extra_attributes = call_user_func_array(
- $this->_casAttributeParserCallbackFunction,
- $this->_casAttributeParserCallbackArgs
- );
- } else {
- phpCAS :: trace("Parse extra attributes: ");
- $attributes = $this->_xml_to_array($success_elements->item(0));
- phpCAS :: trace(print_r($attributes,true). "\nFLATTEN Array: ");
- $extra_attributes = $this->_flatten_array($attributes);
- phpCAS :: trace(print_r($extra_attributes, true)."\nFILTER : ");
- if (array_key_exists("attribute", $extra_attributes)) {
- $extra_attributes = $extra_attributes["attribute"];
- } elseif (array_key_exists("attributes", $extra_attributes)) {
- $extra_attributes = $extra_attributes["attributes"];
- };
- phpCAS :: trace(print_r($extra_attributes, true)."return");
- }
- $this->setAttributes($extra_attributes);
- phpCAS::traceEnd();
- return true;
- }
- /**
- * Add an attribute value to an array of attributes.
- *
- * @param array &$attributeArray reference to array
- * @param string $name name of attribute
- * @param string $value value of attribute
- *
- * @return void
- */
- private function _addAttributeToArray(array &$attributeArray, $name, $value)
- {
- // If multiple attributes exist, add as an array value
- if (isset($attributeArray[$name])) {
- // Initialize the array with the existing value
- if (!is_array($attributeArray[$name])) {
- $existingValue = $attributeArray[$name];
- $attributeArray[$name] = array($existingValue);
- }
- $attributeArray[$name][] = trim($value);
- } else {
- $attributeArray[$name] = trim($value);
- }
- }
- /** @} */
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- // XX XX
- // XX MISC XX
- // XX XX
- // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
- /**
- * @addtogroup internalMisc
- * @{
- */
- // ########################################################################
- // URL
- // ########################################################################
- /**
- * the URL of the current request (without any ticket CGI parameter). Written
- * and read by CAS_Client::getURL().
- *
- * @hideinitializer
- */
- private $_url = '';
- /**
- * This method sets the URL of the current request
- *
- * @param string $url url to set for service
- *
- * @return void
- */
- public function setURL($url)
- {
- // Argument Validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- $this->_url = $url;
- }
- /**
- * This method returns the URL of the current request (without any ticket
- * CGI parameter).
- *
- * @return string The URL
- */
- public function getURL()
- {
- phpCAS::traceBegin();
- // the URL is built when needed only
- if ( empty($this->_url) ) {
- // remove the ticket if present in the URL
- $final_uri = ($this->_isHttps()) ? 'https' : 'http';
- $final_uri .= '://';
- $final_uri .= $this->_getClientUrl();
- $request_uri = explode('?', $_SERVER['REQUEST_URI'], 2);
- $final_uri .= $request_uri[0];
- if (isset($request_uri[1]) && $request_uri[1]) {
- $query_string= $this->_removeParameterFromQueryString('ticket', $request_uri[1]);
- // If the query string still has anything left,
- // append it to the final URI
- if ($query_string !== '') {
- $final_uri .= "?$query_string";
- }
- }
- phpCAS::trace("Final URI: $final_uri");
- $this->setURL($final_uri);
- }
- phpCAS::traceEnd($this->_url);
- return $this->_url;
- }
- /**
- * This method sets the base URL of the CAS server.
- *
- * @param string $url the base URL
- *
- * @return string base url
- */
- public function setBaseURL($url)
- {
- // Argument Validation
- if (gettype($url) != 'string')
- throw new CAS_TypeMismatchException($url, '$url', 'string');
- return $this->_server['base_url'] = $url;
- }
- /**
- * Try to figure out the phpCAS client URL with possible Proxys / Ports etc.
- *
- * @return string Server URL with domain:port
- */
- private function _getClientUrl()
- {
- if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
- // explode the host list separated by comma and use the first host
- $hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
- // see rfc7239#5.3 and rfc7230#2.7.1: port is in HTTP_X_FORWARDED_HOST if non default
- return $hosts[0];
- } else if (!empty($_SERVER['HTTP_X_FORWARDED_SERVER'])) {
- $server_url = $_SERVER['HTTP_X_FORWARDED_SERVER'];
- } else {
- if (empty($_SERVER['SERVER_NAME'])) {
- $server_url = $_SERVER['HTTP_HOST'];
- } else {
- $server_url = $_SERVER['SERVER_NAME'];
- }
- }
- if (!strpos($server_url, ':')) {
- if (empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
- $server_port = $_SERVER['SERVER_PORT'];
- } else {
- $ports = explode(',', $_SERVER['HTTP_X_FORWARDED_PORT']);
- $server_port = $ports[0];
- }
- if ( ($this->_isHttps() && $server_port!=443)
- || (!$this->_isHttps() && $server_port!=80)
- ) {
- $server_url .= ':';
- $server_url .= $server_port;
- }
- }
- return $server_url;
- }
- /**
- * This method checks to see if the request is secured via HTTPS
- *
- * @return bool true if https, false otherwise
- */
- private function _isHttps()
- {
- if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
- return ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
- } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) {
- return ($_SERVER['HTTP_X_FORWARDED_PROTOCOL'] === 'https');
- } elseif ( isset($_SERVER['HTTPS'])
- && !empty($_SERVER['HTTPS'])
- && strcasecmp($_SERVER['HTTPS'], 'off') !== 0
- ) {
- return true;
- }
- return false;
- }
- /**
- * Removes a parameter from a query string
- *
- * @param string $parameterName name of parameter
- * @param string $queryString query string
- *
- * @return string new query string
- *
- * @link http://stackoverflow.com/questions/1842681/regular-expression-to-remove-one-parameter-from-query-string
- */
- private function _removeParameterFromQueryString($parameterName, $queryString)
- {
- $parameterName = preg_quote($parameterName);
- return preg_replace(
- "/&$parameterName(=[^&]*)?|^$parameterName(=[^&]*)?&?/",
- '', $queryString
- );
- }
- /**
- * This method is used to append query parameters to an url. Since the url
- * might already contain parameter it has to be detected and to build a proper
- * URL
- *
- * @param string $url base url to add the query params to
- * @param string $query params in query form with & separated
- *
- * @return string url with query params
- */
- private function _buildQueryUrl($url, $query)
- {
- $url .= (strstr($url, '?') === false) ? '?' : '&';
- $url .= $query;
- return $url;
- }
- /**
- * This method tests if a string starts with a given character.
- *
- * @param string $text text to test
- * @param string $char character to test for
- *
- * @return bool true if the $text starts with $char
- */
- private function _startsWith($text, $char)
- {
- return (strpos($text, $char) === 0);
- }
- /**
- * This method tests if a string ends with a given character
- *
- * @param string $text text to test
- * @param string $char character to test for
- *
- * @return bool true if the $text ends with $char
- */
- private function _endsWith($text, $char)
- {
- return (strpos(strrev($text), $char) === 0);
- }
- /**
- * Answer a valid session-id given a CAS ticket.
- *
- * The output must be deterministic to allow single-log-out when presented with
- * the ticket to log-out.
- *
- *
- * @param string $ticket name of the ticket
- *
- * @return string
- */
- private function _sessionIdForTicket($ticket)
- {
- // Hash the ticket to ensure that the value meets the PHP 7.1 requirement
- // that session-ids have a length between 22 and 256 characters.
- return hash('sha256', $this->_sessionIdSalt . $ticket);
- }
- /**
- * Set a salt/seed for the session-id hash to make it harder to guess.
- *
- * @var string $_sessionIdSalt
- */
- private $_sessionIdSalt = '';
- /**
- * Set a salt/seed for the session-id hash to make it harder to guess.
- *
- * @param string $salt
- *
- * @return void
- */
- public function setSessionIdSalt($salt) {
- $this->_sessionIdSalt = (string)$salt;
- }
- // ########################################################################
- // AUTHENTICATION ERROR HANDLING
- // ########################################################################
- /**
- * This method is used to print the HTML output when the user was not
- * authenticated.
- *
- * @param string $failure the failure that occured
- * @param string $cas_url the URL the CAS server was asked for
- * @param bool $no_response the response from the CAS server (other
- * parameters are ignored if true)
- * @param bool $bad_response bad response from the CAS server ($err_code
- * and $err_msg ignored if true)
- * @param string $cas_response the response of the CAS server
- * @param int $err_code the error code given by the CAS server
- * @param string $err_msg the error message given by the CAS server
- *
- * @return void
- */
- private function _authError(
- $failure,
- $cas_url,
- $no_response=false,
- $bad_response=false,
- $cas_response='',
- $err_code=-1,
- $err_msg=''
- ) {
- phpCAS::traceBegin();
- $lang = $this->getLangObj();
- $this->printHTMLHeader($lang->getAuthenticationFailed());
- printf(
- $lang->getYouWereNotAuthenticated(), htmlentities($this->getURL()),
- isset($_SERVER['SERVER_ADMIN']) ? $_SERVER['SERVER_ADMIN']:''
- );
- phpCAS::trace('CAS URL: '.$cas_url);
- phpCAS::trace('Authentication failure: '.$failure);
- if ( $no_response ) {
- phpCAS::trace('Reason: no response from the CAS server');
- } else {
- if ( $bad_response ) {
- phpCAS::trace('Reason: bad response from the CAS server');
- } else {
- switch ($this->getServerVersion()) {
- case CAS_VERSION_1_0:
- phpCAS::trace('Reason: CAS error');
- break;
- case CAS_VERSION_2_0:
- case CAS_VERSION_3_0:
- if ( $err_code === -1 ) {
- phpCAS::trace('Reason: no CAS error');
- } else {
- phpCAS::trace(
- 'Reason: ['.$err_code.'] CAS error: '.$err_msg
- );
- }
- break;
- }
- }
- phpCAS::trace('CAS response: '.$cas_response);
- }
- $this->printHTMLFooter();
- phpCAS::traceExit();
- throw new CAS_GracefullTerminationException();
- }
- // ########################################################################
- // PGTIOU/PGTID and logoutRequest rebroadcasting
- // ########################################################################
- /**
- * Boolean of whether to rebroadcast pgtIou/pgtId and logoutRequest, and
- * array of the nodes.
- */
- private $_rebroadcast = false;
- private $_rebroadcast_nodes = array();
- /**
- * Constants used for determining rebroadcast node type.
- */
- const HOSTNAME = 0;
- const IP = 1;
- /**
- * Determine the node type from the URL.
- *
- * @param String $nodeURL The node URL.
- *
- * @return int hostname
- *
- */
- private function _getNodeType($nodeURL)
- {
- phpCAS::traceBegin();
- if (preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $nodeURL)) {
- phpCAS::traceEnd(self::IP);
- return self::IP;
- } else {
- phpCAS::traceEnd(self::HOSTNAME);
- return self::HOSTNAME;
- }
- }
- /**
- * Store the rebroadcast node for pgtIou/pgtId and logout requests.
- *
- * @param string $rebroadcastNodeUrl The rebroadcast node URL.
- *
- * @return void
- */
- public function addRebroadcastNode($rebroadcastNodeUrl)
- {
- // Argument validation
- if ( !(bool)preg_match("/^(http|https):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i", $rebroadcastNodeUrl))
- throw new CAS_TypeMismatchException($rebroadcastNodeUrl, '$rebroadcastNodeUrl', 'url');
- // Store the rebroadcast node and set flag
- $this->_rebroadcast = true;
- $this->_rebroadcast_nodes[] = $rebroadcastNodeUrl;
- }
- /**
- * An array to store extra rebroadcast curl options.
- */
- private $_rebroadcast_headers = array();
- /**
- * This method is used to add header parameters when rebroadcasting
- * pgtIou/pgtId or logoutRequest.
- *
- * @param string $header Header to send when rebroadcasting.
- *
- * @return void
- */
- public function addRebroadcastHeader($header)
- {
- if (gettype($header) != 'string')
- throw new CAS_TypeMismatchException($header, '$header', 'string');
- $this->_rebroadcast_headers[] = $header;
- }
- /**
- * Constants used for determining rebroadcast type (logout or pgtIou/pgtId).
- */
- const LOGOUT = 0;
- const PGTIOU = 1;
- /**
- * This method rebroadcasts logout/pgtIou requests. Can be LOGOUT,PGTIOU
- *
- * @param int $type type of rebroadcasting.
- *
- * @return void
- */
- private function _rebroadcast($type)
- {
- phpCAS::traceBegin();
- $rebroadcast_curl_options = array(
- CURLOPT_FAILONERROR => 1,
- CURLOPT_FOLLOWLOCATION => 1,
- CURLOPT_RETURNTRANSFER => 1,
- CURLOPT_CONNECTTIMEOUT => 1,
- CURLOPT_TIMEOUT => 4);
- // Try to determine the IP address of the server
- if (!empty($_SERVER['SERVER_ADDR'])) {
- $ip = $_SERVER['SERVER_ADDR'];
- } else if (!empty($_SERVER['LOCAL_ADDR'])) {
- // IIS 7
- $ip = $_SERVER['LOCAL_ADDR'];
- }
- // Try to determine the DNS name of the server
- if (!empty($ip)) {
- $dns = gethostbyaddr($ip);
- }
- $multiClassName = 'CAS_Request_CurlMultiRequest';
- $multiRequest = new $multiClassName();
- for ($i = 0; $i < sizeof($this->_rebroadcast_nodes); $i++) {
- if ((($this->_getNodeType($this->_rebroadcast_nodes[$i]) == self::HOSTNAME) && !empty($dns) && (stripos($this->_rebroadcast_nodes[$i], $dns) === false))
- || (($this->_getNodeType($this->_rebroadcast_nodes[$i]) == self::IP) && !empty($ip) && (stripos($this->_rebroadcast_nodes[$i], $ip) === false))
- ) {
- phpCAS::trace(
- 'Rebroadcast target URL: '.$this->_rebroadcast_nodes[$i]
- .$_SERVER['REQUEST_URI']
- );
- $className = $this->_requestImplementation;
- $request = new $className();
- $url = $this->_rebroadcast_nodes[$i].$_SERVER['REQUEST_URI'];
- $request->setUrl($url);
- if (count($this->_rebroadcast_headers)) {
- $request->addHeaders($this->_rebroadcast_headers);
- }
- $request->makePost();
- if ($type == self::LOGOUT) {
- // Logout request
- $request->setPostBody(
- 'rebroadcast=false&logoutRequest='.$_POST['logoutRequest']
- );
- } else if ($type == self::PGTIOU) {
- // pgtIou/pgtId rebroadcast
- $request->setPostBody('rebroadcast=false');
- }
- $request->setCurlOptions($rebroadcast_curl_options);
- $multiRequest->addRequest($request);
- } else {
- phpCAS::trace(
- 'Rebroadcast not sent to self: '
- .$this->_rebroadcast_nodes[$i].' == '.(!empty($ip)?$ip:'')
- .'/'.(!empty($dns)?$dns:'')
- );
- }
- }
- // We need at least 1 request
- if ($multiRequest->getNumRequests() > 0) {
- $multiRequest->send();
- }
- phpCAS::traceEnd();
- }
- /** @} */
- }
|