diff --git a/core/src/plugins/authfront.cas/CAS.php b/core/src/plugins/authfront.cas/CAS.php index 60c1f02623..0adae90b6f 100755 --- a/core/src/plugins/authfront.cas/CAS.php +++ b/core/src/plugins/authfront.cas/CAS.php @@ -1,37 +1,44 @@ + * @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 +// hack by Vangelis Haniotakis to handle the absence of $_SERVER['REQUEST_URI'] +// in IIS // if (php_sapi_name() != 'cli') { if (!isset($_SERVER['REQUEST_URI'])) { @@ -44,12 +51,6 @@ define('E_USER_DEPRECATED', E_USER_NOTICE); } -/** - * @file CAS/CAS.php - * Interface class of the phpCAS library - * - * @ingroup public - */ // ######################################################################## // CONSTANTS @@ -62,11 +63,8 @@ /** * phpCAS version. accessible for the user by phpCAS::getVersion(). */ -define('PHPCAS_VERSION', '${phpcas.version}'); +define('PHPCAS_VERSION', '1.3.2+'); -// ------------------------------------------------------------------------ -// CAS VERSIONS -// ------------------------------------------------------------------------ /** * @addtogroup public * @{ @@ -78,8 +76,12 @@ 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 @@ -147,7 +149,7 @@ /** * Default path used when storing PGT's to file */ -define("CAS_PGT_STORAGE_FILE_DEFAULT_PATH", '/tmp'); +define("CAS_PGT_STORAGE_FILE_DEFAULT_PATH", session_save_path()); /** @} */ // ------------------------------------------------------------------------ // SERVICE ACCESS ERRORS @@ -197,12 +199,7 @@ */ define("PHPCAS_PROXIED_SERVICE_IMAP", 'CAS_ProxiedService_Imap'); -/** - * phpCAS::getProxiedService() type for SAMBA - */ -define("PHPCAS_PROXIED_SERVICE_SAMBA", 'CAS_ProxiedService_Samba'); - - +define("PHPCAS_PROXIED_SERVICE_IMAP", 'CAS_ProxiedService_Samba'); /** @} */ // ------------------------------------------------------------------------ // LANGUAGES @@ -212,13 +209,13 @@ * @{ */ -define("PHPCAS_LANG_ENGLISH", 'english'); -define("PHPCAS_LANG_FRENCH", 'french'); -define("PHPCAS_LANG_GREEK", 'greek'); -define("PHPCAS_LANG_GERMAN", 'german'); -define("PHPCAS_LANG_JAPANESE", 'japanese'); -define("PHPCAS_LANG_SPANISH", 'spanish'); -define("PHPCAS_LANG_CATALAN", 'catalan'); +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'); /** @} */ @@ -247,86 +244,53 @@ define('DEFAULT_DEBUG_DIR', '/tmp/'); /** @} */ -// ------------------------------------------------------------------------ -// MISC -// ------------------------------------------------------------------------ -/** - * @addtogroup internalMisc - * @{ - */ - -/** - * This global variable is used by the interface class phpCAS. - * - * @hideinitializer - */ -$GLOBALS['PHPCAS_CLIENT'] = null; - -/** - * This global variable is used to store where the initializer is called from - * (to print a comprehensive error in case of multiple calls). - * - * @hideinitializer - */ -$GLOBALS['PHPCAS_INIT_CALL'] = array ( - 'done' => FALSE, - 'file' => '?', - 'line' => -1, - 'method' => '?' -); - -/** - * This global variable is used to store where the method checking - * the authentication is called from (to print comprehensive errors) - * - * @hideinitializer - */ -$GLOBALS['PHPCAS_AUTH_CHECK_CALL'] = array ( - 'done' => FALSE, - 'file' => '?', - 'line' => -1, - 'method' => '?', - 'result' => FALSE -); - -/** - * This global variable is used to store phpCAS debug mode. - * - * @hideinitializer - */ -$GLOBALS['PHPCAS_DEBUG'] = array ( - 'filename' => FALSE, - 'indent' => 0, - 'unique_id' => '' -); - -/** @} */ - -// ######################################################################## -// CLIENT CLASS -// ######################################################################## - -// include client class -include_once(dirname(__FILE__) . '/CAS/Client.php'); -// ######################################################################## -// INTERFACE CLASS -// ######################################################################## +// include the class autoloader +require_once dirname(__FILE__) . '/CAS/Autoload.php'; /** - * @class phpCAS * The phpCAS class is a simple container for the phpCAS library. It provides CAS * authentication for web applications written in PHP. * * @ingroup public - * @author Pascal Aubry - * - * \internal All its methods access the same object ($PHPCAS_CLIENT, declared - * at the end of CAS/Client.php). + * @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. + * + * @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; + + // ######################################################################## // INITIALIZATION // ######################################################################## @@ -338,101 +302,89 @@ class phpCAS /** * phpCAS client initializer. - * @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()). * - * @param $server_version the version of the CAS server - * @param $server_hostname the hostname of the CAS server - * @param $server_port the port the CAS server is running on - * @param $server_uri the URI the CAS server is responding on - * @param $start_session Have phpCAS start PHP sessions (default true) + * @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, $start_session = true) - { - global $PHPCAS_CLIENT, $PHPCAS_INIT_CALL; - + public static function client($server_version, $server_hostname, + $server_port, $server_uri, $changeSessionID = true + ) { phpCAS :: traceBegin(); - if (is_object($PHPCAS_CLIENT)) { - phpCAS :: error($PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . $PHPCAS_INIT_CALL['file'] . ':' . $PHPCAS_INIT_CALL['line'] . ')'); - } - if (gettype($server_version) != 'string') { - phpCAS :: error('type mismatched for parameter $server_version (should be `string\')'); - } - if (gettype($server_hostname) != 'string') { - phpCAS :: error('type mismatched for parameter $server_hostname (should be `string\')'); - } - if (gettype($server_port) != 'integer') { - phpCAS :: error('type mismatched for parameter $server_port (should be `integer\')'); - } - if (gettype($server_uri) != 'string') { - phpCAS :: error('type mismatched for parameter $server_uri (should be `string\')'); + 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(); - $PHPCAS_INIT_CALL = array ( - 'done' => TRUE, + self::$_PHPCAS_INIT_CALL = array ( + 'done' => true, 'file' => $dbg[0]['file'], 'line' => $dbg[0]['line'], 'method' => __CLASS__ . '::' . __FUNCTION__ ); - // initialize the global object $PHPCAS_CLIENT - $PHPCAS_CLIENT = new CAS_Client($server_version, FALSE /*proxy*/ - , $server_hostname, $server_port, $server_uri, $start_session); + // 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. - * @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()). * - * @param $server_version the version of the CAS server - * @param $server_hostname the hostname of the CAS server - * @param $server_port the port the CAS server is running on - * @param $server_uri the URI the CAS server is responding on - * @param $start_session Have phpCAS start PHP sessions (default true) + * @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, $start_session = true) - { - global $PHPCAS_CLIENT, $PHPCAS_INIT_CALL; - + public static function proxy($server_version, $server_hostname, + $server_port, $server_uri, $changeSessionID = true + ) { phpCAS :: traceBegin(); - if (is_object($PHPCAS_CLIENT)) { - phpCAS :: error($PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . $PHPCAS_INIT_CALL['file'] . ':' . $PHPCAS_INIT_CALL['line'] . ')'); - } - if (gettype($server_version) != 'string') { - phpCAS :: error('type mismatched for parameter $server_version (should be `string\')'); - } - if (gettype($server_hostname) != 'string') { - phpCAS :: error('type mismatched for parameter $server_hostname (should be `string\')'); - } - if (gettype($server_port) != 'integer') { - phpCAS :: error('type mismatched for parameter $server_port (should be `integer\')'); - } - if (gettype($server_uri) != 'string') { - phpCAS :: error('type mismatched for parameter $server_uri (should be `string\')'); + 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(); - $PHPCAS_INIT_CALL = array ( - 'done' => TRUE, + self::$_PHPCAS_INIT_CALL = array ( + 'done' => true, 'file' => $dbg[0]['file'], 'line' => $dbg[0]['line'], 'method' => __CLASS__ . '::' . __FUNCTION__ ); - // initialize the global object $PHPCAS_CLIENT - $PHPCAS_CLIENT = new CAS_Client($server_version, TRUE /*proxy*/ - , $server_hostname, $server_port, $server_uri, $start_session); + // 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(); } @@ -449,39 +401,39 @@ public static function proxy($server_version, $server_hostname, $server_port, $s /** * Set/unset debug mode * - * @param $filename the name of the file used for logging, or FALSE to stop debugging. + * @param string $filename the name of the file used for logging, or false + * to stop debugging. + * + * @return void */ public static function setDebug($filename = '') { - global $PHPCAS_DEBUG; - - 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 && gettype($filename) != 'string') { + phpCAS :: error('type mismatched for parameter $dbg (should be false or the name of the log file)'); } - if ($filename === FALSE) { - $PHPCAS_DEBUG['filename'] = FALSE; + 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 - if (isset ($_ENV['TEMP'])) { - $debugDir = $_ENV['TEMP'] . '/'; - } else { - $debugDir = ''; - } + } else { + $debugDir = ''; + } } else { $debugDir = DEFAULT_DEBUG_DIR; } $filename = $debugDir . 'phpCAS.log'; } - if (empty ($PHPCAS_DEBUG['unique_id'])) { - $PHPCAS_DEBUG['unique_id'] = substr(strtoupper(md5(uniqid(''))), 0, 4); + if (empty (self::$_PHPCAS_DEBUG['unique_id'])) { + self::$_PHPCAS_DEBUG['unique_id'] = substr(strtoupper(md5(uniqid(''))), 0, 4); } - $PHPCAS_DEBUG['filename'] = $filename; + self::$_PHPCAS_DEBUG['filename'] = $filename; + self::$_PHPCAS_DEBUG['indent'] = 0; phpCAS :: trace('START phpCAS-' . PHPCAS_VERSION . ' ******************'); } @@ -491,32 +443,43 @@ public static function setDebug($filename = '') /** * Logs a string in debug mode. * - * @param $str the string to write + * @param string $str the string to write * + * @return void * @private */ public static function log($str) { $indent_str = "."; - global $PHPCAS_DEBUG; - if (!empty($PHPCAS_DEBUG['filename'])) { - for ($i = 0; $i < $PHPCAS_DEBUG['indent']; $i++) { + + 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" . $PHPCAS_DEBUG['unique_id'] . ' ' . $indent_str, $str); - error_log($PHPCAS_DEBUG['unique_id'] . ' ' . $indent_str . $str2 . "\n", 3, $PHPCAS_DEBUG['filename']); + // 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. + * This method is used by interface methods to print an error and where the + * function was originally called from. * - * @param $msg the message to print + * @param string $msg the message to print * + * @return void * @private */ public static function error($msg) @@ -538,12 +501,17 @@ public static function error($msg) } echo "
\nphpCAS error: " . __CLASS__ . "::" . $function . '(): ' . htmlentities($msg) . " in " . $file . " on line " . $line . "
\n"; phpCAS :: trace($msg); - phpCAS :: traceExit(); - exit (); + 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) { @@ -553,11 +521,11 @@ public static function trace($str) /** * This method is used to indicate the start of the execution of a function in debug mode. + * + * @return void */ public static function traceBegin() { - global $PHPCAS_DEBUG; - $dbg = debug_backtrace(); $str = '=> '; if (!empty ($dbg[1]['class'])) { @@ -572,39 +540,50 @@ public static function traceBegin() if (is_object($arg)) { $str .= get_class($arg); } else { - $str .= str_replace(array("\r\n", "\n", "\r"), "", var_export($arg, TRUE)); + $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'; + 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); - $PHPCAS_DEBUG['indent']++; + 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. + * 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 * - * @param $res the result of the function + * @return void */ public static function traceEnd($res = '') { - global $PHPCAS_DEBUG; - - $PHPCAS_DEBUG['indent']--; + 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)); + $str .= '<= ' . str_replace(array("\r\n", "\n", "\r"), "", var_export($res, true)); } phpCAS :: log($str); @@ -612,15 +591,15 @@ public static function traceEnd($res = '') /** * This method is used to indicate the end of the execution of the program + * + * @return void */ public static function traceExit() { - global $PHPCAS_DEBUG; - phpCAS :: log('exit()'); - while ($PHPCAS_DEBUG['indent'] > 0) { + while (self::$_PHPCAS_DEBUG['indent'] > 0) { phpCAS :: log('-'); - $PHPCAS_DEBUG['indent']--; + self::$_PHPCAS_DEBUG['indent']--; } } @@ -629,28 +608,29 @@ public static function traceExit() // INTERNATIONALIZATION // ######################################################################## /** - * @addtogroup publicLang - * @{ - */ + * @addtogroup publicLang + * @{ + */ /** * This method is used to set the language used by phpCAS. - * @note Can be called only once. * - * @param $lang a string representing the language. + * @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) { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - if (gettype($lang) != 'string') { - phpCAS :: error('type mismatched for parameter $lang (should be `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setLang($lang); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setLang($lang); } /** @} */ @@ -658,9 +638,9 @@ public static function setLang($lang) // VERSION // ######################################################################## /** - * @addtogroup public - * @{ - */ + * @addtogroup public + * @{ + */ /** * This method returns the phpCAS version. @@ -677,42 +657,44 @@ public static function getVersion() // HTML OUTPUT // ######################################################################## /** - * @addtogroup publicOutput - * @{ - */ + * @addtogroup publicOutput + * @{ + */ /** * This method sets the HTML header used for all outputs. * - * @param $header the HTML header. + * @param string $header the HTML header. + * + * @return void */ public static function setHTMLHeader($header) { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - if (gettype($header) != 'string') { - phpCAS :: error('type mismatched for parameter $header (should be `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setHTMLHeader($header); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setHTMLHeader($header); } /** * This method sets the HTML footer used for all outputs. * - * @param $footer the HTML footer. + * @param string $footer the HTML footer. + * + * @return void */ public static function setHTMLFooter($footer) { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - if (gettype($footer) != 'string') { - phpCAS :: error('type mismatched for parameter $footer (should be `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setHTMLFooter($footer); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setHTMLFooter($footer); } /** @} */ @@ -720,33 +702,28 @@ public static function setHTMLFooter($footer) // PGT STORAGE // ######################################################################## /** - * @addtogroup publicPGTStorage - * @{ - */ + * @addtogroup publicPGTStorage + * @{ + */ /** * This method can be used to set a custom PGT storage object. * - * @param $storage a PGT storage object that inherits from the CAS_PGTStorage class + * @param CAS_PGTStorage $storage a PGT storage object that inherits from the + * CAS_PGTStorage class + * + * @return void */ public static function setPGTStorage($storage) { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_CLIENT->isProxy()) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if ($PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called before ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() (called at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ')'); - } - if ( !($storage instanceof CAS_PGTStorage) ) { - phpCAS :: error('type mismatched for parameter $storage (should be a CAS_PGTStorage `object\')'); + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->setPGTStorage($storage); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setPGTStorage($storage); phpCAS :: traceEnd(); } @@ -754,112 +731,82 @@ public static function setPGTStorage($storage) * This method is used to tell phpCAS to store the response of the * CAS server to PGT requests in a database. * - * @param $dsn_or_pdo a dsn string to use for creating a PDO object or a PDO object - * @param $username the username to use when connecting to the database - * @param $password the password to use when connecting to the database - * @param $table the table to use for storing and retrieving PGT's - * @param $driver_options any driver options to use when connecting to the 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) - { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - + public static function setPGTStorageDb($dsn_or_pdo, $username='', + $password='', $table='', $driver_options=null + ) { phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_CLIENT->isProxy()) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if ($PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called before ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() (called at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ')'); - } - if (gettype($username) != 'string') { - phpCAS :: error('type mismatched for parameter $username (should be `string\')'); - } - if (gettype($password) != 'string') { - phpCAS :: error('type mismatched for parameter $password (should be `string\')'); - } - if (gettype($table) != 'string') { - phpCAS :: error('type mismatched for parameter $table (should be `string\')'); + 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_CLIENT->setPGTStorageDb($dsn_or_pdo, $username, $password, $table, $driver_options); phpCAS :: traceEnd(); } /** * This method is used to tell phpCAS to store the response of the * CAS server to PGT requests onto the filesystem. - * @param $format the format used to store the PGT's. This parameter has no effect and is only for backwards compatibility - * @param $path the path where the PGT's should be stored + * + * @param string $path the path where the PGT's should be stored + * + * @return void */ - public static function setPGTStorageFile($format = '', $path = '') + public static function setPGTStorageFile($path = '') { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_CLIENT->isProxy()) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if ($PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called before ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() (called at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ')'); - } - if (gettype($format) != 'string') { - phpCAS :: error('type mismatched for parameter $format (should be `string\')'); - } - if (gettype($path) != 'string') { - phpCAS :: error('type mismatched for parameter $format (should be `string\')'); + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->setPGTStorageFile($path); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setPGTStorageFile($path); phpCAS :: traceEnd(); } - /** @} */ // ######################################################################## // ACCESS TO EXTERNAL SERVICES // ######################################################################## /** - * @addtogroup publicServices - * @{ - */ + * @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 - * + * @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) { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_CLIENT->isProxy()) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after the programmer is sure the user has been authenticated (by calling ' . __CLASS__ . '::checkAuthentication() or ' . __CLASS__ . '::forceAuthentication()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); - } - if (gettype($type) != 'string') { - phpCAS :: error('type mismatched for parameter $type (should be `string\')'); - } + phpCAS::_validateProxyExists(); - $res = $PHPCAS_CLIENT->getProxiedService($type); + try { + $res = self::$_PHPCAS_CLIENT->getProxiedService($type); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } phpCAS :: traceEnd(); return $res; @@ -868,7 +815,8 @@ public static function getProxiedService ($type) /** * Initialize a proxied-service handler with the proxy-ticket it should use. * - * @param CAS_ProxiedService $proxiedService + * @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: @@ -878,59 +826,40 @@ public static function getProxiedService ($type) */ public static function initializeProxiedService (CAS_ProxiedService $proxiedService) { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; + phpCAS::_validateProxyExists(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_CLIENT->isProxy()) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); + try { + self::$_PHPCAS_CLIENT->initializeProxiedService($proxiedService); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after the programmer is sure the user has been authenticated (by calling ' . __CLASS__ . '::checkAuthentication() or ' . __CLASS__ . '::forceAuthentication()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); - } - - $PHPCAS_CLIENT->initializeProxiedService($proxiedService); } /** * This method is used to access an HTTP[S] service. * - * @param $url the service to access. - * @param $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 $output the output of the service (also used to give an error - * message on failure). + * @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 TRUE on success, FALSE otherwise (in this later case, $err_code - * gives the reason why it failed and $output contains an error message). + * @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) { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_CLIENT->isProxy()) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after the programmer is sure the user has been authenticated (by calling ' . __CLASS__ . '::checkAuthentication() or ' . __CLASS__ . '::forceAuthentication()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be `string\')'); - } + phpCAS::_validateProxyExists(); - $res = $PHPCAS_CLIENT->serviceWeb($url, $err_code, $output); + 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; @@ -939,187 +868,149 @@ public static function serviceWeb($url, & $err_code, & $output) /** * This method is used to access an IMAP/POP3/NNTP service. * - * @param $url a string giving the URL of the service, including the mailing box - * for IMAP URLs, as accepted by imap_open(). - * @param $service a string giving for CAS retrieve Proxy ticket - * @param $flags options given to imap_open(). - * @param $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 $err_msg an error message on failure - * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL - * on success, FALSE on error). - * - * @return 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). + * @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) { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_CLIENT->isProxy()) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after the programmer is sure the user has been authenticated (by calling ' . __CLASS__ . '::checkAuthentication() or ' . __CLASS__ . '::forceAuthentication()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be `string\')'); - } + phpCAS::_validateProxyExists(); - if (gettype($flags) != 'integer') { - phpCAS :: error('type mismatched for parameter $flags (should be `integer\')'); + try { + $res = self::$_PHPCAS_CLIENT->serviceMail($url, $service, $flags, $err_code, $err_msg, $pt); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $res = $PHPCAS_CLIENT->serviceMail($url, $service, $flags, $err_code, $err_msg, $pt); - phpCAS :: traceEnd($res); return $res; } - public static function serviceSMB($url, $err_code) { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_CLIENT->isProxy()) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after the programmer is sure the user has been authenticated (by calling ' . __CLASS__ . '::checkAuthentication() or ' . __CLASS__ . '::forceAuthentication()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be `string\')'); - } + phpCAS::_validateProxyExists(); - $res = $PHPCAS_CLIENT->serviceSamba($url, $err_code); - //$res = $PHPCAS_CLIENT->serviceMail($url, $service, $flags, $err_code, $err_msg, $pt); + try{ + $res = self::$_PHPCAS_CLIENT->serviceSamba($url, $err_code); + }catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); + } phpCAS :: traceEnd($res); return $res; } - - /** @} */ // ######################################################################## // AUTHENTICATION // ######################################################################## /** - * @addtogroup publicAuth - * @{ - */ + * @addtogroup publicAuth + * @{ + */ /** - * Set the times authentication will be cached before really accessing the CAS server in gateway mode: + * 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 $n an integer. + * @param int $n an integer. + * + * @return void */ public static function setCacheTimesForAuthRecheck($n) { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - if (gettype($n) != 'integer') { - phpCAS :: error('type mismatched for parameter $header (should be `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setCacheTimesForAuthRecheck($n); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setCacheTimesForAuthRecheck($n); } /** * 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). + * 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 callback $function - * @param optional array $additionalArgs + * @param string $function Callback function + * @param array $additionalArgs optional array of arguments + * * @return void */ public static function setPostAuthenticateCallback ($function, array $additionalArgs = array()) { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } + phpCAS::_validateClientExists(); - $PHPCAS_CLIENT->setPostAuthenticateCallback($function, $additionalArgs); + self::$_PHPCAS_CLIENT->setPostAuthenticateCallback($function, $additionalArgs); } /** - * Set a callback function to be run when a single-signout request is received. + * 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). * - * 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 * - * @param callback $function - * @param optional array $additionalArgs * @return void */ public static function setSingleSignoutCallback ($function, array $additionalArgs = array()) { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } + phpCAS::_validateClientExists(); - $PHPCAS_CLIENT->setSingleSignoutCallback($function, $additionalArgs); + 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 + * 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() { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } + phpCAS::_validateClientExists(); - $auth = $PHPCAS_CLIENT->checkAuthentication(); + $auth = self::$_PHPCAS_CLIENT->checkAuthentication(); // store where the authentication has been checked and the result - $dbg = debug_backtrace(); - $PHPCAS_AUTH_CHECK_CALL = array ( - 'done' => TRUE, - 'file' => $dbg[0]['file'], - 'line' => $dbg[0]['line'], - 'method' => __CLASS__ . '::' . __FUNCTION__, - 'result' => $auth - ); + self::$_PHPCAS_CLIENT->markAuthenticationCall($auth); + phpCAS :: traceEnd($auth); return $auth; } @@ -1128,34 +1019,24 @@ public static function checkAuthentication() * 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() { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - - $auth = $PHPCAS_CLIENT->forceAuthentication(); + phpCAS::_validateClientExists(); + $auth = self::$_PHPCAS_CLIENT->forceAuthentication(); // store where the authentication has been checked and the result - $dbg = debug_backtrace(); - $PHPCAS_AUTH_CHECK_CALL = array ( - 'done' => TRUE, - 'file' => $dbg[0]['file'], - 'line' => $dbg[0]['line'], - 'method' => __CLASS__ . '::' . __FUNCTION__, - 'result' => $auth - ); + self::$_PHPCAS_CLIENT->markAuthenticationCall($auth); - if (!$auth) { - phpCAS :: trace('user is not authenticated, redirecting to the CAS server'); - $PHPCAS_CLIENT->forceAuthentication(); + /* 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 :: trace('no need to authenticate (user `' . phpCAS :: getUser() . '\' is already authenticated)'); + }*/ phpCAS :: traceEnd(); return $auth; @@ -1163,27 +1044,20 @@ public static function forceAuthentication() /** * This method is called to renew the authentication. + * + * @return void **/ public static function renewAuthentication() { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - $auth = $PHPCAS_CLIENT->renewAuthentication(); + phpCAS::_validateClientExists(); + + $auth = self::$_PHPCAS_CLIENT->renewAuthentication(); + // store where the authentication has been checked and the result - $dbg = debug_backtrace(); - $PHPCAS_AUTH_CHECK_CALL = array ( - 'done' => TRUE, - 'file' => $dbg[0]['file'], - 'line' => $dbg[0]['line'], - 'method' => __CLASS__ . '::' . __FUNCTION__, - 'result' => $auth - ); + self::$_PHPCAS_CLIENT->markAuthenticationCall($auth); - //$PHPCAS_CLIENT->renewAuthentication(); + //self::$_PHPCAS_CLIENT->renewAuthentication(); phpCAS :: traceEnd(); } @@ -1191,29 +1065,19 @@ public static function renewAuthentication() * 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. + * @return true when the user is authenticated. */ public static function isAuthenticated() { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } + phpCAS::_validateClientExists(); - // call the isAuthenticated method of the global $PHPCAS_CLIENT object - $auth = $PHPCAS_CLIENT->isAuthenticated(); + // call the isAuthenticated method of the $_PHPCAS_CLIENT object + $auth = self::$_PHPCAS_CLIENT->isAuthenticated(); // store where the authentication has been checked and the result - $dbg = debug_backtrace(); - $PHPCAS_AUTH_CHECK_CALL = array ( - 'done' => TRUE, - 'file' => $dbg[0]['file'], - 'line' => $dbg[0]['line'], - 'method' => __CLASS__ . '::' . __FUNCTION__, - 'result' => $auth - ); + self::$_PHPCAS_CLIENT->markAuthenticationCall($auth); + phpCAS :: traceEnd($auth); return $auth; } @@ -1221,144 +1085,126 @@ public static function isAuthenticated() /** * Checks whether authenticated based on $_SESSION. Useful to avoid * server calls. - * @return true if authenticated, false otherwise. + * + * @return bool true if authenticated, false otherwise. * @since 0.4.22 by Brendan Arnold */ public static function isSessionAuthenticated() { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - return ($PHPCAS_CLIENT->isSessionAuthenticated()); + phpCAS::_validateClientExists(); + + return (self::$_PHPCAS_CLIENT->isSessionAuthenticated()); } /** * This method returns the CAS user's login name. - * @warning should not be called only after phpCAS::forceAuthentication() - * or phpCAS::checkAuthentication(). * - * @return the login name of the authenticated user - */ + * @return string the login name of the authenticated user + * @warning should only be called after phpCAS::forceAuthentication() + * or phpCAS::checkAuthentication(). + * */ public static function getUser() { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::forceAuthentication() or ' . __CLASS__ . '::isAuthenticated()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->getUser(); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - return $PHPCAS_CLIENT->getUser(); } /** * Answer attributes about the authenticated user. * - * @warning should not be called only after phpCAS::forceAuthentication() + * @warning should only be called after phpCAS::forceAuthentication() * or phpCAS::checkAuthentication(). * * @return array */ public static function getAttributes() { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::forceAuthentication() or ' . __CLASS__ . '::isAuthenticated()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->getAttributes(); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - return $PHPCAS_CLIENT->getAttributes(); } /** * Answer true if there are attributes for the authenticated user. * - * @warning should not be called only after phpCAS::forceAuthentication() + * @warning should only be called after phpCAS::forceAuthentication() * or phpCAS::checkAuthentication(). * - * @return boolean + * @return bool */ public static function hasAttributes() { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::forceAuthentication() or ' . __CLASS__ . '::isAuthenticated()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->hasAttributes(); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - return $PHPCAS_CLIENT->hasAttributes(); } /** * Answer true if an attribute exists for the authenticated user. * - * @warning should not be called only after phpCAS::forceAuthentication() - * or phpCAS::checkAuthentication(). + * @param string $key attribute name * - * @param string $key - * @return boolean + * @return bool + * @warning should only be called after phpCAS::forceAuthentication() + * or phpCAS::checkAuthentication(). */ public static function hasAttribute($key) { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::forceAuthentication() or ' . __CLASS__ . '::isAuthenticated()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->hasAttribute($key); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - return $PHPCAS_CLIENT->hasAttribute($key); } /** * Answer an attribute for the authenticated user. * - * @warning should not be called only after phpCAS::forceAuthentication() - * or phpCAS::checkAuthentication(). + * @param string $key attribute name * - * @param string $key * @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) { - global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['done']) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::forceAuthentication() or ' . __CLASS__ . '::isAuthenticated()'); - } - if (!$PHPCAS_AUTH_CHECK_CALL['result']) { - phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE'); + phpCAS::_validateClientExists(); + + try { + return self::$_PHPCAS_CLIENT->getAttribute($key); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - return $PHPCAS_CLIENT->getAttribute($key); } /** * 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) { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - return ($PHPCAS_CLIENT->handleLogoutRequests($check_client, $allowed_clients)); + phpCAS::_validateClientExists(); + + return (self::$_PHPCAS_CLIENT->handleLogoutRequests($check_client, $allowed_clients)); } /** @@ -1369,96 +1215,95 @@ public static function handleLogoutRequests($check_client = true, $allowed_clien */ public static function getServerLoginURL() { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - return $PHPCAS_CLIENT->getServerLoginURL(); + phpCAS::_validateClientExists(); + + return self::$_PHPCAS_CLIENT->getServerLoginURL(); } /** * Set the login URL of the CAS server. - * @param $url the login URL + * + * @param string $url the login URL + * + * @return void * @since 0.4.21 by Wyman Chan */ public static function setServerLoginURL($url = '') { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after - ' . __CLASS__ . '::client()'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be - `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerLoginURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setServerLoginURL($url); + phpCAS :: traceEnd(); } /** * Set the serviceValidate URL of the CAS server. * Used only in CAS 1.0 validations - * @param $url the serviceValidate URL - * @since 1.1.0 by Joachim Fritschi + * + * @param string $url the serviceValidate URL + * + * @return void */ public static function setServerServiceValidateURL($url = '') { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after - ' . __CLASS__ . '::client()'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be - `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerServiceValidateURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setServerServiceValidateURL($url); + phpCAS :: traceEnd(); } /** * Set the proxyValidate URL of the CAS server. * Used for all CAS 2.0 validations - * @param $url the proxyValidate URL - * @since 1.1.0 by Joachim Fritschi + * + * @param string $url the proxyValidate URL + * + * @return void */ public static function setServerProxyValidateURL($url = '') { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after - ' . __CLASS__ . '::client()'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be - `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerProxyValidateURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setServerProxyValidateURL($url); + phpCAS :: traceEnd(); } /** * Set the samlValidate URL of the CAS server. - * @param $url the samlValidate URL - * @since 1.1.0 by Joachim Fritschi + * + * @param string $url the samlValidate URL + * + * @return void */ public static function setServerSamlValidateURL($url = '') { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after - ' . __CLASS__ . '::client()'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be - `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerSamlValidateURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setServerSamlValidateURL($url); + phpCAS :: traceEnd(); } @@ -1470,46 +1315,46 @@ public static function setServerSamlValidateURL($url = '') */ public static function getServerLogoutURL() { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()'); - } - return $PHPCAS_CLIENT->getServerLogoutURL(); + phpCAS::_validateClientExists(); + + return self::$_PHPCAS_CLIENT->getServerLogoutURL(); } /** * Set the logout URL of the CAS server. - * @param $url the logout URL + * + * @param string $url the logout URL + * + * @return void * @since 0.4.21 by Wyman Chan */ public static function setServerLogoutURL($url = '') { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after - ' . __CLASS__ . '::client()'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be - `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setServerLogoutURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setServerLogoutURL($url); + phpCAS :: traceEnd(); } /** * This method is used to logout from CAS. - * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server - * @public + * + * @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 = "") { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()'); - } + phpCAS::_validateClientExists(); + $parsedParams = array (); if ($params != "") { if (is_string($params)) { @@ -1525,103 +1370,111 @@ public static function logout($params = "") $parsedParams[$key] = $value; } } - $PHPCAS_CLIENT->logout($parsedParams); + 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 $service a URL that will be transmitted to the CAS server + * This method is used to logout from CAS. Halts by redirecting to the CAS + * server. + * + * @param service $service a URL that will be transmitted to the CAS server + * + * @return void */ public static function logoutWithRedirectService($service) { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()'); - } + phpCAS::_validateClientExists(); + if (!is_string($service)) { phpCAS :: error('type mismatched for parameter $service (should be `string\')'); } - $PHPCAS_CLIENT->logout(array ( - "service" => $service - )); + 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 $url a URL that will be transmitted to the CAS server - * @deprecated The url parameter has been removed from the CAS server as of version 3.3.5.1 + * 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); - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { + 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\')'); } - $PHPCAS_CLIENT->logout(array ( - "url" => $url - )); + 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 $service a URL that will be transmitted to the CAS server - * @param $url a URL that will be transmitted to the CAS server - * @deprecated The url parameter has been removed from the CAS server as of version 3.3.5.1 + * 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); - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()'); - } + 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\')'); } - $PHPCAS_CLIENT->logout(array ( - "service" => $service, - "url" => $url - )); + 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. + * 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 $url the URL + * @param string $url the URL + * + * @return void */ public static function setFixedCallbackURL($url = '') { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (!$PHPCAS_CLIENT->isProxy()) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be `string\')'); + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->setCallbackURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setCallbackURL($url); + phpCAS :: traceEnd(); } @@ -1629,79 +1482,90 @@ public static function setFixedCallbackURL($url = '') * 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 $url the URL + * @param string $url the URL + * + * @return void */ public static function setFixedServiceURL($url) { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (gettype($url) != 'string') { - phpCAS :: error('type mismatched for parameter $url (should be `string\')'); + phpCAS::_validateProxyExists(); + + try { + self::$_PHPCAS_CLIENT->setURL($url); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setURL($url); + phpCAS :: traceEnd(); } /** * Get the URL that is set as the CAS service parameter. + * + * @return string Service Url */ public static function getServiceURL() { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - return ($PHPCAS_CLIENT->getURL()); + 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) { - global $PHPCAS_CLIENT; - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()'); - } - if (gettype($target_service) != 'string') { - phpCAS :: error('type mismatched for parameter $target_service(should be `string\')'); + phpCAS::_validateProxyExists(); + + try { + return (self::$_PHPCAS_CLIENT->retrievePT($target_service, $err_code, $err_msg)); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - return ($PHPCAS_CLIENT->retrievePT($target_service, $err_code, $err_msg)); } /** - * Set the certificate of the CAS server CA. + * Set the certificate of the CAS server CA and if the CN should be properly + * verified. * - * @param $cert the CA certificate + * @param string $cert CA certificate file name + * @param bool $validate_cn Validate CN in certificate (default true) + * + * @return void */ - public static function setCasServerCACert($cert) + public static function setCasServerCACert($cert, $validate_cn = true) { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()'); - } - if (gettype($cert) != 'string') { - phpCAS :: error('type mismatched for parameter $cert (should be `string\')'); + phpCAS::_validateClientExists(); + + try { + self::$_PHPCAS_CLIENT->setCasServerCACert($cert, $validate_cn); + } catch (Exception $e) { + phpCAS :: error(get_class($e) . ': ' . $e->getMessage()); } - $PHPCAS_CLIENT->setCasServerCACert($cert); + phpCAS :: traceEnd(); } /** * Set no SSL validation for the CAS server. + * + * @return void */ public static function setNoCasServerValidation() { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()'); - } - $PHPCAS_CLIENT->setNoCasServerValidation(); + 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(); } @@ -1709,17 +1573,18 @@ public static function setNoCasServerValidation() /** * 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 + * 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() { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()'); - } - $PHPCAS_CLIENT->setNoClearTicketsFromUrl(); + phpCAS::_validateClientExists(); + + self::$_PHPCAS_CLIENT->setNoClearTicketsFromUrl(); phpCAS :: traceEnd(); } @@ -1728,26 +1593,74 @@ public static function setNoClearTicketsFromUrl() /** * Change CURL options. * CURL is used to connect through HTTPS to CAS server - * @param $key the option key - * @param $value the value to set + * + * @param string $key the option key + * @param string $value the value to set + * + * @return void */ public static function setExtraCurlOption($key, $value) { - global $PHPCAS_CLIENT; phpCAS :: traceBegin(); - if (!is_object($PHPCAS_CLIENT)) { - phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()'); - } - $PHPCAS_CLIENT->setExtraCurlOption($key, $value); + phpCAS::_validateClientExists(); + + self::$_PHPCAS_CLIENT->setExtraCurlOption($key, $value); 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. + * This method will only return a non-empty array if we have received and + * validated a Proxy Ticket. * * @return array * @access public @@ -1755,16 +1668,88 @@ public static function setExtraCurlOption($key, $value) */ public static function getProxies () { - global $PHPCAS_CLIENT; - if ( !is_object($PHPCAS_CLIENT) ) { - phpCAS::error('this method should only be called after '.__CLASS__.'::client()'); + 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()); } - return($PHPCAS_CLIENT->getProxies()); + 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(); + } + } +} // ######################################################################## // DOCUMENTATION // ######################################################################## @@ -1837,7 +1822,7 @@ public static function getProxies () /** @defgroup internalProxyServices Proxy other services * @ingroup internalProxy */ -/** @defgroup internalProxied CAS proxied client features (CAS 2.0, Proxy Tickets) +/** @defgroup internalService CAS client features (CAS 2.0, Proxied service) * @ingroup internal */ /** @defgroup internalConfig Configuration @@ -1900,15 +1885,22 @@ public static function getProxies () /** * @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/core/src/plugins/authfront.cas/CAS/AuthenticationException.php b/core/src/plugins/authfront.cas/CAS/AuthenticationException.php new file mode 100755 index 0000000000..a14154d4a3 --- /dev/null +++ b/core/src/plugins/authfront.cas/CAS/AuthenticationException.php @@ -0,0 +1,108 @@ + + * @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='' + ) { + phpCAS::traceBegin(); + $lang = $client->getLangObj(); + $client->printHTMLHeader($lang->getAuthenticationFailed()); + printf( + $lang->getYouWereNotAuthenticated(), + htmlentities($client->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 ($client->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); + } + $client->printHTMLFooter(); + phpCAS::traceExit(); + } + +} +?> diff --git a/core/src/plugins/authfront.cas/CAS/Autoload.php b/core/src/plugins/authfront.cas/CAS/Autoload.php new file mode 100755 index 0000000000..e56dbdfa93 --- /dev/null +++ b/core/src/plugins/authfront.cas/CAS/Autoload.php @@ -0,0 +1,105 @@ + + * @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 (function_exists('spl_autoload_register')) { + 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'); + } + } +} elseif (!function_exists('__autoload')) { + + /** + * Autoload a class + * + * @param string $class Class name + * + * @return bool + */ + function __autoload($class) + { + return CAS_autoload($class); + } +} + +?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/Client.php b/core/src/plugins/authfront.cas/CAS/Client.php index 57d23b5660..7f7e2219e5 100755 --- a/core/src/plugins/authfront.cas/CAS/Client.php +++ b/core/src/plugins/authfront.cas/CAS/Client.php @@ -1,2515 +1,3008 @@ + * @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. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: + * @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 * - * * 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 ESUP-Portail consortium & the JA-SIG - * Collaborative 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. */ -/** - * @file CAS/Client.php - * Main class of the phpCAS library - */ +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 __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->_getServerBaseURL(); + $this->_server['login_url'] .= 'login?service='; + $this->_server['login_url'] .= 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 $_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_OutOfSequenceProxyException + * + * @return void + */ + public function ensureIsProxy() + { + if (!$this->isProxy()) { + throw new CAS_OutOfSequenceProxyException(); + } + } + + /** + * 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_raTypeMismatchException($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()); + } + + // 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 + $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 void + */ + 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() ) { + phpCAS::trace('user already authenticated; renew'); + $this->redirectToCas(false, true); + } else { + $this->redirectToCas(); + } + phpCAS::traceEnd(); + } + + /** + * 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). + * + * @return true when the user is authenticated. Also may redirect to the + * same URL without the ticket. + */ + public function isAuthenticated() + { + 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; + } + } 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 + ); // 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 + ); // 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 + ); // 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'); + } + 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(); + } + } + } + // Mark the auth-check as complete to allow post-authentication + // callbacks to make use of phpCAS::getUser() and similar methods + $this->markAuthenticationCall($res); + 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); + + session_unset(); + session_destroy(); + $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 = preg_replace('/[^a-zA-Z0-9\-]/', '', $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'); + + $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. + * + * @return bool true when successfull and issue a CAS_AuthenticationException + * and false on an error + */ + public function validateCAS10(&$validate_url,&$text_response,&$tree_response) + { + phpCAS::traceBegin(); + $result = false; + // build the URL to validate the ticket + $validate_url = $this->getServerServiceValidateURL() + .'&ticket='.urlencode($this->getTicket()); + + // 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. + * + * @return bool true when successfull and issue a CAS_AuthenticationException + * and false on an error + */ + public function validateSA(&$validate_url,&$text_response,&$tree_response) + { + phpCAS::traceBegin(); + $result = false; + // build the URL to validate the ticket + $validate_url = $this->getServerSamlValidateURL(); + + // 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); + + /* + * modify for pydio + */ + $str_pattern = '/index|plugins|dashboard|settings|welcome|ws-(.*)$/'; + $request_uri = preg_replace($str_pattern,'',$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()); -// include internationalization stuff -include_once(dirname(__FILE__) . '/languages/languages.php'); + $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; -// include PGT storage classes -include_once(dirname(__FILE__) . '/PGTStorage/AbstractStorage.php'); + phpCAS::traceEnd($body); + return ($body); + } -// include class for storing service cookies. -include_once(dirname(__FILE__) . '/CookieJar.php'); + /** @} **/ -// include class for fetching web requests. -include_once(dirname(__FILE__) . '/Request/CurlRequest.php'); + // ######################################################################## + // ACCESS TO EXTERNAL SERVICES + // ######################################################################## -// include classes for proxying access to services -include_once(dirname(__FILE__) . '/ProxiedService/Http/Get.php'); -include_once(dirname(__FILE__) . '/ProxiedService/Http/Post.php'); -include_once(dirname(__FILE__) . '/ProxiedService/Imap.php'); + /** + * @addtogroup internalProxyServices + * @{ + */ -// include Exception classes -include_once(dirname(__FILE__) . '/ProxiedService/Exception.php'); -include_once(dirname(__FILE__) . '/ProxyTicketException.php'); -include_once(dirname(__FILE__) . '/InvalidArgumentException.php'); + /** + * 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; + // For service samba + case PHPCAS_PROXIED_SERVICE_SAMBA; + $proxiedService = new CAS_ProxiedService_Samba(); + if ($proxiedService instanceof CAS_ProxiedService_Samba) { + $proxiedService->setCasClient($this); + } + return $proxiedService; + default: + throw new CAS_InvalidArgumentException( + "Unknown proxied-service type, $type." + ); + } + } -/** - * @class CAS_Client - * The CAS_Client class is a client interface that provides CAS authentication - * to PHP applications. - * - * @author Pascal Aubry - */ + /** + * 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); + } -class CAS_Client -{ + /** + * 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(); - // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - // XX XX - // XX CONFIGURATION XX - // XX XX - // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - // ######################################################################## - // 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 $str the string to filter and output - */ - 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 $title the title of the page - * - * @see HTMLFilterOutput() - */ - private 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. - * - * @see HTMLFilterOutput() - */ - private function printHTMLFooter() - { - $this->HTMLFilterOutput(empty($this->_output_footer) - ?('
phpCAS __PHPCAS_VERSION__ '.$this->getString(CAS_STR_USING_SERVER).' __SERVER_BASE_URL__ (CAS __CAS_VERSION__)
') - :$this->_output_footer); - } - - /** - * This method set the HTML header used for all outputs. - * - * @param $header the HTML header. - */ - public function setHTMLHeader($header) - { - $this->_output_header = $header; - } - - /** - * This method set the HTML footer used for all outputs. - * - * @param $footer the HTML footer. - */ - public function setHTMLFooter($footer) - { - $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). - * - * @hideinitializer - * @sa CAS_Client::_strings, CAS_Client::getString() - */ - private $_lang = ''; - - /** - * This method returns the language used by phpCAS. - * - * @return a string representing the language - */ - private function getLang() - { - if ( empty($this->_lang) ) - $this->setLang(PHPCAS_LANG_DEFAULT); - return $this->_lang; - } - - /** - * array containing the strings used by phpCAS. Written by CAS_Client::setLang(), read by - * CAS_Client::getString() and used by CAS_Client::setLang(). - * - * @note This array is filled by instructions in CAS/languages/<$this->_lang>.php - * - * @see CAS_Client::_lang, CAS_Client::getString(), CAS_Client::setLang(), CAS_Client::getLang() - */ - private $_strings; - - /** - * This method returns a string depending on the language. - * - * @param $str the index of the string in $_string. - * - * @return the string corresponding to $index in $string. - * - */ - private function getString($str) - { - // call CASclient::getLang() to be sure the language is initialized - $this->getLang(); - - if ( !isset($this->_strings[$str]) ) { - trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR); - } - return $this->_strings[$str]; - } - - /** - * This method is used to set the language used by phpCAS. - * @note Can be called only once. - * - * @param $lang a string representing the language. - * - * @sa CAS_LANG_FRENCH, CAS_LANG_ENGLISH - */ - public function setLang($lang) - { - // include the corresponding language file - include(dirname(__FILE__).'/languages/'.$lang.'.php'); - - if ( !is_array($this->_strings) ) { - trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR); - } - $this->_lang = $lang; - } - - /** @} */ - // ######################################################################## - // 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 the version of the CAS server. - */ - private function getServerVersion() - { - return $this->_server['version']; - } - - /** - * This method is used to retrieve the hostname of the CAS server. - * @return 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 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 a URI. - */ - private function getServerURI() - { return $this->_server['uri']; } - - /** - * This method is used to retrieve the base URL of the CAS server. - * @return 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 $gateway true to check authentication, false to force it - * @param $renew true to force the authentication with the CAS server - * NOTE : It is recommended that CAS implementations ignore the - "gateway" parameter if "renew" is set - * @return a URL. - */ - 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->getServerBaseURL(); - $this->_server['login_url'] .= 'login?service='; - $this->_server['login_url'] .= 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 $url the login URL - * @since 0.4.21 by Wyman Chan - */ - public function setServerLoginURL($url) - { - return $this->_server['login_url'] = $url; - } - - - /** - * This method sets the serviceValidate URL of the CAS server. - * @param $url the serviceValidate URL - * @since 1.1.0 by Joachim Fritschi - */ - public function setServerServiceValidateURL($url) - { - return $this->_server['service_validate_url'] = $url; - } - - - /** - * This method sets the proxyValidate URL of the CAS server. - * @param $url the proxyValidate URL - * @since 1.1.0 by Joachim Fritschi - */ - public function setServerProxyValidateURL($url) - { - return $this->_server['proxy_validate_url'] = $url; - } - - - /** - * This method sets the samlValidate URL of the CAS server. - * @param $url the samlValidate URL - * @since 1.1.0 by Joachim Fritschi - */ - public function setServerSamlValidateURL($url) - { - return $this->_server['saml_validate_url'] = $url; - } - - - /** - * This method is used to retrieve the service validating URL of the CAS server. - * @return a 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; - } - } - $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 a 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 a 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; - } - } - $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 a 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: - $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 a 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 $url the logout URL - * @since 0.4.21 by Wyman Chan - */ - public function setServerLogoutURL($url) - { - 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. - */ - 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_RequestInterface. - * By default CAS_CurlRequest is used, but this may be overridden to - * supply alternate request mechanisms for testing. - */ - private $_requestImplementation = 'CAS_CurlRequest'; - - /** - * Override the default implementation used to make web requests in readUrl(). - * This class must implement the CAS_RequestInterface. - * - * @param string $className - * @return void - */ - public function setRequestImplementation ($className) { - $obj = new $className; - if (!($obj instanceof CAS_RequestInterface)) - throw new CAS_InvalidArgumentException('$className must implement the CAS_RequestInterface'); - - $this->_requestImplementation = $className; - } - - /** - * @var boolean $_exitOnAuthError; If true, phpCAS will exit on an authentication error. - */ - private $_exitOnAuthError = true; - - /** - * Configure the client to not call exit() when an authentication failure occurs. - * - * Needed for testing proper failure handling. - * - * @return void - */ - public function setNoExitOnAuthError () { - $this->_exitOnAuthError = false; - } - - /** - * @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 $_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 callback $function - * @param optional array $additionalArgs - * @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 callback $function - * @param optional array $additionalArgs - * @return void - */ - public function setSingleSignoutCallback ($function, array $additionalArgs = array()) { - $this->_signoutCallbackFunction = $function; - $this->_signoutCallbackArgs = $additionalArgs; - } - - /** @} */ - - // ######################################################################## - // CONSTRUCTOR - // ######################################################################## - /** - * @addtogroup internalConfig - * @{ - */ - - /** - * CAS_Client constructor. - * - * @param $server_version the version of the CAS server - * @param $proxy TRUE if the CAS client is a CAS proxy, FALSE otherwise - * @param $server_hostname the hostname of the CAS server - * @param $server_port the port the CAS server is running on - * @param $server_uri the URI the CAS server is responding on - * @param $start_session Have phpCAS start PHP sessions (default true) - * - * @return a newly created CAS_Client object - */ - public function __construct( - $server_version, - $proxy, - $server_hostname, - $server_port, - $server_uri, - $start_session = true) { - - phpCAS::traceBegin(); - - $this->_start_session = $start_session; - - if ($this->_start_session && session_id() !== "") - { - phpCAS :: error("Another session was started before phpcas. Either disable the session" . - " handling for phpcas in the client() call or modify your application to leave" . - " session handling to phpcas"); - } - // skip Session Handling for logout requests and if don't want it' - if ($start_session && !$this->isLogoutRequest()) - { - phpCAS :: trace("Starting a new session"); - session_start(); - } - - - // 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: - 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 - $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 developpers - $ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null); - switch ($this->getServerVersion()) { - case CAS_VERSION_1_0: // check for a Service Ticket - if( preg_match('/^ST-/',$ticket) ) { - phpCAS::trace('ST \''.$ticket.'\' found'); - //ST present - $this->setST($ticket); - //ticket has been taken into account, unset it to hide it to applications - unset($_GET['ticket']); - } else if ( !empty($ticket) ) { - //ill-formed ticket, halt - phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')'); - } - break; - case CAS_VERSION_2_0: // check for a Service or Proxy Ticket - if( preg_match('/^[SP]T-/',$ticket) ) { - phpCAS::trace('ST or PT \''.$ticket.'\' found'); - $this->setPT($ticket); - unset($_GET['ticket']); - } else if ( !empty($ticket) ) { - //ill-formed ticket, halt - phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')'); - } - break; - case SAML_VERSION_1_1: // SAML just does Service Tickets - if( preg_match('/^[SP]T-/',$ticket) ) { - phpCAS::trace('SA \''.$ticket.'\' found'); - $this->setSA($ticket); - unset($_GET['ticket']); - } else if ( !empty($ticket) ) { - //ill-formed ticket, halt - phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')'); - } - break; - } - } - 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 $_start_session = true; - - private function setStartSession($session) - { - $this->_start_session = $session; - } - - public function getStartSession() - { - return $this->_start_session; - } - - /** @} */ - - // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - // XX XX - // XX AUTHENTICATION XX - // XX XX - // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - /** - * @addtogroup internalAuthentication - * @{ - */ - - /** - * The Authenticated user. Written by CAS_Client::setUser(), read by CAS_Client::getUser(). - * @attention client applications should use phpCAS::getUser(). - * - * @hideinitializer - */ - private $_user = ''; - - /** - * This method sets the CAS user's login name. - * - * @param $user the login name of the authenticated user. - * - */ - private function setUser($user) - { - $this->_user = $user; - } - - /** - * This method returns the CAS user's login name. - * @warning should be called only after CAS_Client::forceAuthentication() or - * CAS_Client::isAuthenticated(), otherwise halt with an error. - * - * @return the login name of the authenticated user - */ - public function getUser() - { - if ( empty($this->_user) ) { - phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()'); - } - return $this->_user; - } - - - - /*********************************************************************************************************************** - * Atrributes section - * - * @author Matthias Crauwels , Ghent University, Belgium - * - ***********************************************************************************************************************/ - /** - * 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(); - - public function setAttributes($attributes) - { $this->_attributes = $attributes; } - - public function getAttributes() { - 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; - } - - public function hasAttributes() - { return !empty($this->_attributes); } - - public function hasAttribute($key) - { return (is_array($this->_attributes) && array_key_exists($key, $this->_attributes)); } - - public function getAttribute($key) { - 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 - */ - 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() ) { - phpCAS::trace('user already authenticated; renew'); - $this->redirectToCas(false,true); - } else { - $this->redirectToCas(); - } - phpCAS::traceEnd(); - } - - /** - * 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 $n an integer. - */ - public function setCacheTimesForAuthRecheck($n) - { - $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(); - - if ( $this->isAuthenticated() ) { - phpCAS::trace('user is authenticated'); - $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). - * - * @return TRUE when the user is authenticated. Also may redirect to the same URL without the ticket. - */ - public function isAuthenticated() - { - phpCAS::traceBegin(); - $res = FALSE; - $validate_url = ''; - - if ( $this->wasPreviouslyAuthenticated() ) { - if($this->hasST() || $this->hasPT() || $this->hasSA()){ - // User has a additional ticket but was already authenticated - phpCAS::trace('ticket was present and will be discarded, use renewAuthenticate()'); - header('Location: '.$this->getURL()); - phpCAS::trace( "Prepare redirect to remove ticket: ".$this->getURL() ); - phpCAS::traceExit(); - exit(); - }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; - } - } - else { - if ( $this->hasST() ) { - // if a Service Ticket was given, validate it - phpCAS::trace('ST `'.$this->getST().'\' is present'); - $this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts - phpCAS::trace('ST `'.$this->getST().'\' 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($this->hasAttributes()){ - $_SESSION['phpCAS']['attributes'] = $this->getAttributes(); - } - $res = TRUE; - $logoutTicket = $this->getST(); - } - elseif ( $this->hasPT() ) { - // if a Proxy Ticket was given, validate it - phpCAS::trace('PT `'.$this->getPT().'\' is present'); - $this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts - phpCAS::trace('PT `'.$this->getPT().'\' 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($this->hasAttributes()){ - $_SESSION['phpCAS']['attributes'] = $this->getAttributes(); - } - $res = TRUE; - $logoutTicket = $this->getPT(); - } - elseif ( $this->hasSA() ) { - // if we have a SAML ticket, validate it. - phpCAS::trace('SA `'.$this->getSA().'\' is present'); - $this->validateSA($validate_url,$text_response,$tree_response); // if it fails, it halts - phpCAS::trace('SA `'.$this->getSA().'\' was validated'); - $_SESSION['phpCAS']['user'] = $this->getUser(); - $_SESSION['phpCAS']['attributes'] = $this->getAttributes(); - $res = TRUE; - $logoutTicket = $this->getSA(); - } - else { - // no ticket given, not authenticated - phpCAS::trace('no ticket found'); - } - if ($res) { - // Mark the auth-check as complete to allow post-authentication - // callbacks to make use of phpCAS::getUser() and similar methods - $dbg = debug_backtrace(); - global $PHPCAS_AUTH_CHECK_CALL; - $PHPCAS_AUTH_CHECK_CALL = array ( - 'done' => TRUE, - 'file' => $dbg[0]['file'], - 'line' => $dbg[0]['line'], - 'method' => __CLASS__ . '::' . __FUNCTION__, - 'result' => $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) { - header('Location: '.$this->getURL()); - phpCAS::trace( "Prepare redirect to : ".$this->getURL() ); - phpCAS::traceExit(); - //exit(); - } - } - } - - phpCAS::traceEnd($res); - return $res; - } - - /** - * This method tells if the current session is authenticated. - * @return true if authenticated based soley on $_SESSION variable - * @since 0.4.22 by Brendan Arnold - */ - 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() ) { - $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->setST(''); - $this->setPT(''); - } 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->setST(''); - $this->setPT(''); - } else { - phpCAS::trace('neither user not 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 $gateway true to check authentication, false to force it - * @param $renew true to force the authentication with the CAS server - */ - public function redirectToCas($gateway=false,$renew=false){ - phpCAS::traceBegin(); - $cas_url = $this->getServerLoginURL($gateway,$renew); - header('Location: '.$cas_url); - phpCAS::trace( "Redirect to : ".$cas_url ); - - $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED)); - - printf('

