diff --git a/phpCAS-1.3.6/.gitattributes b/phpCAS-1.3.6/.gitattributes new file mode 100644 index 0000000..3e28f4e --- /dev/null +++ b/phpCAS-1.3.6/.gitattributes @@ -0,0 +1,7 @@ +/docs/ export-ignore +/test/ export-ignore +/utils/ export-ignore +/.buildpath export-ignore +/.gitignore export-ignore +/.project export-ignore +/.travis.yml export-ignore diff --git a/phpCAS-1.3.6/CAS.php b/phpCAS-1.3.6/CAS.php new file mode 100644 index 0000000..8cd549e --- /dev/null +++ b/phpCAS-1.3.6/CAS.php @@ -0,0 +1,30 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +require_once dirname(__FILE__).'/source/CAS.php'; \ No newline at end of file diff --git a/phpCAS-1.3.6/LICENSE b/phpCAS-1.3.6/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/phpCAS-1.3.6/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. diff --git a/phpCAS-1.3.6/NOTICE b/phpCAS-1.3.6/NOTICE new file mode 100644 index 0000000..70d9ffc --- /dev/null +++ b/phpCAS-1.3.6/NOTICE @@ -0,0 +1,81 @@ +Copyright 2007-2011, JA-SIG, Inc. +This project includes software developed by Jasig. +http://www.jasig.org/ + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this software 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. + +=========================================================================== + +Copyright © 2003-2007, The ESUP-Portail consortium + +Requirements for sources originally licensed under the New BSD License: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +- Neither the name of JA-SIG, Inc. nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +=========================================================================== + +Copyright (c) 2009, Regents of the University of Nebraska +All rights reserved. + +Requirements for CAS_Autloader originally licensed under the New BSD License: + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the name of the University of Nebraska nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/phpCAS-1.3.6/README.md b/phpCAS-1.3.6/README.md new file mode 100644 index 0000000..84ae1e3 --- /dev/null +++ b/phpCAS-1.3.6/README.md @@ -0,0 +1,31 @@ +phpCAS +======= + +phpCAS is an authentication library that allows PHP applications to easily authenticate +users via a Central Authentication Service (CAS) server. + +Please see the phpCAS website for more information: + +https://wiki.jasig.org/display/CASC/phpCAS + +[![Build Status](https://travis-ci.org/apereo/phpCAS.png)](https://travis-ci.org/apereo/phpCAS) + + +LICENSE +------- + +Copyright 2007-2015, JA-SIG, Inc. +This project includes software developed by Jasig. +http://www.jasig.org/ + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this software 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. diff --git a/phpCAS-1.3.6/composer.json b/phpCAS-1.3.6/composer.json new file mode 100644 index 0000000..603595b --- /dev/null +++ b/phpCAS-1.3.6/composer.json @@ -0,0 +1,29 @@ +{ + "name": "apereo/phpcas", + "description": "Provides a simple API for authenticating users against a CAS server", + "keywords": ["cas", "jasig", "apereo"], + "homepage": "https://wiki.jasig.org/display/CASC/phpCAS", + "type": "library", + "license": "Apache-2.0", + "authors": [ + {"name": "Joachim Fritschi", "homepage": "https://wiki.jasig.org/display/~fritschi"}, + {"name": "Adam Franco", "homepage": "https://wiki.jasig.org/display/~adamfranco"} + ], + "require": { + "php": ">=5.4.0", + "ext-curl": "*" + }, + "require-dev": { + "phpunit/phpunit": "~3.7.10" + }, + "autoload": { + "classmap": [ + "source/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + } +} diff --git a/phpCAS-1.3.6/source/CAS.php b/phpCAS-1.3.6/source/CAS.php new file mode 100644 index 0000000..d8fd805 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS.php @@ -0,0 +1,2015 @@ + + * @author Olivier Berger + * @author Brett Bieber + * @author Joachim Fritschi + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * @ingroup public + */ + + +// +// hack by Vangelis Haniotakis to handle the absence of $_SERVER['REQUEST_URI'] +// in IIS +// +if (!isset($_SERVER['REQUEST_URI']) && isset($_SERVER['SCRIPT_NAME']) && isset($_SERVER['QUERY_STRING'])) { + $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING']; +} + +// Add a E_USER_DEPRECATED for php versions <= 5.2 +if (!defined('E_USER_DEPRECATED')) { + define('E_USER_DEPRECATED', E_USER_NOTICE); +} + + +// ######################################################################## +// CONSTANTS +// ######################################################################## + +// ------------------------------------------------------------------------ +// CAS VERSIONS +// ------------------------------------------------------------------------ + +/** + * phpCAS version. accessible for the user by phpCAS::getVersion(). + */ +define('PHPCAS_VERSION', '1.3.6'); + +/** + * @addtogroup public + * @{ + */ + +/** + * CAS version 1.0 + */ +define("CAS_VERSION_1_0", '1.0'); +/*! + * CAS version 2.0 +*/ +define("CAS_VERSION_2_0", '2.0'); +/** + * CAS version 3.0 + */ +define("CAS_VERSION_3_0", '3.0'); + +// ------------------------------------------------------------------------ +// SAML defines +// ------------------------------------------------------------------------ + +/** + * SAML protocol + */ +define("SAML_VERSION_1_1", 'S1'); + +/** + * XML header for SAML POST + */ +define("SAML_XML_HEADER", ''); + +/** + * SOAP envelope for SAML POST + */ +define("SAML_SOAP_ENV", ''); + +/** + * SOAP body for SAML POST + */ +define("SAML_SOAP_BODY", ''); + +/** + * SAMLP request + */ +define("SAMLP_REQUEST", ''); +define("SAMLP_REQUEST_CLOSE", ''); + +/** + * SAMLP artifact tag (for the ticket) + */ +define("SAML_ASSERTION_ARTIFACT", ''); + +/** + * SAMLP close + */ +define("SAML_ASSERTION_ARTIFACT_CLOSE", ''); + +/** + * SOAP body close + */ +define("SAML_SOAP_BODY_CLOSE", ''); + +/** + * SOAP envelope close + */ +define("SAML_SOAP_ENV_CLOSE", ''); + +/** + * SAML Attributes + */ +define("SAML_ATTRIBUTES", 'SAMLATTRIBS'); + +/** + * SAML Attributes + */ +define("DEFAULT_ERROR", 'Internal script failure'); + +/** @} */ +/** + * @addtogroup publicPGTStorage + * @{ + */ +// ------------------------------------------------------------------------ +// FILE PGT STORAGE +// ------------------------------------------------------------------------ +/** + * Default path used when storing PGT's to file + */ +define("CAS_PGT_STORAGE_FILE_DEFAULT_PATH", session_save_path()); +/** @} */ +// ------------------------------------------------------------------------ +// SERVICE ACCESS ERRORS +// ------------------------------------------------------------------------ +/** + * @addtogroup publicServices + * @{ + */ + +/** + * phpCAS::service() error code on success + */ +define("PHPCAS_SERVICE_OK", 0); +/** + * phpCAS::service() error code when the PT could not retrieve because + * the CAS server did not respond. + */ +define("PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE", 1); +/** + * phpCAS::service() error code when the PT could not retrieve because + * the response of the CAS server was ill-formed. + */ +define("PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE", 2); +/** + * phpCAS::service() error code when the PT could not retrieve because + * the CAS server did not want to. + */ +define("PHPCAS_SERVICE_PT_FAILURE", 3); +/** + * phpCAS::service() error code when the service was not available. + */ +define("PHPCAS_SERVICE_NOT_AVAILABLE", 4); + +// ------------------------------------------------------------------------ +// SERVICE TYPES +// ------------------------------------------------------------------------ +/** + * phpCAS::getProxiedService() type for HTTP GET + */ +define("PHPCAS_PROXIED_SERVICE_HTTP_GET", 'CAS_ProxiedService_Http_Get'); +/** + * phpCAS::getProxiedService() type for HTTP POST + */ +define("PHPCAS_PROXIED_SERVICE_HTTP_POST", 'CAS_ProxiedService_Http_Post'); +/** + * phpCAS::getProxiedService() type for IMAP + */ +define("PHPCAS_PROXIED_SERVICE_IMAP", 'CAS_ProxiedService_Imap'); + + +/** @} */ +// ------------------------------------------------------------------------ +// LANGUAGES +// ------------------------------------------------------------------------ +/** + * @addtogroup publicLang + * @{ + */ + +define("PHPCAS_LANG_ENGLISH", 'CAS_Languages_English'); +define("PHPCAS_LANG_FRENCH", 'CAS_Languages_French'); +define("PHPCAS_LANG_GREEK", 'CAS_Languages_Greek'); +define("PHPCAS_LANG_GERMAN", 'CAS_Languages_German'); +define("PHPCAS_LANG_JAPANESE", 'CAS_Languages_Japanese'); +define("PHPCAS_LANG_SPANISH", 'CAS_Languages_Spanish'); +define("PHPCAS_LANG_CATALAN", 'CAS_Languages_Catalan'); +define("PHPCAS_LANG_CHINESE_SIMPLIFIED", 'CAS_Languages_ChineseSimplified'); + +/** @} */ + +/** + * @addtogroup internalLang + * @{ + */ + +/** + * phpCAS default language (when phpCAS::setLang() is not used) + */ +define("PHPCAS_LANG_DEFAULT", PHPCAS_LANG_ENGLISH); + +/** @} */ +// ------------------------------------------------------------------------ +// DEBUG +// ------------------------------------------------------------------------ +/** + * @addtogroup publicDebug + * @{ + */ + +/** + * The default directory for the debug file under Unix. + */ +function gettmpdir() { +if (!empty($_ENV['TMP'])) { return realpath($_ENV['TMP']); } +if (!empty($_ENV['TMPDIR'])) { return realpath( $_ENV['TMPDIR']); } +if (!empty($_ENV['TEMP'])) { return realpath( $_ENV['TEMP']); } +return "/tmp"; +} +define('DEFAULT_DEBUG_DIR', gettmpdir()."/"); + +/** @} */ + +// include the class autoloader +require_once dirname(__FILE__) . '/CAS/Autoload.php'; + +/** + * The phpCAS class is a simple container for the phpCAS library. It provides CAS + * authentication for web applications written in PHP. + * + * @ingroup public + * @class phpCAS + * @category Authentication + * @package PhpCAS + * @author Pascal Aubry + * @author Olivier Berger + * @author Brett Bieber + * @author Joachim Fritschi + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +class phpCAS +{ + + /** + * This variable is used by the interface class phpCAS. + * + * @var CAS_Client + * @hideinitializer + */ + private static $_PHPCAS_CLIENT; + + /** + * This variable is used to store where the initializer is called from + * (to print a comprehensive error in case of multiple calls). + * + * @hideinitializer + */ + private static $_PHPCAS_INIT_CALL; + + /** + * This variable is used to store phpCAS debug mode. + * + * @hideinitializer + */ + private static $_PHPCAS_DEBUG; + + /** + * This variable is used to enable verbose mode + * This pevents debug info to be show to the user. Since it's a security + * feature the default is false + * + * @hideinitializer + */ + private static $_PHPCAS_VERBOSE = false; + + + // ######################################################################## + // INITIALIZATION + // ######################################################################## + + /** + * @addtogroup publicInit + * @{ + */ + + /** + * phpCAS client initializer. + * + * @param string $server_version the version of the CAS server + * @param string $server_hostname the hostname of the CAS server + * @param string $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) + * + * @return a newly created CAS_Client object + * @note Only one of the phpCAS::client() and phpCAS::proxy functions should be + * called, only once, and before all other methods (except phpCAS::getVersion() + * and phpCAS::setDebug()). + */ + public static function client($server_version, $server_hostname, + $server_port, $server_uri, $changeSessionID = true + ) { + phpCAS :: traceBegin(); + if (is_object(self::$_PHPCAS_CLIENT)) { + phpCAS :: error(self::$_PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . self::$_PHPCAS_INIT_CALL['file'] . ':' . self::$_PHPCAS_INIT_CALL['line'] . ')'); + } + + // store where the initializer is called from + $dbg = debug_backtrace(); + self::$_PHPCAS_INIT_CALL = array ( + 'done' => true, + 'file' => $dbg[0]['file'], + 'line' => $dbg[0]['line'], + 'method' => __CLASS__ . '::' . __FUNCTION__ + ); + + // initialize the object $_PHPCAS_CLIENT + try { + self::$_PHPCAS_CLIENT = new CAS_Client( + $server_version, false, $server_hostname, $server_port, $server_uri, + $changeSessionID + ); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + phpCAS :: traceEnd(); + } + + /** + * phpCAS proxy initializer. + * + * @param string $server_version the version of the CAS server + * @param string $server_hostname the hostname of the CAS server + * @param string $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) + * + * @return a newly created CAS_Client object + * @note Only one of the phpCAS::client() and phpCAS::proxy functions should be + * called, only once, and before all other methods (except phpCAS::getVersion() + * and phpCAS::setDebug()). + */ + public static function proxy($server_version, $server_hostname, + $server_port, $server_uri, $changeSessionID = true + ) { + phpCAS :: traceBegin(); + if (is_object(self::$_PHPCAS_CLIENT)) { + phpCAS :: error(self::$_PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . self::$_PHPCAS_INIT_CALL['file'] . ':' . self::$_PHPCAS_INIT_CALL['line'] . ')'); + } + + // store where the initialzer is called from + $dbg = debug_backtrace(); + self::$_PHPCAS_INIT_CALL = array ( + 'done' => true, + 'file' => $dbg[0]['file'], + 'line' => $dbg[0]['line'], + 'method' => __CLASS__ . '::' . __FUNCTION__ + ); + + // initialize the object $_PHPCAS_CLIENT + try { + self::$_PHPCAS_CLIENT = new CAS_Client( + $server_version, true, $server_hostname, $server_port, $server_uri, + $changeSessionID + ); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + phpCAS :: traceEnd(); + } + + /** + * Answer whether or not the client or proxy has been initialized + * + * @return bool + */ + public static function isInitialized () + { + return (is_object(self::$_PHPCAS_CLIENT)); + } + + /** @} */ + // ######################################################################## + // DEBUGGING + // ######################################################################## + + /** + * @addtogroup publicDebug + * @{ + */ + + /** + * Set/unset debug mode + * + * @param string $filename the name of the file used for logging, or false + * to stop debugging. + * + * @return void + */ + public static function setDebug($filename = '') + { + if ($filename != false && gettype($filename) != 'string') { + phpCAS :: error('type mismatched for parameter $dbg (should be false or the name of the log file)'); + } + if ($filename === false) { + self::$_PHPCAS_DEBUG['filename'] = false; + + } else { + if (empty ($filename)) { + if (preg_match('/^Win.*/', getenv('OS'))) { + if (isset ($_ENV['TMP'])) { + $debugDir = $_ENV['TMP'] . '/'; + } else { + $debugDir = ''; + } + } else { + $debugDir = DEFAULT_DEBUG_DIR; + } + $filename = $debugDir . 'phpCAS.log'; + } + + if (empty (self::$_PHPCAS_DEBUG['unique_id'])) { + self::$_PHPCAS_DEBUG['unique_id'] = substr(strtoupper(md5(uniqid(''))), 0, 4); + } + + self::$_PHPCAS_DEBUG['filename'] = $filename; + self::$_PHPCAS_DEBUG['indent'] = 0; + + phpCAS :: trace('START ('.date("Y-m-d H:i:s").') phpCAS-' . PHPCAS_VERSION . ' ******************'); + } + } + + /** + * Enable verbose errors messages in the website output + * This is a security relevant since internal status info may leak an may + * help an attacker. Default is therefore false + * + * @param bool $verbose enable verbose output + * + * @return void + */ + public static function setVerbose($verbose) + { + if ($verbose === true) { + self::$_PHPCAS_VERBOSE = true; + } else { + self::$_PHPCAS_VERBOSE = false; + } + } + + + /** + * Show is verbose mode is on + * + * @return boot verbose + */ + public static function getVerbose() + { + return self::$_PHPCAS_VERBOSE; + } + + /** + * Logs a string in debug mode. + * + * @param string $str the string to write + * + * @return void + * @private + */ + public static function log($str) + { + $indent_str = "."; + + + if (!empty(self::$_PHPCAS_DEBUG['filename'])) { + // Check if file exists and modifiy file permissions to be only + // readable by the webserver + if (!file_exists(self::$_PHPCAS_DEBUG['filename'])) { + touch(self::$_PHPCAS_DEBUG['filename']); + // Chmod will fail on windows + @chmod(self::$_PHPCAS_DEBUG['filename'], 0600); + } + for ($i = 0; $i < self::$_PHPCAS_DEBUG['indent']; $i++) { + + $indent_str .= '| '; + } + // allow for multiline output with proper identing. Usefull for + // dumping cas answers etc. + $str2 = str_replace("\n", "\n" . self::$_PHPCAS_DEBUG['unique_id'] . ' ' . $indent_str, $str); + error_log(self::$_PHPCAS_DEBUG['unique_id'] . ' ' . $indent_str . $str2 . "\n", 3, self::$_PHPCAS_DEBUG['filename']); + } + + } + + /** + * This method is used by interface methods to print an error and where the + * function was originally called from. + * + * @param string $msg the message to print + * + * @return void + * @private + */ + public static function error($msg) + { + phpCAS :: traceBegin(); + $dbg = debug_backtrace(); + $function = '?'; + $file = '?'; + $line = '?'; + if (is_array($dbg)) { + for ($i = 1; $i < sizeof($dbg); $i++) { + if (is_array($dbg[$i]) && isset($dbg[$i]['class']) ) { + if ($dbg[$i]['class'] == __CLASS__) { + $function = $dbg[$i]['function']; + $file = $dbg[$i]['file']; + $line = $dbg[$i]['line']; + } + } + } + } + if (self::$_PHPCAS_VERBOSE) { + echo "
\nphpCAS error: " . __CLASS__ . "::" . $function . '(): ' . htmlentities($msg) . " in " . $file . " on line " . $line . "
\n"; + } else { + echo "
\nError: ". DEFAULT_ERROR ."
\n"; + } + phpCAS :: trace($msg . ' in ' . $file . 'on line ' . $line ); + phpCAS :: traceEnd(); + + throw new CAS_GracefullTerminationException(__CLASS__ . "::" . $function . '(): ' . $msg); + } + + /** + * This method is used to log something in debug mode. + * + * @param string $str string to log + * + * @return void + */ + public static function trace($str) + { + $dbg = debug_backtrace(); + phpCAS :: log($str . ' [' . basename($dbg[0]['file']) . ':' . $dbg[0]['line'] . ']'); + } + + /** + * This method is used to indicate the start of the execution of a function + * in debug mode. + * + * @return void + */ + public static function traceBegin() + { + $dbg = debug_backtrace(); + $str = '=> '; + if (!empty ($dbg[1]['class'])) { + $str .= $dbg[1]['class'] . '::'; + } + $str .= $dbg[1]['function'] . '('; + if (is_array($dbg[1]['args'])) { + foreach ($dbg[1]['args'] as $index => $arg) { + if ($index != 0) { + $str .= ', '; + } + if (is_object($arg)) { + $str .= get_class($arg); + } else { + $str .= str_replace(array("\r\n", "\n", "\r"), "", var_export($arg, true)); + } + } + } + if (isset($dbg[1]['file'])) { + $file = basename($dbg[1]['file']); + } else { + $file = 'unknown_file'; + } + if (isset($dbg[1]['line'])) { + $line = $dbg[1]['line']; + } else { + $line = 'unknown_line'; + } + $str .= ') [' . $file . ':' . $line . ']'; + phpCAS :: log($str); + if (!isset(self::$_PHPCAS_DEBUG['indent'])) { + self::$_PHPCAS_DEBUG['indent'] = 0; + } else { + self::$_PHPCAS_DEBUG['indent']++; + } + } + + /** + * This method is used to indicate the end of the execution of a function in + * debug mode. + * + * @param string $res the result of the function + * + * @return void + */ + public static function traceEnd($res = '') + { + if (empty(self::$_PHPCAS_DEBUG['indent'])) { + self::$_PHPCAS_DEBUG['indent'] = 0; + } else { + self::$_PHPCAS_DEBUG['indent']--; + } + $dbg = debug_backtrace(); + $str = ''; + if (is_object($res)) { + $str .= '<= ' . get_class($res); + } else { + $str .= '<= ' . str_replace(array("\r\n", "\n", "\r"), "", var_export($res, true)); + } + + phpCAS :: log($str); + } + + /** + * This method is used to indicate the end of the execution of the program + * + * @return void + */ + public static function traceExit() + { + phpCAS :: log('exit()'); + while (self::$_PHPCAS_DEBUG['indent'] > 0) { + phpCAS :: log('-'); + self::$_PHPCAS_DEBUG['indent']--; + } + } + + /** @} */ + // ######################################################################## + // INTERNATIONALIZATION + // ######################################################################## + /** + * @addtogroup publicLang + * @{ + */ + + /** + * This method is used to set the language used by phpCAS. + * + * @param string $lang string representing the language. + * + * @return void + * + * @sa PHPCAS_LANG_FRENCH, PHPCAS_LANG_ENGLISH + * @note Can be called only once. + */ + public static function setLang($lang) + { + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setLang($lang); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** @} */ + // ######################################################################## + // VERSION + // ######################################################################## + /** + * @addtogroup public + * @{ + */ + + /** + * This method returns the phpCAS version. + * + * @return the phpCAS version. + */ + public static function getVersion() + { + return PHPCAS_VERSION; + } + + /** @} */ + // ######################################################################## + // HTML OUTPUT + // ######################################################################## + /** + * @addtogroup publicOutput + * @{ + */ + + /** + * This method sets the HTML header used for all outputs. + * + * @param string $header the HTML header. + * + * @return void + */ + public static function setHTMLHeader($header) + { + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setHTMLHeader($header); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** + * This method sets the HTML footer used for all outputs. + * + * @param string $footer the HTML footer. + * + * @return void + */ + public static function setHTMLFooter($footer) + { + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setHTMLFooter($footer); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** @} */ + // ######################################################################## + // PGT STORAGE + // ######################################################################## + /** + * @addtogroup publicPGTStorage + * @{ + */ + + /** + * This method can be used to set a custom PGT storage object. + * + * @param CAS_PGTStorage $storage a PGT storage object that inherits from the + * CAS_PGTStorage class + * + * @return void + */ + public static function setPGTStorage($storage) + { + phpCAS :: traceBegin(); + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->setPGTStorage($storage); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + phpCAS :: traceEnd(); + } + + /** + * This method is used to tell phpCAS to store the response of the + * CAS server to PGT requests in a database. + * + * @param string $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 + * PGT's + * @param string $driver_options any driver options to use when connecting + * to the database + * + * @return void + */ + public static function setPGTStorageDb($dsn_or_pdo, $username='', + $password='', $table='', $driver_options=null + ) { + phpCAS :: traceBegin(); + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->setPGTStorageDb($dsn_or_pdo, $username, $password, $table, $driver_options); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + phpCAS :: traceEnd(); + } + + /** + * 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 static function setPGTStorageFile($path = '') + { + phpCAS :: traceBegin(); + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->setPGTStorageFile($path); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + phpCAS :: traceEnd(); + } + /** @} */ + // ######################################################################## + // ACCESS TO EXTERNAL SERVICES + // ######################################################################## + /** + * @addtogroup publicServices + * @{ + */ + + /** + * 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 static function getProxiedService ($type) + { + phpCAS :: traceBegin(); + phpCAS::_validateProxyExists(); + + try { + $res = self::$_PHPCAS_CLIENT->getProxiedService($type); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + return $res; + } + + /** + * Initialize a proxied-service handler with the proxy-ticket it should use. + * + * @param CAS_ProxiedService $proxiedService Proxied 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 + */ + public static function initializeProxiedService (CAS_ProxiedService $proxiedService) + { + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->initializeProxiedService($proxiedService); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** + * This method is used to access an HTTP[S] service. + * + * @param string $url the service to access. + * @param string &$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 static function serviceWeb($url, & $err_code, & $output) + { + phpCAS :: traceBegin(); + phpCAS::_validateProxyExists(); + + try { + $res = self::$_PHPCAS_CLIENT->serviceWeb($url, $err_code, $output); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd($res); + return $res; + } + + /** + * 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 $service a string giving for CAS retrieve Proxy ticket + * @param string $flags options given to imap_open(). + * @param string &$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 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 static function serviceMail($url, $service, $flags, & $err_code, & $err_msg, & $pt) + { + phpCAS :: traceBegin(); + phpCAS::_validateProxyExists(); + + try { + $res = self::$_PHPCAS_CLIENT->serviceMail($url, $service, $flags, $err_code, $err_msg, $pt); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd($res); + return $res; + } + + /** @} */ + // ######################################################################## + // AUTHENTICATION + // ######################################################################## + /** + * @addtogroup publicAuth + * @{ + */ + + /** + * Set the times authentication will be cached before really accessing the + * CAS server in gateway mode: + * - -1: check only once, and then never again (until you pree login) + * - 0: always check + * - n: check every "n" time + * + * @param int $n an integer. + * + * @return void + */ + public static function setCacheTimesForAuthRecheck($n) + { + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setCacheTimesForAuthRecheck($n); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + + /** + * Set a callback function to be run when receiving CAS attributes + * + * The callback function will be passed an $success_elements + * payload of the response (\DOMElement) as its first parameter. + * + * @param string $function Callback function + * @param array $additionalArgs optional array of arguments + * + * @return void + */ + public static function setCasAttributeParserCallback($function, array $additionalArgs = array()) + { + phpCAS::_validateClientExists(); + + self::$_PHPCAS_CLIENT->setCasAttributeParserCallback($function, $additionalArgs); + } + + /** + * 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 the session-id to + * 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 string $function Callback function + * @param array $additionalArgs optional array of arguments + * + * @return void + */ + public static function setPostAuthenticateCallback ($function, array $additionalArgs = array()) + { + phpCAS::_validateClientExists(); + + self::$_PHPCAS_CLIENT->setPostAuthenticateCallback($function, $additionalArgs); + } + + /** + * 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 string $function Callback function + * @param array $additionalArgs optional array of arguments + * + * @return void + */ + public static function setSingleSignoutCallback ($function, array $additionalArgs = array()) + { + phpCAS::_validateClientExists(); + + self::$_PHPCAS_CLIENT->setSingleSignoutCallback($function, $additionalArgs); + } + + /** + * This method is called to check if the user is already authenticated + * locally or has a global cas session. A already existing cas session is + * determined by a cas gateway call.(cas login call without any interactive + * prompt) + * + * @return 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 static function checkAuthentication() + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + $auth = self::$_PHPCAS_CLIENT->checkAuthentication(); + + // store where the authentication has been checked and the result + self::$_PHPCAS_CLIENT->markAuthenticationCall($auth); + + phpCAS :: traceEnd($auth); + return $auth; + } + + /** + * This method is called to force authentication if the user was not already + * authenticated. If the user is not authenticated, halt by redirecting to + * the CAS server. + * + * @return bool Authentication + */ + public static function forceAuthentication() + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + $auth = self::$_PHPCAS_CLIENT->forceAuthentication(); + + // store where the authentication has been checked and the result + self::$_PHPCAS_CLIENT->markAuthenticationCall($auth); + + /* if (!$auth) { + phpCAS :: trace('user is not authenticated, redirecting to the CAS server'); + self::$_PHPCAS_CLIENT->forceAuthentication(); + } else { + phpCAS :: trace('no need to authenticate (user `' . phpCAS :: getUser() . '\' is already authenticated)'); + }*/ + + phpCAS :: traceEnd(); + return $auth; + } + + /** + * This method is called to renew the authentication. + * + * @return void + **/ + public static function renewAuthentication() + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + $auth = self::$_PHPCAS_CLIENT->renewAuthentication(); + + // store where the authentication has been checked and the result + self::$_PHPCAS_CLIENT->markAuthenticationCall($auth); + + //self::$_PHPCAS_CLIENT->renewAuthentication(); + phpCAS :: traceEnd(); + } + + /** + * This method is called to check if the user is authenticated (previously or by + * tickets given in the URL). + * + * @return true when the user is authenticated. + */ + public static function isAuthenticated() + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + // call the isAuthenticated method of the $_PHPCAS_CLIENT object + $auth = self::$_PHPCAS_CLIENT->isAuthenticated(); + + // store where the authentication has been checked and the result + self::$_PHPCAS_CLIENT->markAuthenticationCall($auth); + + phpCAS :: traceEnd($auth); + return $auth; + } + + /** + * Checks whether authenticated based on $_SESSION. Useful to avoid + * server calls. + * + * @return bool true if authenticated, false otherwise. + * @since 0.4.22 by Brendan Arnold + */ + public static function isSessionAuthenticated() + { + phpCAS::_validateClientExists(); + + return (self::$_PHPCAS_CLIENT->isSessionAuthenticated()); + } + + /** + * This method returns the CAS user's login name. + * + * @return string the login name of the authenticated user + * @warning should only be called after phpCAS::forceAuthentication() + * or phpCAS::checkAuthentication(). + * */ + public static function getUser() + { + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->getUser(); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** + * Answer attributes about the authenticated user. + * + * @warning should only be called after phpCAS::forceAuthentication() + * or phpCAS::checkAuthentication(). + * + * @return array + */ + public static function getAttributes() + { + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->getAttributes(); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** + * Answer true if there are attributes for the authenticated user. + * + * @warning should only be called after phpCAS::forceAuthentication() + * or phpCAS::checkAuthentication(). + * + * @return bool + */ + public static function hasAttributes() + { + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->hasAttributes(); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** + * Answer true if an attribute exists for the authenticated user. + * + * @param string $key attribute name + * + * @return bool + * @warning should only be called after phpCAS::forceAuthentication() + * or phpCAS::checkAuthentication(). + */ + public static function hasAttribute($key) + { + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->hasAttribute($key); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** + * Answer an attribute for the authenticated user. + * + * @param string $key attribute name + * + * @return mixed string for a single value or an array if multiple values exist. + * @warning should only be called after phpCAS::forceAuthentication() + * or phpCAS::checkAuthentication(). + */ + public static function getAttribute($key) + { + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->getAttribute($key); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** + * Handle logout requests. + * + * @param bool $check_client additional safety check + * @param array $allowed_clients array of allowed clients + * + * @return void + */ + public static function handleLogoutRequests($check_client = true, $allowed_clients = false) + { + phpCAS::_validateClientExists(); + + return (self::$_PHPCAS_CLIENT->handleLogoutRequests($check_client, $allowed_clients)); + } + + /** + * This method returns the URL to be used to login. + * or phpCAS::isAuthenticated(). + * + * @return the login name of the authenticated user + */ + public static function getServerLoginURL() + { + phpCAS::_validateClientExists(); + + return self::$_PHPCAS_CLIENT->getServerLoginURL(); + } + + /** + * Set the login URL of the CAS server. + * + * @param string $url the login URL + * + * @return void + * @since 0.4.21 by Wyman Chan + */ + public static function setServerLoginURL($url = '') + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerLoginURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + } + + /** + * Set the serviceValidate URL of the CAS server. + * Used for all CAS versions of URL validations. + * Examples: + * CAS 1.0 http://www.exemple.com/validate + * CAS 2.0 http://www.exemple.com/validateURL + * CAS 3.0 http://www.exemple.com/p3/serviceValidate + * + * @param string $url the serviceValidate URL + * + * @return void + */ + public static function setServerServiceValidateURL($url = '') + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerServiceValidateURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + } + + /** + * Set the proxyValidate URL of the CAS server. + * Used for all CAS versions of proxy URL validations + * Examples: + * CAS 1.0 http://www.exemple.com/ + * CAS 2.0 http://www.exemple.com/proxyValidate + * CAS 3.0 http://www.exemple.com/p3/proxyValidate + * + * @param string $url the proxyValidate URL + * + * @return void + */ + public static function setServerProxyValidateURL($url = '') + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerProxyValidateURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + } + + /** + * Set the samlValidate URL of the CAS server. + * + * @param string $url the samlValidate URL + * + * @return void + */ + public static function setServerSamlValidateURL($url = '') + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerSamlValidateURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + } + + /** + * This method returns the URL to be used to login. + * or phpCAS::isAuthenticated(). + * + * @return the login name of the authenticated user + */ + public static function getServerLogoutURL() + { + phpCAS::_validateClientExists(); + + return self::$_PHPCAS_CLIENT->getServerLogoutURL(); + } + + /** + * Set the logout URL of the CAS server. + * + * @param string $url the logout URL + * + * @return void + * @since 0.4.21 by Wyman Chan + */ + public static function setServerLogoutURL($url = '') + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerLogoutURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + } + + /** + * This method is used to logout from CAS. + * + * @param string $params an array that contains the optional url and + * service parameters that will be passed to the CAS server + * + * @return void + */ + public static function logout($params = "") + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + $parsedParams = array (); + if ($params != "") { + if (is_string($params)) { + phpCAS :: error('method `phpCAS::logout($url)\' is now deprecated, use `phpCAS::logoutWithUrl($url)\' instead'); + } + if (!is_array($params)) { + phpCAS :: error('type mismatched for parameter $params (should be `array\')'); + } + foreach ($params as $key => $value) { + if ($key != "service" && $key != "url") { + phpCAS :: error('only `url\' and `service\' parameters are allowed for method `phpCAS::logout($params)\''); + } + $parsedParams[$key] = $value; + } + } + self::$_PHPCAS_CLIENT->logout($parsedParams); + // never reached + phpCAS :: traceEnd(); + } + + /** + * This method is used to logout from CAS. Halts by redirecting to the CAS + * server. + * + * @param string $service a URL that will be transmitted to the CAS server + * + * @return void + */ + public static function logoutWithRedirectService($service) + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + if (!is_string($service)) { + phpCAS :: error('type mismatched for parameter $service (should be `string\')'); + } + self::$_PHPCAS_CLIENT->logout(array ( "service" => $service )); + // never reached + phpCAS :: traceEnd(); + } + + /** + * This method is used to logout from CAS. Halts by redirecting to the CAS + * server. + * + * @param string $url a URL that will be transmitted to the CAS server + * + * @return void + * @deprecated The url parameter has been removed from the CAS server as of + * version 3.3.5.1 + */ + public static function logoutWithUrl($url) + { + trigger_error('Function deprecated for cas servers >= 3.3.5.1', E_USER_DEPRECATED); + phpCAS :: traceBegin(); + if (!is_object(self::$_PHPCAS_CLIENT)) { + phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()'); + } + if (!is_string($url)) { + phpCAS :: error('type mismatched for parameter $url (should be `string\')'); + } + self::$_PHPCAS_CLIENT->logout(array ( "url" => $url )); + // never reached + phpCAS :: traceEnd(); + } + + /** + * This method is used to logout from CAS. Halts by redirecting to the CAS + * server. + * + * @param string $service a URL that will be transmitted to the CAS server + * @param string $url a URL that will be transmitted to the CAS server + * + * @return void + * + * @deprecated The url parameter has been removed from the CAS server as of + * version 3.3.5.1 + */ + public static function logoutWithRedirectServiceAndUrl($service, $url) + { + trigger_error('Function deprecated for cas servers >= 3.3.5.1', E_USER_DEPRECATED); + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + if (!is_string($service)) { + phpCAS :: error('type mismatched for parameter $service (should be `string\')'); + } + if (!is_string($url)) { + phpCAS :: error('type mismatched for parameter $url (should be `string\')'); + } + self::$_PHPCAS_CLIENT->logout( + array ( + "service" => $service, + "url" => $url + ) + ); + // never reached + phpCAS :: traceEnd(); + } + + /** + * Set the fixed URL that will be used by the CAS server to transmit the + * PGT. When this method is not called, a phpCAS script uses its own URL + * for the callback. + * + * @param string $url the URL + * + * @return void + */ + public static function setFixedCallbackURL($url = '') + { + phpCAS :: traceBegin(); + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->setCallbackURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + } + + /** + * Set the fixed URL that will be set as the CAS service parameter. When this + * method is not called, a phpCAS script uses its own URL. + * + * @param string $url the URL + * + * @return void + */ + public static function setFixedServiceURL($url) + { + phpCAS :: traceBegin(); + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->setURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + } + + /** + * Get the URL that is set as the CAS service parameter. + * + * @return string Service Url + */ + public static function getServiceURL() + { + phpCAS::_validateProxyExists(); + return (self::$_PHPCAS_CLIENT->getURL()); + } + + /** + * Retrieve a Proxy Ticket from the CAS server. + * + * @param string $target_service Url string of service to proxy + * @param string &$err_code error code + * @param string &$err_msg error message + * + * @return string Proxy Ticket + */ + public static function retrievePT($target_service, & $err_code, & $err_msg) + { + phpCAS::_validateProxyExists(); + + try { + return (self::$_PHPCAS_CLIENT->retrievePT($target_service, $err_code, $err_msg)); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + } + + /** + * Set the certificate of the CAS server CA and if the CN should be properly + * verified. + * + * @param string $cert CA certificate file name + * @param bool $validate_cn Validate CN in certificate (default true) + * + * @return void + */ + public static function setCasServerCACert($cert, $validate_cn = true) + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setCasServerCACert($cert, $validate_cn); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + } + + /** + * Set no SSL validation for the CAS server. + * + * @return void + */ + public static function setNoCasServerValidation() + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + phpCAS :: trace('You have configured no validation of the legitimacy of the cas server. This is not recommended for production use.'); + self::$_PHPCAS_CLIENT->setNoCasServerValidation(); + phpCAS :: traceEnd(); + } + + + /** + * Disable the removal of a CAS-Ticket from the URL when authenticating + * DISABLING POSES A SECURITY RISK: + * We normally remove the ticket by an additional redirect as a security + * precaution to prevent a ticket in the HTTP_REFERRER or be carried over in + * the URL parameter + * + * @return void + */ + public static function setNoClearTicketsFromUrl() + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + self::$_PHPCAS_CLIENT->setNoClearTicketsFromUrl(); + phpCAS :: traceEnd(); + } + + /** @} */ + + /** + * Change CURL options. + * CURL is used to connect through HTTPS to CAS server + * + * @param string $key the option key + * @param string $value the value to set + * + * @return void + */ + public static function setExtraCurlOption($key, $value) + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + self::$_PHPCAS_CLIENT->setExtraCurlOption($key, $value); + phpCAS :: traceEnd(); + } + + /** + * Set a salt/seed for the session-id hash to make it harder to guess. + * + * When $changeSessionID = true phpCAS will create a session-id that is derived + * from the service ticket. Doing so allows phpCAS to look-up and destroy the + * proper session on single-log-out requests. While the service tickets + * provided by the CAS server may include enough data to generate a strong + * hash, clients may provide an additional salt to ensure that session ids + * are not guessable if the session tickets do not have enough entropy. + * + * @param string $salt The salt to combine with the session ticket. + * + * @return void + */ + public static function setSessionIdSalt($salt) { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + self::$_PHPCAS_CLIENT->setSessionIdSalt($salt); + phpCAS :: traceEnd(); + } + + /** + * If you want your service to be proxied you have to enable it (default + * disabled) and define an accepable list of proxies that are allowed to + * proxy your service. + * + * Add each allowed proxy definition object. For the normal CAS_ProxyChain + * class, the constructor takes an array of proxies to match. The list is in + * reverse just as seen from the service. Proxies have to be defined in reverse + * from the service to the user. If a user hits service A and gets proxied via + * B to service C the list of acceptable on C would be array(B,A). The definition + * of an individual proxy can be either a string or a regexp (preg_match is used) + * that will be matched against the proxy list supplied by the cas server + * when validating the proxy tickets. The strings are compared starting from + * the beginning and must fully match with the proxies in the list. + * Example: + * phpCAS::allowProxyChain(new CAS_ProxyChain(array( + * 'https://app.example.com/' + * ))); + * phpCAS::allowProxyChain(new CAS_ProxyChain(array( + * '/^https:\/\/app[0-9]\.example\.com\/rest\//', + * 'http://client.example.com/' + * ))); + * + * For quick testing or in certain production screnarios you might want to + * allow allow any other valid service to proxy your service. To do so, add + * the "Any" chain: + * phpCAS::allowProxyChain(new CAS_ProxyChain_Any); + * THIS SETTING IS HOWEVER NOT RECOMMENDED FOR PRODUCTION AND HAS SECURITY + * IMPLICATIONS: YOU ARE ALLOWING ANY SERVICE TO ACT ON BEHALF OF A USER + * ON THIS SERVICE. + * + * @param CAS_ProxyChain_Interface $proxy_chain A proxy-chain that will be + * matched against the proxies requesting access + * + * @return void + */ + public static function allowProxyChain(CAS_ProxyChain_Interface $proxy_chain) + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + if (self::$_PHPCAS_CLIENT->getServerVersion() !== CAS_VERSION_2_0 + && self::$_PHPCAS_CLIENT->getServerVersion() !== CAS_VERSION_3_0 + ) { + phpCAS :: error('this method can only be used with the cas 2.0/3.0 protocols'); + } + self::$_PHPCAS_CLIENT->getAllowedProxyChains()->allowProxyChain($proxy_chain); + phpCAS :: traceEnd(); + } + + /** + * 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 + * @since 6/25/09 + */ + public static function getProxies () + { + phpCAS::_validateProxyExists(); + + return(self::$_PHPCAS_CLIENT->getProxies()); + } + + // ######################################################################## + // PGTIOU/PGTID and logoutRequest rebroadcasting + // ######################################################################## + + /** + * Add a pgtIou/pgtId and logoutRequest rebroadcast node. + * + * @param string $rebroadcastNodeUrl The rebroadcast node URL. Can be + * hostname or IP. + * + * @return void + */ + public static function addRebroadcastNode($rebroadcastNodeUrl) + { + phpCAS::traceBegin(); + phpCAS::log('rebroadcastNodeUrl:'.$rebroadcastNodeUrl); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->addRebroadcastNode($rebroadcastNodeUrl); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS::traceEnd(); + } + + /** + * 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 static function addRebroadcastHeader($header) + { + phpCAS :: traceBegin(); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->addRebroadcastHeader($header); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } + + phpCAS :: traceEnd(); + } + + /** + * Checks if a client already exists + * + * @throws CAS_OutOfSequenceBeforeClientException + * + * @return void + */ + private static function _validateClientExists() + { + if (!is_object(self::$_PHPCAS_CLIENT)) { + throw new CAS_OutOfSequenceBeforeClientException(); + } + } + + /** + * Checks of a proxy client aready exists + * + * @throws CAS_OutOfSequenceBeforeProxyException + * + * @return void + */ + private static function _validateProxyExists() + { + if (!is_object(self::$_PHPCAS_CLIENT)) { + throw new CAS_OutOfSequenceBeforeProxyException(); + } + } + + /** + * For testing purposes, use this method to set the client to a test double + * + * @return void + */ + public static function setCasClient(\CAS_Client $client) + { + self::$_PHPCAS_CLIENT = $client; + } +} +// ######################################################################## +// DOCUMENTATION +// ######################################################################## + +// ######################################################################## +// MAIN PAGE + +/** + * @mainpage + * + * The following pages only show the source documentation. + * + */ + +// ######################################################################## +// MODULES DEFINITION + +/** @defgroup public User interface */ + +/** @defgroup publicInit Initialization + * @ingroup public */ + +/** @defgroup publicAuth Authentication + * @ingroup public */ + +/** @defgroup publicServices Access to external services + * @ingroup public */ + +/** @defgroup publicConfig Configuration + * @ingroup public */ + +/** @defgroup publicLang Internationalization + * @ingroup publicConfig */ + +/** @defgroup publicOutput HTML output + * @ingroup publicConfig */ + +/** @defgroup publicPGTStorage PGT storage + * @ingroup publicConfig */ + +/** @defgroup publicDebug Debugging + * @ingroup public */ + +/** @defgroup internal Implementation */ + +/** @defgroup internalAuthentication Authentication + * @ingroup internal */ + +/** @defgroup internalBasic CAS Basic client features (CAS 1.0, Service Tickets) + * @ingroup internal */ + +/** @defgroup internalProxy CAS Proxy features (CAS 2.0, Proxy Granting Tickets) + * @ingroup internal */ + +/** @defgroup internalSAML CAS SAML features (SAML 1.1) + * @ingroup internal */ + +/** @defgroup internalPGTStorage PGT storage + * @ingroup internalProxy */ + +/** @defgroup internalPGTStorageDb PGT storage in a database + * @ingroup internalPGTStorage */ + +/** @defgroup internalPGTStorageFile PGT storage on the filesystem + * @ingroup internalPGTStorage */ + +/** @defgroup internalCallback Callback from the CAS server + * @ingroup internalProxy */ + +/** @defgroup internalProxyServices Proxy other services + * @ingroup internalProxy */ + +/** @defgroup internalService CAS client features (CAS 2.0, Proxied service) + * @ingroup internal */ + +/** @defgroup internalConfig Configuration + * @ingroup internal */ + +/** @defgroup internalBehave Internal behaviour of phpCAS + * @ingroup internalConfig */ + +/** @defgroup internalOutput HTML output + * @ingroup internalConfig */ + +/** @defgroup internalLang Internationalization + * @ingroup internalConfig + * + * To add a new language: + * - 1. define a new constant PHPCAS_LANG_XXXXXX in CAS/CAS.php + * - 2. copy any file from CAS/languages to CAS/languages/XXXXXX.php + * - 3. Make the translations + */ + +/** @defgroup internalDebug Debugging + * @ingroup internal */ + +/** @defgroup internalMisc Miscellaneous + * @ingroup internal */ + +// ######################################################################## +// EXAMPLES + +/** + * @example example_simple.php + */ +/** + * @example example_service.php + */ +/** + * @example example_service_that_proxies.php + */ +/** + * @example example_service_POST.php + */ +/** + * @example example_proxy_serviceWeb.php + */ +/** + * @example example_proxy_serviceWeb_chaining.php + */ +/** + * @example example_proxy_POST.php + */ +/** + * @example example_proxy_GET.php + */ +/** + * @example example_lang.php + */ +/** + * @example example_html.php + */ +/** + * @example example_pgt_storage_file.php + */ +/** + * @example example_pgt_storage_db.php + */ +/** + * @example example_gateway.php + */ +/** + * @example example_logout.php + */ +/** + * @example example_rebroadcast.php + */ +/** + * @example example_custom_urls.php + */ +/** + * @example example_advanced_saml11.php + */ +?> diff --git a/phpCAS-1.3.6/source/CAS/AuthenticationException.php b/phpCAS-1.3.6/source/CAS/AuthenticationException.php new file mode 100644 index 0000000..1a98d75 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/AuthenticationException.php @@ -0,0 +1,111 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This interface defines methods that allow proxy-authenticated service handlers + * to interact with phpCAS. + * + * Proxy service handlers must implement this interface as well as call + * phpCAS::initializeProxiedService($this) at some point in their implementation. + * + * While not required, proxy-authenticated service handlers are encouraged to + * implement the CAS_ProxiedService_Testable interface to facilitate unit testing. + * + * @class CAS_AuthenticationException + * @category Authentication + * @package PhpCAS + * @author Joachim Fritschi + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +class CAS_AuthenticationException +extends RuntimeException +implements CAS_Exception +{ + + /** + * This method is used to print the HTML output when the user was not + * authenticated. + * + * @param CAS_Client $client phpcas client + * @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 + */ + public function __construct($client,$failure,$cas_url,$no_response, + $bad_response='',$cas_response='',$err_code='',$err_msg='' + ) { + $messages = array(); + phpCAS::traceBegin(); + $lang = $client->getLangObj(); + $client->printHTMLHeader($lang->getAuthenticationFailed()); + printf( + $lang->getYouWereNotAuthenticated(), + htmlentities($client->getURL()), + isset($_SERVER['SERVER_ADMIN']) ? $_SERVER['SERVER_ADMIN']:'' + ); + phpCAS::trace($messages[] = 'CAS URL: '.$cas_url); + phpCAS::trace($messages[] = 'Authentication failure: '.$failure); + if ( $no_response ) { + phpCAS::trace($messages[] = 'Reason: no response from the CAS server'); + } else { + if ( $bad_response ) { + phpCAS::trace($messages[] = 'Reason: bad response from the CAS server'); + } else { + switch ($client->getServerVersion()) { + case CAS_VERSION_1_0: + phpCAS::trace($messages[] = 'Reason: CAS error'); + break; + case CAS_VERSION_2_0: + case CAS_VERSION_3_0: + if ( empty($err_code) ) { + phpCAS::trace($messages[] = 'Reason: no CAS error'); + } else { + phpCAS::trace($messages[] = 'Reason: ['.$err_code.'] CAS error: '.$err_msg); + } + break; + } + } + phpCAS::trace($messages[] = 'CAS response: '.$cas_response); + } + $client->printHTMLFooter(); + phpCAS::traceExit(); + + parent::__construct(implode("\n", $messages)); + } + +} +?> diff --git a/phpCAS-1.3.6/source/CAS/Autoload.php b/phpCAS-1.3.6/source/CAS/Autoload.php new file mode 100644 index 0000000..5e9b140 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Autoload.php @@ -0,0 +1,90 @@ + + * @copyright 2008 Regents of the University of Nebraska + * @license http://www1.unl.edu/wdn/wiki/Software_License BSD License + * @link http://code.google.com/p/simplecas/ + **/ + +/** + * Autoload a class + * + * @param string $class Classname to load + * + * @return bool + */ +function CAS_autoload($class) +{ + // Static to hold the Include Path to CAS + static $include_path; + // Check only for CAS classes + if (substr($class, 0, 4) !== 'CAS_') { + return false; + } + // Setup the include path if it's not already set from a previous call + if (empty($include_path)) { + $include_path = array(dirname(dirname(__FILE__)), dirname(dirname(__FILE__)) . '/../test/' ); + } + + // Declare local variable to store the expected full path to the file + + foreach ($include_path as $path) { + $file_path = $path . '/' . str_replace('_', '/', $class) . '.php'; + $fp = @fopen($file_path, 'r', true); + if ($fp) { + fclose($fp); + include $file_path; + if (!class_exists($class, false) && !interface_exists($class, false)) { + die( + new Exception( + 'Class ' . $class . ' was not present in ' . + $file_path . + ' [CAS_autoload]' + ) + ); + } + return true; + } + } + $e = new Exception( + 'Class ' . $class . ' could not be loaded from ' . + $file_path . ', file does not exist (Path="' + . implode(':', $include_path) .'") [CAS_autoload]' + ); + $trace = $e->getTrace(); + if (isset($trace[2]) && isset($trace[2]['function']) + && in_array($trace[2]['function'], array('class_exists', 'interface_exists')) + ) { + return false; + } + if (isset($trace[1]) && isset($trace[1]['function']) + && in_array($trace[1]['function'], array('class_exists', 'interface_exists')) + ) { + return false; + } + die ((string) $e); +} + +// set up __autoload +if (!(spl_autoload_functions()) + || !in_array('CAS_autoload', spl_autoload_functions()) +) { + spl_autoload_register('CAS_autoload'); + if (function_exists('__autoload') + && !in_array('__autoload', spl_autoload_functions()) + ) { + // __autoload() was being used, but now would be ignored, add + // it to the autoload stack + spl_autoload_register('__autoload'); + } +} + +?> diff --git a/phpCAS-1.3.6/source/CAS/Client.php b/phpCAS-1.3.6/source/CAS/Client.php new file mode 100644 index 0000000..960e65b --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Client.php @@ -0,0 +1,3970 @@ + + * @author Olivier Berger + * @author Brett Bieber + * @author Joachim Fritschi + * @author Adam Franco + * @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 + * @author Olivier Berger + * @author Brett Bieber + * @author Joachim Fritschi + * @author Adam Franco + * @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) + ? '__TITLE__

