From: Michael M Slusarz Date: Tue, 8 Jun 2010 04:53:02 +0000 (-0600) Subject: Final parts of the Horde_Auth rewrite. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=6ec7d614c26d88f25c2d2cf49e2d0263a1322106;p=horde.git Final parts of the Horde_Auth rewrite. All session related code has now been moved to horde/Core. Horde_Auth no longer depends on horde/Core. Authentication for each application is handled entirely by the Horde_Core_Auth_Application driver. Removed registry functions: getProvider() requireAuth() isAuthenticated() now has a new option - 'notransparent' Removed the recently added clearAuth() call to isAuthenticated() - isAuthenticated() is meant solely for determining whether the given app is authenticated. Code to invalidate a session (for example, if a checkExistingAuth() call fails) MUST go elsewhere. Moved last login display to a Horde LoginTask. --- diff --git a/folks/edit/password.php b/folks/edit/password.php index 97f7714b1..06a2a9b2b 100644 --- a/folks/edit/password.php +++ b/folks/edit/password.php @@ -161,13 +161,7 @@ do { // reset credentials so user is not forced to relogin if ($registry->getAuthCredential('password') == $info['old']) { $registry->setAuthCredential('password', $info['new']); - $secret = $injector->getInstance('Horde_Secret'); - if ($registry->getProvider() == 'imp' || - !empty($_SESSION['imp']['pass'])) { - $_SESSION['imp']['pass'] = $secret->write($secret->getKey('imp'), $info['new']); - } } - } while (false); // update password reminder prefs diff --git a/folks/lib/Application.php b/folks/lib/Application.php index 9a7f7ea22..d9bca5812 100644 --- a/folks/lib/Application.php +++ b/folks/lib/Application.php @@ -57,7 +57,7 @@ class Folks_Application extends Horde_Registry_Application require_once dirname(__FILE__) . '/base.php'; $GLOBALS['folks_driver'] = Folks_Driver::factory(); if ($_COOKIE['folks_login_code'] == $GLOBALS['folks_driver']->getCookie($_COOKIE['folks_login_user'])) { - Horde_Auth::setAuth($_COOKIE['folks_login_user'], array(), array('transparent' => true, 'nologin' => true)); + $GLOBALS['registry']->setAuth($_COOKIE['folks_login_user']); $auth_ob->setCredential('userId', $_COOKIE['folks_login_user']); $GLOBALS['folks_driver']->resetOnlineUsers(); return true; diff --git a/framework/Auth/lib/Horde/Auth.php b/framework/Auth/lib/Horde/Auth.php index c182650e2..900b2cd1f 100644 --- a/framework/Auth/lib/Horde/Auth.php +++ b/framework/Auth/lib/Horde/Auth.php @@ -1,24 +1,8 @@ - * 'app' - (array) Application-specific authentication. Keys are the - * app names, values are an array containing credentials. If true, - * application does not require any specific credentials. - * 'authId' - (string) The username used during the original authentication. - * 'browser' - (string) The remote browser string. - * 'change' - (boolean) Is a password change requested? - * 'credentials' - (string) The 'app' entry that contains the Horde - * credentials. - * 'driver' - (string) The driver used for base horde auth. - * 'remoteAddr' - (string) The remote IP address of the user. - * 'timestamp' - (integer) The login time. - * 'userId' - (string) The unique Horde username. - * + * The Horde_Auth:: class provides a common abstracted interface for various + * authentication backends. It also provides some useful + * authentication-related utilities. * * Copyright 1999-2010 The Horde Project (http://www.horde.org/) * @@ -34,39 +18,23 @@ class Horde_Auth { /** - * The parameter name for the logout reason. - */ - const REASON_PARAM = 'logout_reason'; - - /** - * The parameter name for the logout message used with type - * REASON_MESSAGE. - */ - const REASON_MSG_PARAM = 'logout_msg'; - - /** - * The 'badlogin' reason. + * Authentication failure reasons. * - * The following 'reasons' for the logout screen are recognized: *
      * REASON_BADLOGIN - Bad username and/or password
-     * REASON_BROWSER - A browser change was detected
      * REASON_FAILED - Login failed
      * REASON_EXPIRED - Password has expired
      * REASON_LOGOUT - Logout due to user request
-     * REASON_MESSAGE - Logout with custom message in REASON_MSG_PARAM
+     * REASON_MESSAGE - Logout with custom message
      * REASON_SESSION - Logout due to session expiration
-     * REASON_SESSIONIP - Logout due to change of IP address during session
      * 
*/ const REASON_BADLOGIN = 1; - const REASON_BROWSER = 2; - const REASON_FAILED = 3; - const REASON_EXPIRED = 4; - const REASON_LOGOUT = 5; - const REASON_MESSAGE = 6; - const REASON_SESSION = 7; - const REASON_SESSIONIP = 8; + const REASON_FAILED = 2; + const REASON_EXPIRED = 3; + const REASON_LOGOUT = 4; + const REASON_MESSAGE = 5; + const REASON_SESSION = 6; /** * 64 characters that are valid for APRMD5 passwords. @@ -81,20 +49,6 @@ class Horde_Auth const NUMBERS = '0123456789'; /** - * A Net_DNS_Resolver object to use to determine hostnames. - * - * @var Net_DNS_Resolver - */ - static public $dnsResolver; - - /** - * The logout reason information. - * - * @var array - */ - static protected $_reason = array(); - - /** * Attempts to return a concrete Horde_Auth_Base instance based on * $driver. * @@ -286,278 +240,6 @@ class Horde_Auth } /** - * Generates a random, hopefully pronounceable, password. This can be used - * when resetting automatically a user's password. - * - * @return string A random password - */ - static public function genRandomPassword() - { - /* Alternate consonant and vowel random chars with two random numbers - * at the end. This should produce a fairly pronounceable password. */ - return substr(self::CONSONANTS, mt_rand(0, strlen(self::CONSONANTS) - 1), 1) . - substr(self::VOWELS, mt_rand(0, strlen(self::VOWELS) - 1), 1) . - substr(self::CONSONANTS, mt_rand(0, strlen(self::CONSONANTS) - 1), 1) . - substr(self::VOWELS, mt_rand(0, strlen(self::VOWELS) - 1), 1) . - substr(self::CONSONANTS, mt_rand(0, strlen(self::CONSONANTS) - 1), 1) . - substr(self::NUMBERS, mt_rand(0, strlen(self::NUMBERS) - 1), 1) . - substr(self::NUMBERS, mt_rand(0, strlen(self::NUMBERS) - 1), 1); - } - - /** - * Calls all applications' removeUser API methods. - * - * @param string $userId The userId to delete. - * - * @throws Horde_Auth_Exception - */ - static public function removeUserData($userId) - { - $errApps = array(); - - foreach ($GLOBALS['registry']->listApps(array('notoolbar', 'hidden', 'active', 'admin')) as $app) { - try { - $GLOBALS['registry']->callByPackage($app, 'removeUserData', array($userId)); - } catch (Horde_Auth_Exception $e) { - Horde::logMessage($e, 'ERR'); - $errApps[] = $app; - } - } - - if (count($errApps)) { - throw new Horde_Auth_Exception(sprintf(_("The following applications encountered errors removing user data: %s"), implode(', ', $errApps))); - } - } - - /** - * Check existing auth for triggers that might invalidate it. - * - * @return boolean Is existing auth valid? - */ - static public function checkExistingAuth() - { - if (!empty($GLOBALS['conf']['auth']['checkip']) && - !empty($_SESSION['horde_auth']['remoteAddr']) && - ($_SESSION['horde_auth']['remoteAddr'] != $_SERVER['REMOTE_ADDR'])) { - self::setAuthError(self::REASON_SESSIONIP); - return false; - } - - if (!empty($GLOBALS['conf']['auth']['checkbrowser'])) { - if ($_SESSION['horde_auth']['browser'] != $GLOBALS['injector']->getInstance('Horde_Browser')->getAgentString()) { - self::setAuthError(self::REASON_BROWSER); - return false; - } - } - - return $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth()->checkExistingAuth(); - } - - /** - * Sets a variable in the session saying that authorization has succeeded, - * note which userId was authorized, and note when the login took place. - * - * If a user name hook was defined in the configuration, it gets applied - * to the $userId at this point. - * - * @param string $authId The userId that has been authorized. - * @param array $credentials The credentials of the user. - * @param array $options Additional options: - *
-     * 'app' - (string) The app to set authentication credentials for.
-     *         DEFAULT: Set horde authentication
-     * 'change' - (boolean) Whether to request that the user change their
-     *            password.
-     *            DEFAULT: No
-     * 'nologin' - (boolean) Don't do login tasks?
-     *             DEFAULT: Perform login tasks
-     * 
- * - * @return boolean Whether authentication was successful. - */ - static public function setAuth($authId, $credentials, $options = array()) - { - $app = empty($options['app']) ? 'horde' : $options['app']; - $authId = $userId = trim($authId); - $is_auth = $GLOBALS['registry']->getAuth(); - - try { - if (!$is_auth) { - $userId = $GLOBALS['registry']->convertUserName($userId, true); - } - list(,$credentials) = self::runHook($userId, $credentials, $app, 'postauthenticate'); - } catch (Horde_Auth_Exception $e) { - return false; - } - - $app_array = $is_auth - ? $_SESSION['horde_auth']['app'] - : array(); - - $secret = $GLOBALS['injector']->getInstance('Horde_Secret'); - $app_array[$app] = $secret->write($secret->getKey('auth'), serialize($credentials)); - - if ($is_auth) { - /* Store app credentials. */ - $_SESSION['horde_auth']['app'] = $app_array; - return true; - } - - /* Clear any existing info. */ - $GLOBALS['registry']->clearAuth(false); - - $_SESSION['horde_auth'] = array( - 'app' => $app_array, - 'authId' => $authId, - 'browser' => $GLOBALS['injector']->getInstance('Horde_Browser')->getAgentString(), - 'change' => !empty($options['change']), - 'credentials' => $app, - 'driver' => $GLOBALS['conf']['auth']['driver'], - 'remoteAddr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null, - 'timestamp' => time(), - 'userId' => $userId - ); - - /* Reload preferences for the new user. */ - $GLOBALS['registry']->loadPrefs(); - Horde_Nls::setLanguageEnvironment($GLOBALS['prefs']->getValue('language'), $app); - - if (!empty($options['nologin'])) { - return true; - } - - /* Fetch the user's last login time. */ - $old_login = @unserialize($GLOBALS['prefs']->getValue('last_login')); - - /* Display it, if we have a notification object and the - * show_last_login preference is active. */ - if (isset($GLOBALS['notification']) && - $GLOBALS['prefs']->getValue('show_last_login')) { - if (empty($old_login['time'])) { - $GLOBALS['notification']->push(_("Last login: Never"), 'horde.message'); - } else { - if (empty($old_login['host'])) { - $GLOBALS['notification']->push(sprintf(_("Last login: %s"), strftime('%c', $old_login['time'])), 'horde.message'); - } else { - $GLOBALS['notification']->push(sprintf(_("Last login: %s from %s"), strftime('%c', $old_login['time']), $old_login['host']), 'horde.message'); - } - } - } - - /* Set the user's last_login information. */ - $host = empty($_SERVER['HTTP_X_FORWARDED_FOR']) - ? $_SERVER['REMOTE_ADDR'] - : $_SERVER['HTTP_X_FORWARDED_FOR']; - - if (!empty(self::$dnsResolver)) { - $ptrdname = $host; - if ($response = self::$dnsResolver->query($host, 'PTR')) { - foreach ($response->answer as $val) { - if (isset($val->ptrdname)) { - $ptrdname = $val->ptrdname; - break; - } - } - } - } else { - $ptrdname = @gethostbyaddr($host); - } - - $last_login = array('time' => time(), 'host' => $ptrdname); - $GLOBALS['prefs']->setValue('last_login', serialize($last_login)); - - return true; - } - - /** - * Runs the pre/post-authenticate hook and parses the result. - * - * @param string $userId The userId who has been authorized. - * @param array $credentials The credentials of the user. - * @param string $app The app currently being authenticated. - * @param string $type Either 'preauthenticate' or - * 'postauthenticate'. - * @param string $method The triggering method (preauthenticate only). - * Either 'authenticate', 'transparent', or - * 'admin' - * - * @return array Two element array, $userId and $credentials. - * @throws Horde_Auth_Exception - */ - static public function runHook($userId, $credentials, $app, $type, - $method = null) - { - $ret_array = array($userId, $credentials); - - if ($type == 'preauthenticate') { - $credentials['authMethod'] = $method; - } - - try { - $result = Horde::callHook($type, array($userId, $credentials), $app); - } catch (Horde_Exception $e) { - throw new Horde_Auth_Exception($e->getMessage()); - } catch (Horde_Exception_HookNotSet $e) { - return $ret_array; - } - - unset($credentials['authMethod']); - - if ($result === false) { - if (self::getAuthError() != self::REASON_MESSAGE) { - self::setAuthError(self::REASON_FAILED); - } - throw new Horde_Auth_Exception($type . ' hook failed'); - } - - if (is_array($result)) { - if ($type == 'postauthenticate') { - $ret_array[1] = $result; - } else { - if (isset($result['userId'])) { - $ret_array[0] = $result['userId']; - } - - if (isset($result['credentials'])) { - $ret_array[1] = $result['credentials']; - } - } - } - - return $ret_array; - } - - /** - * Sets the error message for an invalid authentication. - * - * @param string $type The type of error (self::REASON_* constant). - * @param string $msg The error message/reason for invalid - * authentication. - */ - static public function setAuthError($type, $msg = null) - { - self::$_reason = array( - 'msg' => $msg, - 'type' => $type - ); - } - - /** - * Returns the error type or message for an invalid authentication. - * - * @param boolean $msg If true, returns the message string (if set). - * - * @return mixed Error type, error message (if $msg is true) or false - * if entry doesn't exist. - */ - static public function getAuthError($msg = false) - { - return isset(self::$_reason['type']) - ? ($msg ? self::$_reason['msg'] : self::$_reason['type']) - : false; - } - - /** * Converts to allowed 64 characters for APRMD5 passwords. * * @param string $value TODO @@ -579,4 +261,23 @@ class Horde_Auth return $aprmd5; } + /** + * Generates a random, hopefully pronounceable, password. This can be used + * when resetting automatically a user's password. + * + * @return string A random password + */ + static public function genRandomPassword() + { + /* Alternate consonant and vowel random chars with two random numbers + * at the end. This should produce a fairly pronounceable password. */ + return substr(self::CONSONANTS, mt_rand(0, strlen(self::CONSONANTS) - 1), 1) . + substr(self::VOWELS, mt_rand(0, strlen(self::VOWELS) - 1), 1) . + substr(self::CONSONANTS, mt_rand(0, strlen(self::CONSONANTS) - 1), 1) . + substr(self::VOWELS, mt_rand(0, strlen(self::VOWELS) - 1), 1) . + substr(self::CONSONANTS, mt_rand(0, strlen(self::CONSONANTS) - 1), 1) . + substr(self::NUMBERS, mt_rand(0, strlen(self::NUMBERS) - 1), 1) . + substr(self::NUMBERS, mt_rand(0, strlen(self::NUMBERS) - 1), 1); + } + } diff --git a/framework/Auth/lib/Horde/Auth/Auto.php b/framework/Auth/lib/Horde/Auth/Auto.php index 00186647c..9aff8be75 100644 --- a/framework/Auth/lib/Horde/Auth/Auto.php +++ b/framework/Auth/lib/Horde/Auth/Auto.php @@ -72,7 +72,7 @@ class Horde_Auth_Auto extends Horde_Auth_Base * * @return boolean Whether or not the client is allowed. */ - protected function _transparent() + public function transparent() { $this->_credentials['userId'] = (!empty($this->_params['requestuser']) && isset($_REQUEST['username'])) ? $_REQUEST['username'] diff --git a/framework/Auth/lib/Horde/Auth/Base.php b/framework/Auth/lib/Horde/Auth/Base.php index eece8f6c0..a52d0bf14 100644 --- a/framework/Auth/lib/Horde/Auth/Base.php +++ b/framework/Auth/lib/Horde/Auth/Base.php @@ -46,24 +46,25 @@ abstract class Horde_Auth_Base * @var array */ protected $_credentials = array( + 'change' => false, 'credentials' => array(), - 'params' => array('change' => false), + 'expire' => null, 'userId' => '' ); /** - * Current application for authentication. + * Logger object. * - * @var string + * @var Horde_Log_Logger */ - protected $_app = 'horde'; + protected $_logger; /** - * Logger object. + * Authentication error information. * - * @var Horde_Log_Logger + * @var array */ - protected $_logger; + protected $_error; /** * Constructor. @@ -72,9 +73,6 @@ abstract class Horde_Auth_Base *
      * 'default_user' - (string) The default user.
      * 'logger' - (Horde_Log_Logger) A logger object.
-     * 'notify_expire' - (callback) Callback function to output notification
-     *                   when password is about to expire. Passed one
-     *                   argument: UNIX timestamp of when password expires.
      * 
*/ public function __construct(array $params = array()) @@ -105,42 +103,21 @@ abstract class Horde_Auth_Base */ public function authenticate($userId, $credentials, $login = true) { - $auth = false; $userId = trim($userId); try { - list($userId, $credentials) = Horde_Auth::runHook($userId, $credentials, $this->_app, 'preauthenticate', 'authenticate'); - } catch (Horde_Auth_Exception $e) { - return false; - } - - /* Store the credentials being checked so that subclasses can modify - * them if necessary. */ - $this->_credentials['credentials'] = $credentials; - $this->_credentials['userId'] = $userId; - $this->_credentials['params']['app'] = $this->_app; - - try { $this->_authenticate($userId, $credentials); - - if ($login) { - $auth = Horde_Auth::setAuth( - $this->_credentials['userId'], - $this->_credentials['credentials'], - $this->_credentials['params'] - ); - } else { - $auth = Horde_Auth::checkExistingAuth(); - } + $this->setCredential('userId', $userId); + $this->setCredential('credentials', $credentials); + return true; } catch (Horde_Auth_Exception $e) { if ($e->getCode()) { - Horde_Auth::setAuthError($e->getCode()); + $this->setError($e->getCode()); } else { - Horde_Auth::setAuthError(Horde_Auth::REASON_MESSAGE, $e->getMessage()); + $this->setError(Horde_Auth::REASON_MESSAGE, $e->getMessage()); } + return false; } - - return $auth; } /** @@ -158,11 +135,12 @@ abstract class Horde_Auth_Base abstract protected function _authenticate($userId, $credentials); /** - * Check existing auth for triggers that might invalidate it. + * Checks for triggers that may invalidate the current auth. + * These triggers are independent of the credentials. * - * @return boolean Is existing auth valid? + * @return boolean True if the results of authenticate() are still valid. */ - public function checkExistingAuth() + public function validateAuth() { return true; } @@ -237,36 +215,6 @@ abstract class Horde_Auth_Base /** * Automatic authentication. * - * @return boolean Whether or not the user is authenticated - * automatically. - * @throws Horde_Auth_Exception - */ - public function transparent() - { - $userId = empty($this->_credentials['userId']) - ? $this->_params['default_user'] - : $this->_credentials['userId']; - $credentials = empty($this->_credentials['credentials']) - ? $GLOBALS['registry']->getAuthCredential() - : $this->_credentials['credentials']; - - list($this->_credentials['userId'], $this->_credentials['credentials']) = Horde_Auth::runHook($userId, $credentials, $this->_app, 'preauthenticate', 'transparent'); - $this->_credentials['params']['app'] = $this->_app; - - if ($this->_transparent()) { - return Horde_Auth::setAuth( - $this->_credentials['userId'], - $this->_credentials['credentials'], - $this->_credentials['params'] - ); - } - - return false; - } - - /** - * Transparent authentication stub. - * * Transparent authentication should set 'userId', 'credentials', or * 'params' in $this->_credentials as needed - these values will be used * to set the credentials in the session. @@ -277,7 +225,7 @@ abstract class Horde_Auth_Base * @return boolean Whether transparent login is supported. * @throws Horde_Auth_Exception */ - protected function _transparent() + public function transparent() { return false; } @@ -324,26 +272,87 @@ abstract class Horde_Auth_Base } /** - * Returns information on what login parameters to display on the login - * screen. If not defined, will display the default (username, password). + * Retrieve internal credential value(s). * - * @return array An array with the following elements: + * @param mixed $name The credential value to get. If null, will return + * the entire credential list. Valid names: *
-     * 'js_code' - (array) A list of javascript code to output to the login
-     *              page.
-     * 'js_files' - (array) A list of javascript files to include in the login
-     *              page.
-     * 'params' - (array) TODO
+     * 'change' - (boolean) Do credentials need to be changed?
+     * 'credentials' - (array) The credentials needed to authenticate.
+     * 'expire' - (integer) UNIX timestamp of the credential expiration date.
+     * 'userId' - (string) The user ID.
      * 
- * @throws Horde_Exception + * + * @return mixed Return the credential information, or null if the + * credential doesn't exist. */ - public function getLoginParams() + public function getCredential($name = null) { - return array( - 'js_code' => array(), - 'js_files' => array(), - 'params' => array() + if (is_null($name)) { + return $this->_credentials; + } + + return isset($this->_credentials[$name]) + ? $this->_credentials[$name] + : null; + } + + /** + * Set internal credential value. + * + * @param string $name The credential name to set. + * @param mixed $value The credential value to set. See getCredential() + * for the list of valid credentials/types. + */ + public function setCredential($type, $value) + { + switch ($type) { + case 'change': + $this->_credentials['change'] = (bool)$value; + break; + + case 'credentials': + $this->_credentials['credentials'] = array_filter(array_merge($this->_credentials['credentials'], $value)); + break; + + case 'expire': + $this->_credentials['expire'] = intval($value); + break; + + case 'userId': + $this->_credentials['userId'] = strval($value); + break; + } + } + + /** + * Sets the error message for an invalid authentication. + * + * @param string $type The type of error (Horde_Auth::REASON_* constant). + * @param string $msg The error message/reason for invalid + * authentication. + */ + public function setError($type, $msg = null) + { + $this->_error = array( + 'msg' => $msg, + 'type' => $type ); } + /** + * Returns the error type or message for an invalid authentication. + * + * @param boolean $msg If true, returns the message string (if set). + * + * @return mixed Error type, error message (if $msg is true) or false + * if entry doesn't exist. + */ + public function getError($msg = false) + { + return isset($this->_error['type']) + ? ($msg ? $this->_error['msg'] : $this->_error['type']) + : false; + } + } diff --git a/framework/Auth/lib/Horde/Auth/Composite.php b/framework/Auth/lib/Horde/Auth/Composite.php index f597c76bb..979279f9d 100644 --- a/framework/Auth/lib/Horde/Auth/Composite.php +++ b/framework/Auth/lib/Horde/Auth/Composite.php @@ -47,7 +47,7 @@ class Horde_Auth_Composite extends Horde_Auth_Base */ protected function _authenticate($userId, $credentials) { - return $this->_params['auth_driver']->authenticate($userId, $credentials, false); + return $this->_params['auth_driver']->authenticate($userId, $credentials); } /** @@ -73,7 +73,7 @@ class Horde_Auth_Composite extends Horde_Auth_Base * * @return boolean Whether or not the client is allowed. */ - protected function _transparent() + public function transparent() { try { return $this->_params['auth_driver']->transparent(); diff --git a/framework/Auth/lib/Horde/Auth/Cyrsql.php b/framework/Auth/lib/Horde/Auth/Cyrsql.php index 772202d49..a78ba1c61 100644 --- a/framework/Auth/lib/Horde/Auth/Cyrsql.php +++ b/framework/Auth/lib/Horde/Auth/Cyrsql.php @@ -199,7 +199,7 @@ class Horde_Auth_Cyrsql extends Horde_Auth_Sql if (!empty($this->_params['soft_expiration_field']) && !empty($row[$this->_params['soft_expiration_field']]) && ($now > $row[$this->_params['soft_expiration_field']])) { - $this->_credentials['params']['change'] = true; + $this->setCredential('change', true); } } diff --git a/framework/Auth/lib/Horde/Auth/Cyrus.php b/framework/Auth/lib/Horde/Auth/Cyrus.php index f82430571..4e002b439 100644 --- a/framework/Auth/lib/Horde/Auth/Cyrus.php +++ b/framework/Auth/lib/Horde/Auth/Cyrus.php @@ -230,7 +230,7 @@ class Horde_Auth_Cyrus extends Horde_Auth_Base * * @return boolean Whether or not the client is allowed. */ - protected function _transparent() + public function transparent() { return $this->_backend->transparent(); } diff --git a/framework/Auth/lib/Horde/Auth/Http.php b/framework/Auth/lib/Horde/Auth/Http.php index 128e03c9f..277bd6a3c 100644 --- a/framework/Auth/lib/Horde/Auth/Http.php +++ b/framework/Auth/lib/Horde/Auth/Http.php @@ -110,7 +110,7 @@ class Horde_Auth_Http extends Horde_Auth_Base * * @return boolean Whether or not the client is allowed. */ - protected function _transparent() + public function transparent() { if (empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW'])) { diff --git a/framework/Auth/lib/Horde/Auth/Ipbasic.php b/framework/Auth/lib/Horde/Auth/Ipbasic.php index 959e78b60..330d32961 100644 --- a/framework/Auth/lib/Horde/Auth/Ipbasic.php +++ b/framework/Auth/lib/Horde/Auth/Ipbasic.php @@ -52,7 +52,7 @@ class Horde_Auth_Ipbasic extends Horde_Auth_Base * * @return boolean Whether or not the client is allowed. */ - protected function _transparent() + public function transparent() { if (isset($_SERVER['REMOTE_ADDR'])) { foreach ($this->_params['blocks'] as $cidr) { diff --git a/framework/Auth/lib/Horde/Auth/Kolab.php b/framework/Auth/lib/Horde/Auth/Kolab.php index bdaae04a3..0d0abd5b0 100644 --- a/framework/Auth/lib/Horde/Auth/Kolab.php +++ b/framework/Auth/lib/Horde/Auth/Kolab.php @@ -40,7 +40,7 @@ class Horde_Auth_Kolab extends Horde_Auth_Base public function __construct(array $params = array()) { if (!isset($params['kolab'])) { - throw new InvalidArgumentException('Missing ' . $params . ' parameter.'); + throw new InvalidArgumentException('Missing kolab parameter.'); } parent::__construct($params); diff --git a/framework/Auth/lib/Horde/Auth/Ldap.php b/framework/Auth/lib/Horde/Auth/Ldap.php index 6fae77128..a2ceed4db 100644 --- a/framework/Auth/lib/Horde/Auth/Ldap.php +++ b/framework/Auth/lib/Horde/Auth/Ldap.php @@ -236,16 +236,13 @@ class Horde_Auth_Ldap extends Horde_Auth_Base $toexpire = $shadow['shadowlastchange'] + $shadow['shadowmax'] - $today; - if ($this->_params['notify_expire']) { - $warnday = $shadow['shadowlastchange'] + - $shadow['shadowmax'] - $shadow['shadowwarning']; - if ($today >= $warnday) { - call_user_func($this->_params['notify_expire'], $toexpire); - } + $warnday = $shadow['shadowlastchange'] + $shadow['shadowmax'] - $shadow['shadowwarning']; + if ($today >= $warnday) { + $this->setCredential('expire', $toexpire); } if ($toexpire == 0) { - $this->_credentials['params']['change'] = true; + $this->setCredential('change', true); } elseif ($toexpire < 0) { throw new Horde_Auth_Exception('', Horde_Auth::REASON_EXPIRED); } @@ -267,7 +264,6 @@ class Horde_Auth_Ldap extends Horde_Auth_Base throw new Horde_Auth_Exception(__CLASS__ . ': Adding users is not supported for Active Directory.'); } - list($userId, $credentials) = Horde_Auth::runHook($userId, $credentials, $this->_app, 'preauthenticate', 'admin'); if (isset($credentials['ldap'])) { $entry = $credentials['ldap']; $dn = $entry['dn']; @@ -308,19 +304,17 @@ class Horde_Auth_Ldap extends Horde_Auth_Base * Remove a set of authentication credentials. * * @param string $userId The userId to add. + * @param string $dn TODO * * @throws Horde_Auth_Exception */ - public function removeUser($userId) + public function removeUser($userId, $dn = null) { if ($this->_params['ad']) { throw new Horde_Auth_Exception(__CLASS__ . ': Removing users is not supported for Active Directory'); } - list($userId, $credentials) = Horde_Auth::runHook($userId, array(), $this->_app, 'preauthenticate', 'admin'); - if (isset($credentials['ldap'])) { - $dn = $credentials['ldap']['dn']; - } else { + if (is_null($dn)) { /* Search for the user's full DN. */ $dn = $this->_findDN($userId); } @@ -340,22 +334,19 @@ class Horde_Auth_Ldap extends Horde_Auth_Base * @param string $oldID The old userId. * @param string $newID The new userId. * @param array $credentials The new credentials + * @param string $olddn TODO + * @param string $newdn TODO * * @throws Horde_Auth_Exception */ - public function updateUser($oldID, $newID, $credentials) + public function updateUser($oldID, $newID, $credentials, $olddn = null, + $newdn = null) { if ($this->_params['ad']) { throw new Horde_Auth_Exception(__CLASS__ . ': Updating users is not supported for Active Directory.'); } - list($oldID, $old_credentials) = Horde_Auth::runHook($oldID, $credentials, $this->_app, 'preauthenticate', 'admin'); - if (isset($old_credentials['ldap'])) { - $olddn = $old_credentials['ldap']['dn']; - list($newID, $new_credentials) = Horde_Auth::runHook($newID, $credentials, $this->_app, 'preauthenticate', 'admin'); - $newdn = $new_credentials['ldap']['dn']; - unset($new_credentials['ldap']['dn']); - } else { + if (is_null($olddn)) { /* Search for the user's full DN. */ $dn = $this->_findDN($oldID); diff --git a/framework/Auth/lib/Horde/Auth/Msad.php b/framework/Auth/lib/Horde/Auth/Msad.php index 6ba696aa4..87a549270 100644 --- a/framework/Auth/lib/Horde/Auth/Msad.php +++ b/framework/Auth/lib/Horde/Auth/Msad.php @@ -69,7 +69,6 @@ class Horde_Auth_Msad extends Horde_Auth_Ldap /* Connect to the MSAD server. */ $this->_connect(); - list($accountName, $credentials) = Horde_Auth::runHook($accountName, $credentials, $this->_app, 'preauthenticate', 'admin'); if (isset($credentials['ldap'])) { $dn = $credentials['ldap']['dn']; } else { @@ -117,18 +116,16 @@ class Horde_Auth_Msad extends Horde_Auth_Ldap * Remove a set of authentication credentials. * * @param string $accountName The user sAMAccountName to remove. + * @param string $dn TODO * * @throws Horde_Auth_Exception */ - public function removeUser($accountName) + public function removeUser($accountName, $dn = null) { /* Connect to the MSAD server. */ $this->_connect(); - list($accountName, $credentials) = Horde_Auth::runHook($accountName, $credentials, $this->_app, 'preauthenticate', 'admin'); - if (isset($credentials['ldap'])) { - $dn = $credentials['ldap']['dn']; - } else { + if (is_null($dn)) { /* Search for the user's full DN. */ $dn = $this->_findDN($accountName); } @@ -156,7 +153,6 @@ class Horde_Auth_Msad extends Horde_Auth_Ldap /* Connect to the MSAD server. */ $this->_connect(); - list($oldId, $credentials) = Horde_Auth::runHook($oldId, $credentials, $this->_app, 'preauthenticate', 'admin'); if (isset($credentials['ldap'])) { $olddn = $credentials['ldap']['dn']; } else { diff --git a/framework/Auth/lib/Horde/Auth/Shibboleth.php b/framework/Auth/lib/Horde/Auth/Shibboleth.php index 7b610d9cd..15f1096c1 100644 --- a/framework/Auth/lib/Horde/Auth/Shibboleth.php +++ b/framework/Auth/lib/Horde/Auth/Shibboleth.php @@ -76,23 +76,12 @@ class Horde_Auth_Shibboleth extends Horde_Auth_Base } /** - * Check existing auth for triggers that might invalidate it. - * - * @return boolean Is existing auth valid? - */ - public function checkExistingAuth() - { - return !empty($_SERVER[$this->_params['username_header']]) && - ($this->_removeScope($_SERVER[$this->_params['username_header']]) == $this->_params['default_user']); - } - - /** * Automatic authentication: check if the username is set in the * configured header. * * @return boolean Whether or not the client is allowed. */ - protected function _transparent() + public function transparent() { if (empty($_SERVER[$this->_params['username_header']])) { return false; @@ -101,20 +90,21 @@ class Horde_Auth_Shibboleth extends Horde_Auth_Base $username = $_SERVER[$this->_params['username_header']]; // Remove scope from username, if present. - $this->_credentials['userId'] = $this->_removeScope($username); + $this->setCredential('userId', $this->_removeScope($username)); // Set password for hordeauth login. switch ($this->_params['password_holder']) { case 'header': - $this->_credentials['credentials'] = array( + $this->setCredential('credentials', array( 'password' => $_SERVER[$this->_params['password_header']] - ); + )); break; case 'preferences': - $this->_credentials['credentials'] = array( + $this->setCredential('credentials', array( 'password' => $_SERVER[$this->_params['password_preference']] - ); + )); + break; } return true; @@ -130,7 +120,6 @@ class Horde_Auth_Shibboleth extends Horde_Auth_Base protected function _removeScope($username) { $pos = strrpos($username, '@'); - return ($pos !== false) ? substr($username, 0, $pos) : $username; diff --git a/framework/Auth/lib/Horde/Auth/Sql.php b/framework/Auth/lib/Horde/Auth/Sql.php index 0dde20eba..a5ec6fd95 100644 --- a/framework/Auth/lib/Horde/Auth/Sql.php +++ b/framework/Auth/lib/Horde/Auth/Sql.php @@ -133,7 +133,8 @@ class Horde_Auth_Sql extends Horde_Auth_Base if (!empty($this->_params['soft_expiration_field']) && !empty($row[$this->_params['soft_expiration_field']]) && ($now > $row[$this->_params['soft_expiration_field']])) { - $this->_credentials['params']['change'] = true; + $this->setCredential('change', true); + $this->setCredential('expire', $date); } } @@ -201,12 +202,6 @@ class Horde_Auth_Sql extends Horde_Auth_Base $query .= ', ' . $this->_params['soft_expiration_field'] . ' = ?'; $values[] = $date; - if ($this->_params['notify_expire']) { - call_user_func($this->_params['notify_expire'], $date); - } - - $query .= ', ' . $this->_params['soft_expiration_field'] . ' = ?'; - if (empty($this->_params['hard_expiration_window'])) { $values[] = null; } else { diff --git a/framework/Auth/package.xml b/framework/Auth/package.xml index b407e7ea1..1a32b3c56 100644 --- a/framework/Auth/package.xml +++ b/framework/Auth/package.xml @@ -30,7 +30,9 @@ http://pear.php.net/dtd/package-2.0.xsd"> beta LGPL - * Removed Krb5 driver. + * Remove dependency on horde/Core. + * Remove all non-authentication backend related code. + * Removed Krb5 driver. * Moved signup code to horde/Core. * Split Horde_Auth:: into Horde_Auth:: and Horde_Auth_Base:: components. * Initial Horde 4 package. @@ -103,18 +105,10 @@ http://pear.php.net/dtd/package-2.0.xsd"> 1.5.4 - Core - pear.horde.org - - Exception pear.horde.org - Secret - pear.horde.org - - Util pear.horde.org @@ -125,14 +119,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> pear.horde.org - Form - pear.horde.org - - - History - pear.horde.org - - Imap_Client pear.horde.org @@ -150,9 +136,6 @@ http://pear.php.net/dtd/package-2.0.xsd"> pear.horde.org - gettext - - pam_auth diff --git a/framework/Auth/test/Horde/Auth/credentials.php b/framework/Auth/test/Horde/Auth/credentials.php index a04419020..da51702fe 100644 Binary files a/framework/Auth/test/Horde/Auth/credentials.php and b/framework/Auth/test/Horde/Auth/credentials.php differ diff --git a/framework/Auth/test/Horde/Auth/passwd.phpt b/framework/Auth/test/Horde/Auth/passwd.phpt index 92c882bbd..ddffc2792 100644 --- a/framework/Auth/test/Horde/Auth/passwd.phpt +++ b/framework/Auth/test/Horde/Auth/passwd.phpt @@ -4,7 +4,7 @@ Horde_Auth_Passwd:: test dirname(__FILE__) . '/test.passwd')); @@ -13,7 +13,7 @@ $auth = Horde_Auth::factory('passwd', array('filename' => dirname(__FILE__) . '/ var_dump($auth->listUsers()); // Authenticate -var_dump($auth->authenticate('user', array('password' => 'password'), false)); +var_dump($auth->authenticate('user', array('password' => 'password'))); ?> --EXPECT-- diff --git a/framework/Core/lib/Horde/Core/Auth/Application.php b/framework/Core/lib/Horde/Core/Auth/Application.php index 3b94a7d30..652ca288b 100644 --- a/framework/Core/lib/Horde/Core/Auth/Application.php +++ b/framework/Core/lib/Horde/Core/Auth/Application.php @@ -1,8 +1,7 @@ + * @author Michael Slusarz * @category Horde * @license http://opensource.org/licenses/lgpl-2.1.php LGPL * @package Core @@ -17,6 +17,31 @@ class Horde_Core_Auth_Application extends Horde_Auth_Base { /** + * Authentication failure reasons (additions to Horde_Auth:: reasons). + * + *
+     * REASON_BROWSER - A browser change was detected
+     * REASON_SESSIONIP - Logout due to change of IP address during session
+     * 
+ */ + const REASON_BROWSER = 100; + const REASON_SESSIONIP = 101; + + /** + * The base auth driver, used for horde authentication. + * + * @var Horde_Auth_Base + */ + protected $_base; + + /** + * Application for authentication. + * + * @var string + */ + protected $_app = 'horde'; + + /** * Cache for hasCapability(). * * @var array @@ -47,6 +72,8 @@ class Horde_Core_Auth_Application extends Horde_Auth_Base * @param array $params Required parameters: *
      * 'app' - (string) The application which is providing authentication.
+     * 'base' - (Horde_Auth_Base) The base Horde_Auth driver. Only needed if
+                'app' is 'horde'.
      * 
* * @throws InvalidArgumentException @@ -56,60 +83,53 @@ class Horde_Core_Auth_Application extends Horde_Auth_Base if (!isset($params['app'])) { throw new InvalidArgumentException('Missing app parameter.'); } - $this->_app = $params['app']; + unset($params['app']); - parent::__construct($params); - } - - /** - * Queries the current Auth object to find out if it supports the given - * capability. - * - * @param string $capability The capability to test for. - * - * @return boolean Whether or not the capability is supported. - */ - public function hasCapability($capability) - { - $capability = strtolower($capability); + if ($this->_app == 'horde') { + if (!isset($params['base'])) { + throw new InvalidArgumentException('Missing base parameter.'); + } - if (!in_array($capability, $this->_loaded) && - isset($this->_apiMethods[$capability])) { - $this->_capabilities[$capability] = $GLOBALS['registry']->hasAppMethod($this->_app, $this->_apiMethods[$capability]); - $this->_loaded[] = $capability; + $this->_base = $params['base']; + unset($params['base']); } - return parent::hasCapability($capability); + parent::__construct($params); } /** * Finds out if a set of login credentials are valid, and if requested, * mark the user as logged in in the current session. * - * @param string $userId The userId to check. + * @param string $userId The user ID to check. * @param array $credentials The credentials to check. - * @param boolean $login Whether to log the user in. If false, we'll - * only test the credentials and won't modify - * the current session. Defaults to true. * * @return boolean Whether or not the credentials are valid. */ - public function authenticate($userId, $credentials, $login = true) + public function authenticate($userId, $credentials) { - if (!parent::authenticate($userId, $credentials, $login)) { + try { + list($userId, $credentials) = $this->runHook(trim($userId), $credentials, 'preauthenticate', 'authenticate'); + } catch (Horde_Auth_Exception $e) { return false; } - $this->_authCallback(); + if ($this->_base) { + if (!$this->_base->authenticate($userId, $credentials)) { + return false; + } + } elseif (!parent::authenticate($userId, $credentials)) { + return false; + } - return true; + return $this->_setAuth(); } /** * Find out if a set of login credentials are valid. * - * @param string $userId The userId to check. + * @param string $userId The user ID to check. * @param array $credentials The credentials to use. This object will * always be available in the 'auth_ob' key. * @@ -127,42 +147,33 @@ class Horde_Core_Auth_Application extends Horde_Auth_Base } /** - * List all users in the system. + * Checks for triggers that may invalidate the current auth. + * These triggers are independent of the credentials. * - * @return array The array of userIds. - * @throws Horde_Auth_Exception + * @return boolean True if the results of authenticate() are still valid. */ - public function listUsers() + public function validateAuth() { - return $this->hasCapability('list') - ? $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['list']) - : parent::listUsers(); - } - - /** - * Checks if $userId exists in the system. - * - * @param string $userId User ID to check. - * - * @return boolean Whether or not $userId already exists. - */ - public function exists($userId) - { - return $this->hasCapability('exists') - ? $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['exists'], array('args' => array($userId))) - : parent::exists($userId); + return $this->_base + ? $this->_base->validateAuth() + : parent::validateAuth(); } /** * Add a set of authentication credentials. * - * @param string $userId The userId to add. + * @param string $userId The user ID to add. * @param array $credentials The credentials to use. * * @throws Horde_Auth_Exception */ public function addUser($userId, $credentials) { + if ($this->_base) { + $this->_base->addUser($userId, $credentials); + return; + } + if ($this->hasCapability('add')) { $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['add'], array('args' => array($userId, $credentials))); } else { @@ -173,14 +184,19 @@ class Horde_Core_Auth_Application extends Horde_Auth_Base /** * Update a set of authentication credentials. * - * @param string $oldID The old userId. - * @param string $newID The new userId. + * @param string $oldID The old user ID. + * @param string $newID The new user ID. * @param array $credentials The new credentials * * @throws Horde_Auth_Exception */ public function updateUser($oldID, $newID, $credentials) { + if ($this->_base) { + $this->_base->updateUser($oldID, $newID, $credentials); + return; + } + if ($this->hasCapability('update')) { $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['update'], array('args' => array($oldID, $newID, $credentials))); } else { @@ -189,72 +205,234 @@ class Horde_Core_Auth_Application extends Horde_Auth_Base } /** + * Delete a set of authentication credentials. + * + * @param string $userId The user ID to delete. + * + * @throws Horde_Auth_Exception + */ + public function removeUser($userId) + { + if ($this->_base) { + $this->_base->removeUser($userId); + + try { + $GLOBALS['registry']->callAppMethod('horde', 'removeUserDataFromAllApplications', array('args' => array($userId))); + } catch (Horde_Exception $e) { + throw new Horde_Auth_Exception($e); + } + } else { + if ($this->hasCapability('remove')) { + $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['remove'], array('args' => array($userId))); + } else { + parent::removeUser($userId); + } + + try { + $GLOBALS['registry']->callAppMethod($this->_app, 'removeUserData', array('args' => array($userId))); + } catch (Horde_Exception $e) { + throw new Horde_Auth_Exception($e); + } + } + } + + /** + * List all users in the system. + * + * @return array The array of user IDs. + * @throws Horde_Auth_Exception + */ + public function listUsers() + { + if ($this->_base) { + return $this->_base->listUsers(); + } + + return $this->hasCapability('list') + ? $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['list']) + : parent::listUsers(); + } + + /** + * Checks if a user ID exists in the system. + * + * @param string $userId User ID to check. + * + * @return boolean Whether or not the user ID already exists. + */ + public function exists($userId) + { + if ($this->_base) { + return $this->_base->exists($userId); + } + + return $this->hasCapability('exists') + ? $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['exists'], array('args' => array($userId))) + : parent::exists($userId); + } + + /** + * Automatic authentication. + * + * @return boolean Whether or not the client is allowed. + * @throws Horde_Auth_Exception + */ + public function transparent() + { + global $registry; + + if (!($userId = $this->getCredential('userId'))) { + $userId = $registry->getAuth(); + } + if (!($credentials = $this->getCredential('credentials'))) { + $credentials = $registry->getAuthCredential(); + } + + list($userId, $credentials) = $this->runHook($userId, $credentials, 'preauthenticate', 'transparent'); + + $this->setCredential('userId', $userId); + $this->setCredential('credentials', $credentials); + + if ($this->_base) { + $result = $this->_base->transparent(); + } else { + $result = $this->hasCapability('transparent') + ? $registry->callAppMethod($this->_app, $this->_apiMethods['transparent'], array('args' => array($this), 'noperms' => true)) + /* If this application contains neither transparent nor + * authenticate capabilities, it does not require any + * authentication if already authenticated to Horde. */ + : ($registry->getAuth() && !$this->hasCapability('authenticate')); + } + + return $result && $this->_setAuth(); + } + + /** * Reset a user's password. Used for example when the user does not * remember the existing password. * - * @param string $userId The userId for which to reset the password. + * @param string $userId The user ID for which to reset the password. * * @return string The new password on success. * @throws Horde_Auth_Exception */ public function resetPassword($userId) { + if ($this->_base) { + return $this->_base->resetPassword($userId); + } + return $this->hasCapability('resetpassword') ? $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['resetpassword'], array('args' => array($userId))) : parent::resetPassword(); } /** - * Delete a set of authentication credentials. + * Queries the current driver to find out if it supports the given + * capability. * - * @param string $userId The userId to delete. + * @param string $capability The capability to test for. * - * @throws Horde_Auth_Exception + * @return boolean Whether or not the capability is supported. */ - public function removeUser($userId) + public function hasCapability($capability) { - if ($this->hasCapability('remove')) { - $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['remove'], array('args' => array($userId))); - Horde_Auth::removeUserData($userId); - } else { - parent::removeUser($userId); + if ($this->_base) { + return $this->_base->hasCapability($capability); } + + $capability = strtolower($capability); + + if (!in_array($capability, $this->_loaded) && + isset($this->_apiMethods[$capability])) { + $this->_capabilities[$capability] = $GLOBALS['registry']->hasAppMethod($this->_app, $this->_apiMethods[$capability]); + $this->_loaded[] = $capability; + } + + return parent::hasCapability($capability); } /** - * Automatic authentication. + * Returns the named parameter for the current auth driver. * - * @return boolean Whether or not the client is allowed. - * @throws Horde_Auth_Exception + * @param string $param The parameter to fetch. + * + * @return string The parameter's value, or null if it doesn't exist. */ - public function transparent() + public function getParam($param) { - if (!parent::transparent()) { - return false; - } + return $this->_base + ? $this->_base->getParam($param) + : parent::getParam($param); + } - $this->_authCallback(); + /** + * Retrieve internal credential value(s). + * + * @param mixed $name The credential value to get. If null, will return + * the entire credential list. Valid names: + *
+     * 'change' - (boolean) Do credentials need to be changed?
+     * 'credentials' - (array) The credentials needed to authenticate.
+     * 'expire' - (integer) UNIX timestamp of the credential expiration date.
+     * 'userId' - (string) The user ID.
+     * 
+ * + * @return mixed Return the credential information, or null if the + * credential doesn't exist. + */ + public function getCredential($name = null) + { + return $this->_base + ? $this->_base->getCredential($name) + : parent::getCredential($name); + } - return true; + /** + * Set internal credential value. + * + * @param string $name The credential name to set. + * @param mixed $value The credential value to set. See getCredential() + * for the list of valid credentials/types. + */ + public function setCredential($type, $value) + { + if ($this->_base) { + $this->_base->setCredential($type, $value); + } else { + parent::setCredential($type, $value); + } } /** - * Attempt transparent authentication. The application method is passed a - * single parameter: the current class instance. + * Sets the error message for an invalid authentication. * - * @return boolean Whether transparent login is supported. + * @param string $type The type of error (Horde_Auth::REASON_* constant). + * @param string $msg The error message/reason for invalid + * authentication. */ - protected function _transparent() + public function setError($type, $msg = null) { - if (!$this->hasCapability('transparent')) { - /* If this application contains neither transparent nor - * authenticate capabilities, it does not require any - * authentication if already authenticated to Horde. */ - return ($GLOBALS['registry']->getAuth() && - !$this->hasCapability('authenticate')); + if ($this->_base) { + $this->_base->setError($type, $msg); + } else { + parent::setError($type, $msg); } + } - return $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['transparent'], array('args' => array($this), 'noperms' => true)); + /** + * Returns the error type or message for an invalid authentication. + * + * @param boolean $msg If true, returns the message string (if set). + * + * @return mixed Error type, error message (if $msg is true) or false + * if entry doesn't exist. + */ + public function getError($msg = false) + { + return $this->_base + ? $this->_base->getError($msg) + : parent::getError($msg); } /** @@ -288,81 +466,127 @@ class Horde_Core_Auth_Application extends Horde_Auth_Base */ public function getLoginParams() { - if (!$this->hasCapability('loginparams')) { - return parent::getLoginParams(); + if ($this->hasCapability('loginparams')) { + return $this->_base + ? $this->_base->getLoginParams() + : $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['loginparams'], array('noperms' => true)); } - return $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['loginparams'], array('noperms' => true)); + return array( + 'js_code' => array(), + 'js_files' => array(), + 'params' => array() + ); } /** - * Provide method to get internal credential values. Necessary as the - * application API does not have direct access to the protected member - * variables of this class. - * - * @param mixed $name The credential name to get. If null, will return - * the entire credential list. + * Indicate whether the application requires authentication. * - * @return mixed Return the credential information, or null if the. - * credential doesn't exist. + * @return boolean True if application requires authentication. */ - public function getCredential($name = null) + public function requireAuth() { - if (is_null($name)) { - return $this->_credentials; - } - - return isset($this->_credentials[$name]) - ? $this->_credentials[$name] - : null; + return !$this->_base && + ($this->hasCapability('authenticate') || + $this->hasCapability('transparent')); } /** - * Provide method to set internal credential values. Necessary as the - * application API does not have direct access to the protected member - * variables of this class. + * Runs the pre/post-authenticate hook and parses the result. * - * @param string $name The credential name to set. - * @param mixed $value The credential value to set. If $name is 'userId', - * this must be a text value. If $name is - * 'credentials' or 'params', this is an array of - * values to be merged in. + * @param string $userId The userId who has been authorized. + * @param array $credentials The credentials of the user. + * @param string $type Either 'preauthenticate' or + * 'postauthenticate'. + * @param string $method The triggering method (preauthenticate only). + * Either 'authenticate' or 'transparent'. + * + * @return array Two element array, $userId and $credentials. + * @throws Horde_Auth_Exception */ - public function setCredential($type, $value) + public function runHook($userId, $credentials, $type, $method = null) { - switch ($type) { - case 'userId': - $this->_credentials['userId'] = $value; - break; - - case 'credentials': - case 'params': - $this->_credentials[$type] = array_merge($this->_credentials[$type], $value); - break; + if (!is_array($credentials)) { + $credentials = empty($credentials) + ? array() + : array($credentials); + } + + $ret_array = array($userId, $credentials); + + if ($type == 'preauthenticate') { + $credentials['authMethod'] = $method; + } + + try { + $result = Horde::callHook($type, array($userId, $credentials), $this->_app); + } catch (Horde_Exception $e) { + throw new Horde_Auth_Exception($e); + } catch (Horde_Exception_HookNotSet $e) { + return $ret_array; + } + + unset($credentials['authMethod']); + + if ($result === false) { + if ($this->getError() != Horde_Auth::REASON_MESSAGE) { + $this->setError(Horde_Auth::REASON_FAILED); + } + throw new Horde_Auth_Exception($type . ' hook failed'); + } + + if (is_array($result)) { + if ($type == 'postauthenticate') { + $ret_array[1] = $result; + } else { + if (isset($result['userId'])) { + $ret_array[0] = $result['userId']; + } + + if (isset($result['credentials'])) { + $ret_array[1] = $result['credentials']; + } + } } } /** - * Provide way to finish authentication tasks in an application and ensure - * that the full application environment is loaded. + * Set authentication credentials in the Horde session. * - * @throws Horde_Auth_Exception + * @return boolean True on success, false on failure. */ - protected function _authCallback() + protected function _setAuth() { + if ($GLOBALS['registry']->isAuthenticated(array('app' => $this->_app, 'notransparent' => true))) { + return true; + } + + $userId = $this->getCredential('userId'); + $credentials = $this->getCredential('credentials'); + + try { + list(,$credentials) = $this->runHook($userId, $credentials, 'postauthenticate'); + } catch (Horde_Auth_Exception $e) { + return false; + } + + $GLOBALS['registry']->setAuth($userId, $credentials, array( + 'app' => $this->_app, + 'change' => $this->getCredential('change') + )); + + if ($this->_base && + isset($GLOBALS['notification']) && + ($expire = $this->_base->getCredential('expire'))) { + $toexpire = ($expire - time()) / 86400; + $GLOBALS['notification']->push(sprintf(ngettext("%d day until your password expires.", "%d days until your password expires.", $toexpire), $toexpire), 'horde.warning'); + } + if ($this->hasCapability('authenticatecallback')) { $GLOBALS['registry']->callAppMethod($this->_app, $this->_apiMethods['authenticatecallback'], array('noperms' => true)); } - } - /** - * Indicate whether the application requires authentication. - * - * @return boolean True if application requires authentication. - */ - public function requireAuth() - { - return $this->hasCapability('authenticate') || $this->hasCapability('transparent'); + return true; } } diff --git a/framework/Core/lib/Horde/Core/Auth/Ldap.php b/framework/Core/lib/Horde/Core/Auth/Ldap.php new file mode 100644 index 000000000..a2fef38a8 --- /dev/null +++ b/framework/Core/lib/Horde/Core/Auth/Ldap.php @@ -0,0 +1,72 @@ + + * @category Horde + * @license http://opensource.org/licenses/lgpl-2.1.php LGPL + * @package Core + */ +class Horde_Core_Auth_Ldap extends Horde_Auth_Ldap +{ + /** + * Add a set of authentication credentials. + * + * @param string $userId The user ID to add. + * @param array $credentials The credentials to use. + * + * @throws Horde_Auth_Exception + */ + public function addUser($userId, $credentials) + { + list($userId, $credentials) = $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth()->runHook($userId, $credentials, 'preauthenticate', 'admin'); + + parent::addUser($userId, $credentials); + } + + /** + * Update a set of authentication credentials. + * + * @param string $oldID The old user ID. + * @param string $newID The new user ID. + * @param array $credentials The new credentials + * + * @throws Horde_Auth_Exception + */ + public function updateUser($oldID, $newID, $credentials) + { + $auth = $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth(); + + list($oldID, $old_credentials) = $auth->runHook($oldID, $credentials, 'preauthenticate', 'admin'); + if (isset($old_credentials['ldap'])) { + list($newID, $new_credentials) = $auth->runHook($newID, $credentials, 'preauthenticate', 'admin'); + $olddn = $old_credentials['ldap']['dn']; + $newdn = $new_credentials['ldap']['dn']; + } else { + $olddn = $newdn = null; + } + + parent::updateUser($oldID, $newID, $credentials, $olddn, $newdn); + } + + /** + * Delete a set of authentication credentials. + * + * @param string $userId The user ID to delete. + * + * @throws Horde_Auth_Exception + */ + public function removeUser($userId) + { + list($userId, $credentials) = $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth()->runHook($userId, array(), 'preauthenticate', 'admin'); + + parent::removeUser($userId, isset($credentials['ldap']) ? $credentials['ldap']['dn'] : null); + } + +} diff --git a/framework/Core/lib/Horde/Core/Auth/Msad.php b/framework/Core/lib/Horde/Core/Auth/Msad.php new file mode 100644 index 000000000..34adb5153 --- /dev/null +++ b/framework/Core/lib/Horde/Core/Auth/Msad.php @@ -0,0 +1,63 @@ + + * @category Horde + * @license http://opensource.org/licenses/lgpl-2.1.php LGPL + * @package Core + */ +class Horde_Core_Auth_Msad extends Horde_Auth_Msad +{ + /** + * Add a set of authentication credentials. + * + * @param string $userId The user ID to add. + * @param array $credentials The credentials to use. + * + * @throws Horde_Auth_Exception + */ + public function addUser($userId, $credentials) + { + list($userId, $credentials) = $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth()->runHook($userId, $credentials, 'preauthenticate', 'admin'); + + parent::addUser($userId, $credentials); + } + + /** + * Update a set of authentication credentials. + * + * @param string $oldID The old user ID. + * @param string $newID The new user ID. + * @param array $credentials The new credentials + * + * @throws Horde_Auth_Exception + */ + public function updateUser($oldID, $newID, $credentials) + { + list($oldId, $credentials) = $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth()->runHook($oldId, $credentials, 'preauthenticate', 'admin'); + + parent::updateUser($oldID, $newID, $credentials); + } + + /** + * Delete a set of authentication credentials. + * + * @param string $userId The user ID to delete. + * + * @throws Horde_Auth_Exception + */ + public function removeUser($userId) + { + list($userId, $credentials) = $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth()->runHook($userId, array(), 'preauthenticate', 'admin'); + + parent::removeUser($userId, isset($credentials['ldap']) ? $credentials['ldap']['dn'] : null); + } + +} diff --git a/framework/Core/lib/Horde/Core/Auth/Shibboleth.php b/framework/Core/lib/Horde/Core/Auth/Shibboleth.php new file mode 100644 index 000000000..35bf9eb71 --- /dev/null +++ b/framework/Core/lib/Horde/Core/Auth/Shibboleth.php @@ -0,0 +1,30 @@ + + * @category Horde + * @license http://opensource.org/licenses/lgpl-2.1.php LGPL + * @package Core + */ +class Horde_Core_Auth_Shibboleth extends Horde_Auth_Shibboleth +{ + /** + * Checks for triggers that may invalidate the current auth. + * These triggers are independent of the credentials. + * + * @return boolean True if the results of authenticate() are still valid. + */ + public function validateAuth() + { + return !empty($_SERVER[$this->getParam('username_header')]) && + ($this->_removeScope($_SERVER[$this->getParam('username_header')]) == $GLOBALS['registry']->getAuth()); + } + +} diff --git a/framework/Core/lib/Horde/Core/Autoloader/Callback/Auth.php b/framework/Core/lib/Horde/Core/Autoloader/Callback/Auth.php deleted file mode 100644 index 8897b1e1f..000000000 --- a/framework/Core/lib/Horde/Core/Autoloader/Callback/Auth.php +++ /dev/null @@ -1,16 +0,0 @@ -getInstance('Net_DNS_Resolver'); - } - -} diff --git a/framework/Core/lib/Horde/Core/Factory/Auth.php b/framework/Core/lib/Horde/Core/Factory/Auth.php index ac859c367..384410020 100644 --- a/framework/Core/lib/Horde/Core/Factory/Auth.php +++ b/framework/Core/lib/Horde/Core/Factory/Auth.php @@ -54,36 +54,47 @@ class Horde_Core_Factory_Auth /** * Return the Horde_Auth:: instance. * - * @param string $driver The driver. - * @param array $params Additional parameters to pass to the driver - * (will override Horde defaults). + * @param string $app The application to authenticate to. * * @return Horde_Auth_Base The singleton instance. * @throws Horde_Auth_Exception */ - public function getAuth($driver = null, array $params = array()) + public function getAuth($app = null) { - if (is_null($driver)) { - $driver = $GLOBALS['conf']['auth']['driver']; + if (is_null($app)) { + $app = 'horde'; } - $params = array_merge(Horde::getDriverConfig('auth', $driver), $params); - ksort($params); - - /* Get proper driver name now that we have grabbed the - * configuration. */ - if (strcasecmp($driver, 'httpremote') === 0) { - /* BC */ - $driver = 'Http_Remote'; - } elseif (strcasecmp($driver, 'application') === 0) { - $driver = 'Horde_Core_Auth_Application'; - } else { - $driver = Horde_String::ucfirst(Horde_String::lower(basename($driver))); + if (isset($this->_instances[$app])) { + return $this->_instances[$app]; } - $sig = hash('md5', serialize(array($driver, $params))); + $base_params = array( + 'app' => $app, + 'logger' => $this->_injector->getInstance('Horde_Log_Logger') + ); + + if ($app == 'horde') { + $driver = $GLOBALS['conf']['auth']['driver']; + $params = Horde::getDriverConfig('auth', $driver); + + /* Get proper driver name now that we have grabbed the + * configuration. */ + if (strcasecmp($driver, 'application') === 0) { + $driver = 'Horde_Core_Auth_Application'; + } elseif (strcasecmp($driver, 'httpremote') === 0) { + /* BC */ + $driver = 'Http_Remote'; + } elseif (strcasecmp($driver, 'ldap') === 0) { + $driver = 'Horde_Core_Auth_Ldap'; + } elseif (strcasecmp($driver, 'msad') === 0) { + $driver = 'Horde_Core_Auth_Msad'; + } elseif (strcasecmp($driver, 'shibboleth') === 0) { + $driver = 'Horde_Core_Auth_Shibboleth'; + } else { + $driver = Horde_String::ucfirst(Horde_String::lower(basename($driver))); + } - if (!isset($this->_instances[$sig])) { $lc_driver = Horde_String::lower($driver); switch ($lc_driver) { case 'composite': @@ -126,7 +137,8 @@ class Horde_Core_Factory_Auth $params['kolab'] = $this->_injector->getInstance('Horde_Kolab_Session'); break; - case 'ldap': + case 'horde_core_auth_ldap': + case 'horde_core_auth_msad': $params['ldap'] = $this->_injector->getInstance('Horde_Ldap')->getLdap('horde', 'auth'); break; @@ -137,25 +149,16 @@ class Horde_Core_Factory_Auth $params['default_user'] = $GLOBALS['registry']->getAuth(); $params['logger'] = $this->_injector->getInstance('Horde_Log_Logger'); - $params['notify_expire'] = array($this, 'notifyExpire'); - $this->_instances[$sig] = Horde_Auth::factory($driver, $params); + $base_params['base'] = Horde_Auth::factory($driver, $params); + if ($driver == 'Horde_Core_Auth_Application') { + $this->_instances[$params['app']] = $base_params['base']; + } } - return $this->_instances[$sig]; - } + $this->_instances[$app] = Horde_Auth::factory('Horde_Core_Auth_Application', $base_params); - /** - * Expire notification callback. - * - * @param integer $date UNIX timestamp of password expiration. - */ - public function notifyExpire($date) - { - if (isset($GLOBALS['notification'])) { - $toexpire = ($date - time()) / 86400; - $GLOBALS['notification']->push(sprintf(ngettext("%d day until your password expires.", "%d days until your password expires.", $toexpire), $toexpire), 'horde.warning'); - } + return $this->_instances[$app]; } } diff --git a/framework/Core/lib/Horde/Registry.php b/framework/Core/lib/Horde/Registry.php index 9c7974e85..c321a233b 100644 --- a/framework/Core/lib/Horde/Registry.php +++ b/framework/Core/lib/Horde/Registry.php @@ -224,7 +224,6 @@ class Horde_Registry { /* Define autoloader callbacks. */ $callbacks = array( - 'Horde_Auth' => 'Horde_Core_Autoloader_Callback_Auth', 'Horde_Mime' => 'Horde_Core_Autoloader_Callback_Mime' ); @@ -1085,9 +1084,10 @@ class Horde_Registry * - To all authenticated users if no permission is set on $app. * - To anyone who is allowed by an explicit ACL on $app. */ if ($checkPerms) { - if ($this->getAuth() && !Horde_Auth::checkExistingAuth()) { + if ($this->getAuth() && !$this->checkExistingAuth($app)) { throw new Horde_Exception('User is not authorized', self::AUTH_FAILURE); } + if (!$this->hasPermission($app, Horde_Perms::READ)) { if (!$this->isAuthenticated(array('app' => $app))) { throw new Horde_Exception('User is not authorized', self::AUTH_FAILURE); @@ -1218,7 +1218,7 @@ class Horde_Registry * application auth != Horde admin auth. And there can *never* be * non-SHOW access to an application that requires authentication. */ if (!$this->isAuthenticated(array('app' => $app)) && - $this->requireAuth($app) && + $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth($app)->requireAuth() && ($perms != Horde_Perms::SHOW)) { return false; } @@ -1590,19 +1590,6 @@ class Horde_Registry } /** - * Returns the name of the authentication provider. - * - * @return string The name of the driver currently providing - * authentication, or false if not set. - */ - public function getProvider() - { - return empty($_SESSION['horde_auth']['driver']) - ? false - : $_SESSION['horde_auth']['driver']; - } - - /** * Clears any authentication tokens in the current session. * * @param boolean $destroy Destroy the session? @@ -1652,23 +1639,6 @@ class Horde_Registry } /** - * Checks if an application requires additional authentication above and - * beyond Horde authentication. - * - * @params string $app The application to check. - * - * @return boolean Whether or not the application required additional - * authentication. - * @throws Horde_Exception - */ - public function requireAuth($app) - { - return ($app == 'horde') - ? false - : $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth('application', array('app' => $app))->requireAuth(); - } - - /** * Checks if there is a session with valid auth information. If there * isn't, but the configured Auth driver supports transparent * authentication, then we try that. @@ -1677,34 +1647,33 @@ class Horde_Registry *
      * 'app' - (string) Check authentication for this app.
      *         DEFAULT: Checks horde-wide authentication.
+     * 'notransparent' - (boolean) Do not attempt transparent authentication.
+     *                   DEFAULT: false
      * 
* * @return boolean Whether or not the user is authenticated. */ - public function isAuthenticated($options = array()) + public function isAuthenticated(array $options = array()) { + $app = empty($options['app']) + ? 'horde' + : $options['app']; + /* Check for cached authentication results. */ - if ($GLOBALS['registry']->getAuth()) { - $driver = (empty($options['app']) || ($options['app'] == 'horde')) - ? $GLOBALS['conf']['auth']['driver'] - : $options['app']; - - if (($_SESSION['horde_auth']['driver'] == $driver) || - isset($_SESSION['horde_auth']['app'][$driver])) { - if (Horde_Auth::checkExistingAuth()) { - return true; - } - $this->clearAuth(); - return false; + if ($this->getAuth() && + (($app == 'horde') || + isset($_SESSION['horde_auth']['app'][$app]))) { + if ($this->checkExistingAuth($app)) { + return true; } + + return false; } /* Try transparent authentication. */ - $auth = (empty($options['app']) || ($options['app'] == 'horde')) - ? $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth() - : $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth('application', array('app' => $options['app'])); - - return $auth->transparent(); + return empty($options['notransparent']) + ? $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth($app)->transparent() + : false; } /** @@ -1770,7 +1739,9 @@ class Horde_Registry public function getLogoutUrl(array $options = array()) { if (!isset($options['reason'])) { - $options['reason'] = Horde_Auth::getAuthError(); + // TODO: This only returns the error for Horde-wide + // authentication, not for application auth. + $options['reason'] = $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth()->getError(); } if (empty($options['app']) || @@ -1791,10 +1762,10 @@ class Horde_Registry } if ($options['reason']) { - $params[Horde_Auth::REASON_PARAM] = $options['reason']; + $params['logout_reason'] = $options['reason']; if ($options['reason'] == Horde_Auth::REASON_MESSAGE) { - $params[Horde_Auth::REASON_MSG_PARAM] = empty($options['msg']) - ? Horde_Auth::getAuthError(true) + $params['logout_msg'] = empty($options['msg']) + ? $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth()->getError(true) : $options['msg']; } } @@ -1904,28 +1875,35 @@ class Horde_Registry /** * Sets the requested credential for the currently logged in user. * - * @param string $credential The credential to set. - * @param string $value The value to set the credential to. - * @param string $app The app to update. Defaults to Horde. + * @param mixed $credential The credential to set. If an array, + * overwrites the current credentials array. + * @param string $value The value to set the credential to. If + * $credential is an array, this value is + * ignored. + * @param string $app The app to update. Defaults to Horde. */ - public function setAuthCredential($credential, $value, $app = null) + public function setAuthCredential($credential, $value = null, $app = null) { if (!$this->getAuth()) { return; } - $credentials = $this->_getAuthCredentials($app); + if (is_array($credential)) { + $credentials = $credential; + } else { + if (($credentials = $this->_getAuthCredentials($app)) === false) { + return; + } - if ($credentials !== false) { - if (is_array($credentials)) { - $credentials[$credential] = $value; - } else { - $credentials = array($credential => $value); + if (!is_array($credentials)) { + $credentials = array(); } - $secret = $GLOBALS['injector']->getInstance('Horde_Secret'); - $_SESSION['horde_auth']['app'][$app] = $secret->write($secret->getKey('auth'), serialize($credentials)); + $credentials[$credential] = $value; } + + $secret = $GLOBALS['injector']->getInstance('Horde_Secret'); + $_SESSION['horde_auth']['app'][$app] = $secret->write($secret->getKey('auth'), serialize($credentials)); } /** @@ -1937,16 +1915,113 @@ class Horde_Registry */ protected function _getAuthCredentials($app) { + if (!isset($_SESSION['horde_auth']['app'])) { + return false; + } + if (is_null($app)) { $app = $_SESSION['horde_auth']['credentials']; } - if (!isset($_SESSION['horde_auth']['app'])) { + $secret = $GLOBALS['injector']->getInstance('Horde_Secret'); + return @unserialize($secret->read($secret->getKey('auth'), $_SESSION['horde_auth']['app'][$app])); + } + + /** + * Sets a variable in the session saying that authorization has succeeded, + * note which userId was authorized, and note when the login took place. + * + * If a user name hook was defined in the configuration, it gets applied + * to the $userId at this point. + * + * Horde authentication data is stored in the session in the 'horde_auth' + * array key. That key has the following structure: + *
+     * 'app' - (array) Application-specific authentication. Keys are the
+     *         app names, values are an array containing credentials. If true,
+     *         application does not require any specific credentials.
+     * 'authId' - (string) The username used during the original
+                  authentication.
+     * 'browser' - (string) The remote browser string.
+     * 'change' - (boolean) Is a password change requested?
+     * 'credentials' - (string) The 'app' entry that contains the Horde
+     *                 credentials.
+     * 'remoteAddr' - (string) The remote IP address of the user.
+     * 'timestamp' - (integer) The login time.
+     * 'userId' - (string) The unique Horde username.
+     * 
+ * + * @param string $authId The userId that has been authorized. + * @param array $credentials The credentials of the user. + * @param array $options Additional options: + *
+     * 'app' - (string) The app to set authentication credentials for.
+     *         DEFAULT: 'horde'
+     * 'change' - (boolean) Whether to request that the user change their
+     *            password.
+     *            DEFAULT: No
+     * 
+ */ + public function setAuth($authId, $credentials, array $options = array()) + { + $app = empty($options['app']) ? 'horde' : $options['app']; + + if ($this->getAuth()) { + /* Store app credentials. */ + $this->setAuthCredential($credentials, null, $app); + return; + } + + /* Clear any existing info. */ + $this->clearAuth(false); + + $_SESSION['horde_auth'] = array( + 'app' => array(), + 'authId' => $authId, + 'browser' => $GLOBALS['injector']->getInstance('Horde_Browser')->getAgentString(), + 'change' => !empty($options['change']), + 'credentials' => $app, + 'remoteAddr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null, + 'timestamp' => time(), + 'userId' => $this->convertUsername(trim($authId), true) + ); + + $this->setAuthCredential($credentials, null, $app); + + /* Reload preferences for the new user. */ + $this->loadPrefs(); + Horde_Nls::setLanguageEnvironment($GLOBALS['prefs']->getValue('language'), $app); + } + + /** + * Check existing auth for triggers that might invalidate it. + * + * @param string $app Check authentication for this app. + * + * @return boolean Is existing auth valid? + */ + public function checkExistingAuth($app) + { + if ($app != 'horde') { + return true; + } + + $auth = $GLOBALS['injector']->getInstance('Horde_Auth')->getAuth(); + + if (!empty($GLOBALS['conf']['auth']['checkip']) && + !empty($_SESSION['horde_auth']['remoteAddr']) && + ($_SESSION['horde_auth']['remoteAddr'] != $_SERVER['REMOTE_ADDR'])) { + $auth->setError(Horde_Core_Auth_Application::REASON_SESSIONIP); return false; } - $secret = $GLOBALS['injector']->getInstance('Horde_Secret'); - return @unserialize($secret->read($secret->getKey('auth'), $_SESSION['horde_auth']['app'][$app])); + if (!empty($GLOBALS['conf']['auth']['checkbrowser']) && + ($_SESSION['horde_auth']['browser'] != $GLOBALS['injector']->getInstance('Horde_Browser')->getAgentString())) { + $auth->setError(Horde_Core_Auth_Application::REASON_BROWSER); + return false; + } + + return $auth->validateAuth(); } } diff --git a/framework/Core/package.xml b/framework/Core/package.xml index 714b2996a..e171c25b0 100644 --- a/framework/Core/package.xml +++ b/framework/Core/package.xml @@ -64,6 +64,9 @@ Application Framework. + + + @@ -76,7 +79,6 @@ Application Framework. - @@ -267,6 +269,9 @@ Application Framework. + + + @@ -274,7 +279,6 @@ Application Framework. - diff --git a/gollem/lib/Auth.php b/gollem/lib/Auth.php index 3934e626e..d918169b7 100644 --- a/gollem/lib/Auth.php +++ b/gollem/lib/Auth.php @@ -63,7 +63,7 @@ class Gollem_Auth $credentials = array('password' => $secret->read($secret->getKey('gollem'), $GLOBALS['gollem_be']['params']['password'])); } - $login = ($login && ($GLOBALS['registry']->getProvider() == 'gollem')); + //$login = ($login && ($GLOBALS['registry']->getProvider() == 'gollem')); return parent::authenticate($userID, $credentials, $login); } diff --git a/horde/lib/LoginTasks/Task/LastLogin.php b/horde/lib/LoginTasks/Task/LastLogin.php new file mode 100644 index 000000000..64e4d212f --- /dev/null +++ b/horde/lib/LoginTasks/Task/LastLogin.php @@ -0,0 +1,77 @@ + + * @category Horde + * @license http://www.fsf.org/copyleft/lgpl.html LGPL + * @package Horde + */ +class Horde_LoginTasks_Task_LastLogin extends Horde_LoginTasks_Task +{ + /** + * The interval at which to run the task. + * + * @var integer + */ + public $interval = Horde_LoginTasks::EVERY; + + /** + * Display type. + * + * @var integer + */ + public $display = Horde_LoginTasks::DISPLAY_NONE; + + /** + * Perform all functions for this task. + */ + public function execute() + { + /* Fetch the user's last login time. */ + $old_login = @unserialize($GLOBALS['prefs']->getValue('last_login')); + + /* Display it, if we have a notification object and the + * show_last_login preference is active. */ + if (isset($GLOBALS['notification']) && + $GLOBALS['prefs']->getValue('show_last_login')) { + if (empty($old_login['time'])) { + $GLOBALS['notification']->push(_("Last login: Never"), 'horde.message'); + } elseif (empty($old_login['host'])) { + $GLOBALS['notification']->push(sprintf(_("Last login: %s"), strftime('%c', $old_login['time'])), 'horde.message'); + } else { + $GLOBALS['notification']->push(sprintf(_("Last login: %s from %s"), strftime('%c', $old_login['time']), $old_login['host']), 'horde.message'); + } + } + + /* Set the user's last_login information. */ + $host = empty($_SERVER['HTTP_X_FORWARDED_FOR']) + ? $_SERVER['REMOTE_ADDR'] + : $_SERVER['HTTP_X_FORWARDED_FOR']; + + if ($dns = $GLOBALS['injector']->getInstance('Net_DNS_Resolver')) { + $ptrdname = $host; + if ($response = $dns->query($host, 'PTR')) { + foreach ($response->answer as $val) { + if (isset($val->ptrdname)) { + $ptrdname = $val->ptrdname; + break; + } + } + } + } else { + $ptrdname = @gethostbyaddr($host); + } + + $GLOBALS['prefs']->setValue('last_login', serialize(array( + 'host' => $ptrdname, + 'time' => time() + ))); + } + +} diff --git a/horde/login.php b/horde/login.php index 49c102a71..d355d5d57 100644 --- a/horde/login.php +++ b/horde/login.php @@ -44,10 +44,10 @@ function _getLogoutReasonString($code) case Horde_Auth::REASON_SESSION: return _("Your session has expired. Please login again."); - case Horde_Auth::REASON_SESSIONIP: + case Horde_Core_Auth_Application::REASON_SESSIONIP: return _("Your Internet Address has changed since the beginning of your session. To protect your security, you must login again."); - case Horde_Auth::REASON_BROWSER: + case Horde_Core_Auth_Application::REASON_BROWSER: return _("Your browser appears to have changed since the beginning of your session. To protect your security, you must login again."); case Horde_Auth::REASON_LOGOUT: @@ -97,9 +97,7 @@ if (!$is_auth) { } /* Get an Auth object. */ -$auth = ($app && $is_auth) - ? $injector->getInstance('Horde_Auth')->getAuth('application', array('app' => $app)) - : $injector->getInstance('Horde_Auth')->getAuth(); +$auth = $injector->getInstance('Horde_Auth')->getAuth(($is_auth && $vars->app) ? $vars->app : null); /* Build the list of necessary login parameters. */ $loginparams = array( diff --git a/passwd/lib/Passwd.php b/passwd/lib/Passwd.php index 456a2d2fc..3b5a64a9f 100644 --- a/passwd/lib/Passwd.php +++ b/passwd/lib/Passwd.php @@ -60,10 +60,6 @@ class Passwd { { if ($GLOBALS['registry']->getAuthCredential('password') == $old_password) { $GLOBALS['registry']->setAuthCredential('password', $new_password); - if ($GLOBALS['registry']->getProvider() == 'imp') { - $_SESSION['imp']['pass'] = Secret::write(Secret::getKey('imp'), - $new_password); - } } }