'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'

',$cas_url); - $this->printHTMLFooter(); - - phpCAS::traceExit(); - exit(); - } - - - /** - * This method is used to logout from CAS. - * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server - */ - public function logout($params) { - $res = array(); - 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']); - } + // Argument validation + if (gettype($url) != 'string') + throw new CAS_TypeMismatchException($url, '$url', 'string'); - flush(); - error_reporting(E_ALL); - ini_set('display_errors','On'); - ob_start(null, 0, PHP_OUTPUT_HANDLER_STDFLAGS ^ PHP_OUTPUT_HANDLER_REMOVABLE); - header('Refresh: 5; url='.$cas_url); - die(); - phpCAS::trace( "Prepare redirect logout to : ".$cas_url ); - - session_unset(); - session_destroy(); - - /* - * - * - * - * */ - - $this->printHTMLHeader($this->getString(CAS_STR_LOGOUT)); - printf('

'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'

',$cas_url); - $this->printHTMLFooter(); - - - $loggingResult = 2; - AJXP_XMLWriter::header(); - AJXP_XMLWriter::loggingResult($loggingResult, null, null, null); - AJXP_XMLWriter::close(); - - phpCAS::traceExit(); - exit(); - } - - /** - * @return true if the current request is a logout request. - */ - private function isLogoutRequest() { - return !empty($_POST['logoutRequest']); - } - - /** - * This method handles logout requests. - * @param $check_client true to check the client bofore handling the request, - * false not to perform any access control. True by default. - * @param $allowed_clients an array of host names allowed to send logout requests. - * By default, only the CAs server (declared in the constructor) will be allowed. - */ - 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->_start_session && is_null($this->_signoutCallbackFunction)){ - phpCAS::trace("phpCAS can't handle logout requests if it does not manage the session."); - } - phpCAS::trace("Logout requested"); - phpCAS::trace("SAML REQUEST: ".$_POST['logoutRequest']); - 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); - $allowed = false; - foreach ($allowed_clients as $allowed_client) { - if (($client == $allowed_client) or ($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"); - } - } - if (!$allowed) { - phpCAS::error("Unauthorized logout request from client '".$client."'"); - printf("Unauthorized!"); - phpCAS::traceExit(); - exit(); - } - } else { - phpCAS::trace("No access control set"); - } - // Extract the ticket from the SAML Request - preg_match("|(.*)|", $_POST['logoutRequest'], $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, destroy it. - if ($this->_start_session) { - $session_id = preg_replace('/[^\w]/','',$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(); - } - - printf("Disconnected!"); - phpCAS::traceExit(); - exit(); - } - - /** @} */ - - // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - // XX XX - // XX BASIC CLIENT FEATURES (CAS 1.0) XX - // XX XX - // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - // ######################################################################## - // ST - // ######################################################################## - /** - * @addtogroup internalBasic - * @{ - */ - - /** - * the Service Ticket provided in the URL of the request if present - * (empty otherwise). Written by CAS_Client::CAS_Client(), read by - * CAS_Client::getST() and CAS_Client::hasPGT(). - * - * @hideinitializer - */ - private $_st = ''; - - /** - * This method returns the Service Ticket provided in the URL of the request. - * @return The service ticket. - */ - public function getST() - { return $this->_st; } - - /** - * This method stores the Service Ticket. - * @param $st The Service Ticket. - */ - public function setST($st) - { $this->_st = $st; } - - /** - * This method tells if a Service Ticket was stored. - * @return TRUE if a Service Ticket has been stored. - */ - public function hasST() - { return !empty($this->_st); } - - /** @} */ - - // ######################################################################## - // ST VALIDATION - // ######################################################################## - /** - * @addtogroup internalBasic - * @{ - */ - - /** - * the certificate of the CAS server CA. - * - * @hideinitializer - */ - private $_cas_server_ca_cert = ''; - - /** - * 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 $cert the PEM certificate of the CA that emited the cert of the server - */ - public function setCasServerCACert($cert) - { - $this->_cas_server_ca_cert = $cert; - } - - /** - * Set no SSL validation for the CAS server. - */ - public function setNoCasServerValidation() - { - $this->_no_cas_server_validation = true; - } - - /** - * This method is used to validate a ST; 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. - * Used for all CAS 1.0 validations - * @param $validate_url the URL of the request to the CAS server. - * @param $text_response the response of the CAS server, as is (XML text). - * @param $tree_response the response of the CAS server, as a DOM XML tree. - * - * @return bool TRUE when successfull, halt otherwise by calling CAS_Client::authError(). - */ - public function validateST($validate_url,&$text_response,&$tree_response) - { - phpCAS::traceBegin(); - // build the URL to validate the ticket - $validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST(); - if ( $this->isProxy() ) { - // pass the callback url for CAS proxies - $validate_url .= '&pgtUrl='.urlencode($this->getCallbackURL()); - } - - // 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.')'); - $this->authError('ST not validated', - $validate_url, - TRUE/*$no_response*/); - } - - // analyze the result depending on the version - switch ($this->getServerVersion()) { - case CAS_VERSION_1_0: - if (preg_match('/^no\n/',$text_response)) { - phpCAS::trace('ST has not been validated'); - $this->authError('ST not validated', - $validate_url, - FALSE/*$no_response*/, - FALSE/*$bad_response*/, - $text_response); - } - if (!preg_match('/^yes\n/',$text_response)) { - phpCAS::trace('ill-formed response'); - $this->authError('ST not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - // ST has been validated, extract the user name - $arr = preg_split('/\n/',$text_response); - $this->setUser(trim($arr[1])); - break; - case CAS_VERSION_2_0: - - // 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'); - $this->authError('ST not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - // read the root node of the XML tree - if ( !($tree_response = $dom->documentElement) ) { - phpCAS::trace('documentElement() failed'); - $this->authError('ST not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - // insure that tag name is 'serviceResponse' - if ( $tree_response->localName != 'serviceResponse' ) { - phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->localName.'\''); - $this->authError('ST not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - - 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 - $this->authError('ST not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - $this->setUser(trim($success_elements->item(0)->getElementsByTagName("user")->item(0)->nodeValue)); - $this->readExtraAttributesCas20($success_elements); - } else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) { - phpCAS::trace(' found'); - // authentication failed, extract the error code and message - $auth_fail_list = $tree_response->getElementsByTagName("authenticationFailure"); - $this->authError('ST not validated', - $validate_url, - FALSE/*$no_response*/, - FALSE/*$bad_response*/, - $text_response, - $auth_fail_list->item(0)->getAttribute('code')/*$err_code*/, - trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/); - } else { - phpCAS::trace('neither nor found'); - $this->authError('ST not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - break; - } - $this->renameSession($this->getST()); - // at this step, ST 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 $validate_url the URL of the request to the CAS server. - * @param $text_response the response of the CAS server, as is (XML text). - * @param $tree_response the response of the CAS server, as a DOM XML tree. - * - * @return bool TRUE when successfull, halt otherwise by calling CAS_Client::authError(). - */ - public function validateSA($validate_url,&$text_response,&$tree_response) - { - phpCAS::traceBegin(); - - // build the URL to validate the ticket - $validate_url = $this->getServerSamlValidateURL(); - - // 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.')'); - $this->authError('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'); - $this->authError('SA not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - // read the root node of the XML tree - if ( !($tree_response = $dom->documentElement) ) { - phpCAS::trace('documentElement() failed'); - $this->authError('SA not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - // insure that tag name is 'Envelope' - if ( $tree_response->localName != 'Envelope' ) { - phpCAS::trace('bad XML root node (should be `Envelope\' instead of `'.$tree_response->localName.'\''); - $this->authError('SA not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - // check for the NameIdentifier tag in the SAML response - if ( $tree_response->getElementsByTagName("NameIdentifier")->length != 0) { - $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); - } else { - phpCAS::trace('no tag found in SAML payload'); - $this->authError('SA not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - break; - } - $this->renameSession($this->getSA()); - // at this step, ST has been validated and $this->_user has been set, - phpCAS::traceEnd(TRUE); - return TRUE; - } - - /** - * 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 $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 . "=" . $attr_value); - } - 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; - } - - /** - * This method returns the SAML Ticket provided in the URL of the request. - * @return The SAML ticket. - */ - public function getSA() - { return 'ST'.substr($this->_sa, 2); } - - /** - * This method stores the SAML Ticket. - * @param $sa The SAML Ticket. - */ - public function setSA($sa) - { $this->_sa = $sa; } - - /** - * This method tells if a SAML Ticket was stored. - * @return TRUE if a SAML Ticket has been stored. - */ - public function hasSA() - { return !empty($this->_sa); } - - /** @} */ - - // 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 The Proxy Granting Ticket. - */ - private function getPGT() - { return $this->_pgt; } - - /** - * This method stores the Proxy Granting Ticket. - * @param $pgt The Proxy Granting Ticket. - */ - 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 $callback_mode TRUE to set callback mode, FALSE otherwise. - */ - 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->getServerUrl(); - $request_uri = $_SERVER['REQUEST_URI']; - $request_uri = preg_replace('/\?.*$/','',$request_uri); - - /* - * modify for pydio - */ - $str_pattern = '/index|plugins|dashboard|settings|welcome|ws-(.*)$/'; - $request_uri = preg_replace($str_pattern,'',$request_uri); + 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'); - $final_uri .= $request_uri; - /**************************************/ - $this->setCallbackURL($final_uri); - } - return $this->_callback_url; - } - - /** - * This method sets the callback url. - * - * @param $callback_url url to set callback - */ - public function setCallbackURL($url) - { - 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. - */ - 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(); - }else{ - phpCAS::error('PGT format invalid' . $_GET['pgtId']); - } - }else{ - phpCAS::error('PGTiou format invalid' . $_GET['pgtIou']); - } - phpCAS::traceExit(); - exit(); - } - - /** @} */ - - // ######################################################################## - // 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. - */ - 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 $pgt the PGT to store - * @param $pgt_iou its corresponding Iou - */ - 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 $pgt_iou the PGT Iou - * - * @return 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 $storage a PGT storage object that inherits from the CAS_PGTStorage class - */ - public function setPGTStorage($storage) - { - // 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) ) { - phpCAS::error('Invalid PGT storage 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 $dsn_or_pdo a dsn string to use for creating a PDO object or a PDO object - * @param $username the username to use when connecting to the database - * @param $password the password to use when connecting to the database - * @param $table the table to use for storing and retrieving PGT's - * @param $driver_options any driver options to use when connecting to the database - */ - public function setPGTStorageDb($dsn_or_pdo, $username='', $password='', $table='', $driver_options=null) - { - // 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 $format the format used to store the PGT's (`plain' and `xml' allowed) - * @param $path the path where the PGT's should be stored - */ - public function setPGTStorageFile($path='') - { - // 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 $validate_url the URL of the request to the CAS server. - * @param $text_response the response of the CAS server, as is (XML text); result - * of CAS_Client::validateST() or CAS_Client::validatePT(). - * @param $tree_response the response of the CAS server, as a DOM XML tree; result - * of CAS_Client::validateST() or CAS_Client::validatePT(). - * - * @return bool TRUE when successfull, halt otherwise by calling CAS_Client::authError(). - */ - 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 - $this->authError('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'); - $this->authError('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'); - $this->authError('PGT Iou was transmitted but has wrong fromat', - $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 $target_service the service to ask for with the PT. - * @param $err_code an error code (PHPCAS_SERVICE_OK on success). - * @param $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) - { - 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(); - - phpCAS::trace("cas_url: ".$cas_url); - // 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 '); - } - } - // look for a proxyFailure tag - else if ( $root->getElementsByTagName("proxyFailure")->length != 0) { - $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 ill-formed - $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 $url the URL to access. - * @param $headers an array containing the HTTP header lines of the response - * (an empty array on failure). - * @param $body the body of the response, as a string (empty on failure). - * @param $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) - { - $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); - } - - // add extra stuff if SAML - if ($this->hasSA()) { - $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 = ''; - return true; - } else { - $headers = ''; - $body = ''; - $err_msg = $request->getErrorMessage(); - 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 = $this->getSA(); - - $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); - } - - private $_curl_headers = array(); - /** - * This method is the callback used by readURL method to request HTTP headers. - */ - public function _curl_read_headers($ch, $header) - { - $this->_curl_headers[] = $header; - return strlen($header); - } - - /** @} **/ - - // ######################################################################## - // 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) { - 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; + try { + $service = $this->getProxiedService(PHPCAS_PROXIED_SERVICE_IMAP); + $service->setServiceUrl($serviceUrl); + $service->setMailbox($url); + $service->setOptions($flags); - // For service samba - case PHPCAS_PROXIED_SERVICE_SAMBA; - $proxiedService = new CAS_ProxiedService_Samba(); - 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 - * @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) { - $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 $url the service to access. - * @param $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 $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) - { - 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) { - $output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE), $url, $e->getMessage()); - $err_code = PHPCAS_SERVICE_NOT_AVAILABLE; - return FALSE; - } - } - - /** - * This method is used to access an IMAP/POP3/NNTP service. - * - * @param $url a string giving the URL of the service, including the mailing box - * for IMAP URLs, as accepted by imap_open(). - * @param $service a string giving for CAS retrieve Proxy ticket - * @param $flags options given to imap_open(). - * @param $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 $err_msg an error message on failure - * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL - * on success, FALSE on error). - * - * @return 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) - { - 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) { - $err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE), $url, $e->getMessage()); - $err_code = PHPCAS_SERVICE_NOT_AVAILABLE; - $pt = FALSE; - return FALSE; - } - } + $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; + } + } /** * This method is used to access Samba service. @@ -2537,572 +3030,846 @@ public function serviceSamba($serviceUrl, &$err_code) 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 + * + * @return bool true when successfull and issue a CAS_AuthenticationException + * and false on an error + */ + public function validateCAS20(&$validate_url,&$text_response,&$tree_response) + { + 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()); + } + + // 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("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 if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) { + // authentication succeded, extract the error code and message + $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 { + 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 ( $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': + continue; + 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; + } + + + /** + * 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']); + $server_url = $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'); + } + if ( isset($_SERVER['HTTPS']) + && !empty($_SERVER['HTTPS']) + && $_SERVER['HTTPS'] != 'off' + ) { + return true; + } else { + 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 = preg_replace('/[^a-zA-Z0-9\-]/', '', $ticket); + phpCAS :: trace("Starting session: ". $session_id); + session_id($session_id); + session_start(); + phpCAS :: trace("Restoring old session vars"); + $_SESSION = $old_session; + } else { + phpCAS :: error( + 'Session should only be renamed after successfull authentication' + ); + } + } else { + phpCAS :: trace( + "Skipping session rename since phpCAS is not handling the session." + ); + } + phpCAS::traceEnd(); + } + + + // ######################################################################## + // 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()), + $_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(); + } - /** @} **/ - - // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - // XX XX - // XX PROXIED CLIENT FEATURES (CAS 2.0) XX - // XX XX - // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - - // ######################################################################## - // PT - // ######################################################################## - /** - * @addtogroup internalProxied - * @{ - */ - - /** - * the Proxy Ticket provided in the URL of the request if present - * (empty otherwise). Written by CAS_Client::CAS_Client(), read by - * CAS_Client::getPT() and CAS_Client::hasPGT(). - * - * @hideinitializer - */ - private $_pt = ''; - - /** - * This method returns the Proxy Ticket provided in the URL of the request. - * @return The proxy ticket. - */ - public function getPT() - { - // return 'ST'.substr($this->_pt, 2); - return $this->_pt; - } - - /** - * This method stores the Proxy Ticket. - * @param $pt The Proxy Ticket. - */ - public function setPT($pt) - { $this->_pt = $pt; } - - /** - * This method tells if a Proxy Ticket was stored. - * @return TRUE if a Proxy Ticket has been stored. - */ - public function hasPT() - { return !empty($this->_pt); } - - - /** - * 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::validatePT() 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 - * @since 6/25/09 - */ - public function getProxies () { - return $this->_proxies; - } - - /** - * Set the Proxy array, probably from persistant storage. - * - * @param array $proxies - * @return void - * @access private - * @since 6/25/09 - */ - private function setProxies ($proxies) { - $this->_proxies = $proxies; - } - - - /** @} */ - // ######################################################################## - // PT VALIDATION - // ######################################################################## - /** - * @addtogroup internalProxied - * @{ - */ - - /** - * This method is used to validate a ST or PT; halt on failure - * Used for all CAS 2.0 validations - * @return bool TRUE when successfull, halt otherwise by calling CAS_Client::authError(). - */ - public function validatePT(&$validate_url,&$text_response,&$tree_response) - { - phpCAS::traceBegin(); - phpCAS::trace($text_response); - // build the URL to validate the ticket - $validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT(); - - if ( $this->isProxy() ) { - // pass the callback url for CAS proxies - $validate_url .= '&pgtUrl='.urlencode($this->getCallbackURL()); - } - - // 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.')'); - $this->authError('PT not validated', - $validate_url, - TRUE/*$no_response*/); - } - - // create new DOMDocument object - $dom = new DOMDocument(); - // Fix possible whitspace problems - $dom->preserveWhiteSpace = false; - // read the response of the CAS server into a DOMDocument object - if ( !($dom->loadXML($text_response))) { - // read failed - $this->authError('PT not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - - // read the root node of the XML tree - if ( !($tree_response = $dom->documentElement) ) { - // read failed - $this->authError('PT not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - // insure that tag name is 'serviceResponse' - if ( $tree_response->localName != 'serviceResponse' ) { - // bad root node - $this->authError('PT not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - 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 - $this->authError('PT not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - - $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 - if ( sizeof($arr = $success_elements->item(0)->getElementsByTagName("proxy")) > 0) { - foreach ($arr as $proxyElem) { - phpCAS::trace("Storing Proxy: ".$proxyElem->nodeValue); - $this->_proxies[] = trim($proxyElem->nodeValue); - } - $_SESSION['phpCAS']['proxies'] = $this->_proxies; - } - - } else if ( $tree_response->getElementsByTagName("authenticationFailure")->length != 0) { - // authentication succeded, extract the error code and message - $auth_fail_list = $tree_response->getElementsByTagName("authenticationFailure"); - $this->authError('PT not validated', - $validate_url, - FALSE/*$no_response*/, - FALSE/*$bad_response*/, - $text_response, - $auth_fail_list->item(0)->getAttribute('code')/*$err_code*/, - trim($auth_fail_list->item(0)->nodeValue)/*$err_msg*/); - } else { - $this->authError('PT not validated', - $validate_url, - FALSE/*$no_response*/, - TRUE/*$bad_response*/, - $text_response); - } - - $this->renameSession($this->getPT()); - // at this step, PT has been validated and $this->_user has been set, - - phpCAS::traceEnd(TRUE); - return TRUE; - } - - - /** - * 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 $text_response the XML payload. - * @return bool TRUE when successfull, halt otherwise by calling CAS_Client::authError(). - */ - private function readExtraAttributesCas20($success_elements) - { - # PHPCAS-43 add CAS-2.0 extra attributes - 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 ( $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); - } - } - } - // "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 - // - // - // - else { - 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': - continue; - 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 ref array $array - * @param string $name - * @param string $value - * @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 $url url to set for service - */ - public function setURL($url) - { - $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->getServerUrl(); - $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; - } - - - /** - * Try to figure out the server URL with possible Proxys / Ports etc. - * @return Server URL with domain:port - */ - private function getServerUrl(){ - $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']); - $server_url = $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 ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443) - || (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) { - $server_url .= ':'; - $server_url .= $_SERVER['SERVER_PORT']; - } - } - return $server_url; - } - - /** - * This method checks to see if the request is secured via HTTPS - * @return true if https, false otherwise - */ - private function isHttps() { - if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { - return true; - } else { - return false; - } - } - - /** - * Removes a parameter from a query string - * - * @param string $parameterName - * @param string $queryString - * @return 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 $url base url to add the query params to - * @param $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 - */ - private function renameSession($ticket) - { - phpCAS::traceBegin(); - if($this->_start_session){ - if (!empty ($this->_user)) - { - $old_session = $_SESSION; - session_destroy(); - // set up a new session, of name based on the ticket - $session_id = preg_replace('/[^\w]/', '', $ticket); - phpCAS :: trace("Session ID: ".$session_id); - session_id($session_id); - session_start(); - phpCAS :: trace("Restoring old session vars"); - $_SESSION = $old_session; - } else - { - phpCAS :: error('Session should only be renamed after successfull authentication'); - } - }else{ - phpCAS :: trace("Skipping session rename since phpCAS is not handling the session."); - } - phpCAS::traceEnd(); - } - - - // ######################################################################## - // AUTHENTICATION ERROR HANDLING - // ######################################################################## - /** - * This method is used to print the HTML output when the user was not authenticated. - * - * @param $failure the failure that occured - * @param $cas_url the URL the CAS server was asked for - * @param $no_response the response from the CAS server (other - * parameters are ignored if TRUE) - * @param $bad_response bad response from the CAS server ($err_code - * and $err_msg ignored if TRUE) - * @param $cas_response the response of the CAS server - * @param $err_code the error code given by the CAS server - * @param $err_msg the error message given by the CAS server - */ - private function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='') - { - phpCAS::traceBegin(); - - $this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED)); - printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),htmlentities($this->getURL()),$_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: - 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(); - - if ($this->_exitOnAuthError) - exit(); - } - - /** @} */ + /** @} */ } ?> diff --git a/core/src/plugins/authfront.cas/CAS/CookieJar.php b/core/src/plugins/authfront.cas/CAS/CookieJar.php index ecde6c9fd4..549b8929f0 100755 --- a/core/src/plugins/authfront.cas/CAS/CookieJar.php +++ b/core/src/plugins/authfront.cas/CAS/CookieJar.php @@ -1,329 +1,385 @@ + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS */ -include_once(dirname(__FILE__) . '/InvalidArgumentException.php'); - /** * 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 ref array $storageArray - * @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 An 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']; - // If max-age is zero, then the cookie should be removed imediately - // so set an expiry before now. - else - $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 private - */ - protected function storeCookie ($cookie) { - // Discard any old versions of this cookie. - $this->discardCookie($cookie); - $this->_cookies[] = $cookie; - - } - - /** - * Discard an existing cookie - * - * @param stdClass $cookie - * - * @return void - * - * @access private - */ - 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 private - */ - 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 boolean - * - * @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) { - // 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; - } - // If the cookie host doesn't begin with '.', the host must case-insensitive - // match exactly - else { - 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; - } +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; + } } -?> \ No newline at end of file +?> diff --git a/core/src/plugins/authfront.cas/CAS/Exception.php b/core/src/plugins/authfront.cas/CAS/Exception.php index 3f49c5ee0b..d956d19759 100755 --- a/core/src/plugins/authfront.cas/CAS/Exception.php +++ b/core/src/plugins/authfront.cas/CAS/Exception.php @@ -1,33 +1,31 @@ + * @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. @@ -38,14 +36,24 @@ * * For example, an InvalidArgumentException in phpCAS should be defined as * - * class CAS_InvalidArgumentException - * extends InvalidArgumentException + * class CAS_InvalidArgumentException + * extends InvalidArgumentException * implements CAS_Exception * { } * - * This definition allows the CAS_InvalidArgumentException to be caught as either + * 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 { +interface CAS_Exception +{ -} \ No newline at end of file +} +?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/GracefullTerminationException.php b/core/src/plugins/authfront.cas/CAS/GracefullTerminationException.php new file mode 100755 index 0000000000..6d845dfac8 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/InvalidArgumentException.php b/core/src/plugins/authfront.cas/CAS/InvalidArgumentException.php index b3208da0ea..ba43d39f8a 100755 --- a/core/src/plugins/authfront.cas/CAS/InvalidArgumentException.php +++ b/core/src/plugins/authfront.cas/CAS/InvalidArgumentException.php @@ -1,41 +1,46 @@ + * @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__) . '/Exception.php'); - /** - * Exception that denotes invalid arguments were passed. + * 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 +extends InvalidArgumentException +implements CAS_Exception { -} \ No newline at end of file +} +?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/LICENSE.txt b/core/src/plugins/authfront.cas/CAS/LICENSE.txt deleted file mode 100755 index 261eeb9e9f..0000000000 --- a/core/src/plugins/authfront.cas/CAS/LICENSE.txt +++ /dev/null @@ -1,201 +0,0 @@ - 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/core/src/plugins/authfront.cas/CAS/Languages/Catalan.php b/core/src/plugins/authfront.cas/CAS/Languages/Catalan.php new file mode 100755 index 0000000000..a0b64d8eb5 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/Languages/English.php b/core/src/plugins/authfront.cas/CAS/Languages/English.php new file mode 100755 index 0000000000..002c1ba49e --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/Languages/French.php b/core/src/plugins/authfront.cas/CAS/Languages/French.php new file mode 100755 index 0000000000..b99847a7f0 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/Languages/German.php b/core/src/plugins/authfront.cas/CAS/Languages/German.php new file mode 100755 index 0000000000..ed3150a80c --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/Languages/Greek.php b/core/src/plugins/authfront.cas/CAS/Languages/Greek.php new file mode 100755 index 0000000000..eb0f5efefc --- /dev/null +++ b/core/src/plugins/authfront.cas/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).'; + } +} +?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/Languages/Japanese.php b/core/src/plugins/authfront.cas/CAS/Languages/Japanese.php new file mode 100755 index 0000000000..e9cd121ee7 --- /dev/null +++ b/core/src/plugins/authfront.cas/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 EUC-JP and LF + * + * @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 'using server'; + } + + /** + * 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/core/src/plugins/authfront.cas/CAS/Languages/LanguageInterface.php b/core/src/plugins/authfront.cas/CAS/Languages/LanguageInterface.php new file mode 100755 index 0000000000..5de93aa7b2 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/Languages/Spanish.php b/core/src/plugins/authfront.cas/CAS/Languages/Spanish.php new file mode 100755 index 0000000000..5675a41d80 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/OutOfSequenceBeforeAuthenticationCallException.php b/core/src/plugins/authfront.cas/CAS/OutOfSequenceBeforeAuthenticationCallException.php new file mode 100755 index 0000000000..ef83097958 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/OutOfSequenceBeforeClientException.php b/core/src/plugins/authfront.cas/CAS/OutOfSequenceBeforeClientException.php new file mode 100755 index 0000000000..f1ea7e2447 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/OutOfSequenceBeforeProxyException.php b/core/src/plugins/authfront.cas/CAS/OutOfSequenceBeforeProxyException.php new file mode 100755 index 0000000000..8038542ed1 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/OutOfSequenceException.php b/core/src/plugins/authfront.cas/CAS/OutOfSequenceException.php index d37d6f98fd..d101811b60 100755 --- a/core/src/plugins/authfront.cas/CAS/OutOfSequenceException.php +++ b/core/src/plugins/authfront.cas/CAS/OutOfSequenceException.php @@ -1,44 +1,49 @@ + * @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__) . '/Exception.php'); - /** - * This class defines Exceptions that should be thrown when the sequence of operations - * is invalid. Examples are: + * 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 +extends BadMethodCallException +implements CAS_Exception { -} \ No newline at end of file +} diff --git a/core/src/plugins/authfront.cas/CAS/PGTStorage/AbstractStorage.php b/core/src/plugins/authfront.cas/CAS/PGTStorage/AbstractStorage.php index d5c9550796..c1648984ac 100755 --- a/core/src/plugins/authfront.cas/CAS/PGTStorage/AbstractStorage.php +++ b/core/src/plugins/authfront.cas/CAS/PGTStorage/AbstractStorage.php @@ -1,215 +1,222 @@ + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS */ /** - * @class CAS_PGTStorage_AbstractStorage - * 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. + * 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. * - * @author Pascal Aubry + * @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_parent the CASclient instance that creates the current object. - * - * @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). - * - * @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). - * - * @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 - * @private - * @deprecated not used. - */ - var $_error_message=FALSE; - - /** - * This method sets en error message, which can be read later by - * PGTStorage::getErrorMessage(). - * - * @param $error_message an error message - * - * @protected - * @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. - * - * @public - * @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 - * @private - */ - 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. - * - * @protected - */ - function init() - { - $this->_initialized = TRUE; - } - - // ######################################################################## - // PGT I/O - // ######################################################################## - - /** - * This virtual method stores a PGT and its corresponding PGT Iuo. - * @note Should never be called. - * - * @param $pgt the PGT - * @param $pgt_iou the PGT iou - * - * @protected - */ - 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. - * @note Should never be called. - * - * @param $pgt_iou the PGT iou - * - * @protected - */ - function read($pgt_iou) - { - phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); - } - - /** @} */ + /** + * @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'); + } + + /** @} */ } -// include specific PGT storage classes -include_once(dirname(__FILE__) . '/Db.php'); -include_once(dirname(__FILE__) . '/File.php'); - -?> \ No newline at end of file +?> diff --git a/core/src/plugins/authfront.cas/CAS/PGTStorage/Db.php b/core/src/plugins/authfront.cas/CAS/PGTStorage/Db.php index 2bfce159e3..c331ca0983 100755 --- a/core/src/plugins/authfront.cas/CAS/PGTStorage/Db.php +++ b/core/src/plugins/authfront.cas/CAS/PGTStorage/Db.php @@ -1,400 +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'); + /** - * @class CAS_PGTStorage_Db + * Basic class for PGT database storage * The CAS_PGTStorage_Db class is a class for PGT database storage. * - * @author Daniel Frett + * @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 */ -define('CAS_PGT_STORAGE_DB_DEFAULT_TABLE', 'cas_pgts'); - 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_parent the CAS_Client instance that creates the object. - * @param $dsn_or_pdo a dsn string to use for creating a PDO object or a PDO object - * @param $username the username to use when connecting to the database - * @param $password the password to use when connecting to the database - * @param $table the table to use for storing and retrieving PGT's - * @param $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. - */ - 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 - */ - 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 - */ - 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 - */ - public function createTable() - { - phpCAS::traceBegin(); - - // 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 $pgt the PGT - * @param $pgt_iou the PGT iou - */ - 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 $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; - } - - /** @} */ + /** + * @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; + } + + /** @} */ } -?> \ No newline at end of file +?> diff --git a/core/src/plugins/authfront.cas/CAS/PGTStorage/File.php b/core/src/plugins/authfront.cas/CAS/PGTStorage/File.php index 44bfff772f..80a1ea1fd7 100755 --- a/core/src/plugins/authfront.cas/CAS/PGTStorage/File.php +++ b/core/src/plugins/authfront.cas/CAS/PGTStorage/File.php @@ -1,251 +1,259 @@ + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS */ /** - * @class CAS_PGTStorage_File * The CAS_PGTStorage_File class is a class for PGT file storage. An instance of * this class is returned by CAS_Client::SetPGTStorageFile(). * - * @author Pascal Aubry + * @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_parent the CAS_Client instance that creates the object. - * @param $path the path where the PGT's should be stored - * - * @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"){ - - 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. - * - * @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 $pgt_iou the PGT iou. - * - * @return a filename - * @private - */ - function getPGTIouFilename($pgt_iou) - { - phpCAS::traceBegin(); - $filename = $this->getPath().$pgt_iou.'.plain'; - phpCAS::traceEnd($filename); - return $filename; - } - - /** - * This method stores a PGT and its corresponding PGT Iou into a file. Echoes a - * warning on error. - * - * @param $pgt the PGT - * @param $pgt_iou the PGT iou - * - * @public - */ - function write($pgt,$pgt_iou) - { - phpCAS::traceBegin(); - $fname = $this->getPGTIouFilename($pgt_iou); - if(!file_exists($fname)){ - if ($f=fopen($fname,"w") ) { - if ( fputs($f,$pgt) === FALSE ) { - phpCAS::error('could not write 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 $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::trace('could not open `'.$fname.'\''); - } else { - if ( ($pgt=fgets($f)) === FALSE ) { - phpCAS::trace('could not read PGT from `'.$fname.'\''); - } - fclose($f); - } - - // delete the PGT file - @unlink($fname); - }else{ - phpCAS::trace('No such file `'.$fname.'\''); - } - phpCAS::traceEnd($pgt); - return $pgt; - } - - /** @} */ + /** + * @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") { + + 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().$pgt_iou.'.plain'; + phpCAS::traceEnd($filename); + 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; + } + + /** @} */ + +} ?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/ProxiedService.php b/core/src/plugins/authfront.cas/CAS/ProxiedService.php index 595999a8c8..d70ca9c128 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxiedService.php +++ b/core/src/plugins/authfront.cas/CAS/ProxiedService.php @@ -1,61 +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 - * @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); - +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/core/src/plugins/authfront.cas/CAS/ProxiedService/Abstract.php b/core/src/plugins/authfront.cas/CAS/ProxiedService/Abstract.php index c94e908d2c..fade9e70b7 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxiedService/Abstract.php +++ b/core/src/plugins/authfront.cas/CAS/ProxiedService/Abstract.php @@ -1,126 +1,149 @@ + * @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__) . '/../ProxiedService.php'); -require_once(dirname(__FILE__) . '/Testable.php'); -include_once(dirname(__FILE__) . '/../InvalidArgumentException.php'); -include_once(dirname(__FILE__) . '/../OutOfSequenceException.php'); - - /** * 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 +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 - * @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 - * @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. - phpCAS::traceBegin(); - if (empty($this->_casClient)) - phpCAS::initializeProxiedService($this); - else - $this->_casClient->initializeProxiedService($this); - phpCAS::traceEnd(); - } - + + /** + * 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/core/src/plugins/authfront.cas/CAS/ProxiedService/Exception.php b/core/src/plugins/authfront.cas/CAS/ProxiedService/Exception.php index 6cf71a00cc..5a1e69622a 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxiedService/Exception.php +++ b/core/src/plugins/authfront.cas/CAS/ProxiedService/Exception.php @@ -1,41 +1,46 @@ + * @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__) . '/../Exception.php'); - /** * 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 +extends Exception +implements CAS_Exception { -} \ No newline at end of file +} +?> diff --git a/core/src/plugins/authfront.cas/CAS/ProxiedService/Http.php b/core/src/plugins/authfront.cas/CAS/ProxiedService/Http.php index ea8a8fea7e..7c9824fabc 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxiedService/Http.php +++ b/core/src/plugins/authfront.cas/CAS/ProxiedService/Http.php @@ -1,82 +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 - *********************************************************/ +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 + *********************************************************/ - /** - * Set the URL of the Request - * - * @param string $url - * @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 (); - /** - * Perform the request. - * - * @return boolean TRUE on success, FALSE on failure. - * @throws CAS_OutOfSequenceException If called multiple times. - */ - public function send (); + /********************************************************* + * 3. Access the response + *********************************************************/ - /********************************************************* - * 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 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 (); - /** - * Answer the body of response. - * - * @return string - * @throws CAS_OutOfSequenceException If called before the Request has been sent. - */ - public function getResponseBody (); - } +?> diff --git a/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Abstract.php b/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Abstract.php index 95bcaa16b9..abeddf8bed 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Abstract.php +++ b/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Abstract.php @@ -1,308 +1,360 @@ + * @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__) . '/../Abstract.php'); -require_once(dirname(__FILE__) . '/../Http.php'); -include_once(dirname(__FILE__) . '/../Exception.php'); -include_once(dirname(__FILE__) . '/../../InvalidArgumentException.php'); -include_once(dirname(__FILE__) . '/../../OutOfSequenceException.php'); - - /** * 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 +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_RequestInterface $_requestHandler - */ - protected $_requestHandler; - - /** - * The storage mechanism for cookies set by the target service. - * - * @var CAS_CookieJar $_cookieJar - */ - private $_cookieJar; - - /** - * Constructor. - * - * @param CAS_RequestInterface $requestHandler - * @param CAS_CookieJar $cookieJar - * @return void - */ - public function __construct (CAS_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 - * @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 - * @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_RequestInterface $request - * @return void - */ - abstract protected function populateRequest (CAS_RequestInterface $request); - - /** - * Answer a redirect URL if a redirect header is found, otherwise null. - * - * @param array $responseHeaders - * @return string or null - */ - private 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 boolean - */ - 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 integer - * @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; - } - + /** + * 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/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Get.php b/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Get.php index e6921d8c1a..78e35de16f 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Get.php +++ b/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Get.php @@ -1,80 +1,85 @@ + * @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__) . '/Abstract.php'); - /** * 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; - * } * + * 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 +extends CAS_ProxiedService_Http_Abstract { - - /** - * Add any other parts of the request needed by concrete classes - * - * @param CAS_RequestInterface $request - * @return void - */ - protected function populateRequest (CAS_RequestInterface $request) { - // do nothing, since the URL has already been sent and that is our - // only data. - } - + /** + * 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/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Post.php b/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Post.php index 47cb9be273..7d4ecd3c03 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Post.php +++ b/core/src/plugins/authfront.cas/CAS/ProxiedService/Http/Post.php @@ -1,133 +1,152 @@ + * @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__) . '/Abstract.php'); -include_once(dirname(__FILE__) . '/../Exception.php'); -include_once(dirname(__FILE__) . '/../../InvalidArgumentException.php'); -include_once(dirname(__FILE__) . '/../../OutOfSequenceException.php'); - /** * 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; - * } + * + * 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 +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 - * @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; - } + /** + * 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); + } + } - /** - * Set the body of this POST request. - * - * @param string $body - * @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_RequestInterface $request - * @return void - */ - protected function populateRequest (CAS_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/core/src/plugins/authfront.cas/CAS/ProxiedService/Imap.php b/core/src/plugins/authfront.cas/CAS/ProxiedService/Imap.php index 0a5efa2ddd..847da28c10 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxiedService/Imap.php +++ b/core/src/plugins/authfront.cas/CAS/ProxiedService/Imap.php @@ -1,234 +1,280 @@ + * @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__) . '/Abstract.php'); -include_once(dirname(__FILE__) . '/../Exception.php'); -include_once(dirname(__FILE__) . '/../InvalidArgumentException.php'); -include_once(dirname(__FILE__) . '/../OutOfSequenceException.php'); - /** * 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 +extends CAS_ProxiedService_Abstract { - - /** - * The username to send via imap_open. - * - * @var string $_username; - */ - private $_username; - - /** - * Constructor. - * - * @param string $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 - * @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 - * @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 - * @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. -// $this->_errorMessage = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE), $url, var_export(imap_errors(),TRUE)); - $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 boolean - */ - 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(); - } + + /** + * 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/core/src/plugins/authfront.cas/CAS/ProxiedService/Testable.php b/core/src/plugins/authfront.cas/CAS/ProxiedService/Testable.php index aaf748bb34..51f0767627 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxiedService/Testable.php +++ b/core/src/plugins/authfront.cas/CAS/ProxiedService/Testable.php @@ -1,39 +1,39 @@ + * @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: + * 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. @@ -45,21 +45,31 @@ * 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 - * @return void - * @throws CAS_OutOfSequenceException If called after a proxy ticket has already been initialized/set. - */ - public function setCasClient (CAS_Client $casClient); - +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/core/src/plugins/authfront.cas/CAS/ProxyChain.php b/core/src/plugins/authfront.cas/CAS/ProxyChain.php new file mode 100755 index 0000000000..2594d141e5 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/ProxyChain/AllowedList.php b/core/src/plugins/authfront.cas/CAS/ProxyChain/AllowedList.php new file mode 100755 index 0000000000..62d196ab7f --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/ProxyChain/Any.php b/core/src/plugins/authfront.cas/CAS/ProxyChain/Any.php new file mode 100755 index 0000000000..0cd92f74e9 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/ProxyChain/Interface.php b/core/src/plugins/authfront.cas/CAS/ProxyChain/Interface.php new file mode 100755 index 0000000000..d247115db3 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/ProxyChain/Trusted.php b/core/src/plugins/authfront.cas/CAS/ProxyChain/Trusted.php new file mode 100755 index 0000000000..7fa6129677 --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/ProxyTicketException.php b/core/src/plugins/authfront.cas/CAS/ProxyTicketException.php index 6ef805e156..723304666d 100755 --- a/core/src/plugins/authfront.cas/CAS/ProxyTicketException.php +++ b/core/src/plugins/authfront.cas/CAS/ProxyTicketException.php @@ -1,62 +1,71 @@ + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://wiki.jasig.org/display/CASC/phpCAS * - * 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. */ -require_once(dirname(__FILE__) . '/Exception.php'); - /** * 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 +extends BadMethodCallException +implements CAS_Exception { - - /** - * Constructor - * - * @param string $message - * @param optional int $code - * @param optional Exception $previous - * @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); - } -} \ No newline at end of file + + /** + * 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/core/src/plugins/authfront.cas/CAS/Request/AbstractRequest.php b/core/src/plugins/authfront.cas/CAS/Request/AbstractRequest.php index 1c975ef68e..f3dd28b722 100755 --- a/core/src/plugins/authfront.cas/CAS/Request/AbstractRequest.php +++ b/core/src/plugins/authfront.cas/CAS/Request/AbstractRequest.php @@ -1,299 +1,379 @@ + * @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__) . '/RequestInterface.php'; -include_once(dirname(__FILE__) . '/Exception.php'); -include_once(dirname(__FILE__) . '/../InvalidArgumentException.php'); -include_once(dirname(__FILE__) . '/../OutOfSequenceException.php'); - /** * 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_AbstractRequest - implements CAS_RequestInterface +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; - private $sent = FALSE; - private $responseHeaders = array(); - private $responseBody = null; - private $errorMessage = ''; - - /********************************************************* - * Configure the Request - *********************************************************/ - - /** - * Set the URL of the Request - * - * @param string $url - * @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 - * @param string $value - * @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 - * @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 - * @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 - * @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 - * @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 $sslCertPath - * @return void - * @throws CAS_OutOfSequenceException If called after the Request has been sent. - */ - public function setSslCaCert ($caCertPath) { - if ($this->sent) - throw new CAS_OutOfSequenceException('Request has already been sent cannot '.__METHOD__); - - $this->caCertPath = $caCertPath; - } - - /********************************************************* - * 2. Send the Request - *********************************************************/ - - /** - * Perform the request. - * - * @return boolean 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 boolean TRUE on success, FALSE on failure. - */ - abstract protected function _sendRequest (); - - /** - * Store the response headers. - * - * @param array $headers - * @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 - * @return void - */ - protected function storeResponseHeader ($header) { - $this->responseHeaders[] = $header; - } - - /** - * Store the response body. - * - * @param string $body - * @return void - */ - protected function storeResponseBody ($body) { - $this->responseBody = $body; - } - - /** - * Add a string to our error message. - * - * @param string $message - * @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 integer - * @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; - } -} \ No newline at end of file + 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/core/src/plugins/authfront.cas/CAS/Request/CurlMultiRequest.php b/core/src/plugins/authfront.cas/CAS/Request/CurlMultiRequest.php new file mode 100755 index 0000000000..410aba0e6e --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/Request/CurlRequest.php b/core/src/plugins/authfront.cas/CAS/Request/CurlRequest.php index e0cafe12e2..ea3201e86b 100755 --- a/core/src/plugins/authfront.cas/CAS/Request/CurlRequest.php +++ b/core/src/plugins/authfront.cas/CAS/Request/CurlRequest.php @@ -1,155 +1,199 @@ + * @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__) . '/RequestInterface.php'; -require_once dirname(__FILE__) . '/AbstractRequest.php'; - /** * 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_CurlRequest - extends CAS_AbstractRequest - implements CAS_RequestInterface +class CAS_Request_CurlRequest +extends CAS_Request_AbstractRequest +implements CAS_Request_RequestInterface { - /** - * Set additional curl options - * - * @param array $options - * @return void - */ - public function setCurlOptions (array $options) { - $this->curlOptions = $options; - } - private $curlOptions = array(); - - /** - * Send the request and store the results. - * - * @return boolean TRUE on success, FALSE on failure. - */ - protected function _sendRequest () { - phpCAS::traceBegin(); - - /********************************************************* - * 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) { - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); - curl_setopt($ch, CURLOPT_CAINFO, $this->caCertPath); - phpCAS::trace('CURL: Set CURLOPT_CAINFO'); - } else { - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 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); - } - - /********************************************************* - * 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 for capturing the headers from a curl request. - * - * @param handle $ch - * @param string $header - * @return void - */ - public function _curlReadHeaders ($ch, $header) { - $this->storeResponseHeader($header); - return strlen($header); - } -} \ No newline at end of file + /** + * 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 + */ + private 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); + } + + /********************************************************* + * 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/core/src/plugins/authfront.cas/CAS/Request/Exception.php b/core/src/plugins/authfront.cas/CAS/Request/Exception.php index 86de61a203..14ff3c6b0d 100755 --- a/core/src/plugins/authfront.cas/CAS/Request/Exception.php +++ b/core/src/plugins/authfront.cas/CAS/Request/Exception.php @@ -1,41 +1,45 @@ + * @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__) . '/../Exception.php'); - /** * 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 +extends Exception +implements CAS_Exception { -} \ No newline at end of file +} diff --git a/core/src/plugins/authfront.cas/CAS/Request/MultiRequestInterface.php b/core/src/plugins/authfront.cas/CAS/Request/MultiRequestInterface.php new file mode 100755 index 0000000000..abc448683a --- /dev/null +++ b/core/src/plugins/authfront.cas/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/core/src/plugins/authfront.cas/CAS/Request/RequestInterface.php b/core/src/plugins/authfront.cas/CAS/Request/RequestInterface.php index 98c274ff6f..cc11ba43d9 100755 --- a/core/src/plugins/authfront.cas/CAS/Request/RequestInterface.php +++ b/core/src/plugins/authfront.cas/CAS/Request/RequestInterface.php @@ -1,164 +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_RequestInterface { - - /********************************************************* - * Configure the Request - *********************************************************/ - - /** - * Set the URL of the Request - * - * @param string $url - * @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 - * @param string $value - * @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 - * @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 - * @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 - * @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 - * @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 $sslCertPath - * @return void - * @throws CAS_OutOfSequenceException If called after the Request has been sent. - */ - public function setSslCaCert ($caCertPath); - - - - /********************************************************* - * 2. Send the Request - *********************************************************/ - - /** - * Perform the request. - * - * @return boolean 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 integer - * @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 (); -} \ No newline at end of file +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/core/src/plugins/authfront.cas/CAS/TypeMismatchException.php b/core/src/plugins/authfront.cas/CAS/TypeMismatchException.php new file mode 100755 index 0000000000..4a13c2df45 --- /dev/null +++ b/core/src/plugins/authfront.cas/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 + ); + } +} +?> diff --git a/core/src/plugins/authfront.cas/CAS/languages/catalan.php b/core/src/plugins/authfront.cas/CAS/languages/catalan.php deleted file mode 100755 index fee6a341b9..0000000000 --- a/core/src/plugins/authfront.cas/CAS/languages/catalan.php +++ /dev/null @@ -1,27 +0,0 @@ -_strings = array( -CAS_STR_USING_SERVER -=> 'usant servidor', -CAS_STR_AUTHENTICATION_WANTED -=> 'Autentificació CAS necessària!', -CAS_STR_LOGOUT -=> 'Sortida de CAS necessària!', -CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED -=> 'Ja hauria d\ haver estat redireccionat al servidor CAS. Feu click aquí per a continuar.', -CAS_STR_AUTHENTICATION_FAILED -=> 'Autentificació CAS fallida!', -CAS_STR_YOU_WERE_NOT_AUTHENTICATED -=> '

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.