__TITLE__

' + : $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())? + '
phpCAS __PHPCAS_VERSION__ ' + .$lang->getUsingServer() + .' __SERVER_BASE_URL__ (CAS __CAS_VERSION__)
' + :'' + :$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' => -1, + '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 string 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 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 callback $_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 string $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 callback $_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 string $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_OutOfSequenceBeforeAuthenticationCallException + * + * @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 array Keys are 'file', 'line', and 'method' + */ + 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 array Keys are 'file', 'line', and 'method' + */ + 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 array Keys are 'file', 'line', and 'method' + */ + 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) + * + * @return a newly created CAS_Client object + */ + public function __construct( + $server_version, + $proxy, + $server_hostname, + $server_port, + $server_uri, + $changeSessionID = true + ) { + // 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'); + + phpCAS::traceBegin(); + // true : allow to change the session_id(), false session_id won't be + // change and logout won't be handle because of that + $this->_setChangeSessionID($changeSessionID); + + // skip Session Handling for logout requests and if don't want it' + if (session_id()=="" && !$this->_isLogoutRequest()) { + session_start(); + phpCAS :: trace("Starting a new session " . session_id()); + } + // Only for debug purposes + if ($this->isSessionAuthenticated()){ + phpCAS :: trace("Session is authenticated as: " . $_SESSION['phpCAS']['user']); + } else { + phpCAS :: trace("Session is not authenticated"); + } + // are we in proxy mode ? + $this->_proxy = $proxy; + + // Make cookie handling available. + if ($this->isProxy()) { + if (!isset($_SESSION['phpCAS'])) { + $_SESSION['phpCAS'] = array(); + } + if (!isset($_SESSION['phpCAS']['service_cookies'])) { + $_SESSION['phpCAS']['service_cookies'] = array(); + } + $this->_serviceCookieJar = new CAS_CookieJar( + $_SESSION['phpCAS']['service_cookies'] + ); + } + + //check version + switch ($server_version) { + case CAS_VERSION_1_0: + if ( $this->isProxy() ) { + phpCAS::error( + 'CAS proxies are not supported in CAS '.$server_version + ); + } + break; + case CAS_VERSION_2_0: + case CAS_VERSION_3_0: + break; + case SAML_VERSION_1_1: + break; + default: + phpCAS::error( + 'this version of CAS (`'.$server_version + .'\') is not supported by phpCAS '.phpCAS::getVersion() + ); + } + $this->_server['version'] = $server_version; + + // check hostname + if ( empty($server_hostname) + || !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/', $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\-_abcdefghijklmnopqrstuvwxyz\/]*/', $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() ) { + $this->_setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId'])); + } + + 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 + * @{ + */ + + + /** + * A variable to whether phpcas will use its own session handling. Default = true + * @hideinitializer + */ + private $_change_session_id = true; + + /** + * 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; + } + + /** @} */ + + // 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 arry 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 true when the user is authenticated; otherwise halt. + */ + public function renewAuthentication() + { + phpCAS::traceBegin(); + // Either way, the user is authenticated by CAS + if (isset( $_SESSION['phpCAS']['auth_checked'])) { + unset($_SESSION['phpCAS']['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 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 + if (isset($_SESSION['phpCAS']['auth_checked'])) { + unset($_SESSION['phpCAS']['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 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; + if ( $this->isAuthenticated() ) { + phpCAS::trace('user is authenticated'); + /* The 'auth_checked' variable is removed just in case it's set. */ + unset($_SESSION['phpCAS']['auth_checked']); + $res = true; + } else if (isset($_SESSION['phpCAS']['auth_checked'])) { + // the previous request has redirected the client to the CAS server + // with gateway=true + unset($_SESSION['phpCAS']['auth_checked']); + $res = false; + } else { + // avoid a check against CAS on every request + if (!isset($_SESSION['phpCAS']['unauth_count'])) { + $_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized + } + + if (($_SESSION['phpCAS']['unauth_count'] != -2 + && $this->_cache_times_for_auth_recheck == -1) + || ($_SESSION['phpCAS']['unauth_count'] >= 0 + && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck) + ) { + $res = false; + + if ($this->_cache_times_for_auth_recheck != -1) { + $_SESSION['phpCAS']['unauth_count']++; + phpCAS::trace( + 'user is not authenticated (cached for ' + .$_SESSION['phpCAS']['unauth_count'].' times of ' + .$this->_cache_times_for_auth_recheck.')' + ); + } else { + phpCAS::trace( + 'user is not authenticated (cached for until login pressed)' + ); + } + } else { + $_SESSION['phpCAS']['unauth_count'] = 0; + $_SESSION['phpCAS']['auth_checked'] = true; + phpCAS::trace('user is not authenticated (cache reset)'); + $this->redirectToCas(true/* gateway */); + // never reached + $res = false; + } + } + 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 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' + ); + $_SESSION['phpCAS']['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'); + $_SESSION['phpCAS']['pgt'] = $this->_getPGT(); + } + $_SESSION['phpCAS']['user'] = $this->_getUser(); + if (!empty($this->_attributes)) { + $_SESSION['phpCAS']['attributes'] = $this->_attributes; + } + $proxies = $this->getProxies(); + if (!empty($proxies)) { + $_SESSION['phpCAS']['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' + ); + $_SESSION['phpCAS']['user'] = $this->_getUser(); + $_SESSION['phpCAS']['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 true if authenticated based soley on $_SESSION variable + */ + public function isSessionAuthenticated () + { + return !empty($_SESSION['phpCAS']['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 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() + && !empty($_SESSION['phpCAS']['pgt']) + ) { + // authentication already done + $this->_setUser($_SESSION['phpCAS']['user']); + if (isset($_SESSION['phpCAS']['attributes'])) { + $this->setAttributes($_SESSION['phpCAS']['attributes']); + } + $this->_setPGT($_SESSION['phpCAS']['pgt']); + phpCAS::trace( + 'user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `' + .$_SESSION['phpCAS']['pgt'].'\'' + ); + + // Include the list of proxies + if (isset($_SESSION['phpCAS']['proxies'])) { + $this->_setProxies($_SESSION['phpCAS']['proxies']); + phpCAS::trace( + 'proxies = "' + .implode('", "', $_SESSION['phpCAS']['proxies']).'"' + ); + } + + $auth = true; + } elseif ( $this->isSessionAuthenticated() + && empty($_SESSION['phpCAS']['pgt']) + ) { + // these two variables should be empty or not empty at the same time + phpCAS::trace( + 'username found (`'.$_SESSION['phpCAS']['user'] + .'\') but PGT is empty' + ); + // unset all tickets to enforce authentication + unset($_SESSION['phpCAS']); + $this->setTicket(''); + } elseif ( !$this->isSessionAuthenticated() + && !empty($_SESSION['phpCAS']['pgt']) + ) { + // these two variables should be empty or not empty at the same time + phpCAS::trace( + 'PGT found (`'.$_SESSION['phpCAS']['pgt'] + .'\') but username is empty' + ); + // unset all tickets to enforce authentication + unset($_SESSION['phpCAS']); + $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($_SESSION['phpCAS']['user']); + if (isset($_SESSION['phpCAS']['attributes'])) { + $this->setAttributes($_SESSION['phpCAS']['attributes']); + } + phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\''); + + // Include the list of proxies + if (isset($_SESSION['phpCAS']['proxies'])) { + $this->_setProxies($_SESSION['phpCAS']['proxies']); + phpCAS::trace( + 'proxies = "' + .implode('", "', $_SESSION['phpCAS']['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('

