Horde_Auth rewrite.
authorMichael M Slusarz <slusarz@curecanti.org>
Wed, 15 Jul 2009 05:09:31 +0000 (23:09 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 21 Jul 2009 22:25:59 +0000 (16:25 -0600)
Store data in static class variable instead of globally
Add getLoginParams()
Move logout reason string and changepassword code to login page
Rework session storage of auth information.
All application-specific auth tasks should now be handled properly by
both Horde_Auth and Horde_Auth_Application
Horde_Registry::pushApp() should now do all necessary authentication and
permissions checking in a single call.

12 files changed:
framework/Auth/lib/Horde/Auth.php
framework/Auth/lib/Horde/Auth/Application.php
framework/Auth/lib/Horde/Auth/Base.php
framework/Auth/lib/Horde/Auth/Composite.php
framework/Auth/lib/Horde/Auth/Cyrsql.php
framework/Auth/lib/Horde/Auth/Imap.php
framework/Auth/lib/Horde/Auth/Kolab.php
framework/Auth/lib/Horde/Auth/Ldap.php
framework/Auth/lib/Horde/Auth/Msad.php
framework/Auth/lib/Horde/Auth/Sql.php
framework/Core/lib/Horde.php
framework/Core/lib/Horde/Registry.php

index 924e4ea..71468f1 100644 (file)
@@ -3,13 +3,29 @@
  * The Horde_Auth:: class provides a common abstracted interface into the
  * various backends for the Horde authentication system.
  *
+ * Horde authentication data is stored in the session in the 'horde_auth'
+ * array key.  That key has the following structure:
+ * <pre>
+ * 'app' - (array) Application-specific authentication. Keys are the
+ *         app names, values are an array of credentials.
+ * 'browser' - (string) The remote browser string.
+ * 'change' - (boolean) Is a password change requested?
+ * 'credentials' - (array) The credentials needed for this driver.
+ * 'driver' - (string) The driver used for base horde auth.
+ * 'remoteAddr' - (string) The remote IP address of the user.
+ * 'timestamp' - (integer) The login time.
+ * 'userId' - (username) The horde username.
+ * </pre>
+ *
  * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
  *
  * See the enclosed file COPYING for license information (LGPL). If you did
  * not receive this file, see http://opensource.org/licenses/lgpl-2.1.php
  *
- * @author  Chuck Hagenbuch <chuck@horde.org>
- * @package Horde_Auth
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @package  Horde_Auth
  */
 class Horde_Auth
 {
@@ -68,6 +84,13 @@ class Horde_Auth
     static protected $_instances = array();
 
     /**
+     * The logout reason information.
+     *
+     * @var array
+     */
+    static protected $_reason = array();
+
+    /**
      * Attempts to return a concrete Horde_Auth_Base instance based on
      * $driver.
      *
@@ -312,10 +335,11 @@ class Horde_Auth
     static public function removeUserData($userId)
     {
         $errApps = array();
+        $registry = Horde_Registry::singleton();
 
-        foreach ($GLOBALS['registry']->listApps(array('notoolbar', 'hidden', 'active', 'admin')) as $app) {
+        foreach ($registry->listApps(array('notoolbar', 'hidden', 'active', 'admin')) as $app) {
             try {
-                $GLOBALS['registry']->callByPackage($app, 'removeUserData', array($userId));
+                $registry->callByPackage($app, 'removeUserData', array($userId));
             } catch (Horde_Auth_Exception $e) {
                 Horde::logMessage($e, __FILE__, __LINE__, PEAR_LOG_ERR);
                 $errApps[] = $app;
@@ -332,33 +356,66 @@ class Horde_Auth
      * specified user. If there isn't, but the configured Auth driver supports
      * transparent authentication, then we try that.
      *
-     * @param string $realm  The authentication realm to check.
+     * @params array $options  Additional options:
+     * <pre>
+     * 'app' - (string) Check authentication for this app.
+     *         DEFAULT: Checks horde-wide authentication.
+     * </pre>
      *
      * @return boolean  Whether or not the user is authenticated.
      */
-    static public function isAuthenticated($realm = null)
+    static public function isAuthenticated($options = array())
     {
-        if (isset($_SESSION['horde_auth']) &&
-            !empty($_SESSION['horde_auth']['authenticated']) &&
-            !empty($_SESSION['horde_auth']['userId']) &&
-            ($_SESSION['horde_auth']['realm'] == $realm)) {
-            if (!self::checkSessionIP()) {
-                self::setAuthError(self::REASON_SESSIONIP);
-                return false;
-            } elseif (!self::checkBrowserString()) {
-                self::setAuthError(self::REASON_BROWSER);
-                return false;
+        $driver = empty($options['app'])
+            ? $GLOBALS['conf']['auth']['driver']
+            : $options['app'];
+
+        /* Check for cached authentication results. */
+        if (self::getAuth() &&
+            (($_SESSION['horde_auth']['driver'] == $driver) ||
+             isset($_SESSION['horde_auth']['app'][$driver]))) {
+            return self::checkExistingAuth();
+        }
+
+        if (empty($options['app'])) {
+            $auth = self::singleton($driver);
+        } else {
+            $auth = self::singleton('application', array('app' => $driver));
+
+            /* If we have a horde session, and the app doesn't need additional
+             * auth, mark that in the session and return. */
+            if (self::getAuth() && !$auth->hasCapability('authenticate')) {
+                $_SESSION['horde_auth']['app'][$driver] = true;
+                return true;
             }
-            return true;
         }
 
-        // Try transparent authentication now.
-        $auth = self::singleton($GLOBALS['conf']['auth']['driver']);
-        if ($auth->hasCapability('transparent') && $auth->transparent()) {
-            return self::isAuthenticated($realm);
+        return $auth->transparent();
+    }
+
+    /**
+     * 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'])) {
+            $browser = Horde_Browser::singleton();
+            if ($_SESSION['horde_auth']['browser'] != $browser->getAgentString()) {
+                self::setAuthError(self::REASON_BROWSER);
+                return false;
+            }
         }
 
-        return false;
+        return true;
     }
 
     /**
@@ -369,14 +426,105 @@ class Horde_Auth
      */
     static public function getAuth()
     {
-        if (isset($_SESSION['horde_auth'])) {
-            if (!empty($_SESSION['horde_auth']['authenticated']) &&
-                !empty($_SESSION['horde_auth']['userId'])) {
-                return $_SESSION['horde_auth']['userId'];
+        return (!empty($_SESSION['horde_auth']['userId']))
+            ? $_SESSION['horde_auth']['userId']
+            : false;
+    }
+
+    /**
+     * Redirects to the Horde login page on authentication failure.
+     *
+     * @param string $app         The app which failed authentication.
+     * @param Horde_Exception $e  An exception thrown by
+     *                            Horde_Registry::pushApp().
+     *
+     * @throws Horde_Exception
+     */
+    static public function authenticationFailureRedirect($app = 'horde',
+                                                         $e = null)
+    {
+        if (Horde_Cli::runningFromCLI()) {
+            $cli = Horde_Cli::singleton();
+            $cli->fatal(_("You are not authenticated."));
+        }
+
+        if (is_null($e)) {
+            $params = array();
+        } else {
+            switch ($e->getCode()) {
+            case Horde_Registry::PERMISSION_DENIED:
+                $params = array('app' => $app, 'reason' => Horde_Auth::REASON_MESSAGE, 'msg' => $e->getMessage());
+                break;
+
+            case Horde_Registry::AUTH_FAILURE:
+                $params = array('app' => $app);
+                break;
+
+            default:
+                throw $e;
+            }
+        }
+
+        header('Location: ' . self::getLogoutUrl($params));
+        exit;
+    }
+
+    /**
+     * Return a URL to the login screen, adding the necessary logout
+     * parameters.
+     * If no reason/msg is passed in, use the current global authentication
+     * error message.
+     *
+     * @param array $options  Additional options:
+     * <pre>
+     * 'app' - (string) Authenticate to this application
+     *         DEFAULT: Horde
+     * 'msg' - (string) If reason is self::REASON_MESSAGE, the message
+     *         to display to the user.
+     *         DEFAULT: None
+     * 'params' - (array) Additional params to add to the URL (not allowed:
+     *            'app', 'horde_logout_token', 'msg', 'nosidebar', 'reason',
+     *            'url').
+     *            DEFAULT: None
+     * 'reason' - (integer) The reason for logout
+     *            DEFAULT: None
+     * </pre>
+     *
+     * @return string The formatted URL
+     */
+    static public function getLogoutUrl($options = array())
+    {
+        $registry = Horde_Registry::singleton();
+
+        if (!isset($options['reason'])) {
+            $options['reason'] = self::getAuthError();
+        }
+
+        if ($options['reason'] == self::REASON_LOGOUT) {
+            $params = array(
+                'horde_logout_token' => Horde::getRequestToken('horde.logout'),
+                'nosidebar' => 1
+            );
+        } else {
+            $params = array(
+                'url' => Horde::selfUrl(true)
+            );
+        }
+
+        if (isset($options['app'])) {
+            $params['app'] = $options['app'];
+        }
+
+        if ($options['reason']) {
+            $params[self::REASON_PARAM] = $options['reason'];
+            if ($options['reason'] == self::REASON_MESSAGE) {
+                $params[self::REASON_MSG_PARAM] = empty($options['msg'])
+                    ? ''
+                    : $options['msg'];
             }
         }
 
-        return false;
+        return Horde_Util::addParameter($registry->get('webroot', 'horde') . '/login.php', $params, null, false);
     }
 
     /**
@@ -384,11 +532,9 @@ class Horde_Auth
      *
      * @return boolean Whether the backend requested a password change.
      */
-    static public function isPasswordChangeRequested()
+    static public function passwordChangeRequested()
     {
-        return (isset($_SESSION['horde_auth']) &&
-                !empty($_SESSION['horde_auth']['authenticated']) &&
-                !empty($_SESSION['horde_auth']['changeRequested']));
+        return !empty($_SESSION['horde_auth']['change']);
     }
 
     /**
@@ -401,14 +547,9 @@ class Horde_Auth
     static public function getBareAuth()
     {
         $user = self::getAuth();
-        if ($user) {
-            $pos = strpos($user, '@');
-            if ($pos !== false) {
-                $user = substr($user, 0, $pos);
-            }
-        }
-
-        return $user;
+        return ($user && (($pos = strpos($user, '@')) !== false))
+            ? substr($user, 0, $pos)
+            : $user;
     }
 
     /**
@@ -419,14 +560,10 @@ class Horde_Auth
      */
     static public function getAuthDomain()
     {
-        if ($user = self::getAuth()) {
-            $pos = strpos($user, '@');
-            if ($pos !== false) {
-                return substr($user, $pos + 1);
-            }
-        }
-
-        return false;
+        $user = self::getAuth();
+        return ($user && (($pos = strpos($user, '@')) !== false))
+            ? substr($user, $pos + 1)
+            : false;
     }
 
     /**
@@ -435,25 +572,23 @@ class Horde_Auth
      *
      * @param string $credential  The credential to retrieve.
      *
-     * @return mixed  The requested credential, or false if no user is
-     *                logged in.
+     * @return mixed  The requested credential, all credentials if $credential
+     *                is null, or false if no user is logged in.
      */
-    static public function getCredential($credential)
+    static public function getCredential($credential = null)
     {
-        if (empty($_SESSION['horde_auth']) ||
-            empty($_SESSION['horde_auth']['authenticated'])) {
+        if (!self::getAuth()) {
             return false;
         }
 
         $credentials = Horde_Secret::read(Horde_Secret::getKey('auth'), $_SESSION['horde_auth']['credentials']);
         $credentials = @unserialize($credentials);
 
-        if (is_array($credentials) &&
-            isset($credentials[$credential])) {
-            return $credentials[$credential];
-        }
-
-        return false;
+        return is_null($credential)
+            ? $credentials
+            : ((is_array($credentials) && isset($credentials[$credential]))
+                   ? $credentials[$credential]
+                   : false);
     }
 
     /**
@@ -464,8 +599,7 @@ class Horde_Auth
      */
     static public function setCredential($credential, $value)
     {
-        if (!empty($_SESSION['horde_auth']) &&
-            !empty($_SESSION['horde_auth']['authenticated'])) {
+        if (self::getAuth()) {
             $credentials = @unserialize(Horde_Secret::read(Horde_Secret::getKey('auth'), $_SESSION['horde_auth']['credentials']));
             if (is_array($credentials)) {
                 $credentials[$credential] = $value;
@@ -485,17 +619,23 @@ class Horde_Auth
      *
      * @param string $userId      The userId who has been authorized.
      * @param array $credentials  The credentials of the user.
-     * @param string $realm       The authentication realm to use.
-     * @param boolean $change     Whether to request that the user change
-     *                            their password.
+     * @param array $options      Additional options:
+     * <pre>
+     * 'app' - (string) The app to set authentication credentials for.
+     * 'change' - (boolean) Whether to request that the user change their
+     *            password.
+     * 'login' - (boolean) Do login tasks?
+     *           DEFAULT: TODO
+     * </pre>
+     *
+     * @return boolean  Whether authentication was successful.
      */
-    static public function setAuth($userId, $credentials, $realm = null,
-                                   $change = false)
+    static public function setAuth($userId, $credentials, $options = array())
     {
         $userId = self::addHook(trim($userId));
 
         if (!empty($GLOBALS['conf']['hooks']['postauthenticate'])) {
-            if (Horde::callHook('_horde_hook_postauthenticate', array($userId, $credentials, $realm), 'horde') === false) {
+            if (Horde::callHook('_horde_hook_postauthenticate', array($userId, $credentials, empty($options['app']) ? 'horde' : $options['app']), 'horde') === false) {
                 if (self::getAuthError() != self::REASON_MESSAGE) {
                     self::setAuthError(self::REASON_FAILED);
                 }
@@ -503,36 +643,43 @@ class Horde_Auth
             }
         }
 
+        if (!empty($options['app'])) {
+            if (!empty($_SESSION['horde_auth'])) {
+                $_SESSION['horde_auth']['app'][$options['app']] = true;
+                return true;
+            }
+            $app_array = array($options['app'] => true);
+        } else {
+            $app_array = array();
+        }
+
         /* If we're already set with this userId, don't continue. */
-        if (isset($_SESSION['horde_auth']['userId']) &&
+        if (self::getAuth() &&
             ($_SESSION['horde_auth']['userId'] == $userId)) {
             return true;
         }
 
         /* Clear any existing info. */
-        self::clearAuth($realm);
+        self::clearAuth();
 
         $credentials = Horde_Secret::write(Horde_Secret::getKey('auth'), serialize($credentials));
 
-        if (!empty($realm)) {
-            $userId .= '@' . $realm;
-        }
-
         $browser = Horde_Browser::singleton();
 
         $_SESSION['horde_auth'] = array(
-            'authenticated' => true,
+            'app' => $app_array,
             'browser' => $browser->getAgentString(),
-            'changeRequested' => $change,
+            'change' => !empty($options['change']),
             'credentials' => $credentials,
-            'realm' => $realm,
-            'remote_addr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null,
+            'driver' => empty($options['app']) ? $GLOBALS['conf']['auth']['driver'] : $options['app'],
+            'remoteAddr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null,
             'timestamp' => time(),
             'userId' => $userId
         );
 
         /* Reload preferences for the new user. */
-        $GLOBALS['registry']->loadPrefs();
+        $registry = Horde_Registry::singleton();
+        $registry->loadPrefs();
         Horde_Nls::setLang($GLOBALS['prefs']->getValue('language'));
 
         /* Fetch the user's last login time. */
@@ -571,54 +718,19 @@ class Horde_Auth
         $last_login = array('time' => time(), 'host' => $ptrdname);
         $GLOBALS['prefs']->setValue('last_login', serialize($last_login));
 
-        if ($change) {
-            $GLOBALS['notification']->push(_("Your password has expired."),
-                                           'horde.message');
-
-            $auth = self::singleton($GLOBALS['conf']['auth']['driver']);
-            if ($auth->hasCapability('update')) {
-                /* A bit of a kludge.  URL is set from the login screen, but
-                 * we aren't completely certain we got here from the login
-                 * screen.  So any screen which calls setAuth() which has a
-                 * url will end up going there.  Should be OK. */
-                $url_param = Horde_Util::getFormData('url');
-
-                if ($url_param) {
-                    $url = Horde::url(Horde_Util::removeParameter($url_param, session_name()), true);
-                    $return_to = $GLOBALS['registry']->get('webroot', 'horde') .  '/index.php';
-                    $return_to = Horde_Util::addParameter($return_to, 'url', $url);
-                } else {
-                    $return_to = Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/index.php');
-                }
-
-                $url = Horde::applicationUrl('services/changepassword.php');
-                $url = Horde_Util::addParameter($url, array('return_to' => $return_to), null, false);
-
-                header('Location: ' . $url);
-                exit;
-            }
-        }
-
         return true;
     }
 
     /**
      * Clears any authentication tokens in the current session.
-     *
-     * @param string $realm  The authentication realm to clear.
      */
-    static public function clearAuth($realm = null)
+    static public function clearAuth()
     {
-        if (!empty($realm) && isset($_SESSION['horde_auth'][$realm])) {
-            $_SESSION['horde_auth'][$realm] = array('authenticated' => false);
-        } elseif (isset($_SESSION['horde_auth'])) {
-            $_SESSION['horde_auth'] = array('authenticated' => false);
-        }
+        unset($_SESSION['horde_auth']);
 
         /* Remove the user's cached preferences if they are present. */
-        if (isset($GLOBALS['registry'])) {
-            $GLOBALS['registry']->unloadPrefs();
-        }
+        $registry = Horde_Registry::singleton();
+        $registry->unloadPrefs();
     }
 
     /**
@@ -669,6 +781,7 @@ class Horde_Auth
      * @param string $userId  The authentication backend's user name.
      *
      * @return string  The internal Horde user name.
+     * @throws Horde_Exception
      */
     static public function addHook($userId)
     {
@@ -689,6 +802,7 @@ class Horde_Auth
      * @param string $userId  The internal Horde user name.
      *
      * @return string  The authentication backend's user name.
+     * @throws Horde_Exception
      */
     static public function removeHook($userId)
     {
@@ -700,131 +814,14 @@ class Horde_Auth
     /**
      * Returns the name of the authentication provider.
      *
-     * @param string $driver  Used by recursive calls when untangling composite
-     *                        auth.
-     * @param array $params   Used by recursive calls when untangling composite
-     *                        auth.
-     *
      * @return string  The name of the driver currently providing
-     *                 authentication.
-     */
-    static public function getProvider($driver = null, $params = null)
-    {
-        if (is_null($driver)) {
-            $driver = $GLOBALS['conf']['auth']['driver'];
-        }
-
-        if (is_null($params)) {
-            $params = Horde::getDriverConfig('auth', is_array($driver) ? $driver[1] : $driver);
-        }
-
-        if ($driver == 'application') {
-            return isset($params['app']) ? $params['app'] : 'application';
-        } elseif ($driver == 'composite') {
-            if (($login_driver = self::getDriverByParam('loginscreen_switch', $params)) &&
-                !empty($params['drivers'][$login_driver])) {
-                return self::getProvider($params['drivers'][$login_driver]['driver'],
-                                         isset($params['drivers'][$login_driver]['params']) ? $params['drivers'][$login_driver]['params'] : null);
-            }
-            return 'composite';
-        } else {
-            return $driver;
-        }
-    }
-
-    /**
-     * Returns the logout reason.
-     *
-     * @return string One of the logout reasons (see the AUTH_LOGOUT_*
-     *                constants for the valid reasons).  Returns null if there
-     *                is no logout reason present.
-     */
-    static public function getLogoutReason()
-    {
-        return isset($GLOBALS['horde_auth']['logout']['type'])
-            ? $GLOBALS['horde_auth']['logout']['type']
-            : Horde_Util::getFormData(self::REASON_PARAM);
-    }
-
-    /**
-     * Returns the status string to use for logout messages.
-     *
-     * @return string  The logout reason string.
-     */
-    static public function getLogoutReasonString()
-    {
-        switch (self::getLogoutReason()) {
-        case self::REASON_SESSION:
-            return sprintf(_("Your %s session has expired. Please login again."), $GLOBALS['registry']->get('name'));
-
-        case self::REASON_SESSIONIP:
-            return sprintf(_("Your Internet Address has changed since the beginning of your %s session. To protect your security, you must login again."), $GLOBALS['registry']->get('name'));
-
-        case self::REASON_BROWSER:
-            return sprintf(_("Your browser appears to have changed since the beginning of your %s session. To protect your security, you must login again."), $GLOBALS['registry']->get('name'));
-
-        case self::REASON_LOGOUT:
-            return _("You have been logged out.");
-
-        case self::REASON_FAILED:
-            return _("Login failed.");
-            break;
-
-        case self::REASON_BADLOGIN:
-            return _("Login failed because your username or password was entered incorrectly.");
-            break;
-
-        case self::REASON_EXPIRED:
-            return _("Your login has expired.");
-            break;
-
-        case self::REASON_MESSAGE:
-            return isset($GLOBALS['horde_auth']['logout']['msg'])
-                ? $GLOBALS['horde_auth']['logout']['msg']
-                : Horde_Util::getFormData(self::REASON_MSG_PARAM);
-
-        default:
-            return '';
-        }
-    }
-
-    /**
-     * Generates the correct parameters to pass to the given logout URL.
-     *
-     * If no reason/msg is passed in, use the current global authentication
-     * error message.
-     *
-     * @param string $url     The URL to redirect to.
-     * @param string $reason  The reason for logout.
-     * @param string $msg     If reason is self::REASON_MESSAGE, the message to
-     *                        display to the user.
-     *
-     * @return string The formatted URL
+     *                 authentication, or false if not set.
      */
-    static public function addLogoutParameters($url, $reason = null,
-                                               $msg = null)
+    static public function getProvider()
     {
-        $params = array('horde_logout_token' => Horde::getRequestToken('horde.logout'));
-
-        if (isset($GLOBALS['registry'])) {
-            $params['app'] = $GLOBALS['registry']->getApp();
-        }
-
-        if (is_null($reason)) {
-            $reason = self::getLogoutReason();
-        }
-
-        if ($reason) {
-            $params[self::REASON_PARAM] = $reason;
-            if ($reason == self::REASON_MESSAGE) {
-                if (is_null($msg)) {
-                    $msg = self::getLogoutReasonString();
-                }
-                $params[self::REASON_MSG_PARAM] = $msg;
-            }
-        }
-
-        return Horde_Util::addParameter($url, $params, null, false);
+        return empty($_SESSION['horde_auth']['driver'])
+            ? false
+            : $_SESSION['horde_auth']['driver'];
     }
 
     /**
@@ -833,42 +830,34 @@ class Horde_Auth
      *
      * @param string $session_data  The session data.
      * @param boolean $info         Return session information.  The following
-     *                              information is returned: userid, realm,
-     *                              timestamp, remote_addr, browser.
+     *                              information is returned: userid,
+     *                              timestamp, remoteAddr, browser.
      *
      * @return array  An array of the user's sesion information if
      *                authenticated or false.  The following information is
-     *                returned: userid, realm, timestamp, remote_addr, browser.
+     *                returned: userid, timestamp, remoteAddr, browser.
      */
     static public function readSessionData($session_data)
     {
-        if (empty($session_data)) {
-            return false;
-        }
-
-        $pos = strpos($session_data, 'horde_auth|');
-        if ($pos === false) {
+        if (empty($session_data) ||
+            (($pos = strpos($session_data, 'horde_auth|')) === false)) {
             return false;
         }
 
         $endpos = $pos + 7;
-        $old_error = error_reporting(0);
 
         while ($endpos !== false) {
             $endpos = strpos($session_data, '|', $endpos);
-            $data = unserialize(substr($session_data, $pos + 7, $endpos));
+            $data = @unserialize(substr($session_data, $pos + 7, $endpos));
             if (is_array($data)) {
-                error_reporting($old_error);
-                if (empty($data['authenticated'])) {
-                    return false;
-                }
-                return array(
-                    'browser' => $data['browser'],
-                    'realm' => $data['realm'],
-                    'remote_addr' => $data['remote_addr'],
-                    'timestamp' => $data['timestamp'],
-                    'userid' => $data['userId']
-                );
+                return empty($data)
+                    ? false
+                    : array(
+                        'browser' => $data['browser'],
+                        'remoteAddr' => $data['remoteAddr'],
+                        'timestamp' => $data['timestamp'],
+                        'userid' => $data['userId']
+                    );
             }
             ++$endpos;
         }
@@ -883,9 +872,9 @@ class Horde_Auth
      * @param string $msg   The error message/reason for invalid
      *                      authentication.
      */
-    public function setAuthError($type, $msg = null)
+    static public function setAuthError($type, $msg = null)
     {
-        $GLOBALS['horde_auth']['logout'] = array(
+        self::$_reason = array(
             'msg' => $msg,
             'type' => $type
         );
@@ -896,62 +885,14 @@ class Horde_Auth
      *
      * @return mixed  Error type or false on error.
      */
-    public function getAuthError()
+    static public function getAuthError()
     {
-        return isset($GLOBALS['horde_auth']['logout']['type'])
-            ? $GLOBALS['horde_auth']['logout']['type']
+        return isset(self::$_reason['type'])
+            ? self::$_reason['type']
             : false;
     }
 
     /**
-     * Returns the appropriate authentication driver, if any, selecting by the
-     * specified parameter.
-     *
-     * @param string $name          The parameter name.
-     * @param array $params         The parameter list.
-     * @param string $driverparams  A list of parameters to pass to the driver.
-     *
-     * @return mixed Return value or called user func or null if unavailable
-     */
-    public function getDriverByParam($name, $params,
-                                     $driverparams = array())
-    {
-        if (isset($params[$name]) &&
-            function_exists($params[$name])) {
-            return call_user_func_array($params[$name], $driverparams);
-        }
-
-        return null;
-    }
-
-    /**
-     * Performs check on session to see if IP Address has changed since the
-     * last access.
-     *
-     * @return boolean  True if IP Address is the same (or the check is
-     *                  disabled), false if the address has changed.
-     */
-    static public function checkSessionIP()
-    {
-        return (empty($GLOBALS['conf']['auth']['checkip']) ||
-                (isset($_SESSION['horde_auth']['remote_addr']) &&
-                 ($_SESSION['horde_auth']['remote_addr'] == $_SERVER['REMOTE_ADDR'])));
-    }
-
-    /**
-     * Performs check on session to see if browser string has changed since
-     * the last access.
-     *
-     * @return boolean  True if browser string is the same, false if the
-     *                  string has changed.
-     */
-    static public function checkBrowserString()
-    {
-        return (empty($GLOBALS['conf']['auth']['checkbrowser']) ||
-                ($_SESSION['horde_auth']['browser'] == $GLOBALS['browser']->getAgentString()));
-    }
-
-    /**
      * Converts to allowed 64 characters for APRMD5 passwords.
      *
      * @param string $value   TODO
index f3e8534..a6c544f 100644 (file)
@@ -42,7 +42,9 @@ class Horde_Auth_Application extends Horde_Auth_Base
         'authenticate' => 'authAuthenticate',
         'exists' => 'authUserExists',
         'list' => 'authUserList',
+        'loginparams' => 'authLoginParams',
         'remove' => 'authRemoveUser',
+        'transparent' => 'authTransparent',
         'update' => 'authUpdateUser'
     );
 
@@ -98,23 +100,10 @@ class Horde_Auth_Application extends Horde_Auth_Base
 
         try {
             $result = $registry->callByPackage($this->_params['app'], $this->_apiMethods['authenticate'], array($userId, $credentials));
+            $this->_credentials['params']['app'] = $this->_params['app'];
         } catch (Horde_Auth_Exception $e) {
             throw new Horde_Auth_Exception('', Horde_Auth::REASON_BADLOGIN);
         }
-
-        // Horrific hack.  Avert your eyes.  Since an application may already
-        // set the authentication information necessary, we don't want to
-        // overwrite that info.  Coming into this function, we know that
-        // the authentication has not yet been set in this session.  So after
-        // calling the app-specific auth handler, if authentication
-        // information has suddenly appeared, it follows that the information
-        // has been stored already in the session and we shouldn't overwrite.
-        // So grab the authentication ID set and stick it in $_authCredentials
-        // this will eventually cause setAuth() in authenticate() to exit
-        // before re-setting the auth info values.
-        if ($ret && ($authid = Horde_Auth::getAuth())) {
-            $this->_authCredentials['userId'] = $authid;
-        }
     }
 
     /**
@@ -205,4 +194,44 @@ class Horde_Auth_Application extends Horde_Auth_Base
         }
     }
 
+    /**
+     * Attempt transparent authentication.
+     *
+     * @return boolean  Whether transparent login is supported.
+     * @throws Horde_Auth_Exception
+     */
+    protected function _transparent()
+    {
+        if (!$this->hasCapability('transparent')) {
+            return false;
+        }
+
+        $registry = Horde_Registry::singleton();
+        if (!$registry->callByPackage($this->_params['app'], $this->_apiMethods['transparent'])) {
+            return false;
+        }
+
+        $this->_credentials['params']['app'] = $this->_params['app'];
+        return true;
+    }
+
+    /**
+     * Returns information on what login parameters to display on the login
+     * screen.
+     *
+     * Is defined in an application's API in the function name identified by
+     * self::_apiMethods['loginparams'].
+     *
+     * @throws Horde_Exception
+     */
+    public function getLoginParams()
+    {
+        if (!$this->hasCapability('loginparams')) {
+            return parent::getLoginParams();
+        }
+
+        $registry = Horde_Registry::singleton();
+        return $registry->callByPackage($this->_params['app'], $this->_apiMethods['loginparams']);
+    }
+
 }
index 603db2e..e1b6f3a 100644 (file)
@@ -22,6 +22,7 @@ abstract class Horde_Auth_Base
      */
     protected $_capabilities = array(
         'add'           => false,
+        'authenticate'  => true,
         'groups'        => false,
         'list'          => false,
         'resetpassword' => false,
@@ -42,7 +43,7 @@ abstract class Horde_Auth_Base
      *
      * @var array
      */
-    protected $_authCredentials = array();
+    protected $_credentials = array();
 
     /**
      * Constructor.
@@ -63,18 +64,16 @@ abstract class Horde_Auth_Base
      * @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.
-     * @param string $realm       The authentication realm to check.
      *
      * @return boolean  Whether or not the credentials are valid.
      */
-    public function authenticate($userId, $credentials, $login = true,
-                                 $realm = null)
+    public function authenticate($userId, $credentials, $login = true)
     {
         $auth = false;
         $userId = trim($userId);
 
         if (!empty($GLOBALS['conf']['hooks']['preauthenticate'])) {
-            if (!Horde::callHook('_horde_hook_preauthenticate', array($userId, $credentials, $realm), 'horde')) {
+            if (!Horde::callHook('_horde_hook_preauthenticate', array($userId, $credentials), 'horde')) {
                 if (Horde_Auth::getAuthError() != Horde_Auth::REASON_MESSAGE) {
                     Horde_Auth::setAuthError(Horde_Auth::REASON_FAILED);
                 }
@@ -83,11 +82,10 @@ abstract class Horde_Auth_Base
         }
 
         /* Store the credentials being checked so that subclasses can modify
-         * them if necessary (like transparent auth does). */
-        $this->_authCredentials = array(
-            'changeRequested' => false,
+         * them if necessary. */
+        $this->_credentials = array(
             'credentials' => $credentials,
-            'realm' => $realm,
+            'params' => array('change' => false),
             'userId' => $userId
         );
 
@@ -96,19 +94,12 @@ abstract class Horde_Auth_Base
 
             if ($login) {
                 $auth = Horde_Auth::setAuth(
-                    $this->_authCredentials['userId'],
-                    $this->_authCredentials['credentials'],
-                    $this->_authCredentials['realm'],
-                    $this->_authCredentials['changeRequested']
+                    $this->_credentials['userId'],
+                    $this->_credentials['credentials'],
+                    $this->_credentials['params']
                 );
             } else {
-                if (!Horde_Auth::checkSessionIP()) {
-                    Horde_Auth::setAuthError(self::REASON_SESSIONIP);
-                } elseif (!Horde_Auth::checkBrowserString()) {
-                    Horde_Auth::setAuthError(self::REASON_BROWSER);
-                } else {
-                    $auth = true;
-                }
+                $auth = Horde_Auth::checkExistingAuth();
             }
         } catch (Horde_Auth_Exception $e) {
             if ($e->getCode()) {
@@ -124,9 +115,9 @@ abstract class Horde_Auth_Base
     /**
      * Authentication stub.
      *
-     * Horde_Auth_Exception should pass a message string (if any) in the message
-     * field, and the REASON_* constant in the code field (defaults to
-     * REASON_MESSAGE).
+     * On failure, Horde_Auth_Exception should pass a message string (if any)
+     * in the message field, and the Horde_Auth::REASON_* constant in the code
+     * field (defaults to Horde_Auth::REASON_MESSAGE).
      *
      * @param string $userID      The userID to check.
      * @param array $credentials  An array of login credentials.
@@ -203,29 +194,44 @@ abstract class Horde_Auth_Base
     }
 
     /**
-     * Automatic authentication: Finds out if the client matches an allowed IP
-     * block.
+     * Automatic authentication.
      *
      * @return boolean  Whether or not the client is allowed.
+     * @throws Horde_Auth_Exception
      */
     public function transparent()
     {
-        try {
-            return $this->_transparent();
-        } catch (Horde_Auth_Exception $e) {
-            Horde_Auth::setAuthError($e->getCode() || Horde_Auth::REASON_MESSAGE, $e->getMessage());
-            return false;
+        /* Reset the credentials being checked so that subclasses can modify
+         * them if necessary. */
+        $this->_credentials = array(
+            'credentials' => array(),
+            'params' => array('change' => false),
+            'userId' => ''
+        );
+
+        if ($this->_transparent()) {
+            return Horde_Auth::setAuth(
+                $this->_credentials['userId'],
+                $this->_credentials['credentials'],
+                $this->_credentials['params']
+            );
         }
+
+        return false;
     }
 
     /**
      * Transparent authentication stub.
      *
-     * If the auth error message is desired to be set, Horde_Auth_Exception should
-     * thrown instead of returning false.
-     * The Horde_Auth_Exception object should have a message string (if any) in the
-     * message field, and the REASON_* constant in the code field (defaults to
-     * REASON_MESSAGE).
+     * 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.
+     *
+     * Transparent authentication should normally never throw an error - false
+     * should normally be returned. However, it is also possible that a
+     * transparent authentication is the only available auth method; if so,
+     * attempting to login via a login page may cause an endless loop. In this
+     * case, an Exception should be thrown which will act as a fatal error.
      *
      * @return boolean  Whether transparent login is supported.
      * @throws Horde_Auth_Exception
@@ -277,15 +283,26 @@ abstract class Horde_Auth_Base
     }
 
     /**
-     * Driver-level admin check stub.
+     * Returns information on what login parameters to display on the login
+     * screen. If not defined, will display the default (username, password).
      *
-     * @todo
-     *
-     * @return boolean  False.
+     * @return array  An array with the following elements:
+     * <pre>
+     * '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
+     * </pre>
+     * @throws Horde_Exception
      */
-    public function isAdmin($permission = null, $permlevel = null, $user = null)
+    public function getLoginParams()
     {
-        return false;
+        return array(
+            'js_code' => array(),
+            'js_files' => array(),
+            'params' => array()
+        );
     }
 
 }
index ddb0113..a417d8c 100644 (file)
@@ -1,8 +1,15 @@
 <?php
 /**
- * The Auth_composite class provides a wrapper around
- * application-provided Horde authentication which fits inside the
- * Horde Horde_Auth:: API.
+ * The Horde_Auth_Composite class provides a way to combine two separate
+ * drivers for admin vs. authentication purposes.
+ *
+ * Required parameters:
+ * <pre>
+ * 'admin_driver' - (string) TODO
+ * 'admin_driver_config' - (array) TODO
+ * 'auth_driver' - (string) TODO
+ * 'auth_driver_config' - (string) TODO
+ * </pre>
  *
  * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
  *
@@ -22,23 +29,6 @@ class Horde_Auth_Composite extends Horde_Auth_Base
     protected $_drivers = array();
 
     /**
-     * Return the named parameter for the current auth driver.
-     *
-     * @param string $param  The parameter to fetch.
-     *
-     * @return string  The parameter's value.
-     */
-    public function getParam($param)
-    {
-        if (($login_driver = Horde_Auth::getDriverByParam('loginscreen_switch', $this->_params)) &&
-            $this->_loadDriver($login_driver)) {
-            return $this->_drivers[$login_driver]->getParam($param);
-        }
-
-        return null;
-    }
-
-    /**
      * Find out if a set of login credentials are valid.
      *
      * @param string $userId      The userId to check.
@@ -48,19 +38,8 @@ class Horde_Auth_Composite extends Horde_Auth_Base
      */
     protected function _authenticate($userId, $credentials)
     {
-        if (($auth_driver = Horde_Auth::getDriverByParam('loginscreen_switch', $this->_params)) &&
-            $this->_loadDriver($auth_driver)) {
-            $this->_drivers[$auth_driver]->authenticate($userId, $credentials);
-            return;
-        }
-
-        if (($auth_driver = Horde_Auth::getDriverByParam('username_switch', $this->_params, array($userId))) &&
-            $this->_loadDriver($auth_driver)) {
-            $this->_drivers[$auth_driver]->hasCapability('transparent');
-            return;
-        }
-
-        throw new Horde_Auth_Exception('', Horde_Auth::REASON_FAILED);
+        $driver = $this->_loadDriver('auth');
+        return $driver->authenticate($userId, $credentials, false);
     }
 
     /**
@@ -73,28 +52,10 @@ class Horde_Auth_Composite extends Horde_Auth_Base
      */
     public function hasCapability($capability)
     {
-        switch ($capability) {
-        case 'add':
-        case 'update':
-        case 'remove':
-        case 'list':
-            if (!empty($this->_params['admin_driver']) &&
-                $this->_loadDriver($this->_params['admin_driver'])) {
-                return $this->_drivers[$this->_params['admin_driver']]->hasCapability($capability);
-            } else {
-                return false;
-            }
-            break;
-
-        case 'transparent':
-            if (($login_driver = Horde_Auth::getDriverByParam('loginscreen_switch', $this->_params)) &&
-                $this->_loadDriver($login_driver)) {
-                return $this->_drivers[$login_driver]->hasCapability('transparent');
-            }
-            return false;
-            break;
-
-        default:
+        try {
+            $driver = $this->_loadDriver('admin');
+            return $driver->hasCapability($capability);
+        } catch (Horde_Auth_Exception $e) {
             return false;
         }
     }
@@ -107,12 +68,12 @@ class Horde_Auth_Composite extends Horde_Auth_Base
      */
     protected function _transparent()
     {
-        if (($login_driver = Horde_Auth::getDriverByParam('loginscreen_switch', $this->_params)) &&
-            $this->_loadDriver($login_driver)) {
-            return $this->_drivers[$login_driver]->transparent();
+        try {
+            $driver = $this->_loadDriver('auth');
+            return $driver->transparent();
+        } catch (Horde_Auth_Exception $e) {
+            return false;
         }
-
-        return false;
     }
 
     /**
@@ -125,12 +86,8 @@ class Horde_Auth_Composite extends Horde_Auth_Base
      */
     public function addUser($userId, $credentials)
     {
-        if (!empty($this->_params['admin_driver']) &&
-            $this->_loadDriver($this->_params['admin_driver'])) {
-            $this->_drivers[$this->_params['admin_driver']]->addUser($userId, $credentials);
-        } else {
-            parent::addUser($userId, $credentials);
-        }
+        $driver = $this->_loadDriver('admin');
+        $driver->addUser($userId, $credentials);
     }
 
     /**
@@ -144,12 +101,8 @@ class Horde_Auth_Composite extends Horde_Auth_Base
      */
     public function updateUser($oldID, $newID, $credentials)
     {
-        if (!empty($this->_params['admin_driver']) &&
-            $this->_loadDriver($this->_params['admin_driver'])) {
-            $this->_drivers[$this->_params['admin_driver']]->updateUser($oldID, $newID, $credentials);
-        } else {
-            parent::updateUser($oldID, $newID, $credentials);
-        }
+        $driver = $this->_loadDriver('admin');
+        $driver->updateUser($oldID, $newID, $credentials);
     }
 
     /**
@@ -161,12 +114,8 @@ class Horde_Auth_Composite extends Horde_Auth_Base
      */
     public function removeUser($userId)
     {
-        if (!empty($this->_params['admin_driver']) &&
-            $this->_loadDriver($this->_params['admin_driver'])) {
-            $this->_drivers[$this->_params['admin_driver']]->removeUser($userId);
-        } else {
-            parent::removeUser($userId);
-        }
+        $driver = $this->_loadDriver('admin');
+        $driver->removeUser($userId);
     }
 
     /**
@@ -177,12 +126,8 @@ class Horde_Auth_Composite extends Horde_Auth_Base
      */
     public function listUsers()
     {
-        if (!empty($this->_params['admin_driver']) &&
-            $this->_loadDriver($this->_params['admin_driver'])) {
-            return $this->_drivers[$this->_params['admin_driver']]->listUsers();
-        }
-
-        return parent::listUsers();
+        $driver = $this->_loadDriver('admin');
+        return $driver->listUsers();
     }
 
     /**
@@ -194,12 +139,12 @@ class Horde_Auth_Composite extends Horde_Auth_Base
      */
     public function exists($userId)
     {
-        if (!empty($this->_params['admin_driver']) &&
-            $this->_loadDriver($this->_params['admin_driver'])) {
-            return $this->_drivers[$this->_params['admin_driver']]->exists($userId);
+        try {
+            $driver = $this->_loadDriver('admin');
+            return $driver->exists($userId);
+        } catch (Horde_Auth_Exception $e) {
+            return false;
         }
-
-        return parent::exists($userId);
     }
 
     /**
@@ -208,29 +153,15 @@ class Horde_Auth_Composite extends Horde_Auth_Base
      *
      * @param string $driver  The name of the driver to load.
      *
-     * @return boolean  True if driver successfully initializes.
+     * @throws Horde_Auth_Exception
      */
     protected function _loadDriver($driver)
     {
         if (empty($this->_drivers[$driver])) {
-            // This is a bit specialized for Horde::getDriverConfig(),
-            // so localize it here:
-            global $conf;
-            if (!empty($this->_params['drivers'][$driver]['params'])) {
-                $params = $this->_params['drivers'][$driver]['params'];
-                if (isset($conf[$this->_params['drivers'][$driver]['driver']])) {
-                    $params = array_merge($conf[$this->_params['drivers'][$driver]['driver']], $params);
-                }
-            } elseif (!empty($conf[$driver])) {
-                $params = $conf[$driver];
-            } else {
-                $params = null;
-            }
-
-            $this->_drivers[$driver] = Horde_Auth::singleton($this->_params['drivers'][$driver]['driver'], $params);
+            $this->_drivers[$driver] = Horde_Auth::singleton($this->_params[$driver . '_driver'], $this->_params[$driver . '_driver_config']);
         }
 
-        return isset($this->_drivers[$driver]);
+        return $this->_drivers[$driver];
     }
 
 }
index 4d30aff..39f8ccc 100644 (file)
@@ -217,7 +217,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->_authCredentials['changeRequested'] = true;
+            $this->_credentials['params']['change'] = true;
         }
     }
 
index 6a04500..294cf0f 100644 (file)
@@ -62,7 +62,7 @@ class Horde_Auth_Imap extends Horde_Auth_Base
             'userhierarchy' => 'user.'
         ), $params);
 
-        parent::__construct(array_merge($default_params, $params));
+        parent::__construct($params);
 
         if (!empty($this->_params['admin_user'])) {
             $this->_capabilities['add'] = true;
index 34ee5df..fc91059 100644 (file)
@@ -132,11 +132,10 @@ class Horde_Auth_Kolab extends Horde_Auth_Base
      *
      * @param string $userId            The userId who has been authorized.
      * @param array $credentials        The credentials of the user.
-     * @param string $realm             The authentication realm to use.
-     * @param boolean $changeRequested  Whether to request that the user change
+     * @param boolean $change           Whether to request that the user change
      *                                  their password.
      */
-    function setAuth($userId, $credentials, $realm = null, $changeRequested = false)
+    function setAuth($userId, $credentials, $change = false)
     {
         // TODO - setAuth doesn't exist in Horde_Auth_Base
         //        This should probably use _username_hook_frombackend.
@@ -146,7 +145,7 @@ class Horde_Auth_Kolab extends Horde_Auth_Base
             $userId = $session->user_mail;
         }
 
-        return parent::setAuth($userId, $credentials, $realm, $changeRequested);
+        return parent::setAuth($userId, $credentials, $change);
     }
 
     /**
index c29fcdc..666a43d 100644 (file)
@@ -295,7 +295,7 @@ class Horde_Auth_Ldap extends Horde_Auth_Base
                 }
 
                 if ($toexpire == 0) {
-                    $this->_authCredentials['changeRequested'] = true;
+                    $this->_credentials['params']['change'] = true;
                 } elseif ($toexpire < 0) {
                     throw new Horde_Auth_Exception('', Horde_Auth::REASON_EXPIRED);
                 }
index 327af68..6903d11 100644 (file)
@@ -220,7 +220,7 @@ class Horde_Auth_Msad extends Horde_Auth_Ldap
         $ssl = ($this->_params['ssl']) ? 'ldaps://' : '';
         $this->_ds = ldap_connect($ssl . $this->_params['hostspec'], $this->_params['port']);
         if (!$this->_ds) {
-            return PEAR::raiseError(_("Failed to connect to MSAD server."));
+            throw new Horde_Auth_Exception(_("Failed to connect to MSAD server."));
         }
 
         if (!ldap_set_option($this->_ds, LDAP_OPT_PROTOCOL_VERSION, 3)) {
@@ -249,7 +249,7 @@ class Horde_Auth_Msad extends Horde_Auth_Ldap
         }
 
         if (!$bind) {
-            return PEAR::raiseError(_("Could not bind to MSAD server."));
+            throw new Horde_Auth_Exception(_("Could not bind to MSAD server."));
         }
 
         return true;
@@ -258,13 +258,11 @@ class Horde_Auth_Msad extends Horde_Auth_Ldap
     /**
      * Find the user dn
      *
-     * @access private
-     *
      * @param string $userId  The user UID to find.
      *
      * @return string  The user's full DN
      */
-    function _findDN($userId)
+    protected function _findDN($userId)
     {
         /* Search for the user's full DN. */
         foreach ($this->_params['uid'] as $uid) {
@@ -278,7 +276,7 @@ class Horde_Auth_Msad extends Horde_Auth_Ldap
                                );
             /* Searching the tree is not successful */
             if (!$search) {
-                return PEAR::raiseError(_("Could not search the MSAD server."));
+                throw new Horde_Auth_Exception(_("Could not search the MSAD server."));
             }
 
             /* Fetch the search result */
@@ -289,14 +287,14 @@ class Horde_Auth_Msad extends Horde_Auth_Ldap
             }
         }
 
-        if (is_array($result) && (count($result) > 1)) {
-            $dn = $result[0]['dn'];
-        } else {
-            return PEAR::raiseError(_("Empty result."));
+        if (!is_array($result) || (count($result) <= 1)) {
+            throw new Horde_Auth_Exception(_("Empty result."));
         }
+
         /* Be sure the horde userId is the configured one */
-        $this->_authCredentials['userId'] = $result[0][$this->_params['authId']][0];
-        return $dn;
+        $this->_credentials['userId'] = $result[0][$this->_params['authId']][0];
+
+        return $result[0]['dn'];
     }
 
 }
index 32cc5bb..7b573f2 100644 (file)
@@ -158,7 +158,7 @@ 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->_authCredentials['changeRequested'] = true;
+            $this->_credentials['params']['change'] = true;
         }
     }
 
index 5d58e59..e176172 100644 (file)
@@ -433,12 +433,10 @@ HTML;
             return self::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/problem.php?return_url=' . urlencode(self::selfUrl(true, true, true)));
 
         case 'logout':
-            return self::url(Horde_Auth::addLogoutParameters($GLOBALS['registry']->get('webroot', 'horde') . '/login.php', Horde_Auth::REASON_LOGOUT));
+            return Horde_Auth::getLogoutUrl(array('reason' => Horde_Auth::REASON_LOGOUT));
 
         case 'login':
-            /* Get an Auth object. */
-            $auth = Horde_Auth::singleton($GLOBALS['conf']['auth']['driver']);
-            return $auth->getLoginScreen('', $referrer ? self::selfUrl(true) : null);
+            return self::url($GLOBALS['registry']->get('webroot', 'horde') . '/login.php');
 
         case 'options':
             global $conf;
@@ -1780,23 +1778,6 @@ HTML;
     }
 
     /**
-     * Redirects to the main Horde login page on authentication failure.
-     */
-    static public function authenticationFailureRedirect()
-    {
-        if (Horde_Cli::runningFromCLI()) {
-            $cli = Horde_Cli::singleton();
-            $cli->fatal(_("You are not authenticated."));
-        }
-
-        $url = $GLOBALS['registry']->get('webroot', 'horde') . '/login.php';
-        $url = Horde_Util::addParameter($url, array('url' => self::selfUrl(true), 'nosidebar' => 1), null, false);
-        $url = Horde_Auth::addLogoutParameters($url);
-        header('Location: ' . self::url($url, true));
-        exit;
-    }
-
-    /**
      * Provides a standardised function to call a Horde hook, checking whether
      * a hook config file exists and whether the function itself exists. If
      * these two conditions are not satisfied it will return the specified
index 7d4389f..f74c050 100644 (file)
@@ -21,6 +21,11 @@ class Horde_Registry
     const SESSION_NONE = 1;
     const SESSION_READONLY = 2;
 
+    /* Error codes for pushApp(). */
+    const AUTH_FAILURE = 1;
+    const NOT_ACTIVE = 2;
+    const PERMISSION_DENIED = 3;
+
     /**
      * Singleton value.
      *
@@ -775,6 +780,10 @@ class Horde_Registry
      *
      * @return boolean  Whether or not the _appStack was modified.
      * @throws Horde_Exception
+     *         Code can be one of the following:
+     *         Horde_Registry::AUTH_FAILURE
+     *         Horde_Registry::NOT_ACTIVE
+     *         Horde_Registry::PERMISSION_DENIED
      */
     public function pushApp($app, $checkPerms = true)
     {
@@ -786,19 +795,24 @@ class Horde_Registry
         if (!isset($this->applications[$app]) ||
             $this->applications[$app]['status'] == 'inactive' ||
             ($this->applications[$app]['status'] == 'admin' && !Horde_Auth::isAdmin())) {
-            throw new Horde_Exception($app . ' is not activated.');
+            throw new Horde_Exception($app . ' is not activated.', 'not_active');
         }
 
         /* If permissions checking is requested, return an error if the
          * current user does not have read perms to the application being
          * loaded. We allow access:
-         *
          *  - To all admins.
          *  - To all authenticated users if no permission is set on $app.
          *  - To anyone who is allowed by an explicit ACL on $app. */
-        if ($checkPerms && !$this->hasPermission($app)) {
-            Horde::logMessage(sprintf('%s does not have READ permission for %s', Horde_Auth::getAuth() ? 'User ' . Horde_Auth::getAuth() : 'Guest user', $app), __FILE__, __LINE__, PEAR_LOG_DEBUG);
-            throw new Horde_Exception(sprintf(_('%s is not authorised for %s.'), Horde_Auth::getAuth() ? 'User ' . Horde_Auth::getAuth() : 'Guest user', $this->applications[$app]['name']), 'permission_denied');
+        if ($checkPerms) {
+            if (!Horde_Auth::isAuthenticated(array('app' => $app))) {
+                throw new Horde_Exception('User is not authorized', self::AUTH_FAILURE);
+            }
+
+            if (!$this->hasPermission($app, PERMS_READ)) {
+                Horde::logMessage(sprintf('%s does not have READ permission for %s', Horde_Auth::getAuth() ? 'User ' . Horde_Auth::getAuth() : 'Guest user', $app), __FILE__, __LINE__, PEAR_LOG_DEBUG);
+                throw new Horde_Exception(sprintf(_('%s is not authorized for %s.'), Horde_Auth::getAuth() ? 'User ' . Horde_Auth::getAuth() : 'Guest user', $this->applications[$app]['name']), 'permission_denied');
+            }
         }
 
         /* Set up autoload paths for the current application. This needs to
@@ -889,10 +903,19 @@ class Horde_Registry
      */
     public function hasPermission($app, $perms = PERMS_READ)
     {
-        return Horde_Auth::isAdmin() ||
-               ($GLOBALS['perms']->exists($app)
-                   ? $GLOBALS['perms']->hasPermission($app, Horde_Auth::getAuth(), $perms)
-                   : (bool)Horde_Auth::getAuth());
+        $this->_lastPerms = null;
+
+        if (Horde_Auth::isAdmin()) {
+            return true;
+        }
+
+        if ($GLOBALS['perms']->exists($app) &&
+            !$GLOBALS['perms']->hasPermission($app, Horde_Auth::getAuth(), $perms)) {
+            $this->_lastPerms = 'perms';
+            return false;
+        }
+
+        return true;
     }
 
     /**