', -CAS_STR_SERVICE_UNAVAILABLE -=> 'El servei `%s\' no està disponible (%s).' -); - -?> diff --git a/core/src/plugins/authfront.cas/CAS/languages/english.php b/core/src/plugins/authfront.cas/CAS/languages/english.php deleted file mode 100755 index 3dec924a9f..0000000000 --- a/core/src/plugins/authfront.cas/CAS/languages/english.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @sa @link internalLang Internationalization @endlink - * @ingroup internalLang - */ - -$this->_strings = array( -CAS_STR_USING_SERVER -=> 'using server', -CAS_STR_AUTHENTICATION_WANTED -=> 'CAS Authentication wanted!', -CAS_STR_LOGOUT -=> 'CAS logout wanted!', -CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED -=> 'You should already have been redirected to the CAS server. Click here to continue.', -CAS_STR_AUTHENTICATION_FAILED -=> 'CAS Authentication failed!', -CAS_STR_YOU_WERE_NOT_AUTHENTICATED -=> '

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.

', -CAS_STR_SERVICE_UNAVAILABLE -=> 'The service `%s\' is not available (%s).' -); - -?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/languages/french.php b/core/src/plugins/authfront.cas/CAS/languages/french.php deleted file mode 100755 index 84619a7aeb..0000000000 --- a/core/src/plugins/authfront.cas/CAS/languages/french.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @sa @link internalLang Internationalization @endlink - * @ingroup internalLang - */ - -$this->_strings = array( -CAS_STR_USING_SERVER -=> 'utilisant le serveur', -CAS_STR_AUTHENTICATION_WANTED -=> 'Authentication CAS n�cessaire !', -CAS_STR_LOGOUT -=> 'D�connexion demand�e !', -CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED -=> 'Vous auriez du etre redirig�(e) vers le serveur CAS. Cliquez ici pour continuer.', -CAS_STR_AUTHENTICATION_FAILED -=> 'Authentification CAS infructueuse !', -CAS_STR_YOU_WERE_NOT_AUTHENTICATED -=> '

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.

', -CAS_STR_SERVICE_UNAVAILABLE -=> 'Le service `%s\' est indisponible (%s)' - -); - -?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/languages/german.php b/core/src/plugins/authfront.cas/CAS/languages/german.php deleted file mode 100755 index ffb1c03b5c..0000000000 --- a/core/src/plugins/authfront.cas/CAS/languages/german.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @sa @link internalLang Internationalization @endlink - * @ingroup internalLang - */ - -$this->_strings = array( -CAS_STR_USING_SERVER -=> 'via Server', -CAS_STR_AUTHENTICATION_WANTED -=> 'CAS Authentifizierung erforderlich!', -CAS_STR_LOGOUT -=> 'CAS Abmeldung!', -CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED -=> 'eigentlich häten Sie zum CAS Server weitergeleitet werden sollen. Drücken Sie hier um fortzufahren.', -CAS_STR_AUTHENTICATION_FAILED -=> 'CAS Anmeldung fehlgeschlagen!', -CAS_STR_YOU_WERE_NOT_AUTHENTICATED -=> '

Sie wurden nicht angemeldet.

Um es erneut zu versuchen klicken Sie hier.

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

', -CAS_STR_SERVICE_UNAVAILABLE -=> 'Der Dienst `%s\' ist nicht verfügbar (%s).' -); - -?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/languages/greek.php b/core/src/plugins/authfront.cas/CAS/languages/greek.php deleted file mode 100755 index a3df7f7ff9..0000000000 --- a/core/src/plugins/authfront.cas/CAS/languages/greek.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @sa @link internalLang Internationalization @endlink - * @ingroup internalLang - */ - -$this->_strings = array( - CAS_STR_USING_SERVER - => '��������������� � ������������', - CAS_STR_AUTHENTICATION_WANTED - => '���������� � ����������� CAS!', - CAS_STR_LOGOUT - => '���������� � ���������� ��� CAS!', - CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED - => '�� ������ �� ������ �������������� ���� ����������� CAS. ����� ���� ��� ��� �� ����������.', - CAS_STR_AUTHENTICATION_FAILED - => '� ����������� CAS �������!', - CAS_STR_YOU_WERE_NOT_AUTHENTICATED - => '

��� ���������������.

�������� �� ����������������, �������� ���� ���.

��� �� �������� ���������, ����� �� ����� �� ��� �����������.

', - CAS_STR_SERVICE_UNAVAILABLE - => '� �������� `%s\' ��� ����� ��������� (%s).' -); - -?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/languages/japanese.php b/core/src/plugins/authfront.cas/CAS/languages/japanese.php deleted file mode 100755 index 5092580955..0000000000 --- a/core/src/plugins/authfront.cas/CAS/languages/japanese.php +++ /dev/null @@ -1,27 +0,0 @@ -_strings = array( -CAS_STR_USING_SERVER -=> 'using server', -CAS_STR_AUTHENTICATION_WANTED -=> 'CAS�ˤ��ǧ�ڤ�Ԥ��ޤ�', -CAS_STR_LOGOUT -=> 'CAS����?�����Ȥ��ޤ�!', -CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED -=> 'CAS�����Ф˹Ԥ�ɬ�פ�����ޤ�����ưŪ��ž������ʤ����� ������ �򥯥�å�����³�Ԥ��ޤ���', -CAS_STR_AUTHENTICATION_FAILED -=> 'CAS�ˤ��ǧ�ڤ˼��Ԥ��ޤ���', -CAS_STR_YOU_WERE_NOT_AUTHENTICATED -=> '

ǧ�ڤǤ��ޤ���Ǥ���.

�⤦���٥ꥯ�����Ȥ�������������������򥯥�å�.

���꤬��褷�ʤ����� ���Υ����Ȥδ�������䤤��碌�Ƥ�������.

', -CAS_STR_SERVICE_UNAVAILABLE -=> '�����ӥ� `%s\' �����ѤǤ��ޤ��� (%s).' -); - -?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/languages/languages.php b/core/src/plugins/authfront.cas/CAS/languages/languages.php deleted file mode 100755 index 001cfe445c..0000000000 --- a/core/src/plugins/authfront.cas/CAS/languages/languages.php +++ /dev/null @@ -1,24 +0,0 @@ - - * @sa @link internalLang Internationalization @endlink - * @ingroup internalLang - */ - -//@{ -/** - * a phpCAS string index - */ -define("CAS_STR_USING_SERVER", 1); -define("CAS_STR_AUTHENTICATION_WANTED", 2); -define("CAS_STR_LOGOUT", 3); -define("CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED", 4); -define("CAS_STR_AUTHENTICATION_FAILED", 5); -define("CAS_STR_YOU_WERE_NOT_AUTHENTICATED", 6); -define("CAS_STR_SERVICE_UNAVAILABLE", 7); -//@} - -?> \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/CAS/languages/spanish.php b/core/src/plugins/authfront.cas/CAS/languages/spanish.php deleted file mode 100755 index 75f435c227..0000000000 --- a/core/src/plugins/authfront.cas/CAS/languages/spanish.php +++ /dev/null @@ -1,27 +0,0 @@ -_strings = array( -CAS_STR_USING_SERVER -=> 'usando servidor', -CAS_STR_AUTHENTICATION_WANTED -=> '¡Autentificación CAS necesaria!', -CAS_STR_LOGOUT -=> '¡Salida CAS necesaria!', -CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED -=> 'Ya debería haber sido redireccionado al servidor CAS. Haga click aquí para continuar.', -CAS_STR_AUTHENTICATION_FAILED -=> '¡Autentificación CAS fallida!', -CAS_STR_YOU_WERE_NOT_AUTHENTICATED -=> '