'. $lang->getShouldHaveBeenRedirected(). '

', $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('

'.$lang->getShouldHaveBeenRedirected(). '

', $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 bool $allowed_clients an array of host names allowed to send + * logout requests. + * + * @return void + */ + public function handleLogoutRequests($check_client=true, $allowed_clients=false) + { + 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) { + $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( + "|(.*)|", + $decoded_logout_rq, $tick, PREG_OFFSET_CAPTURE, 3 + ); + $wrappedSamlSessionIndex = preg_replace( + '||', '', $tick[0][0] + ); + $ticket2logout = preg_replace( + '||', '', $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 + * @{ + */ + + /** + * 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) && $this->_requestImplementation !== 'CAS_TestHarness_DummyRequest'){ + 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 + */ + public function validateCAS10(&$validate_url,&$text_response,&$tree_response,$renew=false) + { + phpCAS::traceBegin(); + $result = false; + // 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*/ + ); + $result = false; + } + + 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 + ); + $result = false; + } 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 + ); + $result = false; + } + // ticket has been validated, extract the user name + $arr = preg_split('/\n/', $text_response); + $this->_setUser(trim($arr[1])); + $result = true; + + if ($result) { + $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 + */ + 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 + ); + $result = false; + } + // 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 + ); + $result = false; + } 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 + ); + $result = false; + } 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 tag found in SAML payload'); + throw new CAS_AuthenticationException( + $this, 'SA not validated', $validate_url, + false/*$no_response*/, true/*$bad_response*/, + $text_response + ); + $result = false; + } + } + 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 + * @{ + */ + + /** + * 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; + + /** + * Handler for managing service cookies. + */ + private $_serviceCookieJar; + + /** + * Tells if a CAS client is a CAS proxy or not + * + * @return 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 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. + */ + + /** + * 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 i callback mode, + * false otherwise. + * + * @return A boolean. + */ + private function _isCallbackMode() + { + return $this->_callback_mode; + } + + /** + * 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 The callback URL + */ + private function _getCallbackURL() + { + // the URL is built when needed only + if ( empty($this->_callback_url) ) { + $final_uri = ''; + // 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 void + */ + 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 (preg_match('/^PGTIOU-[\.\-\w]+$/', $_GET['pgtIou'])) { + if (preg_match('/^[PT]GT-[\.\-\w]+$/', $_GET['pgtId'])) { + $this->printHTMLHeader('phpCAS callback'); + $pgt_iou = $_GET['pgtIou']; + $pgt = $_GET['pgtId']; + phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')'); + echo '

Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').

'; + $this->_storePGT($pgt, $pgt_iou); + $this->printHTMLFooter(); + phpCAS::traceExit("Successfull Callback"); + } else { + phpCAS::error('PGT format invalid' . $_GET['pgtId']); + phpCAS::traceExit('PGT format invalid' . $_GET['pgtId']); + } + } else { + phpCAS::error('PGTiou format invalid' . $_GET['pgtIou']); + phpCAS::traceExit('PGTiou format invalid' . $_GET['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(); + } + + + /** @} */ + + // ######################################################################## + // PGT STORAGE + // ######################################################################## + /** + * @addtogroup internalPGTStorage + * @{ + */ + + /** + * 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 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 $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) && gettype($dsn_or_pdo) != 'string') + 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 string $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 + */ + private function _validatePGT(&$validate_url,$text_response,$tree_response) + { + phpCAS::traceBegin(); + if ( $tree_response->getElementsByTagName("proxyGrantingTicket")->length == 0) { + phpCAS::trace(' 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 string &$err_code an error code (PHPCAS_SERVICE_OK on success). + * @param string &$err_msg an error message (empty on success). + * + * @return 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; + + if ( !$bad_response ) { + // 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(' was found, but not '); + } + } 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 nor 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 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 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 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 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 string &$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 + */ + public function validateCAS20(&$validate_url,&$text_response,&$tree_response, $renew=false) + { + phpCAS::traceBegin(); + phpCAS::trace($text_response); + $result = false; + // 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*/ + ); + $result = false; + } + + // 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 + ); + $result = false; + } 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 + ); + $result = false; + } 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 + ); + $result = false; + } 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*/ + ); + $result = false; + } 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 + ); + $result = false; + } 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 + ); + $result = false; + } else { + $result = true; + } + } + } else { + throw new CAS_AuthenticationException( + $this, 'Ticket not validated', $validate_url, + false/*$no_response*/, true/*$bad_response*/, + $text_response + ); + $result = false; + } + if ($result) { + $this->_renameSession($this->getTicket()); + } + // at this step, Ticket 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 XML + * payload and put them into an array, then put the array into the session. + * + * @param string $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(); + + // "Jasig Style" Attributes: + // + // + // + // jsmith + // + // RubyCAS + // Smith + // John + // CN=Staff,OU=Groups,DC=example,DC=edu + // CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu + // + // PGTIOU-84678-8a9d2sfa23casd + // + // + // + 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 + ); + } elseif ( $success_elements->item(0)->getElementsByTagName("attributes")->length != 0) { + $attr_nodes = $success_elements->item(0) + ->getElementsByTagName("attributes"); + phpCas :: trace("Found nested jasig style attributes"); + if ($attr_nodes->item(0)->hasChildNodes()) { + // Nested Attributes + foreach ($attr_nodes->item(0)->childNodes as $attr_child) { + phpCas :: trace( + "Attribute [".$attr_child->localName."] = " + .$attr_child->nodeValue + ); + $this->_addAttributeToArray( + $extra_attributes, $attr_child->localName, + $attr_child->nodeValue + ); + } + } + } else { + // "RubyCAS Style" attributes + // + // + // + // jsmith + // + // RubyCAS + // Smith + // John + // CN=Staff,OU=Groups,DC=example,DC=edu + // CN=Spanish Department,OU=Departments,OU=Groups,DC=example,DC=edu + // + // PGTIOU-84678-8a9d2sfa23casd + // + // + // + phpCas :: trace("Testing for rubycas style attributes"); + $childnodes = $success_elements->item(0)->childNodes; + foreach ($childnodes as $attr_node) { + switch ($attr_node->localName) { + case 'user': + case 'proxies': + case 'proxyGrantingTicket': + break; + default: + if (strlen(trim($attr_node->nodeValue))) { + phpCas :: trace( + "Attribute [".$attr_node->localName."] = ".$attr_node->nodeValue + ); + $this->_addAttributeToArray( + $extra_attributes, $attr_node->localName, + $attr_node->nodeValue + ); + } + } + } + } + + // "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. + // + // + // + // jsmith + // + // + // + // + // + // + // + // PGTIOU-84678-8a9d2sfa23casd + // + // + // + if (!count($extra_attributes) + && $success_elements->item(0)->getElementsByTagName("attribute")->length != 0 + ) { + $attr_nodes = $success_elements->item(0) + ->getElementsByTagName("attribute"); + $firstAttr = $attr_nodes->item(0); + if (!$firstAttr->hasChildNodes() + && $firstAttr->hasAttribute('name') + && $firstAttr->hasAttribute('value') + ) { + phpCas :: trace("Found Name-Value style attributes"); + // Nested Attributes + foreach ($attr_nodes as $attr_node) { + if ($attr_node->hasAttribute('name') + && $attr_node->hasAttribute('value') + ) { + phpCas :: trace( + "Attribute [".$attr_node->getAttribute('name') + ."] = ".$attr_node->getAttribute('value') + ); + $this->_addAttributeToArray( + $extra_attributes, $attr_node->getAttribute('name'), + $attr_node->getAttribute('value') + ); + } + } + } + } + + $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 The URL + */ + public function getURL() + { + phpCAS::traceBegin(); + // the URL is built when needed only + if ( empty($this->_url) ) { + $final_uri = ''; + // 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() + { + $server_url = ''; + 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 url with query params + */ + private function _buildQueryUrl($url, $query) + { + $url .= (strstr($url, '?') === false) ? '?' : '&'; + $url .= $query; + return $url; + } + + /** + * Renaming the session + * + * @param string $ticket name of the ticket + * + * @return void + */ + private 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(); + } + + /** + * 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, + $bad_response='', + $cas_response='', + $err_code='', + $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 ( empty($err_code) ) { + 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 string 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(); + } + + /** @} */ +} + +?> diff --git a/phpCAS-1.3.6/source/CAS/CookieJar.php b/phpCAS-1.3.6/source/CAS/CookieJar.php new file mode 100644 index 0000000..549b892 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/CookieJar.php @@ -0,0 +1,385 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This class provides access to service cookies and handles parsing of response + * headers to pull out cookie values. + * + * @class CAS_CookieJar + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_CookieJar +{ + + private $_cookies; + + /** + * Create a new cookie jar by passing it a reference to an array in which it + * should store cookies. + * + * @param array &$storageArray Array to store cookies + * + * @return void + */ + public function __construct (array &$storageArray) + { + $this->_cookies =& $storageArray; + } + + /** + * Store cookies for a web service request. + * Cookie storage is based on RFC 2965: http://www.ietf.org/rfc/rfc2965.txt + * + * @param string $request_url The URL that generated the response headers. + * @param array $response_headers An array of the HTTP response header strings. + * + * @return void + * + * @access private + */ + public function storeCookies ($request_url, $response_headers) + { + $urlParts = parse_url($request_url); + $defaultDomain = $urlParts['host']; + + $cookies = $this->parseCookieHeaders($response_headers, $defaultDomain); + + // var_dump($cookies); + foreach ($cookies as $cookie) { + // Enforce the same-origin policy by verifying that the cookie + // would match the url that is setting it + if (!$this->cookieMatchesTarget($cookie, $urlParts)) { + continue; + } + + // store the cookie + $this->storeCookie($cookie); + + phpCAS::trace($cookie['name'].' -> '.$cookie['value']); + } + } + + /** + * Retrieve cookies applicable for a web service request. + * Cookie applicability is based on RFC 2965: http://www.ietf.org/rfc/rfc2965.txt + * + * @param string $request_url The url that the cookies will be for. + * + * @return array An array containing cookies. E.g. array('name' => 'val'); + * + * @access private + */ + public function getCookies ($request_url) + { + if (!count($this->_cookies)) { + return array(); + } + + // If our request URL can't be parsed, no cookies apply. + $target = parse_url($request_url); + if ($target === false) { + return array(); + } + + $this->expireCookies(); + + $matching_cookies = array(); + foreach ($this->_cookies as $key => $cookie) { + if ($this->cookieMatchesTarget($cookie, $target)) { + $matching_cookies[$cookie['name']] = $cookie['value']; + } + } + return $matching_cookies; + } + + + /** + * Parse Cookies without PECL + * From the comments in http://php.net/manual/en/function.http-parse-cookie.php + * + * @param array $header array of header lines. + * @param string $defaultDomain The domain to use if none is specified in + * the cookie. + * + * @return array of cookies + */ + protected function parseCookieHeaders( $header, $defaultDomain ) + { + phpCAS::traceBegin(); + $cookies = array(); + foreach ( $header as $line ) { + if ( preg_match('/^Set-Cookie2?: /i', $line)) { + $cookies[] = $this->parseCookieHeader($line, $defaultDomain); + } + } + + phpCAS::traceEnd($cookies); + return $cookies; + } + + /** + * Parse a single cookie header line. + * + * Based on RFC2965 http://www.ietf.org/rfc/rfc2965.txt + * + * @param string $line The header line. + * @param string $defaultDomain The domain to use if none is specified in + * the cookie. + * + * @return array + */ + protected function parseCookieHeader ($line, $defaultDomain) + { + if (!$defaultDomain) { + throw new CAS_InvalidArgumentException( + '$defaultDomain was not provided.' + ); + } + + // Set our default values + $cookie = array( + 'domain' => $defaultDomain, + 'path' => '/', + 'secure' => false, + ); + + $line = preg_replace('/^Set-Cookie2?: /i', '', trim($line)); + + // trim any trailing semicolons. + $line = trim($line, ';'); + + phpCAS::trace("Cookie Line: $line"); + + // This implementation makes the assumption that semicolons will not + // be present in quoted attribute values. While attribute values that + // contain semicolons are allowed by RFC2965, they are hopefully rare + // enough to ignore for our purposes. Most browsers make the same + // assumption. + $attributeStrings = explode(';', $line); + + foreach ( $attributeStrings as $attributeString ) { + // split on the first equals sign and use the rest as value + $attributeParts = explode('=', $attributeString, 2); + + $attributeName = trim($attributeParts[0]); + $attributeNameLC = strtolower($attributeName); + + if (isset($attributeParts[1])) { + $attributeValue = trim($attributeParts[1]); + // Values may be quoted strings. + if (strpos($attributeValue, '"') === 0) { + $attributeValue = trim($attributeValue, '"'); + // unescape any escaped quotes: + $attributeValue = str_replace('\"', '"', $attributeValue); + } + } else { + $attributeValue = null; + } + + switch ($attributeNameLC) { + case 'expires': + $cookie['expires'] = strtotime($attributeValue); + break; + case 'max-age': + $cookie['max-age'] = (int)$attributeValue; + // Set an expiry time based on the max-age + if ($cookie['max-age']) { + $cookie['expires'] = time() + $cookie['max-age']; + } else { + // If max-age is zero, then the cookie should be removed + // imediately so set an expiry before now. + $cookie['expires'] = time() - 1; + } + break; + case 'secure': + $cookie['secure'] = true; + break; + case 'domain': + case 'path': + case 'port': + case 'version': + case 'comment': + case 'commenturl': + case 'discard': + case 'httponly': + $cookie[$attributeNameLC] = $attributeValue; + break; + default: + $cookie['name'] = $attributeName; + $cookie['value'] = $attributeValue; + } + } + + return $cookie; + } + + /** + * Add, update, or remove a cookie. + * + * @param array $cookie A cookie array as created by parseCookieHeaders() + * + * @return void + * + * @access protected + */ + protected function storeCookie ($cookie) + { + // Discard any old versions of this cookie. + $this->discardCookie($cookie); + $this->_cookies[] = $cookie; + + } + + /** + * Discard an existing cookie + * + * @param array $cookie An cookie + * + * @return void + * + * @access protected + */ + protected function discardCookie ($cookie) + { + if (!isset($cookie['domain']) + || !isset($cookie['path']) + || !isset($cookie['path']) + ) { + throw new CAS_InvalidArgumentException('Invalid Cookie array passed.'); + } + + foreach ($this->_cookies as $key => $old_cookie) { + if ( $cookie['domain'] == $old_cookie['domain'] + && $cookie['path'] == $old_cookie['path'] + && $cookie['name'] == $old_cookie['name'] + ) { + unset($this->_cookies[$key]); + } + } + } + + /** + * Go through our stored cookies and remove any that are expired. + * + * @return void + * + * @access protected + */ + protected function expireCookies () + { + foreach ($this->_cookies as $key => $cookie) { + if (isset($cookie['expires']) && $cookie['expires'] < time()) { + unset($this->_cookies[$key]); + } + } + } + + /** + * Answer true if cookie is applicable to a target. + * + * @param array $cookie An array of cookie attributes. + * @param array $target An array of URL attributes as generated by parse_url(). + * + * @return bool + * + * @access private + */ + protected function cookieMatchesTarget ($cookie, $target) + { + if (!is_array($target)) { + throw new CAS_InvalidArgumentException( + '$target must be an array of URL attributes as generated by parse_url().' + ); + } + if (!isset($target['host'])) { + throw new CAS_InvalidArgumentException( + '$target must be an array of URL attributes as generated by parse_url().' + ); + } + + // Verify that the scheme matches + if ($cookie['secure'] && $target['scheme'] != 'https') { + return false; + } + + // Verify that the host matches + // Match domain and mulit-host cookies + if (strpos($cookie['domain'], '.') === 0) { + // .host.domain.edu cookies are valid for host.domain.edu + if (substr($cookie['domain'], 1) == $target['host']) { + // continue with other checks + } else { + // non-exact host-name matches. + // check that the target host a.b.c.edu is within .b.c.edu + $pos = strripos($target['host'], $cookie['domain']); + if (!$pos) { + return false; + } + // verify that the cookie domain is the last part of the host. + if ($pos + strlen($cookie['domain']) != strlen($target['host'])) { + return false; + } + // verify that the host name does not contain interior dots as per + // RFC 2965 section 3.3.2 Rejecting Cookies + // http://www.ietf.org/rfc/rfc2965.txt + $hostname = substr($target['host'], 0, $pos); + if (strpos($hostname, '.') !== false) { + return false; + } + } + } else { + // If the cookie host doesn't begin with '.', + // the host must case-insensitive match exactly + if (strcasecmp($target['host'], $cookie['domain']) !== 0) { + return false; + } + } + + // Verify that the port matches + if (isset($cookie['ports']) + && !in_array($target['port'], $cookie['ports']) + ) { + return false; + } + + // Verify that the path matches + if (strpos($target['path'], $cookie['path']) !== 0) { + return false; + } + + return true; + } + +} + +?> diff --git a/phpCAS-1.3.6/source/CAS/Exception.php b/phpCAS-1.3.6/source/CAS/Exception.php new file mode 100644 index 0000000..d956d19 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Exception.php @@ -0,0 +1,59 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * A root exception interface for all exceptions in phpCAS. + * + * All exceptions thrown in phpCAS should implement this interface to allow them + * to be caught as a category by clients. Each phpCAS exception should extend + * an appropriate SPL exception class that best fits its type. + * + * For example, an InvalidArgumentException in phpCAS should be defined as + * + * class CAS_InvalidArgumentException + * extends InvalidArgumentException + * implements CAS_Exception + * { } + * + * This definition allows the CAS_InvalidArgumentException to be caught as either + * an InvalidArgumentException or as a CAS_Exception. + * + * @class CAS_Exception + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + */ +interface CAS_Exception +{ + +} +?> \ No newline at end of file diff --git a/phpCAS-1.3.6/source/CAS/GracefullTerminationException.php b/phpCAS-1.3.6/source/CAS/GracefullTerminationException.php new file mode 100644 index 0000000..6d845df --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/GracefullTerminationException.php @@ -0,0 +1,86 @@ + + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * An exception for terminatinating execution or to throw for unit testing + * + * @class CAS_GracefullTerminationException.php + * @category Authentication + * @package PhpCAS + * @author Joachim Fritschi + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +class CAS_GracefullTerminationException +extends RuntimeException +implements CAS_Exception +{ + + /** + * Test if exceptions should be thrown or if we should just exit. + * In production usage we want to just exit cleanly when prompting the user + * for a redirect without filling the error logs with uncaught exceptions. + * In unit testing scenarios we cannot exit or we won't be able to continue + * with our tests. + * + * @param string $message Message Text + * @param string $code Error code + * + * @return void + */ + public function __construct ($message = 'Terminate Gracefully', $code = 0) + { + // Exit cleanly to avoid filling up the logs with uncaught exceptions. + if (self::$_exitWhenThrown) { + exit; + } else { + // Throw exceptions to allow unit testing to continue; + parent::__construct($message, $code); + } + } + + private static $_exitWhenThrown = true; + /** + * Force phpcas to thow Exceptions instead of calling exit() + * Needed for unit testing. Generally shouldn't be used in production due to + * an increase in Apache error logging if CAS_GracefulTerminiationExceptions + * are not caught and handled. + * + * @return void + */ + public static function throwInsteadOfExiting() + { + self::$_exitWhenThrown = false; + } + +} +?> \ No newline at end of file diff --git a/phpCAS-1.3.6/source/CAS/InvalidArgumentException.php b/phpCAS-1.3.6/source/CAS/InvalidArgumentException.php new file mode 100644 index 0000000..ba43d39 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/InvalidArgumentException.php @@ -0,0 +1,46 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Exception that denotes invalid arguments were passed. + * + * @class CAS_InvalidArgumentException + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_InvalidArgumentException +extends InvalidArgumentException +implements CAS_Exception +{ + +} +?> \ No newline at end of file diff --git a/phpCAS-1.3.6/source/CAS/Languages/Catalan.php b/phpCAS-1.3.6/source/CAS/Languages/Catalan.php new file mode 100644 index 0000000..a0b64d8 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Languages/Catalan.php @@ -0,0 +1,114 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Catalan language class + * + * @class CAS_Languages_Catalan + * @category Authentication + * @package PhpCAS + * @author Iván-Benjamín García Torà + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ +class CAS_Languages_Catalan implements CAS_Languages_LanguageInterface +{ + /** + * Get the using server string + * + * @return string using server + */ + public function getUsingServer() + { + return 'usant servidor'; + } + + /** + * Get authentication wanted string + * + * @return string authentication wanted + */ + public function getAuthenticationWanted() + { + return 'Autentificació CAS necessària!'; + } + + /** + * Get logout string + * + * @return string logout + */ + public function getLogout() + { + return 'Sortida de CAS necessària!'; + } + + /** + * Get the should have been redirected string + * + * @return string should habe been redirected + */ + public function getShouldHaveBeenRedirected() + { + return 'Ja hauria d\ haver estat redireccionat al servidor CAS. Feu click aquí per a continuar.'; + } + + /** + * Get authentication failed string + * + * @return string authentication failed + */ + public function getAuthenticationFailed() + { + return 'Autentificació CAS fallida!'; + } + + /** + * Get the your were not authenticated string + * + * @return string not authenticated + */ + public function getYouWereNotAuthenticated() + { + return '

No estàs autentificat.

Pots tornar a intentar-ho fent click aquí.

Si el problema persisteix hauría de contactar amb l\'administrador d\'aquest llocc.

'; + } + + /** + * Get the service unavailable string + * + * @return string service unavailable + */ + public function getServiceUnavailable() + { + return 'El servei `%s\' no està disponible (%s).'; + } +} diff --git a/phpCAS-1.3.6/source/CAS/Languages/ChineseSimplified.php b/phpCAS-1.3.6/source/CAS/Languages/ChineseSimplified.php new file mode 100644 index 0000000..bb66593 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Languages/ChineseSimplified.php @@ -0,0 +1,114 @@ +, Phy25 + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Chinese Simplified language class + * + * @class CAS_Languages_ChineseSimplified + * @category Authentication + * @package PhpCAS + * @author Pascal Aubry , Phy25 + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ +class CAS_Languages_ChineseSimplified implements CAS_Languages_LanguageInterface +{ + /** + * Get the using server string + * + * @return string using server + */ + public function getUsingServer() + { + return '连接的服务器'; + } + + /** + * Get authentication wanted string + * + * @return string authentication wanted + */ + public function getAuthenticationWanted() + { + return '请进行 CAS 认证!'; + } + + /** + * Get logout string + * + * @return string logout + */ + public function getLogout() + { + return '请进行 CAS 登出!'; + } + + /** + * Get the should have been redirected string + * + * @return string should habe been redirected + */ + public function getShouldHaveBeenRedirected() + { + return '你正被重定向到 CAS 服务器。点击这里继续。'; + } + + /** + * Get authentication failed string + * + * @return string authentication failed + */ + public function getAuthenticationFailed() + { + return 'CAS 认证失败!'; + } + + /** + * Get the your were not authenticated string + * + * @return string not authenticated + */ + public function getYouWereNotAuthenticated() + { + return '

你没有成功登录。

你可以点击这里重新登录

如果问题依然存在,请联系本站管理员

'; + } + + /** + * Get the service unavailable string + * + * @return string service unavailable + */ + public function getServiceUnavailable() + { + return '服务器 %s 不可用(%s)。'; + } +} \ No newline at end of file diff --git a/phpCAS-1.3.6/source/CAS/Languages/English.php b/phpCAS-1.3.6/source/CAS/Languages/English.php new file mode 100644 index 0000000..002c1ba --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Languages/English.php @@ -0,0 +1,114 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * English language class + * + * @class CAS_Languages_English + * @category Authentication + * @package PhpCAS + * @author Pascal Aubry + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ +class CAS_Languages_English implements CAS_Languages_LanguageInterface +{ + /** + * Get the using server string + * + * @return string using server + */ + public function getUsingServer() + { + return 'using server'; + } + + /** + * Get authentication wanted string + * + * @return string authentication wanted + */ + public function getAuthenticationWanted() + { + return 'CAS Authentication wanted!'; + } + + /** + * Get logout string + * + * @return string logout + */ + public function getLogout() + { + return 'CAS logout wanted!'; + } + + /** + * Get the should have been redirected string + * + * @return string should habe been redirected + */ + public function getShouldHaveBeenRedirected() + { + return 'You should already have been redirected to the CAS server. Click here to continue.'; + } + + /** + * Get authentication failed string + * + * @return string authentication failed + */ + public function getAuthenticationFailed() + { + return 'CAS Authentication failed!'; + } + + /** + * Get the your were not authenticated string + * + * @return string not authenticated + */ + public function getYouWereNotAuthenticated() + { + return '

You were not authenticated.

You may submit your request again by clicking here.

If the problem persists, you may contact the administrator of this site.

'; + } + + /** + * Get the service unavailable string + * + * @return string service unavailable + */ + public function getServiceUnavailable() + { + return 'The service `%s\' is not available (%s).'; + } +} \ No newline at end of file diff --git a/phpCAS-1.3.6/source/CAS/Languages/French.php b/phpCAS-1.3.6/source/CAS/Languages/French.php new file mode 100644 index 0000000..b99847a --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Languages/French.php @@ -0,0 +1,116 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * French language class + * + * @class CAS_Languages_French + * @category Authentication + * @package PhpCAS + * @author Pascal Aubry + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ +class CAS_Languages_French implements CAS_Languages_LanguageInterface +{ + /** + * Get the using server string + * + * @return string using server + */ + public function getUsingServer() + { + return 'utilisant le serveur'; + } + + /** + * Get authentication wanted string + * + * @return string authentication wanted + */ + public function getAuthenticationWanted() + { + return 'Authentication CAS nécessaire !'; + } + + /** + * Get logout string + * + * @return string logout + */ + public function getLogout() + { + return 'Déconnexion demandée !'; + } + + /** + * Get the should have been redirected string + * + * @return string should habe been redirected + */ + public function getShouldHaveBeenRedirected() + { + return 'Vous auriez du etre redirigé(e) vers le serveur CAS. Cliquez ici pour continuer.'; + } + + /** + * Get authentication failed string + * + * @return string authentication failed + */ + public function getAuthenticationFailed() + { + return 'Authentification CAS infructueuse !'; + } + + /** + * Get the your were not authenticated string + * + * @return string not authenticated + */ + public function getYouWereNotAuthenticated() + { + return '

Vous n\'avez pas été authentifié(e).

Vous pouvez soumettre votre requete à nouveau en cliquant ici.

Si le problème persiste, vous pouvez contacter l\'administrateur de ce site.

'; + } + + /** + * Get the service unavailable string + * + * @return string service unavailable + */ + public function getServiceUnavailable() + { + return 'Le service `%s\' est indisponible (%s)'; + } +} + +?> \ No newline at end of file diff --git a/phpCAS-1.3.6/source/CAS/Languages/German.php b/phpCAS-1.3.6/source/CAS/Languages/German.php new file mode 100644 index 0000000..ed3150a --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Languages/German.php @@ -0,0 +1,116 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * German language class + * + * @class CAS_Languages_German + * @category Authentication + * @package PhpCAS + * @author Henrik Genssen + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ +class CAS_Languages_German implements CAS_Languages_LanguageInterface +{ + /** + * Get the using server string + * + * @return string using server + */ + public function getUsingServer() + { + return 'via Server'; + } + + /** + * Get authentication wanted string + * + * @return string authentication wanted + */ + public function getAuthenticationWanted() + { + return 'CAS Authentifizierung erforderlich!'; + } + + /** + * Get logout string + * + * @return string logout + */ + public function getLogout() + { + return 'CAS Abmeldung!'; + } + + /** + * Get the should have been redirected string + * + * @return string should habe been redirected + */ + public function getShouldHaveBeenRedirected() + { + return 'eigentlich häten Sie zum CAS Server weitergeleitet werden sollen. Drücken Sie hier um fortzufahren.'; + } + + /** + * Get authentication failed string + * + * @return string authentication failed + */ + public function getAuthenticationFailed() + { + return 'CAS Anmeldung fehlgeschlagen!'; + } + + /** + * Get the your were not authenticated string + * + * @return string not authenticated + */ + public function getYouWereNotAuthenticated() + { + return '

Sie wurden nicht angemeldet.

Um es erneut zu versuchen klicken Sie hier.

Wenn das Problem bestehen bleibt, kontaktieren Sie den Administrator dieser Seite.

'; + } + + /** + * Get the service unavailable string + * + * @return string service unavailable + */ + public function getServiceUnavailable() + { + return 'Der Dienst `%s\' ist nicht verfügbar (%s).'; + } +} + +?> diff --git a/phpCAS-1.3.6/source/CAS/Languages/Greek.php b/phpCAS-1.3.6/source/CAS/Languages/Greek.php new file mode 100644 index 0000000..888ce24 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Languages/Greek.php @@ -0,0 +1,115 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Greek language class + * + * @class CAS_Languages_Greek + * @category Authentication + * @package PhpCAS + * @author Vangelis Haniotakis + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ +class CAS_Languages_Greek implements CAS_Languages_LanguageInterface +{ + /** + * Get the using server string + * + * @return string using server + */ + public function getUsingServer() + { + return 'χρησιμοποιείται ο εξυπηρετητής'; + } + + /** + * Get authentication wanted string + * + * @return string authentication wanted + */ + public function getAuthenticationWanted() + { + return 'Απαιτείται η ταυτοποίηση CAS!'; + } + + /** + * Get logout string + * + * @return string logout + */ + public function getLogout() + { + return 'Απαιτείται η αποσύνδεση από CAS!'; + } + + /** + * Get the should have been redirected string + * + * @return string should habe been redirected + */ + public function getShouldHaveBeenRedirected() + { + return 'Θα έπρεπε να είχατε ανακατευθυνθεί στον εξυπηρετητή CAS. Κάντε κλίκ εδώ για να συνεχίσετε.'; + } + + /** + * Get authentication failed string + * + * @return string authentication failed + */ + public function getAuthenticationFailed() + { + return 'Η ταυτοποίηση CAS απέτυχε!'; + } + + /** + * Get the your were not authenticated string + * + * @return string not authenticated + */ + public function getYouWereNotAuthenticated() + { + return '

Δεν ταυτοποιηθήκατε.

Μπορείτε να ξαναπροσπαθήσετε, κάνοντας κλίκ εδώ.

Εαν το πρόβλημα επιμείνει, ελάτε σε επαφή με τον διαχειριστή.

'; + } + + /** + * Get the service unavailable string + * + * @return string service unavailable + */ + public function getServiceUnavailable() + { + return 'Η υπηρεσία `%s\' δεν είναι διαθέσιμη (%s).'; + } +} +?> diff --git a/phpCAS-1.3.6/source/CAS/Languages/Japanese.php b/phpCAS-1.3.6/source/CAS/Languages/Japanese.php new file mode 100644 index 0000000..a15bf17 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Languages/Japanese.php @@ -0,0 +1,113 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Japanese language class. Now Encoding is UTF-8. + * + * @class CAS_Languages_Japanese + * @category Authentication + * @package PhpCAS + * @author fnorif + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + **/ +class CAS_Languages_Japanese implements CAS_Languages_LanguageInterface +{ + /** + * Get the using server string + * + * @return string using server + */ + public function getUsingServer() + { + return 'サーバーを使っています。'; + } + + /** + * Get authentication wanted string + * + * @return string authentication wanted + */ + public function getAuthenticationWanted() + { + return 'CASによる認証を行います。'; + } + + /** + * Get logout string + * + * @return string logout + */ + public function getLogout() + { + return 'CASからログアウトします!'; + } + + /** + * Get the should have been redirected string + * + * @return string should habe been redirected + */ + public function getShouldHaveBeenRedirected() + { + return 'CASサーバに行く必要があります。自動的に転送されない場合は こちら をクリックして続行します。'; + } + + /** + * Get authentication failed string + * + * @return string authentication failed + */ + public function getAuthenticationFailed() + { + return 'CASによる認証に失敗しました。'; + } + + /** + * Get the your were not authenticated string + * + * @return string not authenticated + */ + public function getYouWereNotAuthenticated() + { + return '

認証できませんでした。

もう一度リクエストを送信する場合はこちらをクリック。

問題が解決しない場合は このサイトの管理者に問い合わせてください。

'; + } + + /** + * Get the service unavailable string + * + * @return string service unavailable + */ + public function getServiceUnavailable() + { + return 'サービス `%s\' は利用できません (%s)。'; + } +} +?> diff --git a/phpCAS-1.3.6/source/CAS/Languages/LanguageInterface.php b/phpCAS-1.3.6/source/CAS/Languages/LanguageInterface.php new file mode 100644 index 0000000..5de93aa --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Languages/LanguageInterface.php @@ -0,0 +1,96 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Language Interface class for all internationalization files + * + * @class CAS_Languages_LanguageInterface + * @category Authentication + * @package PhpCAS + * @author Joachim Fritschi + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ + +interface CAS_Languages_LanguageInterface +{ + /** + * Get the using server string + * + * @return string using server + */ + public function getUsingServer(); + + /** + * Get authentication wanted string + * + * @return string authentication wanted + */ + public function getAuthenticationWanted(); + + /** + * Get logout string + * + * @return string logout + */ + public function getLogout(); + + /** + * Get the should have been redirected string + * + * @return string should habe been redirected + */ + public function getShouldHaveBeenRedirected(); + + /** + * Get authentication failed string + * + * @return string authentication failed + */ + public function getAuthenticationFailed(); + + /** + * Get the your were not authenticated string + * + * @return string not authenticated + */ + public function getYouWereNotAuthenticated(); + + /** + * Get the service unavailable string + * + * @return string service unavailable + */ + public function getServiceUnavailable(); + +} +?> \ No newline at end of file diff --git a/phpCAS-1.3.6/source/CAS/Languages/Spanish.php b/phpCAS-1.3.6/source/CAS/Languages/Spanish.php new file mode 100644 index 0000000..5675a41 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Languages/Spanish.php @@ -0,0 +1,117 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Spanish language class + * + * @class CAS_Languages_Spanish + * @category Authentication + * @package PhpCAS + * @author Iván-Benjamín García Torà + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + + * @sa @link internalLang Internationalization @endlink + * @ingroup internalLang + */ +class CAS_Languages_Spanish implements CAS_Languages_LanguageInterface +{ + + /** + * Get the using server string + * + * @return string using server + */ + public function getUsingServer() + { + return 'usando servidor'; + } + + /** + * Get authentication wanted string + * + * @return string authentication wanted + */ + public function getAuthenticationWanted() + { + return '¡Autentificación CAS necesaria!'; + } + + /** + * Get logout string + * + * @return string logout + */ + public function getLogout() + { + return '¡Salida CAS necesaria!'; + } + + /** + * Get the should have been redirected string + * + * @return string should habe been redirected + */ + public function getShouldHaveBeenRedirected() + { + return 'Ya debería haber sido redireccionado al servidor CAS. Haga click aquí para continuar.'; + } + + /** + * Get authentication failed string + * + * @return string authentication failed + */ + public function getAuthenticationFailed() + { + return '¡Autentificación CAS fallida!'; + } + + /** + * Get the your were not authenticated string + * + * @return string not authenticated + */ + public function getYouWereNotAuthenticated() + { + return '

No estás autentificado.

Puedes volver a intentarlo haciendo click aquí.

Si el problema persiste debería contactar con el administrador de este sitio.

'; + } + + /** + * Get the service unavailable string + * + * @return string service unavailable + */ + public function getServiceUnavailable() + { + return 'El servicio `%s\' no está disponible (%s).'; + } +} +?> diff --git a/phpCAS-1.3.6/source/CAS/OutOfSequenceBeforeAuthenticationCallException.php b/phpCAS-1.3.6/source/CAS/OutOfSequenceBeforeAuthenticationCallException.php new file mode 100644 index 0000000..ef83097 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/OutOfSequenceBeforeAuthenticationCallException.php @@ -0,0 +1,56 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This class defines Exceptions that should be thrown when the sequence of + * operations is invalid. In this case it should be thrown when an + * authentication call has not yet happened. + * + * @class CAS_OutOfSequenceBeforeAuthenticationCallException + * @category Authentication + * @package PhpCAS + * @author Joachim Fritschi + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_OutOfSequenceBeforeAuthenticationCallException +extends CAS_OutOfSequenceException +implements CAS_Exception +{ + /** + * Return standard error meessage + * + * @return void + */ + public function __construct () + { + parent::__construct('An authentication call hasn\'t happened yet.'); + } +} diff --git a/phpCAS-1.3.6/source/CAS/OutOfSequenceBeforeClientException.php b/phpCAS-1.3.6/source/CAS/OutOfSequenceBeforeClientException.php new file mode 100644 index 0000000..f1ea7e2 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/OutOfSequenceBeforeClientException.php @@ -0,0 +1,58 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This class defines Exceptions that should be thrown when the sequence of + * operations is invalid. In this case it should be thrown when the client() or + * proxy() call has not yet happened and no client or proxy object exists. + * + * @class CAS_OutOfSequenceBeforeClientException + * @category Authentication + * @package PhpCAS + * @author Joachim Fritschi + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_OutOfSequenceBeforeClientException +extends CAS_OutOfSequenceException +implements CAS_Exception +{ + /** + * Return standard error message + * + * @return void + */ + public function __construct () + { + parent::__construct( + 'this method cannot be called before phpCAS::client() or phpCAS::proxy()' + ); + } +} diff --git a/phpCAS-1.3.6/source/CAS/OutOfSequenceBeforeProxyException.php b/phpCAS-1.3.6/source/CAS/OutOfSequenceBeforeProxyException.php new file mode 100644 index 0000000..8038542 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/OutOfSequenceBeforeProxyException.php @@ -0,0 +1,59 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This class defines Exceptions that should be thrown when the sequence of + * operations is invalid. In this case it should be thrown when the proxy() call + * has not yet happened and no proxy object exists. + * + * @class CAS_OutOfSequenceBeforeProxyException + * @category Authentication + * @package PhpCAS + * @author Joachim Fritschi + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_OutOfSequenceBeforeProxyException +extends CAS_OutOfSequenceException +implements CAS_Exception +{ + + /** + * Return standard error message + * + * @return void + */ + public function __construct () + { + parent::__construct( + 'this method cannot be called before phpCAS::proxy()' + ); + } +} diff --git a/phpCAS-1.3.6/source/CAS/OutOfSequenceException.php b/phpCAS-1.3.6/source/CAS/OutOfSequenceException.php new file mode 100644 index 0000000..d101811 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/OutOfSequenceException.php @@ -0,0 +1,49 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This class defines Exceptions that should be thrown when the sequence of + * operations is invalid. Examples are: + * - Requesting the response before executing a request. + * - Changing the URL of a request after executing the request. + * + * @class CAS_OutOfSequenceException + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_OutOfSequenceException +extends BadMethodCallException +implements CAS_Exception +{ + +} diff --git a/phpCAS-1.3.6/source/CAS/PGTStorage/AbstractStorage.php b/phpCAS-1.3.6/source/CAS/PGTStorage/AbstractStorage.php new file mode 100644 index 0000000..c164898 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/PGTStorage/AbstractStorage.php @@ -0,0 +1,222 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Basic class for PGT storage + * The CAS_PGTStorage_AbstractStorage class is a generic class for PGT storage. + * This class should not be instanciated itself but inherited by specific PGT + * storage classes. + * + * @class CAS_PGTStorage_AbstractStorage + * @category Authentication + * @package PhpCAS + * @author Pascal Aubry + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * @ingroup internalPGTStorage + */ + +abstract class CAS_PGTStorage_AbstractStorage +{ + /** + * @addtogroup internalPGTStorage + * @{ + */ + + // ######################################################################## + // CONSTRUCTOR + // ######################################################################## + + /** + * The constructor of the class, should be called only by inherited classes. + * + * @param CAS_Client $cas_parent the CAS _client instance that creates the + * current object. + * + * @return void + * + * @protected + */ + function __construct($cas_parent) + { + phpCAS::traceBegin(); + if ( !$cas_parent->isProxy() ) { + phpCAS::error( + 'defining PGT storage makes no sense when not using a CAS proxy' + ); + } + phpCAS::traceEnd(); + } + + // ######################################################################## + // DEBUGGING + // ######################################################################## + + /** + * This virtual method returns an informational string giving the type of storage + * used by the object (used for debugging purposes). + * + * @return void + * + * @public + */ + function getStorageType() + { + phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); + } + + /** + * This virtual method returns an informational string giving informations on the + * parameters of the storage.(used for debugging purposes). + * + * @return void + * + * @public + */ + function getStorageInfo() + { + phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); + } + + // ######################################################################## + // ERROR HANDLING + // ######################################################################## + + /** + * string used to store an error message. Written by + * PGTStorage::setErrorMessage(), read by PGTStorage::getErrorMessage(). + * + * @hideinitializer + * @deprecated not used. + */ + var $_error_message=false; + + /** + * This method sets en error message, which can be read later by + * PGTStorage::getErrorMessage(). + * + * @param string $error_message an error message + * + * @return void + * + * @deprecated not used. + */ + function setErrorMessage($error_message) + { + $this->_error_message = $error_message; + } + + /** + * This method returns an error message set by PGTStorage::setErrorMessage(). + * + * @return an error message when set by PGTStorage::setErrorMessage(), FALSE + * otherwise. + * + * @deprecated not used. + */ + function getErrorMessage() + { + return $this->_error_message; + } + + // ######################################################################## + // INITIALIZATION + // ######################################################################## + + /** + * a boolean telling if the storage has already been initialized. Written by + * PGTStorage::init(), read by PGTStorage::isInitialized(). + * + * @hideinitializer + */ + var $_initialized = false; + + /** + * This method tells if the storage has already been intialized. + * + * @return a boolean + * + * @protected + */ + function isInitialized() + { + return $this->_initialized; + } + + /** + * This virtual method initializes the object. + * + * @return void + */ + function init() + { + $this->_initialized = true; + } + + // ######################################################################## + // PGT I/O + // ######################################################################## + + /** + * This virtual method stores a PGT and its corresponding PGT Iuo. + * + * @param string $pgt the PGT + * @param string $pgt_iou the PGT iou + * + * @return void + * + * @note Should never be called. + * + */ + function write($pgt,$pgt_iou) + { + phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); + } + + /** + * This virtual method reads a PGT corresponding to a PGT Iou and deletes + * the corresponding storage entry. + * + * @param string $pgt_iou the PGT iou + * + * @return void + * + * @note Should never be called. + */ + function read($pgt_iou) + { + phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); + } + + /** @} */ + +} + +?> diff --git a/phpCAS-1.3.6/source/CAS/PGTStorage/Db.php b/phpCAS-1.3.6/source/CAS/PGTStorage/Db.php new file mode 100644 index 0000000..c331ca0 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/PGTStorage/Db.php @@ -0,0 +1,440 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +define('CAS_PGT_STORAGE_DB_DEFAULT_TABLE', 'cas_pgts'); + +/** + * Basic class for PGT database storage + * The CAS_PGTStorage_Db class is a class for PGT database storage. + * + * @class CAS_PGTStorage_Db + * @category Authentication + * @package PhpCAS + * @author Daniel Frett + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * @ingroup internalPGTStorageDb + */ + +class CAS_PGTStorage_Db extends CAS_PGTStorage_AbstractStorage +{ + /** + * @addtogroup internalCAS_PGTStorageDb + * @{ + */ + + /** + * the PDO object to use for database interactions + */ + private $_pdo; + + /** + * This method returns the PDO object to use for database interactions. + * + * @return the PDO object + */ + private function _getPdo() + { + return $this->_pdo; + } + + /** + * database connection options to use when creating a new PDO object + */ + private $_dsn; + private $_username; + private $_password; + private $_table_options; + + /** + * the table to use for storing/retrieving pgt's + */ + private $_table; + + /** + * This method returns the table to use when storing/retrieving PGT's + * + * @return the name of the pgt storage table. + */ + private function _getTable() + { + return $this->_table; + } + + // ######################################################################## + // DEBUGGING + // ######################################################################## + + /** + * This method returns an informational string giving the type of storage + * used by the object (used for debugging purposes). + * + * @return an informational string. + */ + public function getStorageType() + { + return "db"; + } + + /** + * This method returns an informational string giving informations on the + * parameters of the storage.(used for debugging purposes). + * + * @return an informational string. + * @public + */ + public function getStorageInfo() + { + return 'table=`'.$this->_getTable().'\''; + } + + // ######################################################################## + // CONSTRUCTOR + // ######################################################################## + + /** + * The class constructor. + * + * @param CAS_Client $cas_parent the CAS_Client instance that creates + * the object. + * @param string $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 PGT's + * @param string $driver_options any driver options to use when + * connecting to the database + */ + public function __construct( + $cas_parent, $dsn_or_pdo, $username='', $password='', $table='', + $driver_options=null + ) { + phpCAS::traceBegin(); + // call the ancestor's constructor + parent::__construct($cas_parent); + + // set default values + if ( empty($table) ) { + $table = CAS_PGT_STORAGE_DB_DEFAULT_TABLE; + } + if ( !is_array($driver_options) ) { + $driver_options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION); + } + + // store the specified parameters + if ($dsn_or_pdo instanceof PDO) { + $this->_pdo = $dsn_or_pdo; + } else { + $this->_dsn = $dsn_or_pdo; + $this->_username = $username; + $this->_password = $password; + $this->_driver_options = $driver_options; + } + + // store the table name + $this->_table = $table; + + phpCAS::traceEnd(); + } + + // ######################################################################## + // INITIALIZATION + // ######################################################################## + + /** + * This method is used to initialize the storage. Halts on error. + * + * @return void + */ + public function init() + { + phpCAS::traceBegin(); + // if the storage has already been initialized, return immediatly + if ($this->isInitialized()) { + return; + } + + // initialize the base object + parent::init(); + + // create the PDO object if it doesn't exist already + if (!($this->_pdo instanceof PDO)) { + try { + $this->_pdo = new PDO( + $this->_dsn, $this->_username, $this->_password, + $this->_driver_options + ); + } + catch(PDOException $e) { + phpCAS::error('Database connection error: ' . $e->getMessage()); + } + } + + phpCAS::traceEnd(); + } + + // ######################################################################## + // PDO database interaction + // ######################################################################## + + /** + * attribute that stores the previous error mode for the PDO handle while + * processing a transaction + */ + private $_errMode; + + /** + * This method will enable the Exception error mode on the PDO object + * + * @return void + */ + private function _setErrorMode() + { + // get PDO object and enable exception error mode + $pdo = $this->_getPdo(); + $this->_errMode = $pdo->getAttribute(PDO::ATTR_ERRMODE); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + + /** + * this method will reset the error mode on the PDO object + * + * @return void + */ + private function _resetErrorMode() + { + // get PDO object and reset the error mode to what it was originally + $pdo = $this->_getPdo(); + $pdo->setAttribute(PDO::ATTR_ERRMODE, $this->_errMode); + } + + // ######################################################################## + // database queries + // ######################################################################## + // these queries are potentially unsafe because the person using this library + // can set the table to use, but there is no reliable way to escape SQL + // fieldnames in PDO yet + + /** + * This method returns the query used to create a pgt storage table + * + * @return the create table SQL, no bind params in query + */ + protected function createTableSql() + { + return 'CREATE TABLE ' . $this->_getTable() + . ' (pgt_iou VARCHAR(255) NOT NULL PRIMARY KEY, pgt VARCHAR(255) NOT NULL)'; + } + + /** + * This method returns the query used to store a pgt + * + * @return the store PGT SQL, :pgt and :pgt_iou are the bind params contained + * in the query + */ + protected function storePgtSql() + { + return 'INSERT INTO ' . $this->_getTable() + . ' (pgt_iou, pgt) VALUES (:pgt_iou, :pgt)'; + } + + /** + * This method returns the query used to retrieve a pgt. the first column + * of the first row should contain the pgt + * + * @return the retrieve PGT SQL, :pgt_iou is the only bind param contained + * in the query + */ + protected function retrievePgtSql() + { + return 'SELECT pgt FROM ' . $this->_getTable() . ' WHERE pgt_iou = :pgt_iou'; + } + + /** + * This method returns the query used to delete a pgt. + * + * @return the delete PGT SQL, :pgt_iou is the only bind param contained in + * the query + */ + protected function deletePgtSql() + { + return 'DELETE FROM ' . $this->_getTable() . ' WHERE pgt_iou = :pgt_iou'; + } + + // ######################################################################## + // PGT I/O + // ######################################################################## + + /** + * This method creates the database table used to store pgt's and pgtiou's + * + * @return void + */ + public function createTable() + { + phpCAS::traceBegin(); + + // initialize this PGTStorage object if it hasn't been initialized yet + if ( !$this->isInitialized() ) { + $this->init(); + } + + // initialize the PDO object for this method + $pdo = $this->_getPdo(); + $this->_setErrorMode(); + + try { + $pdo->beginTransaction(); + + $query = $pdo->query($this->createTableSQL()); + $query->closeCursor(); + + $pdo->commit(); + } + catch(PDOException $e) { + // attempt rolling back the transaction before throwing a phpCAS error + try { + $pdo->rollBack(); + } + catch(PDOException $e) { + } + phpCAS::error('error creating PGT storage table: ' . $e->getMessage()); + } + + // reset the PDO object + $this->_resetErrorMode(); + + phpCAS::traceEnd(); + } + + /** + * This method stores a PGT and its corresponding PGT Iou in the database. + * Echoes a warning on error. + * + * @param string $pgt the PGT + * @param string $pgt_iou the PGT iou + * + * @return void + */ + public function write($pgt, $pgt_iou) + { + phpCAS::traceBegin(); + + // initialize the PDO object for this method + $pdo = $this->_getPdo(); + $this->_setErrorMode(); + + try { + $pdo->beginTransaction(); + + $query = $pdo->prepare($this->storePgtSql()); + $query->bindValue(':pgt', $pgt, PDO::PARAM_STR); + $query->bindValue(':pgt_iou', $pgt_iou, PDO::PARAM_STR); + $query->execute(); + $query->closeCursor(); + + $pdo->commit(); + } + catch(PDOException $e) { + // attempt rolling back the transaction before throwing a phpCAS error + try { + $pdo->rollBack(); + } + catch(PDOException $e) { + } + phpCAS::error('error writing PGT to database: ' . $e->getMessage()); + } + + // reset the PDO object + $this->_resetErrorMode(); + + phpCAS::traceEnd(); + } + + /** + * This method reads a PGT corresponding to a PGT Iou and deletes the + * corresponding db entry. + * + * @param string $pgt_iou the PGT iou + * + * @return the corresponding PGT, or FALSE on error + */ + public function read($pgt_iou) + { + phpCAS::traceBegin(); + $pgt = false; + + // initialize the PDO object for this method + $pdo = $this->_getPdo(); + $this->_setErrorMode(); + + try { + $pdo->beginTransaction(); + + // fetch the pgt for the specified pgt_iou + $query = $pdo->prepare($this->retrievePgtSql()); + $query->bindValue(':pgt_iou', $pgt_iou, PDO::PARAM_STR); + $query->execute(); + $pgt = $query->fetchColumn(0); + $query->closeCursor(); + + // delete the specified pgt_iou from the database + $query = $pdo->prepare($this->deletePgtSql()); + $query->bindValue(':pgt_iou', $pgt_iou, PDO::PARAM_STR); + $query->execute(); + $query->closeCursor(); + + $pdo->commit(); + } + catch(PDOException $e) { + // attempt rolling back the transaction before throwing a phpCAS error + try { + $pdo->rollBack(); + } + catch(PDOException $e) { + } + phpCAS::trace('error reading PGT from database: ' . $e->getMessage()); + } + + // reset the PDO object + $this->_resetErrorMode(); + + phpCAS::traceEnd(); + return $pgt; + } + + /** @} */ + +} + +?> diff --git a/phpCAS-1.3.6/source/CAS/PGTStorage/File.php b/phpCAS-1.3.6/source/CAS/PGTStorage/File.php new file mode 100644 index 0000000..4be80c5 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/PGTStorage/File.php @@ -0,0 +1,261 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * The CAS_PGTStorage_File class is a class for PGT file storage. An instance of + * this class is returned by CAS_Client::SetPGTStorageFile(). + * + * @class CAS_PGTStorage_File + * @category Authentication + * @package PhpCAS + * @author Pascal Aubry + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + * + * @ingroup internalPGTStorageFile + */ + +class CAS_PGTStorage_File extends CAS_PGTStorage_AbstractStorage +{ + /** + * @addtogroup internalPGTStorageFile + * @{ + */ + + /** + * a string telling where PGT's should be stored on the filesystem. Written by + * PGTStorageFile::PGTStorageFile(), read by getPath(). + * + * @private + */ + var $_path; + + /** + * This method returns the name of the directory where PGT's should be stored + * on the filesystem. + * + * @return the name of a directory (with leading and trailing '/') + * + * @private + */ + function getPath() + { + return $this->_path; + } + + // ######################################################################## + // DEBUGGING + // ######################################################################## + + /** + * This method returns an informational string giving the type of storage + * used by the object (used for debugging purposes). + * + * @return an informational string. + * @public + */ + function getStorageType() + { + return "file"; + } + + /** + * This method returns an informational string giving informations on the + * parameters of the storage.(used for debugging purposes). + * + * @return an informational string. + * @public + */ + function getStorageInfo() + { + return 'path=`'.$this->getPath().'\''; + } + + // ######################################################################## + // CONSTRUCTOR + // ######################################################################## + + /** + * The class constructor, called by CAS_Client::SetPGTStorageFile(). + * + * @param CAS_Client $cas_parent the CAS_Client instance that creates the object. + * @param string $path the path where the PGT's should be stored + * + * @return void + * + * @public + */ + function __construct($cas_parent,$path) + { + phpCAS::traceBegin(); + // call the ancestor's constructor + parent::__construct($cas_parent); + + if (empty($path)) { + $path = CAS_PGT_STORAGE_FILE_DEFAULT_PATH; + } + // check that the path is an absolute path + if (getenv("OS")=="Windows_NT" || strtoupper(substr(PHP_OS,0,3)) == 'WIN') { + + if (!preg_match('`^[a-zA-Z]:`', $path)) { + phpCAS::error('an absolute path is needed for PGT storage to file'); + } + + } else { + + if ( $path[0] != '/' ) { + phpCAS::error('an absolute path is needed for PGT storage to file'); + } + + // store the path (with a leading and trailing '/') + $path = preg_replace('|[/]*$|', '/', $path); + $path = preg_replace('|^[/]*|', '/', $path); + } + + $this->_path = $path; + phpCAS::traceEnd(); + } + + // ######################################################################## + // INITIALIZATION + // ######################################################################## + + /** + * This method is used to initialize the storage. Halts on error. + * + * @return void + * @public + */ + function init() + { + phpCAS::traceBegin(); + // if the storage has already been initialized, return immediatly + if ($this->isInitialized()) { + return; + } + // call the ancestor's method (mark as initialized) + parent::init(); + phpCAS::traceEnd(); + } + + // ######################################################################## + // PGT I/O + // ######################################################################## + + /** + * This method returns the filename corresponding to a PGT Iou. + * + * @param string $pgt_iou the PGT iou. + * + * @return a filename + * @private + */ + function getPGTIouFilename($pgt_iou) + { + phpCAS::traceBegin(); + $filename = $this->getPath()."phpcas-".hash("sha256", $pgt_iou); +// $filename = $this->getPath().$pgt_iou.'.plain'; + phpCAS::trace("Sha256 filename:" . $filename); + phpCAS::traceEnd(); + return $filename; + } + + /** + * This method stores a PGT and its corresponding PGT Iou into a file. Echoes a + * warning on error. + * + * @param string $pgt the PGT + * @param string $pgt_iou the PGT iou + * + * @return void + * + * @public + */ + function write($pgt,$pgt_iou) + { + phpCAS::traceBegin(); + $fname = $this->getPGTIouFilename($pgt_iou); + if (!file_exists($fname)) { + touch($fname); + // Chmod will fail on windows + @chmod($fname, 0600); + if ($f=fopen($fname, "w")) { + if (fputs($f, $pgt) === false) { + phpCAS::error('could not write PGT to `'.$fname.'\''); + } + phpCAS::trace('Successful write of PGT to `'.$fname.'\''); + fclose($f); + } else { + phpCAS::error('could not open `'.$fname.'\''); + } + } else { + phpCAS::error('File exists: `'.$fname.'\''); + } + phpCAS::traceEnd(); + } + + /** + * This method reads a PGT corresponding to a PGT Iou and deletes the + * corresponding file. + * + * @param string $pgt_iou the PGT iou + * + * @return the corresponding PGT, or FALSE on error + * + * @public + */ + function read($pgt_iou) + { + phpCAS::traceBegin(); + $pgt = false; + $fname = $this->getPGTIouFilename($pgt_iou); + if (file_exists($fname)) { + if (!($f=fopen($fname, "r"))) { + phpCAS::error('could not open `'.$fname.'\''); + } else { + if (($pgt=fgets($f)) === false) { + phpCAS::error('could not read PGT from `'.$fname.'\''); + } + phpCAS::trace('Successful read of PGT to `'.$fname.'\''); + fclose($f); + } + // delete the PGT file + @unlink($fname); + } else { + phpCAS::error('No such file `'.$fname.'\''); + } + phpCAS::traceEnd($pgt); + return $pgt; + } + + /** @} */ + +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxiedService.php b/phpCAS-1.3.6/source/CAS/ProxiedService.php new file mode 100644 index 0000000..d70ca9c --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxiedService.php @@ -0,0 +1,72 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This interface defines methods that allow proxy-authenticated service handlers + * to interact with phpCAS. + * + * Proxy service handlers must implement this interface as well as call + * phpCAS::initializeProxiedService($this) at some point in their implementation. + * + * While not required, proxy-authenticated service handlers are encouraged to + * implement the CAS_ProxiedService_Testable interface to facilitate unit testing. + * + * @class CAS_ProxiedService + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +interface CAS_ProxiedService +{ + + /** + * Answer a service identifier (URL) for whom we should fetch a proxy ticket. + * + * @return string + * @throws Exception If no service url is available. + */ + public function getServiceUrl (); + + /** + * Register a proxy ticket with the ProxiedService that it can use when + * making requests. + * + * @param string $proxyTicket Proxy ticket string + * + * @return void + * @throws InvalidArgumentException If the $proxyTicket is invalid. + * @throws CAS_OutOfSequenceException If called after a proxy ticket has + * already been initialized/set. + */ + public function setProxyTicket ($proxyTicket); + +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxiedService/Abstract.php b/phpCAS-1.3.6/source/CAS/ProxiedService/Abstract.php new file mode 100644 index 0000000..fade9e7 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxiedService/Abstract.php @@ -0,0 +1,149 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This class implements common methods for ProxiedService implementations included + * with phpCAS. + * + * @class CAS_ProxiedService_Abstract + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +abstract class CAS_ProxiedService_Abstract +implements CAS_ProxiedService, CAS_ProxiedService_Testable +{ + + /** + * The proxy ticket that can be used when making service requests. + * @var string $_proxyTicket; + */ + private $_proxyTicket; + + /** + * Register a proxy ticket with the Proxy that it can use when making requests. + * + * @param string $proxyTicket proxy ticket + * + * @return void + * @throws InvalidArgumentException If the $proxyTicket is invalid. + * @throws CAS_OutOfSequenceException If called after a proxy ticket has + * already been initialized/set. + */ + public function setProxyTicket ($proxyTicket) + { + if (empty($proxyTicket)) { + throw new CAS_InvalidArgumentException( + 'Trying to initialize with an empty proxy ticket.' + ); + } + if (!empty($this->_proxyTicket)) { + throw new CAS_OutOfSequenceException( + 'Already initialized, cannot change the proxy ticket.' + ); + } + $this->_proxyTicket = $proxyTicket; + } + + /** + * Answer the proxy ticket to be used when making requests. + * + * @return string + * @throws CAS_OutOfSequenceException If called before a proxy ticket has + * already been initialized/set. + */ + protected function getProxyTicket () + { + if (empty($this->_proxyTicket)) { + throw new CAS_OutOfSequenceException( + 'No proxy ticket yet. Call $this->initializeProxyTicket() to aquire the proxy ticket.' + ); + } + + return $this->_proxyTicket; + } + + /** + * @var CAS_Client $_casClient; + */ + private $_casClient; + + /** + * Use a particular CAS_Client->initializeProxiedService() rather than the + * static phpCAS::initializeProxiedService(). + * + * This method should not be called in standard operation, but is needed for unit + * testing. + * + * @param CAS_Client $casClient cas client + * + * @return void + * @throws CAS_OutOfSequenceException If called after a proxy ticket has + * already been initialized/set. + */ + public function setCasClient (CAS_Client $casClient) + { + if (!empty($this->_proxyTicket)) { + throw new CAS_OutOfSequenceException( + 'Already initialized, cannot change the CAS_Client.' + ); + } + + $this->_casClient = $casClient; + } + + /** + * Fetch our proxy ticket. + * + * Descendent classes should call this method once their service URL is available + * to initialize their proxy ticket. + * + * @return void + * @throws CAS_OutOfSequenceException If called after a proxy ticket has + * already been initialized. + */ + protected function initializeProxyTicket() + { + if (!empty($this->_proxyTicket)) { + throw new CAS_OutOfSequenceException( + 'Already initialized, cannot initialize again.' + ); + } + // Allow usage of a particular CAS_Client for unit testing. + if (empty($this->_casClient)) { + phpCAS::initializeProxiedService($this); + } else { + $this->_casClient->initializeProxiedService($this); + } + } + +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxiedService/Exception.php b/phpCAS-1.3.6/source/CAS/ProxiedService/Exception.php new file mode 100644 index 0000000..5a1e696 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxiedService/Exception.php @@ -0,0 +1,46 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * An Exception for problems communicating with a proxied service. + * + * @class CAS_ProxiedService_Exception + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_ProxiedService_Exception +extends Exception +implements CAS_Exception +{ + +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxiedService/Http.php b/phpCAS-1.3.6/source/CAS/ProxiedService/Http.php new file mode 100644 index 0000000..7c9824f --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxiedService/Http.php @@ -0,0 +1,91 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This interface defines methods that clients should use for configuring, sending, + * and receiving proxied HTTP requests. + * + * @class CAS_ProxiedService_Http + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +interface CAS_ProxiedService_Http +{ + + /********************************************************* + * Configure the Request + *********************************************************/ + + /** + * Set the URL of the Request + * + * @param string $url Url to set + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setUrl ($url); + + /********************************************************* + * 2. Send the Request + *********************************************************/ + + /** + * Perform the request. + * + * @return bool TRUE on success, FALSE on failure. + * @throws CAS_OutOfSequenceException If called multiple times. + */ + public function send (); + + /********************************************************* + * 3. Access the response + *********************************************************/ + + /** + * Answer the headers of the response. + * + * @return array An array of header strings. + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseHeaders (); + + /** + * Answer the body of response. + * + * @return string + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseBody (); + +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxiedService/Http/Abstract.php b/phpCAS-1.3.6/source/CAS/ProxiedService/Http/Abstract.php new file mode 100644 index 0000000..abeddf8 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxiedService/Http/Abstract.php @@ -0,0 +1,360 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This class implements common methods for ProxiedService implementations included + * with phpCAS. + * + * @class CAS_ProxiedService_Http_Abstract + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +abstract class CAS_ProxiedService_Http_Abstract extends +CAS_ProxiedService_Abstract implements CAS_ProxiedService_Http +{ + /** + * The HTTP request mechanism talking to the target service. + * + * @var CAS_Request_RequestInterface $requestHandler + */ + protected $requestHandler; + + /** + * The storage mechanism for cookies set by the target service. + * + * @var CAS_CookieJar $_cookieJar + */ + private $_cookieJar; + + /** + * Constructor. + * + * @param CAS_Request_RequestInterface $requestHandler request handler object + * @param CAS_CookieJar $cookieJar cookieJar object + * + * @return void + */ + public function __construct(CAS_Request_RequestInterface $requestHandler, + CAS_CookieJar $cookieJar + ) { + $this->requestHandler = $requestHandler; + $this->_cookieJar = $cookieJar; + } + + /** + * The target service url. + * @var string $_url; + */ + private $_url; + + /** + * Answer a service identifier (URL) for whom we should fetch a proxy ticket. + * + * @return string + * @throws Exception If no service url is available. + */ + public function getServiceUrl() + { + if (empty($this->_url)) { + throw new CAS_ProxiedService_Exception( + 'No URL set via ' . get_class($this) . '->setUrl($url).' + ); + } + + return $this->_url; + } + + /********************************************************* + * Configure the Request + *********************************************************/ + + /** + * Set the URL of the Request + * + * @param string $url url to set + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setUrl($url) + { + if ($this->hasBeenSent()) { + throw new CAS_OutOfSequenceException( + 'Cannot set the URL, request already sent.' + ); + } + if (!is_string($url)) { + throw new CAS_InvalidArgumentException('$url must be a string.'); + } + + $this->_url = $url; + } + + /********************************************************* + * 2. Send the Request + *********************************************************/ + + /** + * Perform the request. + * + * @return void + * @throws CAS_OutOfSequenceException If called multiple times. + * @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 sending the + * request to the target service. + */ + public function send() + { + if ($this->hasBeenSent()) { + throw new CAS_OutOfSequenceException( + 'Cannot send, request already sent.' + ); + } + + phpCAS::traceBegin(); + + // Get our proxy ticket and append it to our URL. + $this->initializeProxyTicket(); + $url = $this->getServiceUrl(); + if (strstr($url, '?') === false) { + $url = $url . '?ticket=' . $this->getProxyTicket(); + } else { + $url = $url . '&ticket=' . $this->getProxyTicket(); + } + + try { + $this->makeRequest($url); + } catch (Exception $e) { + phpCAS::traceEnd(); + throw $e; + } + } + + /** + * Indicator of the number of requests (including redirects performed. + * + * @var int $_numRequests; + */ + private $_numRequests = 0; + + /** + * The response headers. + * + * @var array $_responseHeaders; + */ + private $_responseHeaders = array(); + + /** + * The response status code. + * + * @var string $_responseStatusCode; + */ + private $_responseStatusCode = ''; + + /** + * The response headers. + * + * @var string $_responseBody; + */ + private $_responseBody = ''; + + /** + * Build and perform a request, following redirects + * + * @param string $url url for the request + * + * @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 sending the + * request to the target service. + */ + protected function makeRequest($url) + { + // Verify that we are not in a redirect loop + $this->_numRequests++; + if ($this->_numRequests > 4) { + $message = 'Exceeded the maximum number of redirects (3) in proxied service request.'; + phpCAS::trace($message); + throw new CAS_ProxiedService_Exception($message); + } + + // Create a new request. + $request = clone $this->requestHandler; + $request->setUrl($url); + + // Add any cookies to the request. + $request->addCookies($this->_cookieJar->getCookies($url)); + + // Add any other parts of the request needed by concrete classes + $this->populateRequest($request); + + // Perform the request. + phpCAS::trace('Performing proxied service request to \'' . $url . '\''); + if (!$request->send()) { + $message = 'Could not perform proxied service request to URL`' + . $url . '\'. ' . $request->getErrorMessage(); + phpCAS::trace($message); + throw new CAS_ProxiedService_Exception($message); + } + + // Store any cookies from the response; + $this->_cookieJar->storeCookies($url, $request->getResponseHeaders()); + + // Follow any redirects + if ($redirectUrl = $this->getRedirectUrl($request->getResponseHeaders()) + ) { + phpCAS::trace('Found redirect:' . $redirectUrl); + $this->makeRequest($redirectUrl); + } else { + + $this->_responseHeaders = $request->getResponseHeaders(); + $this->_responseBody = $request->getResponseBody(); + $this->_responseStatusCode = $request->getResponseStatusCode(); + } + } + + /** + * Add any other parts of the request needed by concrete classes + * + * @param CAS_Request_RequestInterface $request request interface object + * + * @return void + */ + abstract protected function populateRequest( + CAS_Request_RequestInterface $request + ); + + /** + * Answer a redirect URL if a redirect header is found, otherwise null. + * + * @param array $responseHeaders response header to extract a redirect from + * + * @return string or null + */ + protected function getRedirectUrl(array $responseHeaders) + { + // Check for the redirect after authentication + foreach ($responseHeaders as $header) { + if ( preg_match('/^(Location:|URI:)\s*([^\s]+.*)$/', $header, $matches) + ) { + return trim(array_pop($matches)); + } + } + return null; + } + + /********************************************************* + * 3. Access the response + *********************************************************/ + + /** + * Answer true if our request has been sent yet. + * + * @return bool + */ + protected function hasBeenSent() + { + return ($this->_numRequests > 0); + } + + /** + * Answer the headers of the response. + * + * @return array An array of header strings. + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseHeaders() + { + if (!$this->hasBeenSent()) { + throw new CAS_OutOfSequenceException( + 'Cannot access response, request not sent yet.' + ); + } + + return $this->_responseHeaders; + } + + /** + * Answer HTTP status code of the response + * + * @return int + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseStatusCode() + { + if (!$this->hasBeenSent()) { + throw new CAS_OutOfSequenceException( + 'Cannot access response, request not sent yet.' + ); + } + + return $this->_responseStatusCode; + } + + /** + * Answer the body of response. + * + * @return string + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseBody() + { + if (!$this->hasBeenSent()) { + throw new CAS_OutOfSequenceException( + 'Cannot access response, request not sent yet.' + ); + } + + return $this->_responseBody; + } + + /** + * Answer the cookies from the response. This may include cookies set during + * redirect responses. + * + * @return array An array containing cookies. E.g. array('name' => 'val'); + */ + public function getCookies() + { + return $this->_cookieJar->getCookies($this->getServiceUrl()); + } + +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxiedService/Http/Get.php b/phpCAS-1.3.6/source/CAS/ProxiedService/Http/Get.php new file mode 100644 index 0000000..78e35de --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxiedService/Http/Get.php @@ -0,0 +1,85 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This class is used to make proxied service requests via the HTTP GET method. + * + * Usage Example: + * + * try { + * $service = phpCAS::getProxiedService(PHPCAS_PROXIED_SERVICE_HTTP_GET); + * $service->setUrl('http://www.example.com/path/'); + * $service->send(); + * if ($service->getResponseStatusCode() == 200) + * return $service->getResponseBody(); + * else + * // The service responded with an error code 404, 500, etc. + * throw new Exception('The service responded with an error.'); + * + * } catch (CAS_ProxyTicketException $e) { + * if ($e->getCode() == PHPCAS_SERVICE_PT_FAILURE) + * return "Your login has timed out. You need to log in again."; + * else + * // Other proxy ticket errors are from bad request format + * // (shouldn't happen) or CAS server failure (unlikely) + * // so lets just stop if we hit those. + * throw $e; + * } catch (CAS_ProxiedService_Exception $e) { + * // Something prevented the service request from being sent or received. + * // We didn't even get a valid error response (404, 500, etc), so this + * // might be caused by a network error or a DNS resolution failure. + * // We could handle it in some way, but for now we will just stop. + * throw $e; + * } + * + * @class CAS_ProxiedService_Http_Get + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_ProxiedService_Http_Get +extends CAS_ProxiedService_Http_Abstract +{ + + /** + * Add any other parts of the request needed by concrete classes + * + * @param CAS_Request_RequestInterface $request request interface + * + * @return void + */ + protected function populateRequest (CAS_Request_RequestInterface $request) + { + // do nothing, since the URL has already been sent and that is our + // only data. + } +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxiedService/Http/Post.php b/phpCAS-1.3.6/source/CAS/ProxiedService/Http/Post.php new file mode 100644 index 0000000..7d4ecd3 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxiedService/Http/Post.php @@ -0,0 +1,152 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This class is used to make proxied service requests via the HTTP POST method. + * + * Usage Example: + * + * try { + * $service = phpCAS::getProxiedService(PHPCAS_PROXIED_SERVICE_HTTP_POST); + * $service->setUrl('http://www.example.com/path/'); + * $service->setContentType('text/xml'); + * $service->setBody('example.search'); + * $service->send(); + * if ($service->getResponseStatusCode() == 200) + * return $service->getResponseBody(); + * else + * // The service responded with an error code 404, 500, etc. + * throw new Exception('The service responded with an error.'); + * + * } catch (CAS_ProxyTicketException $e) { + * if ($e->getCode() == PHPCAS_SERVICE_PT_FAILURE) + * return "Your login has timed out. You need to log in again."; + * else + * // Other proxy ticket errors are from bad request format + * // (shouldn't happen) or CAS server failure (unlikely) so lets just + * // stop if we hit those. + * throw $e; + * } catch (CAS_ProxiedService_Exception $e) { + * // Something prevented the service request from being sent or received. + * // We didn't even get a valid error response (404, 500, etc), so this + * // might be caused by a network error or a DNS resolution failure. + * // We could handle it in some way, but for now we will just stop. + * throw $e; + * } + * + * @class CAS_ProxiedService_Http_Post + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_ProxiedService_Http_Post +extends CAS_ProxiedService_Http_Abstract +{ + + /** + * The content-type of this request + * + * @var string $_contentType + */ + private $_contentType; + + /** + * The body of the this request + * + * @var string $_body + */ + private $_body; + + /** + * Set the content type of this POST request. + * + * @param string $contentType content type + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setContentType ($contentType) + { + if ($this->hasBeenSent()) { + throw new CAS_OutOfSequenceException( + 'Cannot set the content type, request already sent.' + ); + } + + $this->_contentType = $contentType; + } + + /** + * Set the body of this POST request. + * + * @param string $body body to set + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setBody ($body) + { + if ($this->hasBeenSent()) { + throw new CAS_OutOfSequenceException( + 'Cannot set the body, request already sent.' + ); + } + + $this->_body = $body; + } + + /** + * Add any other parts of the request needed by concrete classes + * + * @param CAS_Request_RequestInterface $request request interface class + * + * @return void + */ + protected function populateRequest (CAS_Request_RequestInterface $request) + { + if (empty($this->_contentType) && !empty($this->_body)) { + throw new CAS_ProxiedService_Exception( + "If you pass a POST body, you must specify a content type via " + .get_class($this).'->setContentType($contentType).' + ); + } + + $request->makePost(); + if (!empty($this->_body)) { + $request->addHeader('Content-Type: '.$this->_contentType); + $request->addHeader('Content-Length: '.strlen($this->_body)); + $request->setPostBody($this->_body); + } + } + + +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxiedService/Imap.php b/phpCAS-1.3.6/source/CAS/ProxiedService/Imap.php new file mode 100644 index 0000000..847da28 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxiedService/Imap.php @@ -0,0 +1,280 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Provides access to a proxy-authenticated IMAP stream + * + * @class CAS_ProxiedService_Imap + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_ProxiedService_Imap +extends CAS_ProxiedService_Abstract +{ + + /** + * The username to send via imap_open. + * + * @var string $_username; + */ + private $_username; + + /** + * Constructor. + * + * @param string $username Username + * + * @return void + */ + public function __construct ($username) + { + if (!is_string($username) || !strlen($username)) { + throw new CAS_InvalidArgumentException('Invalid username.'); + } + + $this->_username = $username; + } + + /** + * The target service url. + * @var string $_url; + */ + private $_url; + + /** + * Answer a service identifier (URL) for whom we should fetch a proxy ticket. + * + * @return string + * @throws Exception If no service url is available. + */ + public function getServiceUrl () + { + if (empty($this->_url)) { + throw new CAS_ProxiedService_Exception( + 'No URL set via '.get_class($this).'->getServiceUrl($url).' + ); + } + + return $this->_url; + } + + /********************************************************* + * Configure the Stream + *********************************************************/ + + /** + * Set the URL of the service to pass to CAS for proxy-ticket retrieval. + * + * @param string $url Url to set + * + * @return void + * @throws CAS_OutOfSequenceException If called after the stream has been opened. + */ + public function setServiceUrl ($url) + { + if ($this->hasBeenOpened()) { + throw new CAS_OutOfSequenceException( + 'Cannot set the URL, stream already opened.' + ); + } + if (!is_string($url) || !strlen($url)) { + throw new CAS_InvalidArgumentException('Invalid url.'); + } + + $this->_url = $url; + } + + /** + * The mailbox to open. See the $mailbox parameter of imap_open(). + * + * @var string $_mailbox + */ + private $_mailbox; + + /** + * Set the mailbox to open. See the $mailbox parameter of imap_open(). + * + * @param string $mailbox Mailbox to set + * + * @return void + * @throws CAS_OutOfSequenceException If called after the stream has been opened. + */ + public function setMailbox ($mailbox) + { + if ($this->hasBeenOpened()) { + throw new CAS_OutOfSequenceException( + 'Cannot set the mailbox, stream already opened.' + ); + } + if (!is_string($mailbox) || !strlen($mailbox)) { + throw new CAS_InvalidArgumentException('Invalid mailbox.'); + } + + $this->_mailbox = $mailbox; + } + + /** + * A bit mask of options to pass to imap_open() as the $options parameter. + * + * @var int $_options + */ + private $_options = null; + + /** + * Set the options for opening the stream. See the $options parameter of + * imap_open(). + * + * @param int $options Options for the stream + * + * @return void + * @throws CAS_OutOfSequenceException If called after the stream has been opened. + */ + public function setOptions ($options) + { + if ($this->hasBeenOpened()) { + throw new CAS_OutOfSequenceException( + 'Cannot set options, stream already opened.' + ); + } + if (!is_int($options)) { + throw new CAS_InvalidArgumentException('Invalid options.'); + } + + $this->_options = $options; + } + + /********************************************************* + * 2. Open the stream + *********************************************************/ + + /** + * Open the IMAP stream (similar to imap_open()). + * + * @return resource Returns an IMAP stream on success + * @throws CAS_OutOfSequenceException If called multiple times. + * @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 sending the + * request to the target service. + */ + public function open () + { + if ($this->hasBeenOpened()) { + throw new CAS_OutOfSequenceException('Stream already opened.'); + } + if (empty($this->_mailbox)) { + throw new CAS_ProxiedService_Exception( + 'You must specify a mailbox via '.get_class($this) + .'->setMailbox($mailbox)' + ); + } + + phpCAS::traceBegin(); + + // Get our proxy ticket and append it to our URL. + $this->initializeProxyTicket(); + phpCAS::trace('opening IMAP mailbox `'.$this->_mailbox.'\'...'); + $this->_stream = @imap_open( + $this->_mailbox, $this->_username, $this->getProxyTicket(), + $this->_options + ); + if ($this->_stream) { + phpCAS::trace('ok'); + } else { + phpCAS::trace('could not open mailbox'); + // @todo add localization integration. + $message = 'IMAP Error: '.$this->_url.' '. var_export(imap_errors(), true); + phpCAS::trace($message); + throw new CAS_ProxiedService_Exception($message); + } + + phpCAS::traceEnd(); + return $this->_stream; + } + + /** + * Answer true if our request has been sent yet. + * + * @return bool + */ + protected function hasBeenOpened () + { + return !empty($this->_stream); + } + + /********************************************************* + * 3. Access the result + *********************************************************/ + /** + * The IMAP stream + * + * @var resource $_stream + */ + private $_stream; + + /** + * Answer the IMAP stream + * + * @return resource + */ + public function getStream () + { + if (!$this->hasBeenOpened()) { + throw new CAS_OutOfSequenceException( + 'Cannot access stream, not opened yet.' + ); + } + return $this->_stream; + } + + /** + * CAS_Client::serviceMail() needs to return the proxy ticket for some reason, + * so this method provides access to it. + * + * @return string + * @throws CAS_OutOfSequenceException If called before the stream has been + * opened. + */ + public function getImapProxyTicket () + { + if (!$this->hasBeenOpened()) { + throw new CAS_OutOfSequenceException( + 'Cannot access errors, stream not opened yet.' + ); + } + return $this->getProxyTicket(); + } +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxiedService/Testable.php b/phpCAS-1.3.6/source/CAS/ProxiedService/Testable.php new file mode 100644 index 0000000..51f0767 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxiedService/Testable.php @@ -0,0 +1,75 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This interface defines methods that allow proxy-authenticated service handlers + * to be tested in unit tests. + * + * Classes implementing this interface SHOULD store the CAS_Client passed and + * initialize themselves with that client rather than via the static phpCAS + * method. For example: + * + * / ** + * * Fetch our proxy ticket. + * * / + * protected function initializeProxyTicket() { + * // Allow usage of a particular CAS_Client for unit testing. + * if (is_null($this->casClient)) + * phpCAS::initializeProxiedService($this); + * else + * $this->casClient->initializeProxiedService($this); + * } + * + * @class CAS_ProxiedService_Testabel + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +interface CAS_ProxiedService_Testable +{ + + /** + * Use a particular CAS_Client->initializeProxiedService() rather than the + * static phpCAS::initializeProxiedService(). + * + * This method should not be called in standard operation, but is needed for unit + * testing. + * + * @param CAS_Client $casClient Cas client object + * + * @return void + * @throws CAS_OutOfSequenceException If called after a proxy ticket has + * already been initialized/set. + */ + public function setCasClient (CAS_Client $casClient); + +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxyChain.php b/phpCAS-1.3.6/source/CAS/ProxyChain.php new file mode 100644 index 0000000..2594d14 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxyChain.php @@ -0,0 +1,127 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * A normal proxy-chain definition that lists each level of the chain as either + * a string or regular expression. + * + * @class CAS_ProxyChain + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +class CAS_ProxyChain +implements CAS_ProxyChain_Interface +{ + + protected $chain = array(); + + /** + * A chain is an array of strings or regexp strings that will be matched + * against. Regexp will be matched with preg_match and strings will be + * matched from the beginning. A string must fully match the beginning of + * an proxy url. So you can define a full domain as acceptable or go further + * down. + * Proxies have to be defined in reverse from the service to the user. If a + * user hits service A get proxied via B to service C the list of acceptable + * proxies on C would be array(B,A); + * + * @param array $chain A chain of proxies + */ + public function __construct(array $chain) + { + // Ensure that we have an indexed array + $this->chain = array_values($chain); + } + + /** + * Match a list of proxies. + * + * @param array $list The list of proxies in front of this service. + * + * @return bool + */ + public function matches(array $list) + { + $list = array_values($list); // Ensure that we have an indexed array + if ($this->isSizeValid($list)) { + $mismatch = false; + foreach ($this->chain as $i => $search) { + $proxy_url = $list[$i]; + if (preg_match('/^\/.*\/[ixASUXu]*$/s', $search)) { + if (preg_match($search, $proxy_url)) { + phpCAS::trace( + "Found regexp " . $search . " matching " . $proxy_url + ); + } else { + phpCAS::trace( + "No regexp match " . $search . " != " . $proxy_url + ); + $mismatch = true; + break; + } + } else { + if (strncasecmp($search, $proxy_url, strlen($search)) == 0) { + phpCAS::trace( + "Found string " . $search . " matching " . $proxy_url + ); + } else { + phpCAS::trace( + "No match " . $search . " != " . $proxy_url + ); + $mismatch = true; + break; + } + } + } + if (!$mismatch) { + phpCAS::trace("Proxy chain matches"); + return true; + } + } else { + phpCAS::trace("Proxy chain skipped: size mismatch"); + } + return false; + } + + /** + * Validate the size of the the list as compared to our chain. + * + * @param array $list List of proxies + * + * @return bool + */ + protected function isSizeValid (array $list) + { + return (sizeof($this->chain) == sizeof($list)); + } +} diff --git a/phpCAS-1.3.6/source/CAS/ProxyChain/AllowedList.php b/phpCAS-1.3.6/source/CAS/ProxyChain/AllowedList.php new file mode 100644 index 0000000..62d196a --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxyChain/AllowedList.php @@ -0,0 +1,119 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + + +/** + * ProxyChain is a container for storing chains of valid proxies that can + * be used to validate proxied requests to a service + * + * @class CAS_ProxyChain_AllowedList + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +class CAS_ProxyChain_AllowedList +{ + + private $_chains = array(); + + /** + * Check whether proxies are allowed by configuration + * + * @return bool + */ + public function isProxyingAllowed() + { + return (count($this->_chains) > 0); + } + + /** + * Add a chain of proxies to the list of possible chains + * + * @param CAS_ProxyChain_Interface $chain A chain of proxies + * + * @return void + */ + public function allowProxyChain(CAS_ProxyChain_Interface $chain) + { + $this->_chains[] = $chain; + } + + /** + * Check if the proxies found in the response match the allowed proxies + * + * @param array $proxies list of proxies to check + * + * @return bool whether the proxies match the allowed proxies + */ + public function isProxyListAllowed(array $proxies) + { + phpCAS::traceBegin(); + if (empty($proxies)) { + phpCAS::trace("No proxies were found in the response"); + phpCAS::traceEnd(true); + return true; + } elseif (!$this->isProxyingAllowed()) { + phpCAS::trace("Proxies are not allowed"); + phpCAS::traceEnd(false); + return false; + } else { + $res = $this->contains($proxies); + phpCAS::traceEnd($res); + return $res; + } + } + + /** + * Validate the proxies from the proxy ticket validation against the + * chains that were definded. + * + * @param array $list List of proxies from the proxy ticket validation. + * + * @return if any chain fully matches the supplied list + */ + public function contains(array $list) + { + phpCAS::traceBegin(); + $count = 0; + foreach ($this->_chains as $chain) { + phpCAS::trace("Checking chain ". $count++); + if ($chain->matches($list)) { + phpCAS::traceEnd(true); + return true; + } + } + phpCAS::trace("No proxy chain matches."); + phpCAS::traceEnd(false); + return false; + } +} +?> diff --git a/phpCAS-1.3.6/source/CAS/ProxyChain/Any.php b/phpCAS-1.3.6/source/CAS/ProxyChain/Any.php new file mode 100644 index 0000000..0cd92f7 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxyChain/Any.php @@ -0,0 +1,64 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * A proxy-chain definition that will match any list of proxies. + * + * Use this class for quick testing or in certain production screnarios you + * might want to allow allow any other valid service to proxy your service. + * + * THIS CLASS IS HOWEVER NOT RECOMMENDED FOR PRODUCTION AND HAS SECURITY + * IMPLICATIONS: YOU ARE ALLOWING ANY SERVICE TO ACT ON BEHALF OF A USER + * ON THIS SERVICE. + * + * @class CAS_ProxyChain_Any + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_ProxyChain_Any +implements CAS_ProxyChain_Interface +{ + + /** + * Match a list of proxies. + * + * @param array $list The list of proxies in front of this service. + * + * @return bool + */ + public function matches(array $list) + { + phpCAS::trace("Using CAS_ProxyChain_Any. No proxy validation is performed."); + return true; + } + +} diff --git a/phpCAS-1.3.6/source/CAS/ProxyChain/Interface.php b/phpCAS-1.3.6/source/CAS/ProxyChain/Interface.php new file mode 100644 index 0000000..d247115 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxyChain/Interface.php @@ -0,0 +1,53 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * An interface for classes that define a list of allowed proxies in front of + * the current application. + * + * @class CAS_ProxyChain_Interface + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +interface CAS_ProxyChain_Interface +{ + + /** + * Match a list of proxies. + * + * @param array $list The list of proxies in front of this service. + * + * @return bool + */ + public function matches(array $list); + +} \ No newline at end of file diff --git a/phpCAS-1.3.6/source/CAS/ProxyChain/Trusted.php b/phpCAS-1.3.6/source/CAS/ProxyChain/Trusted.php new file mode 100644 index 0000000..7fa6129 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxyChain/Trusted.php @@ -0,0 +1,59 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * A proxy-chain definition that defines a chain up to a trusted proxy and + * delegates the resposibility of validating the rest of the chain to that + * trusted proxy. + * + * @class CAS_ProxyChain_Trusted + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_ProxyChain_Trusted +extends CAS_ProxyChain +implements CAS_ProxyChain_Interface +{ + + /** + * Validate the size of the the list as compared to our chain. + * + * @param array $list list of proxies + * + * @return bool + */ + protected function isSizeValid (array $list) + { + return (sizeof($this->chain) <= sizeof($list)); + } + +} diff --git a/phpCAS-1.3.6/source/CAS/ProxyTicketException.php b/phpCAS-1.3.6/source/CAS/ProxyTicketException.php new file mode 100644 index 0000000..7233046 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/ProxyTicketException.php @@ -0,0 +1,71 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + * + */ + +/** + * An Exception for errors related to fetching or validating proxy tickets. + * + * @class CAS_ProxyTicketException + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_ProxyTicketException +extends BadMethodCallException +implements CAS_Exception +{ + + /** + * Constructor + * + * @param string $message Message text + * @param int $code Error code + * + * @return void + */ + public function __construct ($message, $code = PHPCAS_SERVICE_PT_FAILURE) + { + // Warn if the code is not in our allowed list + $ptCodes = array( + PHPCAS_SERVICE_PT_FAILURE, + PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, + PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE, + ); + if (!in_array($code, $ptCodes)) { + trigger_error( + 'Invalid code '.$code + .' passed. Must be one of PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, or PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE.' + ); + } + + parent::__construct($message, $code); + } +} diff --git a/phpCAS-1.3.6/source/CAS/Request/AbstractRequest.php b/phpCAS-1.3.6/source/CAS/Request/AbstractRequest.php new file mode 100644 index 0000000..f3dd28b --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Request/AbstractRequest.php @@ -0,0 +1,379 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Provides support for performing web-requests via curl + * + * @class CAS_Request_AbstractRequest + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +abstract class CAS_Request_AbstractRequest +implements CAS_Request_RequestInterface +{ + + protected $url = null; + protected $cookies = array(); + protected $headers = array(); + protected $isPost = false; + protected $postBody = null; + protected $caCertPath = null; + protected $validateCN = true; + private $_sent = false; + private $_responseHeaders = array(); + private $_responseBody = null; + private $_errorMessage = ''; + + /********************************************************* + * Configure the Request + *********************************************************/ + + /** + * Set the URL of the Request + * + * @param string $url Url to set + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setUrl ($url) + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + + $this->url = $url; + } + + /** + * Add a cookie to the request. + * + * @param string $name Name of entry + * @param string $value value of entry + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function addCookie ($name, $value) + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + + $this->cookies[$name] = $value; + } + + /** + * Add an array of cookies to the request. + * The cookie array is of the form + * array('cookie_name' => 'cookie_value', 'cookie_name2' => cookie_value2') + * + * @param array $cookies cookies to add + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function addCookies (array $cookies) + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + + $this->cookies = array_merge($this->cookies, $cookies); + } + + /** + * Add a header string to the request. + * + * @param string $header Header to add + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function addHeader ($header) + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + + $this->headers[] = $header; + } + + /** + * Add an array of header strings to the request. + * + * @param array $headers headers to add + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function addHeaders (array $headers) + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + + $this->headers = array_merge($this->headers, $headers); + } + + /** + * Make the request a POST request rather than the default GET request. + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function makePost () + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + + $this->isPost = true; + } + + /** + * Add a POST body to the request + * + * @param string $body body to add + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setPostBody ($body) + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + if (!$this->isPost) { + throw new CAS_OutOfSequenceException( + 'Cannot add a POST body to a GET request, use makePost() first.' + ); + } + + $this->postBody = $body; + } + + /** + * Specify the path to an SSL CA certificate to validate the server with. + * + * @param string $caCertPath path to cert + * @param bool $validate_cn valdiate CN of certificate + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setSslCaCert ($caCertPath,$validate_cn=true) + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + $this->caCertPath = $caCertPath; + $this->validateCN = $validate_cn; + } + + /********************************************************* + * 2. Send the Request + *********************************************************/ + + /** + * Perform the request. + * + * @return bool TRUE on success, FALSE on failure. + * @throws CAS_OutOfSequenceException If called multiple times. + */ + public function send () + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot send again.' + ); + } + if (is_null($this->url) || !$this->url) { + throw new CAS_OutOfSequenceException( + 'A url must be specified via setUrl() before the request can be sent.' + ); + } + $this->_sent = true; + return $this->sendRequest(); + } + + /** + * Send the request and store the results. + * + * @return bool TRUE on success, FALSE on failure. + */ + abstract protected function sendRequest (); + + /** + * Store the response headers. + * + * @param array $headers headers to store + * + * @return void + */ + protected function storeResponseHeaders (array $headers) + { + $this->_responseHeaders = array_merge($this->_responseHeaders, $headers); + } + + /** + * Store a single response header to our array. + * + * @param string $header header to store + * + * @return void + */ + protected function storeResponseHeader ($header) + { + $this->_responseHeaders[] = $header; + } + + /** + * Store the response body. + * + * @param string $body body to store + * + * @return void + */ + protected function storeResponseBody ($body) + { + $this->_responseBody = $body; + } + + /** + * Add a string to our error message. + * + * @param string $message message to add + * + * @return void + */ + protected function storeErrorMessage ($message) + { + $this->_errorMessage .= $message; + } + + /********************************************************* + * 3. Access the response + *********************************************************/ + + /** + * Answer the headers of the response. + * + * @return array An array of header strings. + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseHeaders () + { + if (!$this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has not been sent yet. Cannot '.__METHOD__ + ); + } + return $this->_responseHeaders; + } + + /** + * Answer HTTP status code of the response + * + * @return int + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseStatusCode () + { + if (!$this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has not been sent yet. Cannot '.__METHOD__ + ); + } + + if (!preg_match( + '/HTTP\/[0-9.]+\s+([0-9]+)\s*(.*)/', + $this->_responseHeaders[0], $matches + ) + ) { + throw new CAS_Request_Exception( + 'Bad response, no status code was found in the first line.' + ); + } + + return intval($matches[1]); + } + + /** + * Answer the body of response. + * + * @return string + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseBody () + { + if (!$this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has not been sent yet. Cannot '.__METHOD__ + ); + } + + return $this->_responseBody; + } + + /** + * Answer a message describing any errors if the request failed. + * + * @return string + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getErrorMessage () + { + if (!$this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has not been sent yet. Cannot '.__METHOD__ + ); + } + return $this->_errorMessage; + } +} diff --git a/phpCAS-1.3.6/source/CAS/Request/CurlMultiRequest.php b/phpCAS-1.3.6/source/CAS/Request/CurlMultiRequest.php new file mode 100644 index 0000000..7099608 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Request/CurlMultiRequest.php @@ -0,0 +1,146 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This interface defines a class library for performing multiple web requests + * in batches. Implementations of this interface may perform requests serially + * or in parallel. + * + * @class CAS_Request_CurlMultiRequest + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_Request_CurlMultiRequest +implements CAS_Request_MultiRequestInterface +{ + private $_requests = array(); + private $_sent = false; + + /********************************************************* + * Add Requests + *********************************************************/ + + /** + * Add a new Request to this batch. + * Note, implementations will likely restrict requests to their own concrete + * class hierarchy. + * + * @param CAS_Request_RequestInterface $request reqest to add + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + * @throws CAS_InvalidArgumentException If passed a Request of the wrong + * implmentation. + */ + public function addRequest (CAS_Request_RequestInterface $request) + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + if (!$request instanceof CAS_Request_CurlRequest) { + throw new CAS_InvalidArgumentException( + 'As a CAS_Request_CurlMultiRequest, I can only work with CAS_Request_CurlRequest objects.' + ); + } + + $this->_requests[] = $request; + } + + /** + * Retrieve the number of requests added to this batch. + * + * @return number of request elements + */ + public function getNumRequests() + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot '.__METHOD__ + ); + } + return count($this->_requests); + } + + /********************************************************* + * 2. Send the Request + *********************************************************/ + + /** + * Perform the request. After sending, all requests will have their + * responses poulated. + * + * @return bool TRUE on success, FALSE on failure. + * @throws CAS_OutOfSequenceException If called multiple times. + */ + public function send () + { + if ($this->_sent) { + throw new CAS_OutOfSequenceException( + 'Request has already been sent cannot send again.' + ); + } + if (!count($this->_requests)) { + throw new CAS_OutOfSequenceException( + 'At least one request must be added via addRequest() before the multi-request can be sent.' + ); + } + + $this->_sent = true; + + // Initialize our handles and configure all requests. + $handles = array(); + $multiHandle = curl_multi_init(); + foreach ($this->_requests as $i => $request) { + $handle = $request->initAndConfigure(); + curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); + $handles[$i] = $handle; + curl_multi_add_handle($multiHandle, $handle); + } + + // Execute the requests in parallel. + do { + curl_multi_exec($multiHandle, $running); + } while ($running > 0); + + // Populate all of the responses or errors back into the request objects. + foreach ($this->_requests as $i => $request) { + $buf = curl_multi_getcontent($handles[$i]); + $request->_storeResponseBody($buf); + curl_multi_remove_handle($multiHandle, $handles[$i]); + curl_close($handles[$i]); + } + + curl_multi_close($multiHandle); + } +} diff --git a/phpCAS-1.3.6/source/CAS/Request/CurlRequest.php b/phpCAS-1.3.6/source/CAS/Request/CurlRequest.php new file mode 100644 index 0000000..86d2492 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Request/CurlRequest.php @@ -0,0 +1,200 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Provides support for performing web-requests via curl + * + * @class CAS_Request_CurlRequest + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_Request_CurlRequest +extends CAS_Request_AbstractRequest +implements CAS_Request_RequestInterface +{ + + /** + * Set additional curl options + * + * @param array $options option to set + * + * @return void + */ + public function setCurlOptions (array $options) + { + $this->_curlOptions = $options; + } + private $_curlOptions = array(); + + /** + * Send the request and store the results. + * + * @return bool true on success, false on failure. + */ + protected function sendRequest () + { + phpCAS::traceBegin(); + + /********************************************************* + * initialize the CURL session + *********************************************************/ + $ch = $this->initAndConfigure(); + + /********************************************************* + * Perform the query + *********************************************************/ + $buf = curl_exec($ch); + if ( $buf === false ) { + phpCAS::trace('curl_exec() failed'); + $this->storeErrorMessage( + 'CURL error #'.curl_errno($ch).': '.curl_error($ch) + ); + $res = false; + } else { + $this->storeResponseBody($buf); + phpCAS::trace("Response Body: \n".$buf."\n"); + $res = true; + + } + // close the CURL session + curl_close($ch); + + phpCAS::traceEnd($res); + return $res; + } + + /** + * Internal method to initialize our cURL handle and configure the request. + * This method should NOT be used outside of the CurlRequest or the + * CurlMultiRequest. + * + * @return resource The cURL handle on success, false on failure + */ + public function initAndConfigure() + { + /********************************************************* + * initialize the CURL session + *********************************************************/ + $ch = curl_init($this->url); + + if (version_compare(PHP_VERSION, '5.1.3', '>=')) { + //only avaible in php5 + curl_setopt_array($ch, $this->_curlOptions); + } else { + foreach ($this->_curlOptions as $key => $value) { + curl_setopt($ch, $key, $value); + } + } + + /********************************************************* + * Set SSL configuration + *********************************************************/ + if ($this->caCertPath) { + if ($this->validateCN) { + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + } else { + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + } + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); + curl_setopt($ch, CURLOPT_CAINFO, $this->caCertPath); + phpCAS::trace('CURL: Set CURLOPT_CAINFO ' . $this->caCertPath); + } else { + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + } + + /********************************************************* + * Configure curl to capture our output. + *********************************************************/ + // return the CURL output into a variable + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + + // get the HTTP header with a callback + curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, '_curlReadHeaders')); + + /********************************************************* + * Add cookie headers to our request. + *********************************************************/ + if (count($this->cookies)) { + $cookieStrings = array(); + foreach ($this->cookies as $name => $val) { + $cookieStrings[] = $name.'='.$val; + } + curl_setopt($ch, CURLOPT_COOKIE, implode(';', $cookieStrings)); + } + + /********************************************************* + * Add any additional headers + *********************************************************/ + if (count($this->headers)) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers); + } + + /********************************************************* + * Flag and Body for POST requests + *********************************************************/ + if ($this->isPost) { + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $this->postBody); + } + + return $ch; + } + + /** + * Store the response body. + * This method should NOT be used outside of the CurlRequest or the + * CurlMultiRequest. + * + * @param string $body body to stor + * + * @return void + */ + private function _storeResponseBody ($body) + { + $this->storeResponseBody($body); + } + + /** + * Internal method for capturing the headers from a curl request. + * + * @param handle $ch handle of curl + * @param string $header header + * + * @return void + */ + private function _curlReadHeaders ($ch, $header) + { + $this->storeResponseHeader($header); + return strlen($header); + } +} diff --git a/phpCAS-1.3.6/source/CAS/Request/Exception.php b/phpCAS-1.3.6/source/CAS/Request/Exception.php new file mode 100644 index 0000000..14ff3c6 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Request/Exception.php @@ -0,0 +1,45 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * An Exception for problems performing requests + * + * @class CAS_Request_Exception + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_Request_Exception +extends Exception +implements CAS_Exception +{ + +} diff --git a/phpCAS-1.3.6/source/CAS/Request/MultiRequestInterface.php b/phpCAS-1.3.6/source/CAS/Request/MultiRequestInterface.php new file mode 100644 index 0000000..abc4486 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Request/MultiRequestInterface.php @@ -0,0 +1,83 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This interface defines a class library for performing multiple web requests + * in batches. Implementations of this interface may perform requests serially + * or in parallel. + * + * @class CAS_Request_MultiRequestInterface + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +interface CAS_Request_MultiRequestInterface +{ + + /********************************************************* + * Add Requests + *********************************************************/ + + /** + * Add a new Request to this batch. + * Note, implementations will likely restrict requests to their own concrete + * class hierarchy. + * + * @param CAS_Request_RequestInterface $request request interface + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been + * sent. + * @throws CAS_InvalidArgumentException If passed a Request of the wrong + * implmentation. + */ + public function addRequest (CAS_Request_RequestInterface $request); + + /** + * Retrieve the number of requests added to this batch. + * + * @return number of request elements + */ + public function getNumRequests (); + + /********************************************************* + * 2. Send the Request + *********************************************************/ + + /** + * Perform the request. After sending, all requests will have their + * responses poulated. + * + * @return bool TRUE on success, FALSE on failure. + * @throws CAS_OutOfSequenceException If called multiple times. + */ + public function send (); +} diff --git a/phpCAS-1.3.6/source/CAS/Request/RequestInterface.php b/phpCAS-1.3.6/source/CAS/Request/RequestInterface.php new file mode 100644 index 0000000..cc11ba4 --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/Request/RequestInterface.php @@ -0,0 +1,179 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * This interface defines a class library for performing web requests. + * + * @class CAS_Request_RequestInterface + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +interface CAS_Request_RequestInterface +{ + + /********************************************************* + * Configure the Request + *********************************************************/ + + /** + * Set the URL of the Request + * + * @param string $url url to set + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setUrl ($url); + + /** + * Add a cookie to the request. + * + * @param string $name name of cookie + * @param string $value value of cookie + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function addCookie ($name, $value); + + /** + * Add an array of cookies to the request. + * The cookie array is of the form + * array('cookie_name' => 'cookie_value', 'cookie_name2' => cookie_value2') + * + * @param array $cookies cookies to add + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function addCookies (array $cookies); + + /** + * Add a header string to the request. + * + * @param string $header header to add + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function addHeader ($header); + + /** + * Add an array of header strings to the request. + * + * @param array $headers headers to add + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function addHeaders (array $headers); + + /** + * Make the request a POST request rather than the default GET request. + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function makePost (); + + /** + * Add a POST body to the request + * + * @param string $body body to add + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setPostBody ($body); + + + /** + * Specify the path to an SSL CA certificate to validate the server with. + * + * @param string $caCertPath path to cert file + * @param boolean $validate_cn validate CN of SSL certificate + * + * @return void + * @throws CAS_OutOfSequenceException If called after the Request has been sent. + */ + public function setSslCaCert ($caCertPath, $validate_cn = true); + + + + /********************************************************* + * 2. Send the Request + *********************************************************/ + + /** + * Perform the request. + * + * @return bool TRUE on success, FALSE on failure. + * @throws CAS_OutOfSequenceException If called multiple times. + */ + public function send (); + + /********************************************************* + * 3. Access the response + *********************************************************/ + + /** + * Answer the headers of the response. + * + * @return array An array of header strings. + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseHeaders (); + + /** + * Answer HTTP status code of the response + * + * @return int + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseStatusCode (); + + /** + * Answer the body of response. + * + * @return string + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getResponseBody (); + + /** + * Answer a message describing any errors if the request failed. + * + * @return string + * @throws CAS_OutOfSequenceException If called before the Request has been sent. + */ + public function getErrorMessage (); +} diff --git a/phpCAS-1.3.6/source/CAS/TypeMismatchException.php b/phpCAS-1.3.6/source/CAS/TypeMismatchException.php new file mode 100644 index 0000000..4a13c2d --- /dev/null +++ b/phpCAS-1.3.6/source/CAS/TypeMismatchException.php @@ -0,0 +1,70 @@ + + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ + +/** + * Exception that denotes invalid arguments were passed. + * + * @class CAS_InvalidArgumentException + * @category Authentication + * @package PhpCAS + * @author Adam Franco + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS + */ +class CAS_TypeMismatchException +extends CAS_InvalidArgumentException +{ + /** + * Constructor, provides a nice message. + * + * @param mixed $argument Argument + * @param string $argumentName Argument Name + * @param string $type Type + * @param string $message Error Message + * @param integer $code Code + * + * @return void + */ + public function __construct ( + $argument, $argumentName, $type, $message = '', $code = 0 + ) { + if (is_object($argument)) { + $foundType = get_class($argument).' object'; + } else { + $foundType = gettype($argument); + } + + parent::__construct( + 'type mismatched for parameter ' + . $argumentName . ' (should be \'' . $type .' \'), ' + . $foundType . ' given. ' . $message, $code + ); + } +} +?>