No estás autentificado.

Puedes volver a intentarlo haciendo click aquí.

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

', -CAS_STR_SERVICE_UNAVAILABLE -=> 'El servicio `%s\' no está disponible (%s).' -); - -?> diff --git a/core/src/plugins/authfront.cas/change_log b/core/src/plugins/authfront.cas/change_log index 04696e0ca1..7d2bb305d2 100755 --- a/core/src/plugins/authfront.cas/change_log +++ b/core/src/plugins/authfront.cas/change_log @@ -2,4 +2,8 @@ CAS/Client.php 2. add: public function serviceSamba($serviceUrl, &$err_code) 3. modify: public function getProxiedService ($type) -4. modify: private function getCallbackURL() \ No newline at end of file +4. modify: private function getCallbackURL() + +CAS.php +6. add define("PHPCAS_PROXIED_SERVICE_IMAP", 'CAS_ProxiedService_Samba'); +5. add public static function serviceSMB($url, $err_code) \ No newline at end of file diff --git a/core/src/plugins/authfront.cas/class.CasAuthFrontend.php b/core/src/plugins/authfront.cas/class.CasAuthFrontend.php index fb9fa7994b..45a75ec6cf 100755 --- a/core/src/plugins/authfront.cas/class.CasAuthFrontend.php +++ b/core/src/plugins/authfront.cas/class.CasAuthFrontend.php @@ -45,6 +45,7 @@ class CasAuthFrontend extends AbstractAuthFrontend private $cas_debug_file; private $cas_modify_login_page; private $cas_additional_role; + private $cas_setFixedCallbackURL; function tryToLogUser(&$httpVars, $isLast = false) @@ -61,24 +62,23 @@ function tryToLogUser(&$httpVars, $isLast = false) $logged = isset($_SESSION['LOGGED_IN_BY_CAS']); $enre = !empty($httpVars['put_action_enable_redirect']); $ticket = !empty($httpVars['ticket']); + $pgt = !empty($_SESSION['phpCAS']['pgt']); if ($this->cas_modify_login_page) { + if(($flag == 0) && ($enre) && !$logged && !$pgtIou){ $_SESSION['AUTHENTICATE_BY_CAS'] = 1; - }elseif(($flag == 1) && (!$enre) && !$logged && !$pgtIou && !$ticket){ + }elseif(($flag == 1) && (!$enre) && !$logged && !$pgtIou && !$ticket && !$pgt){ $_SESSION['AUTHENTICATE_BY_CAS'] = 0; }elseif(($flag == 1) && ($enre) && !$logged && !$pgtIou){ $_SESSION['AUTHENTICATE_BY_CAS'] = 1; - }elseif($pgtIou){ + }elseif($pgtIou || $ticket || $pgt){ $_SESSION['AUTHENTICATE_BY_CAS'] = 1; }elseif($logged && $pgtIou){ $_SESSION['AUTHENTICATE_BY_CAS'] = 2; - }elseif($ticket){ - $_SESSION['AUTHENTICATE_BY_CAS'] = 1; }else{ $_SESSION['AUTHENTICATE_BY_CAS'] = 0; } - if ($_SESSION['AUTHENTICATE_BY_CAS'] < 1) { return false; } @@ -151,6 +151,10 @@ function tryToLogUser(&$httpVars, $isLast = false) phpCAS::setDebug($file_path); } + if(!empty($this->cas_setFixedCallbackURL)){ + phpCAS::setFixedCallbackURL($this->cas_setFixedCallbackURL); + } + // /** * PTG storage */ @@ -291,7 +295,9 @@ function loadConfig() if (!empty($this->pluginConf["PHPCAS_MODE"]["PROXIED_SERVICE_SMB"])) { $this->cas_proxied_service = $this->pluginConf["PHPCAS_MODE"]["PROXIED_SERVICE_SMB"]; } - + if (!empty($this->pluginConf["PHPCAS_MODE"]["FIXED_CALLBACK_URL"])) { + $this->cas_setFixedCallbackURL = $this->pluginConf["PHPCAS_MODE"]["FIXED_CALLBACK_URL"]; + } if (!empty($this->pluginConf["DEBUG_MODE"])) { $this->cas_debug_mode = $this->pluginConf["DEBUG_MODE"]; } @@ -333,9 +339,10 @@ private function setPTGStorage() if (!empty($dbconfig->sqlDriver["username"])) { $db_username = $dbconfig->sqlDriver["username"]; $db_password = $dbconfig->sqlDriver["password"]; - $db_database = "mysql:host=" . $dbconfig->sqlDriver["host"] . ";dbname=" . $dbconfig->sqlDriver["database"]; + $db_database = "mysql:"."dbname=".$dbconfig->sqlDriver["database"].";host=".$dbconfig->sqlDriver["host"]; $db_table = "ajxp_cas_pgt"; - phpCAS::setPGTStorageDB($db_database, $db_username, $db_password, $db_table); + AJXP_Logger::info(__CLASS__, __FUNCTION__, $db_database); + phpCAS::setPGTStorageDB($db_database, $db_username, $db_password, $db_table, ""); } } break; diff --git a/core/src/plugins/authfront.cas/i18n/it.php b/core/src/plugins/authfront.cas/i18n/it.php old mode 100644 new mode 100755 diff --git a/core/src/plugins/authfront.cas/logoutCAS.png b/core/src/plugins/authfront.cas/logoutCAS.png deleted file mode 100755 index bb265c2d1c..0000000000 Binary files a/core/src/plugins/authfront.cas/logoutCAS.png and /dev/null differ diff --git a/core/src/plugins/authfront.cas/manifest.xml b/core/src/plugins/authfront.cas/manifest.xml index fd02f36d3a..ce28271c9a 100755 --- a/core/src/plugins/authfront.cas/manifest.xml +++ b/core/src/plugins/authfront.cas/manifest.xml @@ -43,6 +43,8 @@ +