--- /dev/null
+<?php
+/**
+ * The Horde_Auth:: class provides a common abstracted interface into the
+ * various backends for the Horde authentication system.
+ *
+ * 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
+ */
+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.
+ *
+ * The following 'reasons' for the logout screen are recognized:
+ * <pre>
+ * 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_SESSION - Logout due to session expiration
+ * REASON_SESSIONIP - Logout due to change of IP address during session
+ * </pre>
+ */
+ 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;
+
+ /**
+ * 64 characters that are valid for APRMD5 passwords.
+ */
+ const APRMD5_VALID = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+
+ /**
+ * Characters used when generating a password.
+ */
+ const VOWELS = 'aeiouy';
+ const CONSONANTS = 'bcdfghjklmnpqrstvwxz';
+ const NUMBERS = '0123456789';
+
+ /**
+ * Singleton instances.
+ *
+ * @var array
+ */
+ static protected $_instances = array();
+
+ /**
+ * Attempts to return a concrete Horde_Auth_Driver instance based on
+ * $driver.
+ *
+ * @param mixed $driver The type of concrete Horde_Auth_Driver subclass
+ * to return. If $driver is an array, then look
+ * in $driver[0]/lib/Auth/ for the subclass
+ * implementation named $driver[1].php.
+ * @param array $params A hash containing any additional configuration or
+ * parameters a subclass might need.
+ *
+ * @return Horde_Auth_Driver The newly created concrete instance.
+ * @throws Horde_Exception
+ */
+ static public function factory($driver, $params = null)
+ {
+ if (is_array($driver)) {
+ list($app, $driv_name) = $driver;
+ $driver = basename($driv_name);
+ } else {
+ $driver = basename($driver);
+ }
+
+ /* Return a base Horde_Auth_Driver object if no driver is
+ * specified. */
+ if (empty($driver) || (strcasecmp($driver, 'none') == 0)) {
+ return new Horde_Auth_Driver();
+ }
+
+ if (empty($params)) {
+ $params = Horde::getDriverConfig('auth', $driver);
+ }
+
+ $class = (empty($app) ? 'Horde' : $app) . '_Auth_' . ucfirst($driver);
+
+ if (class_exists($class)) {
+ return new $class($params);
+ }
+
+ throw new Horde_Exception('Class definition of ' . $class . ' not found.');
+ }
+
+ /**
+ * Attempts to return a reference to a concrete instance based on $driver.
+ * It will only create a new instance if no instance with the same
+ * parameters currently exists.
+ *
+ * This method must be invoked as: $var = Horde_Auth::singleton()
+ *
+ * @param mixed $driver The type of concrete Horde_Auth_Driver subclass
+ * to return. If $driver is an array, then look
+ * in $driver[0]/lib/Auth/ for the subclass
+ * implementation named $driver[1].php.
+ * @param array $params A hash containing any additional configuration or
+ * connection parameters a subclass might need.
+ *
+ * @return Horde_Auth_Driver The concrete reference.
+ * @throws Horde_Exception
+ */
+ static public function singleton($driver, $params = array())
+ {
+ ksort($params);
+ $signature = hash('md5', serialize(array($driver, $params)));
+
+ if (!isset(self::$_instances[$signature])) {
+ self::$_instances[$signature] = Horde_Auth::factory($driver, $params);
+ }
+
+ return self::$_instances[$signature];
+ }
+
+ /**
+ * Formats a password using the current encryption.
+ *
+ * @param string $plaintext The plaintext password to encrypt.
+ * @param string $salt The salt to use to encrypt the password.
+ * If not present, a new salt will be
+ * generated.
+ * @param string $encryption The kind of pasword encryption to use.
+ * Defaults to md5-hex.
+ * @param boolean $show_encrypt Some password systems prepend the kind of
+ * encryption to the crypted password ({SHA},
+ * etc). Defaults to false.
+ *
+ * @return string The encrypted password.
+ */
+ static public function getCryptedPassword($plaintext, $salt = '',
+ $encryption = 'md5-hex',
+ $show_encrypt = false)
+ {
+ /* Get the salt to use. */
+ $salt = self::getSalt($encryption, $salt, $plaintext);
+
+ /* Encrypt the password. */
+ switch ($encryption) {
+ case 'plain':
+ return $plaintext;
+
+ case 'msad':
+ return Horde_String::convertCharset('"' . $plaintext . '"', 'ISO-8859-1', 'UTF-16LE');
+
+ case 'sha':
+ $encrypted = base64_encode(pack('H*', hash('sha1', $plaintext)));
+ return $show_encrypt ? '{SHA}' . $encrypted : $encrypted;
+
+ case 'crypt':
+ case 'crypt-des':
+ case 'crypt-md5':
+ case 'crypt-blowfish':
+ return ($show_encrypt ? '{crypt}' : '') . crypt($plaintext, $salt);
+
+ case 'md5-base64':
+ $encrypted = base64_encode(pack('H*', hash('md5', $plaintext)));
+ return $show_encrypt ? '{MD5}' . $encrypted : $encrypted;
+
+ case 'ssha':
+ $encrypted = base64_encode(pack('H*', hash('sha1', $plaintext . $salt)) . $salt);
+ return $show_encrypt ? '{SSHA}' . $encrypted : $encrypted;
+
+ case 'smd5':
+ $encrypted = base64_encode(pack('H*', hash('md5', $plaintext . $salt)) . $salt);
+ return $show_encrypt ? '{SMD5}' . $encrypted : $encrypted;
+
+ case 'aprmd5':
+ $length = strlen($plaintext);
+ $context = $plaintext . '$apr1$' . $salt;
+ $binary = pack('H*', hash('md5', $plaintext . $salt . $plaintext));
+
+ for ($i = $length; $i > 0; $i -= 16) {
+ $context .= substr($binary, 0, ($i > 16 ? 16 : $i));
+ }
+ for ($i = $length; $i > 0; $i >>= 1) {
+ $context .= ($i & 1) ? chr(0) : $plaintext[0];
+ }
+
+ $binary = pack('H*', hash('md5', $context));
+
+ for ($i = 0; $i < 1000; ++$i) {
+ $new = ($i & 1) ? $plaintext : substr($binary, 0, 16);
+ if ($i % 3) {
+ $new .= $salt;
+ }
+ if ($i % 7) {
+ $new .= $plaintext;
+ }
+ $new .= ($i & 1) ? substr($binary, 0, 16) : $plaintext;
+ $binary = pack('H*', hash('md5', $new));
+ }
+
+ $p = array();
+ for ($i = 0; $i < 5; $i++) {
+ $k = $i + 6;
+ $j = $i + 12;
+ if ($j == 16) {
+ $j = 5;
+ }
+ $p[] = self::_toAPRMD5((ord($binary[$i]) << 16) |
+ (ord($binary[$k]) << 8) |
+ (ord($binary[$j])),
+ 5);
+ }
+
+ return '$apr1$' . $salt . '$' . implode('', $p) . self::_toAPRMD5(ord($binary[11]), 3);
+
+ case 'md5-hex':
+ default:
+ return ($show_encrypt) ? '{MD5}' . hash('md5', $plaintext) : hash('md5', $plaintext);
+ }
+ }
+
+ /**
+ * Returns a salt for the appropriate kind of password encryption.
+ * Optionally takes a seed and a plaintext password, to extract the seed
+ * of an existing password, or for encryption types that use the plaintext
+ * in the generation of the salt.
+ *
+ * @param string $encryption The kind of pasword encryption to use.
+ * Defaults to md5-hex.
+ * @param string $seed The seed to get the salt from (probably a
+ * previously generated password). Defaults to
+ * generating a new seed.
+ * @param string $plaintext The plaintext password that we're generating
+ * a salt for. Defaults to none.
+ *
+ * @return string The generated or extracted salt.
+ */
+ static public function getSalt($encryption = 'md5-hex', $seed = '',
+ $plaintext = '')
+ {
+ switch ($encryption) {
+ case 'crypt':
+ case 'crypt-des':
+ return $seed
+ ? substr(preg_replace('|^{crypt}|i', '', $seed), 0, 2)
+ : substr(hash('md5', mt_rand()), 0, 2);
+
+ case 'crypt-md5':
+ return $seed
+ ? substr(preg_replace('|^{crypt}|i', '', $seed), 0, 12)
+ : '$1$' . substr(hash('md5', mt_rand()), 0, 8) . '$';
+
+ case 'crypt-blowfish':
+ return $seed
+ ? substr(preg_replace('|^{crypt}|i', '', $seed), 0, 16)
+ : '$2$' . substr(hash('md5', mt_rand()), 0, 12) . '$';
+
+ case 'ssha':
+ return $seed
+ ? substr(base64_decode(preg_replace('|^{SSHA}|i', '', $seed)), 20)
+ : substr(pack('H*', sha1(substr(pack('h*', hash('md5', mt_rand())), 0, 8) . $plaintext)), 0, 4);
+
+ case 'smd5':
+ return $seed
+ ? substr(base64_decode(preg_replace('|^{SMD5}|i', '', $seed)), 16)
+ : substr(pack('H*', hash('md5', substr(pack('h*', hash('md5', mt_rand())), 0, 8) . $plaintext)), 0, 4);
+
+ case 'aprmd5':
+ if ($seed) {
+ return substr(preg_replace('/^\$apr1\$(.{8}).*/', '\\1', $seed), 0, 8);
+ } else {
+ $salt = '';
+ $valid = self::APRMD5_VALID;
+ for ($i = 0; $i < 8; ++$i) {
+ $salt .= $valid[mt_rand(0, 63)];
+ }
+ return $salt;
+ }
+
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * 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_Exception
+ */
+ static public function removeUserData($userId)
+ {
+ $errApps = array();
+
+ foreach ($GLOBALS['registry']->listApps(array('notoolbar', 'hidden', 'active', 'admin')) as $app) {
+ if ($GLOBALS['registry']->hasMethod('removeUserData', $app) &&
+ is_a($result = $GLOBALS['registry']->callByPackage($app, 'removeUserData', array($userId)), 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ $errApps[] = $app;
+ }
+ }
+
+ if (count($errApps)) {
+ throw new Horde_Exception(sprintf(_("The following applications encountered errors removing user data: %s"), implode(', ', $errApps)));
+ }
+ }
+
+ /**
+ * Checks if there is a session with valid auth information. for the
+ * 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.
+ *
+ * @return boolean Whether or not the user is authenticated.
+ */
+ static public function isAuthenticated($realm = null)
+ {
+ 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;
+ }
+ return true;
+ }
+
+ // Try transparent authentication now.
+ $auth = self::singleton($GLOBALS['conf']['auth']['driver']);
+ if ($auth->hasCapability('transparent') && $auth->transparent()) {
+ return self::isAuthenticated($realm);
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the currently logged in user, if there is one.
+ *
+ * @return mixed The userId of the current user, or false if no user is
+ * logged in.
+ */
+ 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 false;
+ }
+
+ /**
+ * Return whether the authentication backend requested a password change.
+ *
+ * @return boolean Whether the backend requested a password change.
+ */
+ static public function isPasswordChangeRequested()
+ {
+ return (isset($_SESSION['horde_auth']) &&
+ !empty($_SESSION['horde_auth']['authenticated']) &&
+ !empty($_SESSION['horde_auth']['changeRequested']));
+ }
+
+ /**
+ * Returns the curently logged-in user without any domain information
+ * (e.g., bob@example.com would be returned as 'bob').
+ *
+ * @return mixed The user ID of the current user, or false if no user
+ * is logged in.
+ */
+ static public function getBareAuth()
+ {
+ $user = self::getAuth();
+ if ($user) {
+ $pos = strpos($user, '@');
+ if ($pos !== false) {
+ $user = substr($user, 0, $pos);
+ }
+ }
+
+ return $user;
+ }
+
+ /**
+ * Returns the domain of currently logged-in user (e.g., bob@example.com
+ * would be returned as 'example.com').
+ *
+ * @return mixed The domain suffix of the current user, or false.
+ */
+ static public function getAuthDomain()
+ {
+ if ($user = self::getAuth()) {
+ $pos = strpos($user, '@');
+ if ($pos !== false) {
+ return substr($user, $pos + 1);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the requested credential for the currently logged in user, if
+ * present.
+ *
+ * @param string $credential The credential to retrieve.
+ *
+ * @return mixed The requested credential, or false if no user is
+ * logged in.
+ */
+ static public function getCredential($credential)
+ {
+ if (empty($_SESSION['horde_auth']) ||
+ empty($_SESSION['horde_auth']['authenticated'])) {
+ 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;
+ }
+
+ /**
+ * 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.
+ */
+ static public function setCredential($credential, $value)
+ {
+ if (!empty($_SESSION['horde_auth']) &&
+ !empty($_SESSION['horde_auth']['authenticated'])) {
+ $credentials = @unserialize(Horde_Secret::read(Horde_Secret::getKey('auth'), $_SESSION['horde_auth']['credentials']));
+ if (is_array($credentials)) {
+ $credentials[$credential] = $value;
+ } else {
+ $credentials = array($credential => $value);
+ }
+ $_SESSION['horde_auth']['credentials'] = Horde_Secret::write(Horde_Secret::getKey('auth'), serialize($credentials));
+ }
+ }
+
+ /**
+ * 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 $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.
+ */
+ static public function setAuth($userId, $credentials, $realm = null,
+ $change = false)
+ {
+ $userId = self::addHook(trim($userId));
+
+ if (!empty($GLOBALS['conf']['hooks']['postauthenticate']) &&
+ !Horde::callHook('_horde_hook_postauthenticate', array($userId, $credentials, $realm), 'horde', false)) {
+ if (self::getAuthError() != self::REASON_MESSAGE) {
+ self::setAuthError(self::REASON_FAILED);
+ }
+ return false;
+ }
+
+ /* If we're already set with this userId, don't continue. */
+ if (isset($_SESSION['horde_auth']['userId']) &&
+ ($_SESSION['horde_auth']['userId'] == $userId)) {
+ return true;
+ }
+
+ /* Clear any existing info. */
+ self::clearAuth($realm);
+
+ $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,
+ 'browser' => $browser->getAgentString(),
+ 'changeRequested' => $change,
+ 'credentials' => $credentials,
+ 'realm' => $realm,
+ 'remote_addr' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null,
+ 'timestamp' => time(),
+ 'userId' => $userId
+ );
+
+ /* Reload preferences for the new user. */
+ $GLOBALS['registry']->loadPrefs();
+ NLS::setLang($GLOBALS['prefs']->getValue('language'));
+
+ /* 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 (class_exists('Net_DNS')) {
+ $resolver = new Net_DNS_Resolver();
+ $resolver->retry = isset($GLOBALS['conf']['dns']['retry']) ? $GLOBALS['conf']['dns']['retry'] : 1;
+ $resolver->retrans = isset($GLOBALS['conf']['dns']['retrans']) ? $GLOBALS['conf']['dns']['retrans'] : 1;
+ $response = $resolver->query($host, 'PTR');
+ $ptrdname = $response ? $response->answer[0]->ptrdname : $host;
+ } else {
+ $ptrdname = @gethostbyaddr($host);
+ }
+
+ $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)
+ {
+ 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);
+ }
+
+ /* Remove the user's cached preferences if they are present. */
+ if (isset($GLOBALS['registry'])) {
+ $GLOBALS['registry']->unloadPrefs();
+ }
+ }
+
+ /**
+ * Is the current user an administrator?
+ *
+ * @param string $permission Allow users with this permission admin access
+ * in the current context.
+ * @param integer $permlevel The level of permissions to check for
+ * (PERMS_EDIT, PERMS_DELETE, etc). Defaults
+ * to PERMS_EDIT.
+ * @param string $user The user to check. Defaults to
+ * self::getAuth().
+ *
+ * @return boolean Whether or not this is an admin user.
+ */
+ static public function isAdmin($permission = null, $permlevel = null,
+ $user = null)
+ {
+ if (is_null($user)) {
+ $user = self::getAuth();
+ }
+
+ if ($user &&
+ @is_array($GLOBALS['conf']['auth']['admins']) &&
+ in_array($user, $GLOBALS['conf']['auth']['admins'])) {
+ return true;
+ }
+
+ if (!is_null($permission)) {
+ if (is_null($permlevel)) {
+ $permlevel = PERMS_EDIT;
+ }
+ return $GLOBALS['perms']->hasPermission($permission, $user, $permlevel);
+ }
+
+ return false;
+ }
+
+ /**
+ * Applies a hook defined by the function _username_hook_frombackend() to
+ * the given user name if this function exists and user hooks are enabled.
+ *
+ * This method should be called if a authentication backend's user name
+ * needs to be converted to a (unique) Horde user name. The backend's user
+ * name is what the user sees and uses, but internally we use the Horde
+ * user name.
+ *
+ * @param string $userId The authentication backend's user name.
+ *
+ * @return string The internal Horde user name.
+ */
+ static public function addHook($userId)
+ {
+ if (!empty($GLOBALS['conf']['hooks']['username'])) {
+ $newId = Horde::callHook('_username_hook_frombackend', array($userId));
+ if (!is_a($newId, 'PEAR_Error')) {
+ return $newId;
+ }
+ }
+
+ return $userId;
+ }
+
+ /**
+ * Applies a hook defined by the function _username_hook_tobackend() to
+ * the given user name if this function exists and user hooks are enabled.
+ *
+ * This method should be called if a Horde user name needs to be converted
+ * to an authentication backend's user name or displayed to the user. The
+ * backend's user name is what the user sees and uses, but internally we
+ * use the Horde user name.
+ *
+ * @param string $userId The internal Horde user name.
+ *
+ * @return string The authentication backend's user name.
+ */
+ static public function removeHook($userId)
+ {
+ if (!empty($GLOBALS['conf']['hooks']['username'])) {
+ $newId = Horde::callHook('_username_hook_tobackend', array($userId));
+ if (!is_a($newId, 'PEAR_Error')) {
+ return $newId;
+ }
+ }
+
+ return $userId;
+ }
+
+ /**
+ * 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
+ */
+ static public function addLogoutParameters($url, $reason = null,
+ $msg = null)
+ {
+ $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);
+ }
+
+ /**
+ * Reads session data to determine if it contains Horde authentication
+ * credentials.
+ *
+ * @param string $session_data The session data.
+ * @param boolean $info Return session information. The following
+ * information is returned: userid, realm,
+ * timestamp, remote_addr, 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.
+ */
+ static public function readSessionData($session_data)
+ {
+ if (empty($session_data)) {
+ return false;
+ }
+
+ $pos = strpos($session_data, 'horde_auth|');
+ if ($pos === 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));
+ 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']
+ );
+ }
+ ++$endpos;
+ }
+
+ return false;
+ }
+
+ /**
+ * 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.
+ */
+ public function setAuthError($type, $msg = null)
+ {
+ $GLOBALS['horde_auth']['logout'] = array(
+ 'msg' => $msg,
+ 'type' => $type
+ );
+ }
+
+ /**
+ * Returns the error type for an invalid authentication or false on error.
+ *
+ * @return mixed Error type or false on error.
+ */
+ public function getAuthError()
+ {
+ return isset($GLOBALS['horde_auth']['logout']['type'])
+ ? $GLOBALS['horde_auth']['logout']['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
+ * @param integer $count TODO
+ *
+ * @return string $value converted to the 64 MD5 characters.
+ */
+ static protected function _toAPRMD5($value, $count)
+ {
+ $aprmd5 = '';
+ $count = abs($count);
+ $valid = self::APRMD5_VALID;
+
+ while (--$count) {
+ $aprmd5 .= $valid[$value & 0x3f];
+ $value >>= 6;
+ }
+
+ return $aprmd5;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Application class provides a wrapper around
+ * application-provided Horde authentication which fits inside the
+ * Horde Horde_Auth:: API.
+ *
+ * Required parameters:
+ * <pre>
+ * 'app' - (string) The application which is providing authentication.
+ * </pre>
+ *
+ * Copyright 2002-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
+ */
+class Horde_Auth_Application extends Horde_Auth_Driver
+{
+ /**
+ * Cache for hasCapability().
+ *
+ * @var array
+ */
+ protected $_loaded = array();
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ public function __construct($params = array())
+ {
+ Horde::assertDriverConfig($params, 'auth', array('app'), 'authentication application');
+
+ 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);
+
+ $methods = array(
+ 'add' => 'addUser',
+ 'exists' => 'userExists',
+ 'list' => 'userList',
+ 'remove' => 'removeUser',
+ 'update' => 'updateUser'
+ );
+
+ if (!in_array($capability, $this->_loaded) &&
+ isset($methods[$capability])) {
+ $this->_capabilities[$capability] = $GLOBALS['registry']->hasMethod($methods[$capability], $this->_params['app']);
+ $this->_loaded[] = $capability;
+ }
+
+ return parent::hasCapability($capability);
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials The credentials to use.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ if (!$GLOBALS['registry']->hasMethod('authenticate', $this->_params['app'])) {
+ throw new Horde_Exception($this->_params['app'] . ' does not provide an authenticate() method.');
+ }
+
+ if (!$GLOBALS['registry']->callByPackage($this->_params['app'], 'authenticate', array('userId' => $userId, 'credentials' => $credentials, 'params' => $this->_params))) {
+ throw new Horde_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;
+ }
+ }
+
+ /**
+ * Return the URI of the login screen for this authentication method.
+ *
+ * @param string $app The application to use.
+ * @param string $url The URL to redirect to after login.
+ *
+ * @return string The login screen URI.
+ */
+ public function getLoginScreen($app = 'horde', $url = '')
+ {
+ return parent::getLoginScreen($this->_params['app'], $url);
+ }
+
+ /**
+ * List all users in the system.
+ *
+ * @return array The array of userIds.
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ return $this->hasCapability('list')
+ ? $GLOBALS['registry']->callByPackage($this->_params['app'], 'userList')
+ : 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']->callByPackage($this->_params['app'], 'userExists', array($userId))
+ : parent::exists($userId);
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to use.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ if ($this->hasCapability('add')) {
+ $GLOBALS['registry']->callByPackage($this->_params['app'], 'addUser', array($userId, $credentials));
+ } else {
+ parent::addUser($userId, $credentials);
+ }
+ }
+
+ /**
+ * Update a set of authentication credentials.
+ *
+ * @param string $oldID The old userId.
+ * @param string $newID The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ public function updateUser($oldID, $newID, $credentials)
+ {
+ if ($this->hasCapability('update')) {
+ $GLOBALS['registry']->callByPackage($this->_params['app'], 'updateUser', array($oldID, $newID, $credentials));
+ } else {
+ parent::addUser($userId, $credentials);
+ }
+ }
+
+ /**
+ * Delete a set of authentication credentials.
+ *
+ * @param string $userId The userId to delete.
+ *
+ * @throws Horde_Exception
+ */
+ public function removeUser($userId)
+ {
+ if ($this->hasCapability('remove')) {
+ $res = $GLOBALS['registry']->callByPackage($this->_params['app'], 'removeUser', array($userId));
+ if (is_a($res, 'PEAR_Error')) {
+ throw new Horde_Exception($result);
+ }
+
+ Horde_Auth::removeUserData($userId);
+ } else {
+ parent::removeUser($userId);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Auto class transparently logs users in to Horde using ONE
+ * username, either defined in the config or defaulting to 'horde_user'.
+ * This is only for use in testing or behind a firewall; it should NOT be
+ * used on a public, production machine.
+ *
+ * Optional parameters:
+ * <pre>
+ * 'password' - (string) The password to record in the user's credentials.
+ * DEFAULT: none
+ * 'requestuser' - (boolean) If true, allow username to be passed by GET, POST
+ * or cookie.
+ * DEFAULT: No
+ * 'username' - (string) The username to authenticate everyone as.
+ * DEFAULT: 'horde_user'
+ * </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
+ */
+class Horde_Auth_Auto extends Horde_Auth_Driver
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'transparent' => true
+ );
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing parameters.
+ */
+ public function __construct($params = array())
+ {
+ parent::__construct($params);
+
+ if (!isset($this->_params['username'])) {
+ $this->_params['username'] = 'horde_user';
+ }
+ }
+
+ /**
+ * Automatic authentication: Set the user allowed IP block.
+ *
+ * @return boolean Whether or not the client is allowed.
+ */
+ protected function _transparent()
+ {
+ $username = (!empty($this->_params['requestuser']) && isset($_REQUEST['username']))
+ ? $_REQUEST['username']
+ : $this->_params['username'];
+
+ return Horde_Auth::setAuth($username, array(
+ 'transparent' => 1,
+ 'password' => isset($this->_params['password']) ? $this->_params['password'] : null
+ ));
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Auth_composite class provides a wrapper around
+ * application-provided Horde authentication which fits inside the
+ * Horde Horde_Auth:: API.
+ *
+ * Copyright 2002-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
+ */
+class Horde_Auth_Composite extends Horde_Auth_Driver
+{
+ /**
+ * Hash containing any instantiated drivers.
+ *
+ * @var array
+ */
+ 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.
+ * @param array $credentials The credentials to use.
+ *
+ * @throws Horde_Exception
+ */
+ 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_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ /**
+ * Query 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)
+ {
+ 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:
+ return false;
+ }
+ }
+
+ /**
+ * Automatic authentication: Find out if the client matches an allowed IP
+ * block.
+ *
+ * @return boolean Whether or not the client is allowed.
+ */
+ protected function _transparent()
+ {
+ if (($login_driver = Horde_Auth::getDriverByParam('loginscreen_switch', $this->_params)) &&
+ $this->_loadDriver($login_driver)) {
+ return $this->_drivers[$login_driver]->transparent();
+ }
+
+ return false;
+ }
+
+ /**
+ * Return the URI of the login screen for this authentication object.
+ *
+ * @param string $app The application to use.
+ * @param string $url The URL to redirect to after login.
+ *
+ * @return string The login screen URI.
+ */
+ public function getLoginScreen($app = 'horde', $url = '')
+ {
+ if (($login_driver = Horde_Auth::getDriverByParam('loginscreen_switch', $this->_params)) &&
+ $this->_loadDriver($login_driver)) {
+ return $this->_drivers[$login_driver]->getLoginScreen($app, $url);
+ }
+
+ return parent::getLoginScreen($app, $url);
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to use.
+ *
+ * @throws Horde_Exception
+ */
+ 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);
+ }
+ }
+
+ /**
+ * Update a set of authentication credentials.
+ *
+ * @param string $oldID The old userId.
+ * @param string $newID The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ 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);
+ }
+ }
+
+ /**
+ * Delete a set of authentication credentials.
+ *
+ * @param string $userId The userId to delete.
+ *
+ * @throws Horde_Exception
+ */
+ 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);
+ }
+ }
+
+ /**
+ * List all users in the system.
+ *
+ * @return array The array of userIds.
+ * @throws Horde_Exception
+ */
+ 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();
+ }
+
+ /**
+ * Checks if a userId exists in the system.
+ *
+ * @param string $userId User ID to check
+ *
+ * @return boolean Whether or not the userId already exists.
+ */
+ 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);
+ }
+
+ return parent::exists($userId);
+ }
+
+ /**
+ * Loads one of the drivers in our configuration array, if it isn't already
+ * loaded.
+ *
+ * @param string $driver The name of the driver to load.
+ *
+ * @return boolean True if driver successfully initializes.
+ */
+ 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);
+ }
+
+ return isset($this->_drivers[$driver]);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Customsql class provides a sql implementation of the Horde
+ * authentication system with the possibility to set custom-made queries.
+ *
+ * Required parameters: See Horde_Auth_Sql driver.
+ * <pre>
+ * Some special tokens can be used in the sql query. They are replaced
+ * at the query stage:
+ *
+ * - '\L' will be replaced by the user's login
+ * - '\P' will be replaced by the user's password.
+ * - '\O' will be replaced by the old user's login (required for update)
+ *
+ * Eg: "SELECT * FROM users WHERE uid = \L
+ * AND passwd = \P
+ * AND billing = 'paid'
+ *
+ * 'query_auth' Authenticate the user. '\L' & '\P'
+ * 'query_add' Add user. '\L' & '\P'
+ * 'query_getpw' Get one user's password. '\L'
+ * 'query_update' Update user. '\O', '\L' & '\P'
+ * 'query_resetpassword' Reset password. '\L', & '\P'
+ * 'query_remove' Remove user. '\L'
+ * 'query_list' List user.
+ * 'query_exists' Check for existance of user. '\L'
+ * </pre>
+ *
+ * Optional parameters: See Horde_Auth_Sql driver.
+ *
+ * Copyright 2002 Ronnie Garcia <ronnie@mk2.net>
+ *
+ * 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 Ronnie Garcia <ronnie@mk2.net>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Joel Vandal <joel@scopserv.com>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Customsql extends Horde_Auth_Sql
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'add' => true,
+ 'list' => true,
+ 'remove' => true,
+ 'resetpassword' => true,
+ 'update' => true
+ );
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ public function __construct($params = array())
+ {
+ Horde::assertDriverConfig($params, 'auth',
+ array('query_auth'),
+ 'authentication custom SQL');
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials The credentials to use.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ try {
+ $this->_connect();
+ } catch (Horde_Exception $e) {
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ /* Build a custom query, based on the config file. */
+ $query = $this->_params['query_auth'];
+ $query = str_replace('\L', $this->_db->quote($userId), $query);
+ $query = str_replace('\P', $this->_db->quote(Horde_Auth::getCryptedPassword(
+ $credentials['password'],
+ $this->_getPassword($userId),
+ $this->_params['encryption'],
+ $this->_params['show_encryption'])), $query);
+
+ $result = $this->_db->query($query);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ $row = $result->fetchRow(DB_GETMODE_ASSOC);
+
+ /* If we have at least one returned row, then the user is valid. */
+ if (is_array($row)) {
+ $result->free();
+ return;
+ }
+
+ $result->free();
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to add.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ $this->_connect();
+
+ /* Build a custom query, based on the config file. */
+ $query = $this->_params['query_add'];
+ $query = str_replace('\L', $this->_db->quote($userId), $query);
+ $query = str_replace('\P', $this->_db->quote(Horde_Auth::getCryptedPassword(
+ $credentials['password'], '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption'])), $query);
+
+ $result = $this->_db->query($query);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+ }
+
+ /**
+ * Update a set of authentication credentials.
+ *
+ * @param string $oldId The old userId.
+ * @param string $newId The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ function updateUser($oldId, $newId, $credentials)
+ {
+ $this->_connect();
+
+ /* Build a custom query, based on the config file. */
+ $query = $this->_params['query_update'];
+ $query = str_replace('\O', $this->_db->quote($oldId), $query);
+ $query = str_replace('\L', $this->_db->quote($newId), $query);
+ $query = str_replace('\P', $this->_db->quote(Horde_Auth::getCryptedPassword(
+ $credentials['password'],
+ $this->_getPassword($oldId),
+ $this->_params['encryption'],
+ $this->_params['show_encryption'])), $query);
+
+ $result = $this->_db->query($query);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+ }
+
+ /**
+ * Resets a user's password. Used for example when the user does not
+ * remember the existing password.
+ *
+ * @param string $userId The user id for which to reset the password.
+ *
+ * @return string The new password on success.
+ * @throws Horde_Exception
+ */
+ public function resetPassword($userId)
+ {
+ $this->_connect();
+
+ /* Get a new random password. */
+ $password = Horde_Auth::genRandomPassword();
+
+ /* Build the SQL query. */
+ $query = $this->_params['query_resetpassword'];
+ $query = str_replace('\L', $this->_db->quote($userId), $query);
+ $query = str_replace('\P', $this->_db->quote(Horde_Auth::getCryptedPassword($password,
+ '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption'])), $query);
+
+ $result = $this->_db->query($query);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+
+ return $password;
+ }
+
+ /**
+ * Delete a set of authentication credentials.
+ *
+ * @param string $userId The userId to delete.
+ *
+ * @throws Horde_Exception
+ */
+ public function removeUser($userId)
+ {
+ $this->_connect();
+
+ /* Build a custom query, based on the config file. */
+ $query = $this->_params['query_remove'];
+ $query = str_replace('\L', $this->_db->quote($userId), $query);
+
+ $result = $this->_db->query($query);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+
+ $this->removeUserData($userId);
+ }
+
+ /**
+ * List all users in the system.
+ *
+ * @return array The array of userIds.
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ $this->_connect();
+
+ /* Build a custom query, based on the config file. */
+ $query = $this->_params['query_list'];
+ $query = str_replace('\L', $this->_db->quote(Horde_Auth::getAuth()), $query);
+
+ $result = $this->_db->getAll($query, null, DB_FETCHMODE_ORDERED);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+
+ /* Loop through and build return array. */
+ $users = array();
+ foreach ($result as $ar) {
+ $users[] = $ar[0];
+ }
+
+ return $users;
+ }
+
+ /**
+ * Checks if a userId exists in the system.
+ *
+ * @return boolean Whether or not the userId already exists.
+ */
+ public function exists($userId)
+ {
+ try {
+ $this->_connect();
+ } catch (Horde_Exception $e) {
+ return false;
+ }
+
+ /* Build a custom query, based on the config file. */
+ $query = $this->_params['query_exists'];
+ $query = str_replace('\L', $this->_db->quote($userId), $query);
+
+ $result = $this->_db->getOne($query);
+
+ return ($result instanceof PEAR_Error)
+ ? false
+ : (bool)$result;
+ }
+
+ /**
+ * Fetch $userId's current password - needed for the salt with some
+ * encryption schemes when doing authentication or updates.
+ *
+ * @param string $userId TODO
+ *
+ * @return string $userId's current password.
+ */
+ protected function _getPassword($userId)
+ {
+ /* Retrieve the old password in case we need the salt. */
+ $query = $this->_params['query_getpw'];
+ $query = str_replace('\L', $this->_db->quote($userId), $query);
+ $pw = $this->_db->getOne($query);
+ if ($pw instanceof PEAR_Error) {
+ Horde::logMessage($pw, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return '';
+ }
+
+ return $pw;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Cyrsql class provides a SQL implementation of the Horde
+ * authentication system for the Cyrus IMAP server. Most of the functionality
+ * is the same as for the SQL class; only what is different overrides the
+ * parent class implementations.
+ *
+ * Required parameters: See Horde_Auth_Sql driver.
+ * <pre>
+ * 'cyradmin' The username of the cyrus administrator.
+ * 'cyrpass' The password for the cyrus administrator.
+ * 'hostspec' The hostname or IP address of the server.
+ * DEFAULT: 'localhost'
+ * 'port' The server port to which we will connect.
+ * IMAP is generally 143, while IMAP-SSL is generally 993.
+ * DEFAULT: Encryption port default
+ * 'secure' The encryption to use. Either 'none', 'ssl', or 'tls'.
+ * DEFAULT: 'none'
+ * </pre>
+ *
+ * Optional parameters: See Horde_Auth_Sql driver.
+ * <pre>
+ * 'domain_field' If set to anything other than 'none' this is used as
+ * field name where domain is stored.
+ * DEFAULT: 'domain_name'
+ * 'hidden_accounts' An array of system accounts to hide from the user
+ * interface.
+ * 'folders' An array of folders to create under username.
+ * DEFAULT: NONE
+ * 'quota' The quota (in kilobytes) to grant on the mailbox.
+ * DEFAULT: NONE
+ * 'unixhier' The value of imapd.conf's unixhierarchysep setting.
+ * Set this to true if the value is true in imapd.conf.
+ * </pre>
+ *
+ * The table structure for the auth system is as follows:
+ * <pre>
+ * CREATE TABLE accountuser (
+ * username VARCHAR(255) BINARY NOT NULL DEFAULT '',
+ * password VARCHAR(32) BINARY NOT NULL DEFAULT '',
+ * prefix VARCHAR(50) NOT NULL DEFAULT '',
+ * domain_name VARCHAR(255) NOT NULL DEFAULT '',
+ * UNIQUE KEY username (username)
+ * );
+ *
+ * CREATE TABLE adminuser (
+ * username VARCHAR(50) BINARY NOT NULL DEFAULT '',
+ * password VARCHAR(50) BINARY NOT NULL DEFAULT '',
+ * type INT(11) NOT NULL DEFAULT '0',
+ * SID VARCHAR(255) NOT NULL DEFAULT '',
+ * home VARCHAR(255) NOT NULL DEFAULT '',
+ * PRIMARY KEY (username)
+ * );
+ *
+ * CREATE TABLE alias (
+ * alias VARCHAR(255) NOT NULL DEFAULT '',
+ * dest LONGTEXT,
+ * username VARCHAR(50) NOT NULL DEFAULT '',
+ * status INT(11) NOT NULL DEFAULT '1',
+ * PRIMARY KEY (alias)
+ * );
+ *
+ * CREATE TABLE domain (
+ * domain_name VARCHAR(255) NOT NULL DEFAULT '',
+ * prefix VARCHAR(50) NOT NULL DEFAULT '',
+ * maxaccounts INT(11) NOT NULL DEFAULT '20',
+ * quota INT(10) NOT NULL DEFAULT '20000',
+ * transport VARCHAR(255) NOT NULL DEFAULT 'cyrus',
+ * freenames ENUM('YES','NO') NOT NULL DEFAULT 'NO',
+ * freeaddress ENUM('YES','NO') NOT NULL DEFAULT 'NO',
+ * PRIMARY KEY (domain_name),
+ * UNIQUE KEY prefix (prefix)
+ * );
+ *
+ * CREATE TABLE domainadmin (
+ * domain_name VARCHAR(255) NOT NULL DEFAULT '',
+ * adminuser VARCHAR(255) NOT NULL DEFAULT ''
+ * );
+ *
+ * CREATE TABLE search (
+ * search_id VARCHAR(255) NOT NULL DEFAULT '',
+ * search_sql TEXT NOT NULL,
+ * perpage INT(11) NOT NULL DEFAULT '0',
+ * timestamp TIMESTAMP(14) NOT NULL,
+ * PRIMARY KEY (search_id),
+ * KEY search_id (search_id)
+ * );
+ *
+ * CREATE TABLE virtual (
+ * alias VARCHAR(255) NOT NULL DEFAULT '',
+ * dest LONGTEXT,
+ * username VARCHAR(50) NOT NULL DEFAULT '',
+ * status INT(11) NOT NULL DEFAULT '1',
+ * KEY alias (alias)
+ * );
+ *
+ * CREATE TABLE log (
+ * id INT(11) NOT NULL AUTO_INCREMENT,
+ * msg TEXT NOT NULL,
+ * user VARCHAR(255) NOT NULL DEFAULT '',
+ * host VARCHAR(255) NOT NULL DEFAULT '',
+ * time DATETIME NOT NULL DEFAULT '2000-00-00 00:00:00',
+ * pid VARCHAR(255) NOT NULL DEFAULT '',
+ * PRIMARY KEY (id)
+ * );
+ * </pre>
+ *
+ * Copyright 2002-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 Ilya Krel <mail@krel.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Cyrsql extends Horde_Auth_Sql
+{
+ /**
+ * Horde_Imap_Client object.
+ *
+ * @var Horde_Imap_Client_Base
+ */
+ protected $_ob;
+
+ /**
+ * Hierarchy separator to use (e.g., is it user/mailbox or user.mailbox)
+ *
+ * @var string
+ */
+ protected $_separator = '.';
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ public function __construct($params = array())
+ {
+ parent::__construct($params);
+
+ $admin_params = array(
+ 'admin_user' => $params['cyradmin'],
+ 'admin_password' => $params['cyrpass'],
+ 'dsn' => $params['imap_dsn']
+ );
+
+ if (!empty($this->_params['unixhier'])) {
+ $admin_params['userhierarchy'] = 'user/';
+ }
+
+ if (!empty($this->_params['unixhier'])) {
+ $this->_separator = '/';
+ }
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials The credentials to use.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ try {
+ $this->_connect();
+ } catch (Horde_Exception $e) {
+ Horde::logMessage($e, __FILE__, __LINE__, PEAR_LOG_ERR);
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ if (!empty($this->_params['domain_field']) &&
+ ($this->_params['domain_field'] != 'none')) {
+ /* Build the SQL query with domain. */
+ $query = sprintf('SELECT * FROM %s WHERE %s = ? AND %s = ?',
+ $this->_params['table'],
+ $this->_params['username_field'],
+ $this->_params['domain_field']);
+ $values = explode('@', $userId);
+ } else {
+ /* Build the SQL query without domain. */
+ $query = sprintf('SELECT * FROM %s WHERE %s = ?',
+ $this->_params['table'],
+ $this->_params['username_field']);
+ $values = array($userId);
+ }
+
+ Horde::logMessage('SQL Query by Horde_Auth_Cyrsql::_authenticate(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_db->query($query, $values);
+ if ($result instanceof PEAR_Error) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ $row = $result->fetchRow(DB_GETMODE_ASSOC);
+ if (is_array($row)) {
+ $result->free();
+ } else {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ if (!$this->_comparePasswords($row[$this->_params['password_field']],
+ $credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ $now = time();
+ if (!empty($this->_params['hard_expiration_field']) &&
+ !empty($row[$this->_params['hard_expiration_field']]) &&
+ ($now > $row[$this->_params['hard_expiration_field']])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_EXPIRED);
+ }
+
+ 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;
+ }
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to add.
+ *
+ * @throw Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ $this->_connect();
+
+ if (!empty($this->_params['domain_field']) &&
+ ($this->_params['domain_field'] != 'none')) {
+ list($name, $domain) = explode('@', $userId);
+ /* Build the SQL query. */
+ $query = sprintf('INSERT INTO %s (%s, %s, %s) VALUES (?, ?, ?)',
+ $this->_params['table'],
+ $this->_params['username_field'],
+ $this->_params['domain_field'],
+ $this->_params['password_field']);
+ $values = array($name,
+ $domain,
+ Horde_Auth::getCryptedPassword($credentials['password'],
+ '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption']));
+
+ Horde::logMessage('SQL Query by Horde_Auth_Cyrsql::addUser(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $dbresult = $this->_db->query($query, $values);
+ $query = 'INSERT INTO virtual (alias, dest, username, status) VALUES (?, ?, ?, 1)';
+ $values = array($userId, $userId, $name);
+
+ Horde::logMessage('SQL Query by Horde_Auth_Cyrsql::addUser(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $dbresult2 = $this->_db->query($query, $values);
+ if ($dbresult2 instanceof PEAR_Error) {
+ throw new Horde_Exception($dbresult2);
+ }
+ } else {
+ parent::addUser($userId, $credentials);
+ }
+
+ try {
+ $mailbox = Horde_String::convertCharset($this->_params['userhierarchy'] . $userId, NLS::getCharset(), 'utf7-imap');
+ $ob->createMailbox($mailbox);
+ $ob->setACL($mailbox, $this->_params['cyradm'], 'lrswipcda');
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+
+ foreach ($this->_params['folders'] as $folders) {
+ if (!empty($this->_params['domain_field']) &&
+ ($this->_params['domain_field'] != 'none')) {
+ list($userName, $domain) = explode('@', $userName);
+ $tmp = $userName . $this->_separator . $value . '@' . $domain;
+Horde_String::convertCharset($userName . $this->_separator . $value . '@' . $domain, NLS::getCharset(), 'utf7-imap');
+ } else {
+ $tmp = $userName . $this->_separator . $value;
+ }
+
+ $tmp = Horde_String::convertCharset($tmp, NLS::getCharset(), 'utf7-imap');
+ $ob->createMailbox($tmp);
+ $ob->setACL($tmp, $this->_params['cyradm'], 'lrswipcda');
+ }
+
+ if (isset($this->_params['quota']) &&
+ ($this->_params['quota'] >= 0)) {
+ try {
+ $this->_ob->setQuota($mailbox, array('storage' => $this->_params['quota']));
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+ }
+
+ if (isset($this->_params['quota']) &&
+ ($this->_params['quota'] >= 0) &&
+ !@imap_set_quota($this->_imapStream, 'user' . $this->_separator . $userId, $this->_params['quota'])) {
+ throw new Horde_Exception(sprintf(_("IMAP mailbox quota creation failed: %s"), imap_last_error()));
+ }
+ }
+
+ /**
+ * Delete a set of authentication credentials.
+ *
+ * @param string $userId The userId to delete.
+ *
+ * @throws Horde_Exception
+ */
+ function removeUser($userId)
+ {
+ $this->_connect();
+
+ if (!empty($this->_params['domain_field']) &&
+ ($this->_params['domain_field'] != 'none')) {
+ list($name, $domain) = explode('@', $userId);
+ /* Build the SQL query. */
+ $query = sprintf('DELETE FROM %s WHERE %s = ? and %s = ?',
+ $this->_params['table'],
+ $this->_params['username_field'],
+ $this->_params['domain_field']);
+ $values = array($name, $domain);
+
+ Horde::logMessage('SQL Query by Horde_Auth_Cyrsql::removeUser(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $dbresult = $this->_db->query($query, $values);
+ $query = 'DELETE FROM virtual WHERE dest = ?';
+ $values = array($userId);
+
+ Horde::logMessage('SQL Query by Horde_Auth_Cyrsql::removeUser(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $dbresult2 = $this->_db->query($query, $values);
+ if ($dbresult2 instanceof PEAR_Error) {
+ return $dbresult2;
+ }
+ } else {
+ parent::removeUser($userId);
+ }
+
+ /* Set ACL for mailbox deletion. */
+ list($admin) = explode('@', $this->_params['cyradmin']);
+
+ try {
+ $this->_ob->setACL($mailbox, $admin, array('rights' => 'lrswipcda'));
+ $this->_ob->deleteMailbox($mailbox);
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+
+ Horde_Auth::removeUserData($userId);
+ }
+
+ /**
+ * List all users in the system.
+ *
+ * @return mixed The array of userIds.
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ $this->_connect();
+
+ if (!empty($this->_params['domain_field']) &&
+ ($this->_params['domain_field'] != 'none')) {
+ /* Build the SQL query with domain. */
+ $query = sprintf('SELECT %s, %s FROM %s ORDER BY %s',
+ $this->_params['username_field'],
+ $this->_params['domain_field'],
+ $this->_params['table'],
+ $this->_params['username_field']);
+ } else {
+ /* Build the SQL query without domain. */
+ $query = sprintf('SELECT %s FROM %s ORDER BY %s',
+ $this->_params['username_field'],
+ $this->_params['table'],
+ $this->_params['username_field']);
+ }
+
+ Horde::logMessage('SQL Query by Horde_Auth_Cyrsql::listUsers(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_db->getAll($query, null, DB_FETCHMODE_ORDERED);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+
+ /* Loop through and build return array. */
+ $users = array();
+ if (!empty($this->_params['domain_field'])
+ && ($this->_params['domain_field'] != 'none')) {
+ foreach ($result as $ar) {
+ if (!in_array($ar[0], $this->_params['hidden_accounts'])) {
+ $users[] = $ar[0] . '@' . $ar[1];
+ }
+ }
+ } else {
+ foreach ($result as $ar) {
+ if (!in_array($ar[0], $this->_params['hidden_accounts'])) {
+ $users[] = $ar[0];
+ }
+ }
+ }
+
+ return $users;
+ }
+
+ /**
+ * Update a set of authentication credentials.
+ *
+ * @param string $oldID The old userId.
+ * @param string $newID The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ public function updateUser($oldID, $newID, $credentials)
+ {
+ $this->_connect();
+
+ if (!empty($this->_params['domain_field']) &&
+ ($this->_params['domain_field'] != 'none')) {
+ list($name, $domain) = explode('@', $oldID);
+ /* Build the SQL query with domain. */
+ $query = sprintf('UPDATE %s SET %s = ? WHERE %s = ? and %s = ?',
+ $this->_params['table'],
+ $this->_params['password_field'],
+ $this->_params['username_field'],
+ $this->_params['domain_field']);
+ $values = array(Horde_Auth::getCryptedPassword($credentials['password'],
+ '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption']),
+ $name, $domain);
+ } else {
+ /* Build the SQL query. */
+ $query = sprintf('UPDATE %s SET %s = ? WHERE %s = ?',
+ $this->_params['table'],
+ $this->_params['password_field'],
+ $this->_params['username_field']);
+ $values = array(Horde_Auth::getCryptedPassword($credentials['password'],
+ '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption']),
+ $oldID);
+ }
+
+ Horde::logMessage('SQL Query by Horde_Auth_Cyrsql::updateUser(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $res = $this->_db->query($query, $values);
+ if ($res instanceof PEAR_Error) {
+ throw new Horde_Exception($res);
+ }
+ }
+
+ /**
+ * Attempts to open connections to the SQL and IMAP servers.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _connect()
+ {
+ if ($this->_connected) {
+ return;
+ }
+
+ parent::_connect();
+
+ if (!isset($this->_params['hidden_accounts'])) {
+ $this->_params['hidden_accounts'] = array('cyrus');
+ }
+
+ // Reset the $_connected flag; we haven't yet successfully
+ // opened everything.
+ $this->_connected = false;
+
+ $imap_config = array(
+ 'hostspec' => empty($this->_params['hostspec']) ? null : $this->_params['hostspec'],
+ 'password' => $this->_params['cyrpass'],
+ 'port' => empty($this->_params['port']) ? null : $this->_params['port'],
+ 'secure' => ($this->_params['secure'] == 'none') ? null : $this->_params['secure'],
+ 'username' => $this->_params['cyradmin']
+ );
+
+ try {
+ $this->_ob = Horde_Imap_Client::getInstance('Socket', $imap_config);
+ $this->_ob->login();
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+
+ $this->_connected = true;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Cyrus class provides Horde with the ability of
+ * administrating a Cyrus mail server authentications against another backend
+ * that Horde can update (eg SQL or LDAP).
+ *
+ * Required parameters:
+ * <pre>
+ * 'backend' The complete hash for the Auth_* driver that cyrus
+ * authenticates against (eg SQL, LDAP).
+ * 'cyradmin' The username of the cyrus administrator
+ * 'cyrpass' The password for the cyrus administrator
+ * 'hostspec' The hostname or IP address of the server.
+ * DEFAULT: 'localhost'
+ * 'port' The server port to which we will connect.
+ * IMAP is generally 143, while IMAP-SSL is generally 993.
+ * DEFAULT: Encryption port default
+ * 'secure' The encryption to use. Either 'none', 'ssl', or 'tls'.
+ * DEFAULT: 'none'
+ * </pre>
+ *
+ * Optional values:
+ * <pre>
+ * 'folders' An array of folders to create under username.
+ * Doesn't create subfolders by default.
+ * 'quota' The quota (in kilobytes) to grant on the mailbox.
+ * Does not establish quota by default.
+ * 'separator' Hierarchy separator to use (e.g., is it user/mailbox or
+ * user.mailbox)
+ * 'unixhier' The value of imapd.conf's unixhierarchysep setting.
+ * Set this to 'true' if the value is true in imapd.conf
+ * </pre>
+ *
+ * Example Usage:
+ * <pre>
+ * $conf['auth']['driver'] = 'composite';
+ * $conf['auth']['params']['loginscreen_switch'] = '_horde_select_loginscreen';
+ * $conf['auth']['params']['admin_driver'] = 'cyrus';
+ * $conf['auth']['params']['drivers']['imp'] = array(
+ * 'driver' => 'application',
+ * 'params' => array('app' => 'imp')
+ * );
+ * $conf['auth']['params']['drivers']['cyrus'] = array(
+ * 'driver' => 'cyrus',
+ * 'params' => array(
+ * 'cyradmin' => 'cyrus',
+ * 'cyrpass' => 'password',
+ * 'hostspec' => 'imap.example.com',
+ * 'secure' => 'none'
+ * 'separator' => '.'
+ * )
+ * );
+ * $conf['auth']['params']['drivers']['cyrus']['params']['backend'] = array(
+ * 'driver' => 'sql',
+ * 'params' => array(
+ * 'phptype' => 'mysql',
+ * 'hostspec' => 'database.example.com',
+ * 'protocol' => 'tcp',
+ * 'username' => 'username',
+ * 'password' => 'password',
+ * 'database' => 'mail',
+ * 'table' => 'accountuser',
+ * 'encryption' => 'md5-hex',
+ * 'username_field' => 'username',
+ * 'password_field' => 'password'
+ * )
+ * );
+ *
+ * if (!function_exists('_horde_select_loginscreen')) {
+ * function _horde_select_loginscreen()
+ * {
+ * return 'imp';
+ * }
+ * }
+ * </pre>
+ *
+ * Copyright 2002-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 Ilya Krel <mail@krel.org>
+ * @author Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Cyrus extends Horde_Auth_Driver
+{
+ /**
+ * Horde_Imap_Client object.
+ *
+ * @var Horde_Imap_Client_Base
+ */
+ protected $_ob;
+
+ /**
+ * Pointer to another backend that Cyrus authenticates against.
+ *
+ * @var Horde_Auth_Driver
+ */
+ protected $_backend;
+
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'add' => true,
+ 'remove' => true,
+ 'update' => true
+ );
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ public function __construct($params = array())
+ {
+ parent::__construct($params);
+
+ if (!isset($this->_params['separator'])) {
+ $this->_params['separator'] = '.';
+ }
+
+ if (isset($this->_params['unixhier']) &&
+ $this->_params['unixhier'] == true) {
+ $this->_params['separator'] = '/';
+ }
+
+ // Create backend instance.
+ $this->_backend = Horde_Auth::singleton($this->_params['backend']['driver'], $this->_params['backend']['params']);
+
+ // Check the capabilities of the backend.
+ if (!$this->_backend->hasCapability('add') ||
+ !$this->_backend->hasCapability('update') ||
+ !$this->_backend->hasCapability('remove')) {
+ Horde::fatal('Horde_Auth_Cyrus: Backend does not have required capabilites.', __FILE__, __LINE__);
+ }
+
+ $this->_capabilities['list'] = $this->_backend->hasCapability('list');
+ $this->_capabilities['groups'] = $this->_backend->hasCapability('groups');
+ $this->_capabilities['transparent'] = $this->_backend->hasCapability('transparent');
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to add.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ $this->_connect();
+
+ $this->_backend->addUser($userId, $credentials);
+
+ $mailbox = Horde_String::convertCharset('user' . $this->_params['separator'] . $userId, NLS::getCharset(), 'utf7-imap');
+
+ try {
+ $this->_ob->createMailbox($mailbox);
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+
+ if (isset($this->_params['folders']) &&
+ is_array($this->_params['folders'])) {
+ foreach ($this->_params['folders'] as $folder) {
+ try {
+ $this->_ob->createMailbox($mailbox . Horde_String::convertCharset($this->_params['separator'] . $folder, NLS::getCharset(), 'utf7-imap'));
+ } catch (Horde_Imap_Client_Exception $e) {}
+ }
+ }
+
+ if (isset($this->_params['quota']) &&
+ ($this->_params['quota'] >= 0)) {
+ try {
+ $this->_ob->setQuota($mailbox, array('storage' => $this->_params['quota']));
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+ }
+ }
+
+ /**
+ * Delete a set of authentication credentials.
+ *
+ * @param string $userId The userId to delete.
+ *
+ * @throws Horde_Exception
+ */
+ public function removeUser($userId)
+ {
+ $this->_connect();
+
+ $this->_backend->removeUser($userId);
+
+ $mailbox = Horde_String::convertCharset('user' . $this->_params['separator'] . $userId, NLS::getCharset(), 'utf7-imap');
+
+ /* Set ACL for mailbox deletion. */
+ list($admin) = explode('@', $this->_params['cyradmin']);
+
+ try {
+ $this->_ob->setACL($mailbox, $admin, array('rights' => 'lrswipcda'));
+ $this->_ob->deleteMailbox($mailbox);
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+
+ Horde_Auth::removeUserData($userId);
+ }
+
+ /**
+ * Attempts to open connections to the IMAP servers.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _connect()
+ {
+ if ($this->_ob) {
+ return;
+ }
+
+ $imap_config = array(
+ 'hostspec' => empty($this->_params['hostspec']) ? null : $this->_params['hostspec'],
+ 'password' => $pass,
+ 'port' => empty($this->_params['port']) ? null : $this->_params['port'],
+ 'secure' => ($this->_params['secure'] == 'none') ? null : $this->_params['secure'],
+ 'username' => $user
+ );
+
+ try {
+ $this->_ob = Horde_Imap_Client::getInstance('Socket', $imap_config);
+ $this->_ob->login();
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+ }
+
+ /**
+ * List all users in the system.
+ *
+ * @return array The array of userIds.
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ return $this->_backend->listUsers();
+ }
+
+ /**
+ * Update a set of authentication credentials.
+ *
+ * @param string $oldID The old userId.
+ * @param string $newID The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ public function updateUser($oldID, $newID, $credentials)
+ {
+ $this->_backend->updateUser($oldID, $newID, $credentials);
+ }
+
+ /**
+ * Return the URI of the login screen for this authentication method.
+ *
+ * @param string $app The application to use.
+ * @param string $url The URL to redirect to after login.
+ *
+ * @return string The login screen URI.
+ */
+ public function getLoginScreen($app = 'horde', $url = '')
+ {
+ return $this->_backend->getLoginScreen($app, $url);
+ }
+
+ /**
+ * Checks if a userId exists in the system.
+ *
+ * @return boolean Whether or not the userId already exists.
+ */
+ public function exists($userId)
+ {
+ return $this->_backend->exists($userId);
+ }
+
+ /**
+ * Automatic authentication: Find out if the client matches an allowed IP
+ * block.
+ *
+ * @return boolean Whether or not the client is allowed.
+ */
+ protected function _transparent()
+ {
+ return $this->_backend->transparent();
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Driver:: class provides a common abstracted interface to
+ * creating various authentication backends.
+ *
+ * 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>
+ * @author Michael Slusarz <slusarz@curecanti.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Driver
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'add' => false,
+ 'groups' => false,
+ 'list' => false,
+ 'resetpassword' => false,
+ 'remove' => false,
+ 'transparent' => false,
+ 'update' => false
+ );
+
+ /**
+ * Hash containing parameters needed for the drivers.
+ *
+ * @var array
+ */
+ protected $_params = array();
+
+ /**
+ * The credentials currently being authenticated.
+ *
+ * @var array
+ */
+ protected $_authCredentials = array();
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing parameters.
+ */
+ public function __construct($params = array())
+ {
+ $this->_params = $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 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.
+ * @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)
+ {
+ $auth = false;
+ $userId = trim($userId);
+
+ if (!empty($GLOBALS['conf']['hooks']['preauthenticate'])) {
+ if (!Horde::callHook('_horde_hook_preauthenticate', array($userId, $credentials, $realm), 'horde', false)) {
+ if (Horde_Auth::getAuthError() != Horde_Auth::REASON_MESSAGE) {
+ Horde_Auth::setAuthError(Horde_Auth::REASON_FAILED);
+ }
+ return false;
+ }
+ }
+
+ /* Store the credentials being checked so that subclasses can modify
+ * them if necessary (like transparent auth does). */
+ $this->_authCredentials = array(
+ 'changeRequested' => false,
+ 'credentials' => $credentials,
+ 'realm' => $realm,
+ 'userId' => $userId
+ );
+
+ try {
+ $this->_authenticate($userId, $credentials);
+
+ if ($login) {
+ $auth = Horde_Auth::setAuth(
+ $this->_authCredentials['userId'],
+ $this->_authCredentials['credentials'],
+ $this->_authCredentials['realm'],
+ $this->_authCredentials['changeRequested']
+ );
+ } else {
+ if (!Horde_Auth::checkSessionIP()) {
+ Horde_Auth::setAuthError(self::REASON_SESSIONIP);
+ } elseif (!Horde_Auth::checkBrowserString()) {
+ Horde_Auth::setAuthError(self::REASON_BROWSER);
+ } else {
+ $auth = true;
+ }
+ }
+ } catch (Horde_Exception $e) {
+ Horde_Auth::setAuthError($e->getCode() || Horde_Auth::REASON_MESSAGE, $e->getMessage());
+ }
+
+ return $auth;
+ }
+
+ /**
+ * Authentication stub.
+ *
+ * Horde_Exception should pass a message string (if any) in the message
+ * field, and the REASON_* constant in the code field (defaults to
+ * REASON_MESSAGE).
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate()
+ {
+ }
+
+ /**
+ * Adds a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to use.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ throw new Horde_Exception('unsupported');
+ }
+
+ /**
+ * Updates a set of authentication credentials.
+ *
+ * @param string $oldID The old userId.
+ * @param string $newID The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ public function updateUser($oldID, $newID, $credentials)
+ {
+ throw new Horde_Exception('unsupported');
+ }
+
+ /**
+ * Deletes a set of authentication credentials.
+ *
+ * @param string $userId The userId to delete.
+ *
+ * @throws Horde_Exception
+ */
+ public function removeUser($userId)
+ {
+ throw new Horde_Exception('unsupported');
+ }
+
+ /**
+ * Lists all users in the system.
+ *
+ * @return mixed The array of userIds.
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ throw new Horde_Exception('unsupported');
+ }
+
+ /**
+ * Checks if $userId exists in the system.
+ *
+ * @param string $userId User ID for which to check
+ *
+ * @return boolean Whether or not $userId already exists.
+ */
+ public function exists($userId)
+ {
+ try {
+ $users = $this->listUsers();
+ return in_array($userId, $users);
+ } catch (Horde_Exception $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Automatic authentication: Finds out if the client matches an allowed IP
+ * block.
+ *
+ * @return boolean Whether or not the client is allowed.
+ */
+ public function transparent()
+ {
+ try {
+ return $this->_transparent();
+ } catch (Horde_Exception $e) {
+ Horde_Auth::setAuthError($e->getCode() || Horde_Auth::REASON_MESSAGE, $e->getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Transparent authentication stub.
+ *
+ * If the auth error message is desired to be set, Horde_Exception should
+ * thrown instead of returning false.
+ * The Horde_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).
+ *
+ * @return boolean Whether transparent login is supported.
+ * @throws Horde_Exception
+ */
+ protected function _transparent()
+ {
+ return false;
+ }
+
+ /**
+ * Reset a user's password. Used for example when the user does not
+ * remember the existing password.
+ *
+ * @param string $userId The user id for which to reset the password.
+ *
+ * @return string The new password on success.
+ * @throws Horde_Exception
+ */
+ public function resetPassword($userId)
+ {
+ throw new Horde_Exception('unsupported');
+ }
+
+ /**
+ * Queries the current driver 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)
+ {
+ return !empty($this->_capabilities[$capability]);
+ }
+
+ /**
+ * Returns the URI of the login screen for the current authentication
+ * method.
+ *
+ * @param string $app The application to use.
+ * @param string $url The URL to redirect to after login.
+ *
+ * @return string The login screen URI.
+ */
+ public function getLoginScreen($app = 'horde', $url = '')
+ {
+ $login = Horde::url($GLOBALS['registry']->get('webroot', $app) . '/login.php', true);
+ if (!empty($url)) {
+ $login = Horde_Util::addParameter($login, 'url', $url);
+ }
+ return $login;
+ }
+
+ /**
+ * Returns the named parameter for the current auth driver.
+ *
+ * @param string $param The parameter to fetch.
+ *
+ * @return string The parameter's value, or null if it doesn't exist.
+ */
+ public function getParam($param)
+ {
+ return isset($this->_params[$param])
+ ? $this->_params[$param]
+ : null;
+ }
+
+ /**
+ * Driver-level admin check stub.
+ *
+ * @todo
+ *
+ * @return boolean False.
+ */
+ public function isAdmin($permission = null, $permlevel = null, $user = null)
+ {
+ return false;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Ftp class provides an FTP implementation of the Horde
+ * authentication system.
+ *
+ * Optional parameters:
+ * <pre>
+ * 'hostspec' - (string) The hostname or IP address of the FTP server.
+ * DEFAULT: 'localhost'
+ * 'port' - (integer) The server port to connect to.
+ * DEFAULT: 21
+ * </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>
+ * @author Max Kalika <max@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Ftp extends Horde_Auth_Driver
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ *
+ * @throws Horde_Exception
+ */
+ public function __construct($params = array())
+ {
+ if (!Horde_Util::extensionExists('ftp')) {
+ throw new Horde_Exception('Horde_Auth_Ftp: Required FTP extension not found. Compile PHP with the --enable-ftp switch.');
+ return false;
+ }
+
+ $params = array_merge(array(
+ 'hostspec' => 'localhost',
+ 'port' => 21
+ ), $params);
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials. For FTP,
+ * this must contain a password entry.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ $ftp = @ftp_connect($this->_params['hostspec'], $this->_params['port']);
+
+ $res = $ftp && @ftp_login($ftp, $userId, $credentials['password']);
+ @ftp_quit($ftp);
+
+ if ($res) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Http class transparently logs users in to Horde using
+ * already present HTTP authentication headers.
+ *
+ * The 'encryption' parameter specifies what kind of passwords are in
+ * the .htpasswd file. The supported options are 'crypt-des' (standard
+ * crypted htpasswd entries) and 'aprmd5'. This information is used if
+ * you want to directly authenticate users with this driver, instead
+ * of relying on transparent auth.
+ *
+ * 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
+ */
+class Horde_Auth_Http extends Horde_Auth_Driver
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'transparent' => true
+ );
+
+ /**
+ * Array of usernames and hashed passwords.
+ *
+ * @var array
+ */
+ protected $_users = array();
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing parameters.
+ */
+ public function __construct($params = array())
+ {
+ parent::__construct($params);
+
+ // Default to DES passwords.
+ if (empty($this->_params['encryption'])) {
+ $this->_params['encryption'] = 'crypt-des';
+ }
+
+ if (!empty($this->_params['htpasswd_file'])) {
+ $users = file($this->_params['htpasswd_file']);
+ if (is_array($users)) {
+ // Enable the list users capability.
+ $this->_capabilities['list'] = true;
+
+ // Put users into alphabetical order.
+ sort($users);
+
+ foreach ($users as $line) {
+ list($user, $pass) = explode(':', $line, 2);
+ $this->_users[trim($user)] = trim($pass);
+ }
+ }
+ }
+ }
+
+ /**
+ * Find out if a set of login credentials are valid. Only supports
+ * htpasswd files with DES passwords right now.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials. For IMAP,
+ * this must contain a password entry.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ if (empty($credentials['password']) ||
+ empty($this->_users[$userId])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ $hash = Horde_Auth::getCryptedPassword($credentials['password'], $this->_users[$userId], $this->_params['encryption'], !empty($this->_params['show_encryption']));
+
+ if ($hash != $this->_users[$userId]) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+ }
+
+ /**
+ * Return the URI of the login screen for this authentication object.
+ *
+ * @param string $app The application to use.
+ * @param string $url The URL to redirect to after login.
+ *
+ * @return string The login screen URI.
+ */
+ public function getLoginScreen($app = 'horde', $url = '')
+ {
+ if (!empty($this->_params['loginScreen'])) {
+ return $url
+ ? Horde_Util::addParameter($this->_params['loginScreen'], 'url', $url)
+ : $this->_params['loginScreen'];
+ }
+
+ return parent::getLoginScreen($app, $url);
+ }
+
+ /**
+ * List all users in the system.
+ *
+ * @return array The array of userIds.
+ */
+ public function listUsers()
+ {
+ return array_keys($this->_users);
+ }
+
+ /**
+ * Automatic authentication: Find out if the client has HTTP
+ * authentication info present.
+ *
+ * @return boolean Whether or not the client is allowed.
+ * @throws Horde_Exception
+ */
+ protected function _transparent()
+ {
+ if (!empty($_SERVER['PHP_AUTH_USER']) &&
+ !empty($_SERVER['PHP_AUTH_PW'])) {
+ return Horde_Auth::setAuth(Horde_Util::dispelMagicQuotes($_SERVER['PHP_AUTH_USER']), array('password' => Horde_Util::dispelMagicQuotes($_SERVER['PHP_AUTH_PW']), 'transparent' => 1));
+ }
+
+ throw new Horde_Exception(_("HTTP Authentication not found."));
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Auth_Http_Remote class authenticates users against a remote
+ * HTTP-Auth endpoint.
+ *
+ * Copyright 2007-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 Duck <duck@obala.net>
+ * @package Horde_Auth
+ */
+class Horde_Auth_HttpRemote extends Horde_Auth_Driver
+{
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ $options = array(
+ 'allowRedirects' => true,
+ 'method' => 'GET',
+ 'timeout' => 5
+ );
+
+ if (!empty($GLOBALS['conf']['http']['proxy']['proxy_host'])) {
+ $options = array_merge($options, $GLOBALS['conf']['http']['proxy']);
+ }
+
+ $request = new HTTP_Request($this->_params['url'], $options);
+ $request->setBasicAuth($userId, $credentials['password']);
+
+ $request->sendRequest();
+
+ if ($request->getResponseCode() != 200) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Imap:: class provides an IMAP implementation of the Horde
+ * authentication system.
+ *
+ * Optional parameters:
+ * <pre>
+ * 'admin_password' The password of the adminstrator.
+ * DEFAULT: null
+ * 'admin_user' The name of a user with admin privileges.
+ * DEFAULT: null
+ * 'hostspec' The hostname or IP address of the server.
+ * DEFAULT: 'localhost'
+ * 'port' The server port to which we will connect.
+ * IMAP is generally 143, while IMAP-SSL is generally 993.
+ * DEFAULT: Encryption port default
+ * 'secure' The encryption to use. Either 'none', 'ssl', or 'tls'.
+ * DEFAULT: 'none'
+ * 'userhierarchy' The hierarchy where user mailboxes are stored.
+ * DEFAULT: 'user.'
+ * </pre>
+ *
+ * If setting up as Horde auth handler in conf.php, this is a sample entry:
+ * <pre>
+ * $conf['auth']['params']['hostspec'] = 'imap.example.com';
+ * $conf['auth']['params']['port'] = 143;
+ * $conf['auth']['params']['secure'] = 'none';
+ * </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>
+ * @author Gaudenz Steinlin <gaudenz@soziologie.ch>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Imap extends Horde_Auth_Driver
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ public function __construct($params = array())
+ {
+ $params = array_merge(array(
+ 'admin_password' => null,
+ 'admin_user' => null,
+ 'hostspec' => '',
+ 'port' => '',
+ 'secure' => 'none',
+ 'userhierarchy' => 'user.'
+ ), $params);
+
+ parent::__construct(array_merge($default_params, $params));
+
+ if (!empty($this->_params['admin_user'])) {
+ $this->_capabilities['add'] = true;
+ $this->_capabilities['remove'] = true;
+ $this->_capabilities['list'] = true;
+ }
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials. For IMAP,
+ * this must contain a password entry.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ try {
+ $ob->_getOb($userId, $credentials['password']);
+ $ob->login();
+ $ob->logout();
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to use.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ try {
+ $ob->_getOb($this->_params['admin_user'], $this->_params['admin_password']);
+ $mailbox = Horde_String::convertCharset($this->_params['userhierarchy'] . $userId, NLS::getCharset(), 'utf7-imap');
+ $ob->createMailbox($mailbox);
+ $ob->setACL($mailbox, $this->_params['admin_user'], 'lrswipcda');
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+ }
+
+ /**
+ * Delete a set of authentication credentials.
+ *
+ * @param string $userId The userId to delete.
+ *
+ * @throws Horde_Exception
+ */
+ public function removeUser($userId)
+ {
+ try {
+ $ob->_getOb($this->_params['admin_user'], $this->_params['admin_password']);
+ $ob->setACL($mailbox, $this->_params['admin_user'], 'lrswipcda');
+ $ob->deleteMailbox(Horde_String::convertCharset($this->_params['userhierarchy'] . $userId, NLS::getCharset(), 'utf7-imap'));
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+
+ Horde_Auth::removeUserData($userId);
+ }
+
+ /**
+ * List all users in the system.
+ *
+ * @return array The array of userIds.
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ try {
+ $ob->_getOb($this->_params['admin_user'], $this->_params['admin_password']);
+ $list = $ob->listMailboxes($this->_params['userhierarchy'] . '%', Horde_Imap_Client::MBOX_ALL, array('flat' => true));
+ } catch (Horde_Imap_Client_Exception $e) {
+ throw new Horde_Exception($e);
+ }
+
+ return empty($list)
+ ? array()
+ : preg_replace('/.*' . preg_quote($this->_params['userhierarchy'], '/') . '(.*)/', '\\1', $list);
+ }
+
+ /**
+ * Get Horde_Imap_Client object.
+ *
+ * @return Horde_Imap_Client_Base IMAP client object.
+ */
+ protected function _getOb($user, $pass)
+ {
+ $imap_config = array(
+ 'hostspec' => empty($this->_params['hostspec']) ? null : $this->_params['hostspec'],
+ 'password' => $pass,
+ 'port' => empty($this->_params['port']) ? null : $this->_params['port'],
+ 'secure' => ($this->_params['secure'] == 'none') ? null : $this->_params['secure'],
+ 'username' => $user
+ );
+
+ return Horde_Imap_Client::getInstance('Socket', $imap_config);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Imsp class provides basic authentication against an IMSP
+ * server.
+ * This will be most benificial if already using an IMSP based preference
+ * system or IMSP based addressbook system
+ *
+ * Copyright 2004-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 Michael Rubinsky <mrubinsk@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_imsp extends Horde_Auth_Driver
+{
+ /**
+ * Private authentication function.
+ *
+ * @param string $userID Username for IMSP server.
+ * @param array $credentials Hash containing 'password' element.
+ *
+ * @return boolean True on success / False on failure.
+ */
+ protected function _authenticate($userID, $credentials)
+ {
+ $this->_params['username'] = $userID;
+ $this->_params['password'] = $credentials['password'];
+
+ $imsp = Net_IMSP_Auth::singleton($this->_params['auth_method']);
+ if ($imsp instanceof PEAR_Error) {
+ throw new Horde_Exception($imsp->getMessage());
+ }
+
+ $result = $imsp->authenticate($this->_params, false);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Ipbasic class provides access control based on CIDR masks
+ * (client IP addresses). It is not meant for user-based systems, but
+ * for times when you want a block of IPs to be able to access a site,
+ * and that access is simply on/off - no preferences, etc.
+ *
+ * Optional Parameters:
+ * <pre>
+ * 'blocks' - (array) CIDR masks which are allowed access.
+ * </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
+ */
+class Horde_Auth_Ipbasic extends Horde_Auth_Driver
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'transparent' => true
+ );
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing parameters.
+ */
+ public function __construct($params = array())
+ {
+ if (empty($params['blocks'])) {
+ $params['blocks'] = array();
+ } elseif (!is_array($params['blocks'])) {
+ $params['blocks'] = array($params['blocks']);
+ }
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Automatic authentication: Find out if the client matches an allowed IP
+ * block.
+ *
+ * @return boolean Whether or not the client is allowed.
+ * @throws Horde_Exception
+ */
+ protected function _transparent()
+ {
+ if (!isset($_SERVER['REMOTE_ADDR'])) {
+ throw new Horde_Exception(_("IP Address not avaialble."));
+ }
+
+ $client = $_SERVER['REMOTE_ADDR'];
+ foreach ($this->_params['blocks'] as $cidr) {
+ if ($this->_addressWithinCIDR($client, $cidr)) {
+ return Horde_Auth::setAuth($cidr, array('transparent' => 1));
+ }
+ }
+
+ throw new Horde_Exception(_("IP Address not within allowed CIDR block."));
+ }
+
+ /**
+ * Determine if an IP address is within a CIDR block.
+ *
+ * @param string $address The IP address to check.
+ * @param string $cidr The block (e.g. 192.168.0.0/16) to test against.
+ *
+ * @return boolean Whether or not the address matches the mask.
+ */
+ protected function _addressWithinCIDR($address, $cidr)
+ {
+ $address = ip2long($address);
+ list($quad, $bits) = explode('/', $cidr);
+ $bits = intval($bits);
+ $quad = ip2long($quad);
+
+ return (($address >> (32 - $bits)) == ($quad >> (32 - $bits)));
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Kolab implementation of the Horde authentication system.
+ * Derives from the Horde_Auth_Imap authentication object, and provides
+ * parameters to it based on the global Kolab configuration.
+ *
+ * Copyright 2004-2007 Stuart Binge <s.binge@codefusion.co.za>
+ * Copyright 2008-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 Stuart Binge <s.binge@codefusion.co.za>
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Kolab extends Horde_Auth_Driver
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $capabilities = array(
+ 'add' => true,
+ 'list' => true
+ );
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * For Kolab this requires to identify the IMAP server the user should
+ * be authenticated against before the credentials can be checked using
+ * this server. The Kolab_Server module handles identification of the
+ * correct IMAP server.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials. For Kolab,
+ * this must contain a "password" entry.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ global $conf;
+
+ $params = array();
+
+ if (class_exists('Horde_Kolab_Session')) {
+ try {
+ $session = Horde_Kolab_Session::singleton($userId, $credentials, true);
+ } catch (Horde_Kolab_Server_MissingObjectException $e) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ } catch (Exception $e) {
+ Horde::logMessage($e, __FILE__, __LINE__, PEAR_LOG_ERR);
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+ } else {
+ throw new Horde_Exception('The class Horde_Kolab_Session is required for the Kolab auth driver but it is missing!', Horde_Auth::REASON_MESSAGE);
+ }
+
+ if (!isset($conf['auth']['params']) ||
+ $conf['auth']['params']['login_block'] != 1) {
+ // Return if feature is disabled.
+ return $session->auth;
+ }
+
+ if ($session->auth !== true &&
+ class_exists('Horde_History')) {
+ $history = Horde_History::singleton();
+
+ $history_identifier = "$userId@logins.kolab";
+ $history_log = $history->getHistory($history_identifier);
+ $history_list = array();
+
+ // Extract history list from log.
+ if ($history_log && !($history_log instanceof PEAR_Error)) {
+ $data = $history_log->getData();
+ if (!empty($data)) {
+ $entry = array_shift($data);
+ $history_list = $entry['history_list'];
+ }
+ }
+
+ // Calculate the time range.
+ $start_time = (time() - $conf['auth']['params']['login_block_time'] * 60);
+
+ $new_history_list = array();
+ $count = 0;
+
+ // Copy and count all relevant timestamps.
+ foreach ($history_list as $entry) {
+ $timestamp = $entry[ 'timestamp' ];
+
+ if ($timestamp > $start_time) {
+ $new_history_list[] = $entry;
+ $count++;
+ }
+ }
+
+ $max_count = $conf['auth']['params']['login_block_count'];
+
+ if ($count > $max_count) {
+ // Add entry for current failed login.
+ $entry = array();
+ $entry[ 'timestamp' ] = time();
+ $new_history_list[] = $entry;
+
+ // Write back history.
+ $history->log($history_identifier,
+ array('action' => 'add', 'who' => $userId,
+ 'history_list' => $new_history_list), true);
+
+ if ($count > $max_count) {
+ throw new Horde_Exception(_("Too many invalid logins during the last minutes."));
+ }
+
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+ }
+
+ return ($session->auth === true);
+ }
+
+ /**
+ * Sets a variable in the session saying that authorization has succeeded,
+ * note which userId was authorized, and note when the login took place.
+ *
+ * The kolab driver rewrites UIDs into the correct mail addresses that
+ * need to be used to log into the IMAP server.
+ *
+ * @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
+ * their password.
+ */
+ function setAuth($userId, $credentials, $realm = null, $changeRequested = false)
+ {
+ // TODO - setAuth doesn't exist in Horde_Auth_Driver
+ // This should probably use _username_hook_frombackend.
+
+ if (class_exists('Horde_Kolab_Session')) {
+ $session = Horde_Kolab_Session::singleton($userId);
+ $userId = $session->user_mail;
+ }
+
+ return parent::setAuth($userId, $credentials, $realm, $changeRequested);
+ }
+
+ /**
+ * List Users
+ *
+ * @return array List of Users
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ if (!class_exists('Horde_Kolab_Session')) {
+ throw new Horde_Exception('The Horde_Kolab_Session class is not available.');
+ }
+
+ $session = Horde_Kolab_Session::singleton();
+ $server = $session->getServer();
+ if ($server instanceof PEAR_Error) {
+ return $server;
+ }
+ $users = $server->listObjects(KOLAB_OBJECT_USER);
+ $mails = array();
+ foreach ($users as $user) {
+ $mails[] = $user->get(KOLAB_ATTR_MAIL);
+ }
+ return $mails;
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to be set.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ if (!class_exists('Horde_Kolab_Session')) {
+ throw new Horde_Exception('The Horde_Kolab_Session class is not available.');
+ }
+
+ $session = Horde_Kolab_Session::singleton();
+ $server = $session->getServer();
+ if ($server instanceof PEAR_Error) {
+ return $server;
+ }
+
+ $result = $server->store(KOLAB_OBJECT_USER, $userId, $credentials);
+
+ if (is_a($result, KOLAB_OBJECT_USER)) {
+ return true;
+ } else if ($result instanceof PEAR_Error) {
+ return $result;
+ } else {
+ throw new Horde_Exception(sprintf('The new Kolab object is a %s rather than a ' . KOLAB_OBJECT_USER, get_class($result)));
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Krb5 class provides an kerberos implementation of the Horde
+ * authentication system.
+ *
+ * This driver requires the 'krb5' PHP extension to be loaded.
+ * The module can be downloaded here:
+ * http://www.horde.org/download/php/phpkrb5.tar.gz
+ *
+ * Kerberos must be correctly configured on your system (e.g. /etc/krb5.conf)
+ * for this class to work correctly.
+ *
+ * Copyright 2002-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 Michael Slusarz <slusarz@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Krb5 extends Horde_Auth_Driver
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params Not used.
+ *
+ * @throws Horde_Exception
+ */
+ public function __construct($params = array())
+ {
+ if (!Horde_Util::extensionExists('krb5')) {
+ throw new Horde_Exception(_("Horde_Auth_Krb5: Required krb5 extension not found."));
+ }
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials.
+ * For kerberos, this must contain a password
+ * entry.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ if (empty($credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ $result = krb5_login($userId, $credentials['password']);
+
+ switch ($result) {
+ case KRB5_OK:
+ return;
+
+ case KRB5_BAD_PASSWORD:
+ throw new Horde_Exception(_("Bad kerberos password."));
+
+ case KRB5_BAD_USER:
+ throw new Horde_Exception(_("Bad kerberos username."));
+
+ default:
+ throw new Horde_Exception(_("Kerberos server rejected authentication."));
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Ldap class provides an LDAP implementation of the Horde
+ * authentication system.
+ *
+ * Required parameters:
+ * <pre>
+ * 'basedn' The base DN for the LDAP server.
+ * 'hostspec' The hostname of the LDAP server.
+ * 'uid' The username search key.
+ * 'filter' The LDAP formatted search filter to search for users. This
+ * setting overrides the 'objectclass' method below.
+ * 'objectclass' The objectclass filter used to search for users. Can be a
+ * single objectclass or an array.
+ * </pre>
+ *
+ * Optional parameters:
+ * <pre>
+ * 'binddn' The DN used to bind to the LDAP server
+ * 'password' The password used to bind to the LDAP server
+ * 'version' The version of the LDAP protocol to use.
+ * DEFAULT: NONE (system default will be used)
+ * </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 Jon Parise <jon@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Ldap extends Horde_Auth_Driver
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'add' => true,
+ 'update' => true,
+ 'remove' => true,
+ 'list' => true
+ );
+
+ /**
+ * LDAP connection handle.
+ *
+ * @var resource
+ */
+ protected $_ds;
+
+ /**
+ * Construct.
+ *
+ * @param array $params A hash containing connection parameters.
+ *
+ * @throws Horde_Exception
+ */
+ public function __construct($params = array())
+ {
+ if (!Horde_Util::extensionExists('ldap')) {
+ throw new Horde_Exception(_("Horde_Auth_Ldap: Required LDAP extension not found."));
+ }
+
+ /* Ensure we've been provided with all of the necessary parameters. */
+ Horde::assertDriverConfig($params, 'auth',
+ array('hostspec', 'basedn', 'uid'),
+ 'authentication LDAP');
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Does an ldap connect and binds as the guest user or as the optional dn.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _connect()
+ {
+ /* Connect to the LDAP server. */
+ $this->_ds = @ldap_connect($this->_params['hostspec']);
+ if (!$this->_ds) {
+ throw new Horde_Exception(_("Failed to connect to LDAP server."));
+ }
+
+ if (isset($this->_params['version'])) {
+ if (!ldap_set_option($this->_ds, LDAP_OPT_PROTOCOL_VERSION,
+ $this->_params['version'])) {
+ Horde::logMessage(
+ sprintf('Set LDAP protocol version to %d failed: [%d] %s',
+ $this->_params['version'],
+ @ldap_errno($this->_ds),
+ @ldap_error($this->_ds)),
+ __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ }
+
+ /* Start TLS if we're using it. */
+ if (!empty($this->_params['tls'])) {
+ if (!@ldap_start_tls($this->_ds)) {
+ Horde::logMessage(
+ sprintf('STARTTLS failed: [%d] %s',
+ @ldap_errno($this->_ds),
+ @ldap_error($this->_ds)),
+ __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ }
+
+ /* Work around Active Directory quirk. */
+ if (!empty($this->_params['ad'])) {
+ if (!ldap_set_option($this->_ds, LDAP_OPT_REFERRALS, false)) {
+ Horde::logMessage(
+ sprintf('Unable to disable directory referrals on this connection to Active Directory: [%d] %s',
+ @ldap_errno($this->_ds),
+ @ldap_error($this->_ds)),
+ __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ }
+
+ $bind = isset($this->_params['binddn'])
+ ? @ldap_bind($this->_ds, $this->_params['binddn'], $this->_params['password'])
+ : @ldap_bind($this->_ds);
+
+ if (!$bind) {
+ throw new Horde_Exception(_("Could not bind to LDAP server."));
+ }
+ }
+
+ /**
+ * Find the user dn
+ *
+ * @param string $userId The userId to find.
+ *
+ * @return string The users full DN
+ * @throws Horde_Exception
+ */
+ protected function _findDN($userId)
+ {
+ /* Search for the user's full DN. */
+ $filter = $this->_getParamFilter();
+ $filter = '(&(' . $this->_params['uid'] . '=' . $userId . ')' .
+ $filter . ')';
+
+ $func = ($this->_params['scope'] == 'one')
+ ? 'ldap_list'
+ : 'ldap_search';
+
+ $search = @$func($this->_ds, $this->_params['basedn'], $filter,
+ array($this->_params['uid']));
+ if (!$search) {
+ Horde::logMessage(ldap_error($this->_ds), __FILE__, __LINE__, PEAR_LOG_ERR);
+ throw new Horde_Exception(_("Could not search the LDAP server."));
+ }
+
+ $result = @ldap_get_entries($this->_ds, $search);
+ if (is_array($result) && (count($result) > 1)) {
+ $dn = $result[0]['dn'];
+ } else {
+ throw new Horde_Exception(_("Empty result."));
+ }
+
+ return $dn;
+ }
+
+ /**
+ * Checks for shadowLastChange and shadowMin/Max support and returns their
+ * values. We will also check for pwdLastSet if Active Directory is
+ * support is requested. For this check to succeed we need to be bound
+ * to the directory.
+ *
+ * @param string $dn The dn of the user.
+ *
+ * @return array Array with keys being "shadowlastchange", "shadowmin"
+ * "shadowmax", "shadowwarning" and containing their
+ * respective values or false for no support.
+ */
+ protected function _lookupShadow($dn)
+ {
+ /* Init the return array. */
+ $lookupshadow = array(
+ 'shadowlastchange' => false,
+ 'shadowmin' => false,
+ 'shadowmax' => false,
+ 'shadowwarning' => false
+ );
+
+ /* According to LDAP standard, to read operational attributes, you
+ * must request them explicitly. Attributes involved in password
+ * expiration policy:
+ * pwdlastset: Active Directory
+ * shadow*: shadowUser schema
+ * passwordexpirationtime: Sun and Fedora Directory Server */
+ $result = @ldap_read($this->_ds, $dn, '(objectClass=*)',
+ array('pwdlastset', 'shadowmax', 'shadowmin',
+ 'shadowlastchange', 'shadowwarning',
+ 'passwordexpirationtime'));
+ if ($result) {
+ $information = @ldap_get_entries($this->_ds, $result);
+
+ if ($this->_params['ad']) {
+ if (isset($information[0]['pwdlastset'][0])) {
+ /* Active Directory handles timestamps a bit differently.
+ * Convert the timestamp to a UNIX timestamp. */
+ $lookupshadow['shadowlastchange'] = floor((($information[0]['pwdlastset'][0] / 10000000) - 11644406783) / 86400) - 1;
+
+ /* Password expiry attributes are in a policy. We cannot
+ * read them so use the Horde config. */
+ $lookupshadow['shadowwarning'] = $this->_params['warnage'];
+ $lookupshadow['shadowmin'] = $this->_params['minage'];
+ $lookupshadow['shadowmax'] = $this->_params['maxage'];
+ }
+ } elseif (isset($information[0]['passwordexpirationtime'][0])) {
+ /* Sun/Fedora Directory Server uses a special attribute
+ * passwordexpirationtime. It has precedence over shadow*
+ * because it actually locks the expired password at the LDAP
+ * server level. The correct way to check expiration should
+ * be using LDAP controls, unfortunately PHP doesn't support
+ * controls on bind() responses. */
+ $ldaptimepattern = "/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})Z/";
+ if (preg_match($ldaptimepattern, $information[0]['passwordexpirationtime'][0], $regs)) {
+ /* Sun/Fedora Directory Server return expiration time, not
+ * last change time. We emulate the behaviour taking it
+ * back to maxage. */
+ $lookupshadow['shadowlastchange'] = floor(mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]) / 86400) - $this->_params['maxage'];
+
+ /* Password expiry attributes are in not accessible policy
+ * entry. */
+ $lookupshadow['shadowwarning'] = $this->_params['warnage'];
+ $lookupshadow['shadowmin'] = $this->_params['minage'];
+ $lookupshadow['shadowmax'] = $this->_params['maxage'];
+ } else {
+ Horde::logMessage('Wrong time format: ' . $information[0]['passwordexpirationtime'][0], __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ } else {
+ if (isset($information[0]['shadowmax'][0])) {
+ $lookupshadow['shadowmax'] =
+ $information[0]['shadowmax'][0];
+ }
+ if (isset($information[0]['shadowmin'][0])) {
+ $lookupshadow['shadowmin'] =
+ $information[0]['shadowmin'][0];
+ }
+ if (isset($information[0]['shadowlastchange'][0])) {
+ $lookupshadow['shadowlastchange'] =
+ $information[0]['shadowlastchange'][0];
+ }
+ if (isset($information[0]['shadowwarning'][0])) {
+ $lookupshadow['shadowwarning'] =
+ $information[0]['shadowwarning'][0];
+ }
+ }
+ }
+
+ return $lookupshadow;
+ }
+
+ /**
+ * Find out if the given set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ /* Connect to the LDAP server. */
+ $this->_connect();
+
+ /* Search for the user's full DN. */
+ $dn = $this->_findDN($userId);
+
+ /* Attempt to bind to the LDAP server as the user. */
+ $bind = @ldap_bind($this->_ds, $dn, $credentials['password']);
+ if ($bind == false) {
+ @ldap_close($this->_ds);
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ if ($this->_params['password_expiration'] == 'yes') {
+ $shadow = $this->_lookupShadow($dn);
+ if ($shadow['shadowmax'] && $shadow['shadowlastchange'] &&
+ $shadow['shadowwarning']) {
+ $today = floor(time() / 86400);
+ $warnday = $shadow['shadowlastchange'] +
+ $shadow['shadowmax'] - $shadow['shadowwarning'];
+ $toexpire = $shadow['shadowlastchange'] +
+ $shadow['shadowmax'] - $today;
+
+ if ($today >= $warnday) {
+ $GLOBALS['notification']->push(sprintf(ngettext("%d day until your password expires.", "%d days until your password expires.", $toexpire), $toexpire), 'horde.warning');
+ }
+
+ if ($toexpire == 0) {
+ $this->_authCredentials['changeRequested'] = true;
+ } elseif ($toexpire < 0) {
+ throw new Horde_Exception('', Horde_Auth::REASON_EXPIRED);
+ }
+ }
+ }
+
+ @ldap_close($this->_ds);
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to be set.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ if ($this->_params['ad']) {
+ throw new Horde_Exception(_("Horde_Auth_Ldap: Adding users is not supported for Active Directory"));
+ }
+
+ /* Connect to the LDAP server. */
+ $this->_connect();
+
+ global $conf;
+ if (!empty($conf['hooks']['authldap'])) {
+ $entry = Horde::callHook('_horde_hook_authldap', array($userId, $credentials));
+ if ($entry instanceof PEAR_Error) {
+ throw new Horde_Exception($entry);
+ }
+ $dn = $entry['dn'];
+ /* Remove the dn entry from the array. */
+ unset($entry['dn']);
+ } else {
+ /* Try this simple default and hope it works. */
+ $dn = $this->_params['uid'] . '=' . $userId . ','
+ . $this->_params['basedn'];
+ $entry['cn'] = $userId;
+ $entry['sn'] = $userId;
+ $entry[$this->_params['uid']] = $userId;
+ $entry['objectclass'] = array_merge(
+ array('top'),
+ $this->_params['newuser_objectclass']);
+ $entry['userPassword'] = Horde_Auth::getCryptedPassword(
+ $credentials['password'], '',
+ $this->_params['encryption'],
+ 'true');
+
+ if ($this->_params['password_expiration'] == 'yes') {
+ $entry['shadowMin'] = $this->_params['minage'];
+ $entry['shadowMax'] = $this->_params['maxage'];
+ $entry['shadowWarning'] = $this->_params['warnage'];
+ $entry['shadowLastChange'] = floor(time() / 86400);
+ }
+ }
+ $result = @ldap_add($this->_ds, $dn, $entry);
+
+ if (!$result) {
+ throw new Horde_Exception(sprintf(_("Horde_Auth_Ldap: Unable to add user \"%s\". This is what the server said: "), $userId) . @ldap_error($this->_ds));
+ }
+
+ @ldap_close($this->_ds);
+ }
+
+ /**
+ * Remove a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ *
+ * @throws Horde_Exception
+ */
+ public function removeUser($userId)
+ {
+ if ($this->_params['ad']) {
+ throw new Horde_Exception(_("Horde_Auth_Ldap: Removing users is not supported for Active Directory"));
+ }
+
+ /* Connect to the LDAP server. */
+ $this->_connect();
+
+ if (!empty($GLOBALS['conf']['hooks']['authldap'])) {
+ $entry = Horde::callHook('_horde_hook_authldap', array($userId));
+ if ($entry instanceof PEAR_Error) {
+ throw new Horde_Exception($entry);
+ }
+ $dn = $entry['dn'];
+ } else {
+ /* Search for the user's full DN. */
+ $dn = $this->_findDN($userId);
+ }
+
+ $result = @ldap_delete($this->_ds, $dn);
+ if (!$result) {
+ throw new Horde_Exception(sprintf(_("Auth_ldap: Unable to remove user \"%s\""), $userId));
+ }
+
+ @ldap_close($this->_ds);
+
+ Horde_Auth::removeUserData($userId);
+ }
+
+ /**
+ * Update a set of authentication credentials.
+ *
+ * @param string $oldID The old userId.
+ * @param string $newID The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ public function updateUser($oldID, $newID, $credentials)
+ {
+ if ($this->_params['ad']) {
+ throw new Horde_Exception(_("Horde_Auth_Ldap: Updating users is not supported for Active Directory."));
+ }
+
+ /* Connect to the LDAP server. */
+ $this->_connect();
+
+ if (!empty($GLOBLS['conf']['hooks']['authldap'])) {
+ $entry = Horde::callHook('_horde_hook_authldap', array($oldID, $credentials));
+ if ($entry instanceof PEAR_Error) {
+ throw new Horde_Exception($entry);
+ }
+ $olddn = $entry['dn'];
+ $entry = Horde::callHook('_horde_hook_authldap', array($newID, $credentials));
+ $newdn = $entry['dn'];
+ unset($entry['dn']);
+ } else {
+ /* Search for the user's full DN. */
+ $dn = $this->_findDN($oldID);
+
+ $olddn = $dn;
+ $newdn = preg_replace('/uid=.*?,/', 'uid=' . $newID . ',', $dn, 1);
+ $shadow = $this->_lookupShadow($dn);
+
+ /* If shadowmin hasn't yet expired only change when we are
+ administrator */
+ if ($shadow['shadowlastchange'] &&
+ $shadow['shadowmin'] &&
+ ($shadow['shadowlastchange'] + $shadow['shadowmin'] > (time() / 86400))) {
+ throw new Horde_Exception(_("Minimum password age has not yet expired"));
+ }
+
+ /* Set the lastchange field */
+ if ($shadow['shadowlastchange']) {
+ $entry['shadowlastchange'] = floor(time() / 86400);
+ }
+
+ /* Encrypt the new password */
+ $entry['userpassword'] = Horde_Auth::getCryptedPassword(
+ $credentials['password'], '',
+ $this->_params['encryption'],
+ 'true');
+ }
+
+ if ($oldID != $newID) {
+ if (LDAP_OPT_PROTOCOL_VERSION == 3) {
+ ldap_rename($this->_ds, $olddn, $newdn,
+ $this->_params['basedn'], true);
+
+ $result = ldap_modify($this->_ds, $newdn, $entry);
+ } else {
+ /* Get the complete old record first */
+ $result = @ldap_read($this->_ds, $olddn, 'objectClass=*');
+
+ if ($result) {
+ $information = @ldap_get_entries($this->_ds, $result);
+
+ /* Remove the count elements from the array */
+ $counter = 0;
+ $newrecord = array();
+ while (isset($information[0][$counter])) {
+ if ($information[0][$information[0][$counter]]['count'] == 1) {
+ $newrecord[$information[0][$counter]] = $information[0][$information[0][$counter]][0];
+ } else {
+ $newrecord[$information[0][$counter]] = $information[0][$information[0][$counter]];
+ unset($newrecord[$information[0][$counter]]['count']);
+ }
+ $counter++;
+ }
+
+ /* Adjust the changed parameters */
+ unset($newrecord['dn']);
+ $newrecord[$this->_params['uid']] = $newID;
+ $newrecord['userpassword'] = $entry['userpassword'];
+ if (isset($entry['shadowlastchange'])) {
+ $newrecord['shadowlastchange'] = $entry['shadowlastchange'];
+ }
+
+ $result = ldap_add($this->_ds, $newdn, $newrecord);
+ if ($result) {
+ $result = @ldap_delete($this->_ds, $olddn);
+ }
+ }
+ }
+ } else {
+ $result = @ldap_modify($this->_ds, $olddn, $entry);
+ }
+
+ if (!$result) {
+ throw new Horde_Exception(sprintf(_("Horde_Auth_Ldap: Unable to update user \"%s\""), $newID));
+ }
+
+ @ldap_close($this->_ds);
+ }
+
+ /**
+ * List Users
+ *
+ * @return array List of Users
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ /* Connect to the LDAP server. */
+ $this->_connect();
+
+ $filter = $this->_getParamFilter();
+
+ $func = ($this->_params['scope'] == 'one')
+ ? 'ldap_list'
+ : 'ldap_search';
+
+ /* Add a sizelimit, if specified. Default is 0, which means no limit.
+ * Note: You cannot override a server-side limit with this. */
+ $sizelimit = isset($this->_params['sizelimit']) ? $this->_params['sizelimit'] : 0;
+ $search = @$func($this->_ds, $this->_params['basedn'], $filter,
+ array($this->_params['uid']), 0, $sizelimit);
+
+ $entries = @ldap_get_entries($this->_ds, $search);
+ $userlist = array();
+ $uid = Horde_String::lower($this->_params['uid']);
+ for ($i = 0; $i < $entries['count']; $i++) {
+ $userlist[$i] = $entries[$i][$uid][0];
+ }
+
+ return $userlist;
+ }
+
+ /**
+ * Return a formatted LDAP filter as configured within the parameters.
+ *
+ * @return string LDAP search filter
+ */
+ protected function _getParamFilter()
+ {
+ if (!empty($this->_params['filter'])) {
+ $filter = $this->_params['filter'];
+ } elseif (!is_array($this->_params['objectclass'])) {
+ $filter = 'objectclass=' . $this->_params['objectclass'];
+ } else {
+ $filter = '';
+ if (count($this->_params['objectclass']) > 1) {
+ $filter = '(&' . $filter;
+ foreach ($this->_params['objectclass'] as $objectclass) {
+ $filter .= '(objectclass=' . $objectclass . ')';
+ }
+ $filter .= ')';
+ } elseif (count($this->_params['objectclass']) == 1) {
+ $filter = '(objectClass=' . $this->_params['objectclass'][0] . ')';
+ }
+ }
+ return $filter;
+ }
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_login:: class provides a system login implementation of
+ * the Horde authentication system.
+ * This Auth driver is useful if you have a shadow password system
+ * where the Horde_Auth_Passwd driver doesn't work.
+ *
+ * Optional parameters:
+ * <pre>
+ * 'location' - (string) Location of the su binary.
+ * DEFAULT: /bin/su
+ * </pre>
+ *
+ * Copyright 2004-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 Jan Schneider <jan@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Login extends Horde_Auth_Driver
+{
+ /**
+ * List of users that should be excluded from being listed/handled
+ * in any way by this driver.
+ *
+ * @var array
+ */
+ protected $_exclude = array(
+ 'root', 'daemon', 'bin', 'sys', 'sync', 'games', 'man', 'lp', 'mail',
+ 'news', 'uucp', 'proxy', 'postgres', 'www-data', 'backup', 'operator',
+ 'list', 'irc', 'gnats', 'nobody', 'identd', 'sshd', 'gdm', 'postfix',
+ 'mysql', 'cyrus', 'ftp'
+ );
+
+ /**
+ * Constructs a new Login authentication object.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ public function __construct($params = array())
+ {
+ if (empty($params['location'])) {
+ $params['location'] = '/bin/su';
+ }
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials.
+ *
+ * @return boolean Whether or not the credentials are valid.
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ if (empty($credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ $proc = @popen($this->_location . ' -c /bin/true ' . $userId, 'w');
+ if (!is_resource($proc)) {
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ fwrite($proc, $credentials['password']);
+ if (@pclose($proc) !== 0) {
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Msad class provides an experimental MSAD extension of the
+ * LDAP implementation of the Horde authentication system.
+ *
+ * Required parameters: See Horde_Auth_Ldap.
+ *
+ * Optional parameters: See Horde_Auth_Ldap.
+ *
+ * Copyright 2007-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 Francois Helly <fhelly@bebop-design.net>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Msad extends Horde_Auth_Ldap
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ *
+ * @throws Horde_Exception
+ */
+ public function __construct($params = array())
+ {
+ if (!Horde_Util::extensionExists('ldap')) {
+ throw new Horde_Exception(_("Horde_Auth_Ldap: Required LDAP extension not found."));
+ }
+
+ $params = array_merge(array(
+ 'adduser' => true,
+ 'authId' => 'initials',
+ 'encryption' => 'msad',
+ 'newuser_objectclass' => 'user',
+ 'password_expiration' => 'no',
+ 'port' => 389,
+ 'ssl' => false,
+ 'uid' => array('samaccountname')
+ ), $params);
+
+ if (!is_array($params['uid'])) {
+ $params['uid'] = array($params['uid']);
+ }
+
+ /* Ensure we've been provided with all of the necessary parameters. */
+ Horde::assertDriverConfig($params, 'auth',
+ array('hostspec', 'basedn'), 'authentication MSAD');
+
+ $this->_params = $params;
+ /* Adjust capabilities: depending on if SSL encryption is
+ * enabled or not */
+ $this->_capabilities = array(
+ 'add' => ($params['ssl'] || $params['adduser']),
+ 'list' => true,
+ 'remove' => true,
+ 'resetpassword' => $params['ssl'],
+ 'update' => $params['ssl']
+ );
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $accountName The user sAMAccountName to find.
+ * @param array $credentials The credentials to be set.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($accountName, $credentials)
+ {
+ /* Connect to the MSAD server. */
+ $this->_connect();
+
+ $entry = Horde::callHook('_horde_hook_authmsad', array($accountName, $credentials), 'horde', null);
+ if (!is_null($entry)) {
+ $dn = $entry['dn'];
+ unset($entry['dn']);
+ } else {
+ $basedn = (isset($credentials['basedn'])) ?
+ $credentials['basedn'] : $this->_params['basedn'];
+
+ /* Set a default CN */
+ $dn = 'cn=' . $accountName . ',' . $basedn;
+
+ $entry['cn'] = $accountName;
+ $entry['samaccountname'] = $accountName;
+
+ $entry['objectclass'][0] = "top";
+ $entry['objectclass'][1] = "person";
+ $entry['objectclass'][2] = "organizationalPerson";
+ $entry['objectclass'][3] = "user";
+
+ $entry['description'] = (isset($credentials['description'])) ?
+ $credentials['description'] : 'New horde user';
+
+ if ($this->_params['ssl']) {
+ $entry["AccountDisabled"] = false;
+ }
+ $entry['userPassword'] = Horde_Auth::getCryptedPassword($credentials['password'],'',
+ $this->_params['encryption'],
+ false);
+
+ if (isset($this->_params['binddn'])) {
+ $entry['manager'] = $this->_params['binddn'];
+ }
+
+ }
+
+ $success = @ldap_add($this->_ds, $dn, $entry);
+
+ if (!$success) {
+ throw new Horde_Exception(sprintf(_("Auth_msad: Unable to add user \"%s\". This is what the server said: "), $accountName) . ldap_error($this->_ds));
+ }
+
+ @ldap_close($this->_ds);
+ }
+
+ /**
+ * Remove a set of authentication credentials.
+ *
+ * @param string $accountName The user sAMAccountName to remove.
+ *
+ * @throws Horde_Exception
+ */
+ public function removeUser($accountName)
+ {
+ /* Connect to the MSAD server. */
+ $this->_connect();
+
+ $entry = Horde::callHook('_horde_hook_authmsad', array($accountName), 'horde', null);
+ if (!is_null($entry)) {
+ $dn = $entry['dn'];
+ } else {
+ /* Search for the user's full DN. */
+ $dn = $this->_findDN($accountName);
+ }
+
+ if (!@ldap_delete($this->_ds, $dn)) {
+ throw new Horde_Exception(sprintf(_("Horde_Auth_Msad: Unable to remove user \"%s\""), $accountName));
+ }
+ @ldap_close($this->_ds);
+
+ /* Remove user data */
+ Horde_Auth::removeUserData($accountName);
+ }
+
+ /**
+ * Update a set of authentication credentials.
+ *
+ * @param string $oldID The old userId.
+ * @param string $newID The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ public function updateUser($oldId, $newId, $credentials)
+ {
+ /* Connect to the MSAD server. */
+ $this->_connect();
+
+ $entry = Horde::callHook('_horde_hook_authmsad', array($oldId, $credentials), 'horde', null);
+ if (!is_null($entry)) {
+ $olddn = $entry['dn'];
+ unset($entry['dn']);
+ } else {
+ /* Search for the user's full DN. */
+ $dn = $this->_findDN($oldId);
+
+ /* Encrypt the new password */
+ if (isset($credentials['password'])) {
+ $entry['userpassword'] = Horde_Auth::getCryptedPassword($credentials['password'],'',
+ $this->_params['encryption'],
+ true);
+ }
+ }
+
+ if ($oldID != $newID) {
+ $newdn = str_replace($oldId, $newID, $dn);
+ ldap_rename($this->_ds, $olddn, $newdn, $this->_params['basedn'], true);
+ $success = @ldap_modify($this->_ds, $newdn, $entry);
+ } else {
+ $success = @ldap_modify($this->_ds, $olddn, $entry);
+ }
+
+ if (!$success) {
+ throw new Horde_Exception(sprintf(_("Horde_Auth_Msad: Unable to update user \"%s\""), $newID), __FILE__, __LINE__);
+ }
+
+ @ldap_close($this->_ds);
+ }
+
+ /**
+ * Reset a user's password. Used for example when the user does not
+ * remember the existing password.
+ *
+ * @param string $user_id The user id for which to reset the password.
+ *
+ * @return string The new password on success.
+ * @throws Horde_Exception
+ */
+ public function resetPassword($user_id)
+ {
+ /* Get a new random password. */
+ $password = Horde_Auth::genRandomPassword() . '/';
+ $this->updateUser($user_id, $user_id, array('userPassword' => $password));
+ return $password;
+ }
+
+ /**
+ * Does an ldap connect and binds as the guest user.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _connect()
+ {
+ /* Connect to the MSAD server. */
+ $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."));
+ }
+
+ if (!ldap_set_option($this->_ds, LDAP_OPT_PROTOCOL_VERSION, 3)) {
+ Horde::logMessage(
+ sprintf('Set MSAD protocol version to %d failed: [%d] %s',
+ 3,
+ ldap_errno($conn),
+ ldap_error($conn),
+ __FILE__, __LINE__));
+ }
+ if (!ldap_set_option($this->_ds, LDAP_OPT_REFERRALS, 0)) {
+ Horde::logMessage(
+ sprintf('Set MSAD referrals option to %d failed: [%d] %s',
+ 0,
+ ldap_errno($conn),
+ ldap_error($conn),
+ __FILE__, __LINE__));
+ }
+
+ if (isset($this->_params['binddn'])) {
+ $bind = ldap_bind($this->_ds,
+ $this->_params['binddn'],
+ $this->_params['password']);
+ } else {
+ $bind = ldap_bind($this->_ds);
+ }
+
+ if (!$bind) {
+ return PEAR::raiseError(_("Could not bind to MSAD server."));
+ }
+
+ return true;
+ }
+
+ /**
+ * Find the user dn
+ *
+ * @access private
+ *
+ * @param string $userId The user UID to find.
+ *
+ * @return string The user's full DN
+ */
+ function _findDN($userId)
+ {
+ /* Search for the user's full DN. */
+ foreach ($this->_params['uid'] as $uid) {
+ $entries = array($uid);
+ if ($uid != $this->_params['authId']) {
+ array_push($entries, $this->_params['authId']);
+ }
+ $search = @ldap_search($this->_ds, $this->_params['basedn'],
+ $uid . '=' . $userId,
+ $entries
+ );
+ /* Searching the tree is not successful */
+ if (!$search) {
+ return PEAR::raiseError(_("Could not search the MSAD server."));
+ }
+
+ /* Fetch the search result */
+ $result = @ldap_get_entries($this->_ds, $search);
+ /* The result isn't empty: the DN was found */
+ if (is_array($result) && (count($result) > 1)) {
+ break;
+ }
+ }
+
+ if (is_array($result) && (count($result) > 1)) {
+ $dn = $result[0]['dn'];
+ } else {
+ return PEAR::raiseError(_("Empty result."));
+ }
+ /* Be sure the horde userId is the configured one */
+ $this->_authCredentials['userId'] = $result[0][$this->_params['authId']][0];
+ return $dn;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Pam:: class provides a PAM-based implementation of the Horde
+ * authentication system.
+ *
+ * PAM (Pluggable Authentication Modules) is a flexible mechanism for
+ * authenticating users. It has become the standard authentication system for
+ * Linux, Solaris and FreeBSD.
+ *
+ * This driver relies on the PECL PAM package:
+ *
+ * http://pecl.php.net/package/PAM
+ *
+ * Optional parameters:
+ * <pre>
+ * 'service' - (string) The name of the PAM service to use when
+ * authenticating.
+ * DEFAULT: php
+ * </pre>
+ *
+ * Copyright 2004-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 Jon Parise <jon@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Pam extends Horde_Auth_Driver
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ *
+ * @throws Horde_Exception
+ */
+ public function __construct($params = array())
+ {
+ if (!Horde_Util::extensionExists('pam')) {
+ throw new Horde_Exception(_("PAM authentication is not available."));
+ }
+
+ if (!empty($params['service'])) {
+ ini_set('pam.servicename', $params['service']);
+ }
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ if (empty($credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ $error = null;
+ if (!pam_auth($userId, $credentials['password'], $error)) {
+ throw new Horde_Exception($error);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Passwd:: class provides a passwd-file implementation of
+ * the Horde authentication system.
+ *
+ * Required parameters:
+ * <pre>
+ * 'filename' - (string) The passwd file to use.
+ * </pre>
+ *
+ * Optional parameters:
+ * <pre>
+ * 'encryption' The encryption to use to store the password in
+ * the table (e.g. plain, crypt, md5-hex,
+ * md5-base64, smd5, sha, ssha, aprmd5).
+ * DEFAULT: 'crypt-des'
+ * 'lock' Should we lock the passwd file? (boolean) The password
+ * file cannot be changed (add, edit, or delete users)
+ * unless this is true.
+ * DEFAULT: false<
+ * 'show_encryption' Whether or not to prepend the encryption in the
+ * password field.
+ * DEFAULT: 'false'
+ * </pre>
+ *
+ * Copyright 1997-2007 Rasmus Lerdorf <rasmus@php.net>
+ * Copyright 2002-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 Rasmus Lerdorf <rasmus@php.net>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Passwd extends Horde_Auth_Driver
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'list' => true
+ );
+
+ /**
+ * Hash list of users.
+ *
+ * @var array
+ */
+ protected $_users = null;
+
+ /**
+ * Array of groups and members.
+ *
+ * @var array
+ */
+ protected $_groups = array();
+
+ /**
+ * Filehandle for lockfile.
+ *
+ * @var integer
+ */
+ protected $_fplock;
+
+ /**
+ * Locking state.
+ *
+ * @var boolean
+ */
+ protected $_locked;
+
+ /**
+ * List of users that should be excluded from being listed/handled
+ * in any way by this driver.
+ *
+ * @var array
+ */
+ protected $_exclude = array(
+ 'root', 'daemon', 'bin', 'sys', 'sync', 'games', 'man', 'lp', 'mail',
+ 'news', 'uucp', 'proxy', 'postgres', 'www-data', 'backup', 'operator',
+ 'list', 'irc', 'gnats', 'nobody', 'identd', 'sshd', 'gdm', 'postfix',
+ 'mysql', 'cyrus', 'ftp',
+ );
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ public function __construct($params = array())
+ {
+ $params = array_merge(array(
+ 'encryption' => 'crypt-des',
+ 'lock' => false,
+ 'show_encryption' => false
+ ), $params);
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Writes changes to passwd file and unlocks it. Takes no arguments and
+ * has no return value. Called on script shutdown.
+ */
+ public function __destruct()
+ {
+ if ($this->_locked) {
+ foreach ($this->_users as $user => $pass) {
+ if ($this->_users[$user]) {
+ fputs($this->_fplock, "$user:$pass:" . $this->_users[$user] . "\n");
+ } else {
+ fputs($this->_fplock, "$user:$pass\n");
+ }
+ }
+ rename($this->_lockfile, $this->_params['filename']);
+ flock($this->_fplock, LOCK_UN);
+ $this->_locked = false;
+ fclose($this->_fplock);
+ }
+ }
+
+ /**
+ * 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)
+ {
+ if ($this->_params['lock']) {
+ switch ($capability) {
+ case 'add':
+ case 'update':
+ case 'resetpassword':
+ case 'remove':
+ return true;
+ }
+ }
+
+ return parent::hasCapability($capability);
+ }
+
+ /**
+ * Read and, if requested, lock the password file.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _read()
+ {
+ if (is_array($this->_users)) {
+ return;
+ }
+
+ if (empty($this->_params['filename'])) {
+ throw new Horde_Exception('No password file set.');
+ }
+
+ if ($this->_params['lock']) {
+ $this->_fplock = fopen(Horde::getTempDir() . '/passwd.lock', 'w');
+ flock($this->_fplock, LOCK_EX);
+ $this->_locked = true;
+ }
+
+ $fp = fopen($this->_params['filename'], 'r');
+ if (!$fp) {
+ throw new Horde_Exception("Couldn't open '" . $this->_params['filename'] . "'.");
+ }
+
+ $this->_users = array();
+ while (!feof($fp)) {
+ $line = trim(fgets($fp, 128));
+ if (empty($line)) {
+ continue;
+ }
+
+ $parts = explode(':', $line);
+ if (!count($parts)) {
+ continue;
+ }
+
+ $user = $parts[0];
+ $userinfo = array();
+ if (strlen($user) && !in_array($user, $this->_exclude)) {
+ if (isset($parts[1])) {
+ $userinfo['password'] = $parts[1];
+ }
+ if (isset($parts[2])) {
+ $userinfo['uid'] = $parts[2];
+ }
+ if (isset($parts[3])) {
+ $userinfo['gid'] = $parts[3];
+ }
+ if (isset($parts[4])) {
+ $userinfo['info'] = $parts[4];
+ }
+ if (isset($parts[5])) {
+ $userinfo['home'] = $parts[5];
+ }
+ if (isset($parts[6])) {
+ $userinfo['shell'] = $parts[6];
+ }
+
+ $this->_users[$user] = $userinfo;
+ }
+ }
+
+ fclose($fp);
+
+ if (!empty($this->_params['group_filename'])) {
+ $fp = fopen($this->_params['group_filename'], 'r');
+ if (!$fp) {
+ throw new Horde_Exception("Couldn't open '" . $this->_params['group_filename'] . "'.");
+ }
+
+ $this->_groups = array();
+ while (!feof($fp)) {
+ $line = trim(fgets($fp));
+ if (empty($line)) {
+ continue;
+ }
+
+ $parts = explode(':', $line);
+ $group = array_shift($parts);
+ $users = array_pop($parts);
+ $this->_groups[$group] = array_flip(preg_split('/\s*[,\s]\s*/', trim($users), -1, PREG_SPLIT_NO_EMPTY));
+ }
+
+ fclose($fp);
+ }
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials. For MCAL,
+ * this must contain a password entry.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ if (empty($credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ try {
+ $this->_read();
+ } catch (Horde_Exception $e) {
+ Horde::logMessage($e, __FILE__, __LINE__, PEAR_LOG_ERR);
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ if (!isset($this->_users[$userId]) ||
+ !$this->_comparePasswords($this->_users[$userId]['password'], $credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ if (!empty($this->_params['required_groups'])) {
+ $allowed = false;
+ foreach ($this->_params['required_groups'] as $group) {
+ if (isset($this->_groups[$group][$userId])) {
+ $allowed = true;
+ break;
+ }
+ }
+
+ if (!$allowed) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+ }
+ }
+
+ /**
+ * List all users in the system.
+ *
+ * @return array The array of userIds.
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ $this->_read();
+
+ $users = array_keys($this->_users);
+ if (empty($this->_params['required_groups'])) {
+ return $users;
+ }
+
+ $groupUsers = array();
+ foreach ($this->_params['required_groups'] as $group) {
+ $groupUsers = array_merge($groupUsers, array_intersect($users, array_keys($this->_groups[$group])));
+ }
+
+ return $groupUsers;
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to add.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ $this->_read();
+
+ if (!$this->_locked) {
+ throw new Horde_Exception('Password file not locked');
+ }
+
+ if (isset($this->_users[$userId])) {
+ throw new Horde_Exception("Couldn't add user '$user', because the user already exists.");
+ }
+
+ $this->_users[$userId] = array(
+ 'password' => Horde_Auth::getCryptedPassword($credentials['password'],
+ '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption']),
+
+ );
+ }
+
+ /**
+ * Update a set of authentication credentials.
+ *
+ * @param string $oldID The old userId.
+ * @param string $newID The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ public function updateUser($oldID, $newID, $credentials)
+ {
+ $this->_read();
+
+ if (!$this->_locked) {
+ throw new Horde_Exception('Password file not locked');
+ }
+
+ if (!isset($this->_users[$userId])) {
+ throw new Horde_Exception("Couldn't modify user '$oldID', because the user doesn't exist.");
+ }
+
+ $this->_users[$newID] = array(
+ 'password' => Horde_Auth::getCryptedPassword($credentials['password'],
+ '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption']),
+ );
+ return true;
+ }
+
+ /**
+ * Reset a user's password. Used for example when the user does not
+ * remember the existing password.
+ *
+ * @param string $userId The user id for which to reset the password.
+ *
+ * @return string The new password.
+ * @throws Horde_Exception
+ */
+ public function resetPassword($userId)
+ {
+ /* Get a new random password. */
+ $password = Horde_Auth::genRandomPassword();
+ $this->updateUser($userId, $userId, array('password' => $password));
+ return $password;
+ }
+
+ /**
+ * Delete a set of authentication credentials.
+ *
+ * @param string $userId The userId to delete.
+ *
+ * @throws Horde_Exception
+ */
+ public function removeUser($userId)
+ {
+ $this->_read();
+
+ if (!$this->_locked) {
+ throw new Horde_Exception('Password file not locked');
+ }
+
+ if (!isset($this->_users[$userId])) {
+ throw new Horde_Exception("Couldn't delete user '$oldID', because the user doesn't exist.");
+ }
+
+ unset($this->_users[$userId]);
+
+ Horde_Auth::removeUserData($userId);
+ }
+
+
+ /**
+ * Compare an encrypted password to a plaintext string to see if
+ * they match.
+ *
+ * @param string $encrypted The crypted password to compare against.
+ * @param string $plaintext The plaintext password to verify.
+ *
+ * @return boolean True if matched, false otherwise.
+ */
+ protected function _comparePasswords($encrypted, $plaintext)
+ {
+ return $encrypted == Horde_Auth::getCryptedPassword($plaintext,
+ $encrypted,
+ $this->_params['encryption'],
+ $this->_params['show_encryption']);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Peclsasl:: class provides a SASL-based implementation of the
+ * Horde authentication system.
+ *
+ * SASL is the Simple Authentication and Security Layer (as defined by RFC
+ * 2222). It provides a system for adding plugable authenticating support to
+ * connection-based protocols.
+ *
+ * This driver relies on the PECL sasl package:
+ *
+ * http://pecl.php.net/package/sasl
+ *
+ * Optional parameters:
+ * <pre>
+ * 'app' The name of the authenticating application.
+ * DEFAULT: horde
+ * 'service' The name of the SASL service to use when authenticating.
+ * DEFAULT: php
+ * </pre>
+ *
+ * Copyright 2004-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 Jon Parise <jon@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Peclsasl extends Horde_Auth_Driver
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ *
+ * @throws Horde_Exception
+ */
+ public function __construct($params = array())
+ {
+ if (!Horde_Util::extensionExists('sasl')) {
+ throw new Horde_Exception('Horde_Auth_Peclsasl:: requires the sasl PECL extension to be loaded.');
+ }
+
+ $params = array_merge(array(
+ 'app' => 'horde',
+ 'service' => 'php'
+ ), $params);
+
+ parent::__construct($params);
+
+ sasl_server_init($this->_params['app']);
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ if (empty($credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ $conn = sasl_server_new($this->_params['service']);
+ if (!is_resource($conn)) {
+ throw new Horde_Exception(_("Failed to create new SASL connection."));
+ }
+
+ if (!sasl_checkpass($conn, $userId, $credentials['password'])) {
+ throw new Horde_Exception(sasl_errdetail($conn));
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Radius class provides a RADIUS implementation of the Horde
+ * authentication system.
+ *
+ * This class requires the 'radius' PECL extension:
+ * http://pecl.php.net/package/radius
+ *
+ * On *nix-y machines, this extension can be installed as follows:
+ * <pre>
+ * pecl install radius
+ * </pre>
+ *
+ * Then, edit your php.ini file and make sure the following line is present:
+ * <pre>
+ * For Windows machines: extension=php_radius.dll
+ * For all others: extension=radius.so
+ * </pre>
+ *
+ * Required parameters:
+ * <pre>
+ * 'host' - (string) The RADIUS host to use (IP address or fully qualified
+ * hostname).
+ * 'method' - (string) The RADIUS method to use for validating the request.
+ * Either: 'PAP', 'CHAP_MD5', 'MSCHAPv1', or 'MSCHAPv2'.
+ * ** CURRENTLY, only 'PAP' is supported. **
+ * 'secret' - (string) The RADIUS shared secret string for the host. The
+ * RADIUS protocol ignores all but the leading 128 bytes
+ * of the shared secret.
+ * </pre>
+ *
+ * Optional parameters:
+ * <pre>
+ * 'nas' - (string) The RADIUS NAS identifier to use.
+ * DEFAULT: The value of $_SERVER['HTTP_HOST'] or, if not
+ * defined, then 'localhost'.
+ * 'port' - (integer) The port to use on the RADIUS server.
+ * DEFAULT: Whatever the local system identifies as the
+ * 'radius' UDP port
+ * 'retries' - (integer) The maximum number of repeated requests to make
+ * before giving up.
+ * DEFAULT: 3
+ * 'suffix' - (string) The domain name to add to unqualified user names.
+ * DEFAULT: NONE
+ * 'timeout' - (integer) The timeout for receiving replies from the server (in
+ * seconds).
+ * DEFAULT: 3
+ * </pre>
+ *
+ * Copyright 2002-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 Michael Slusarz <slusarz@horde.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Radius extends Horde_Auth_Driver
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ *
+ * @throws Horde_Exception
+ */
+ public function __construct($params = array())
+ {
+ parent::__construct($params);
+
+ if (!Horde_Util::extensionExists('radius')) {
+ throw new Horde_Exception('Horde_Auth_Radius:: requires the radius PECL extension to be loaded.');
+ }
+
+ /* A RADIUS host is required. */
+ if (empty($this->_params['host'])) {
+ throw new Horde_Exception('Horde_Auth_Radius:: requires a RADIUS host to connect to.');
+ }
+
+ /* A RADIUS secret string is required. */
+ if (empty($this->_params['secret'])) {
+ throw new Horde_Exception('Horde_Auth_Radius:: requires a RADIUS secret string.');
+ }
+
+ /* A RADIUS authentication method is required. */
+ if (empty($this->_params['method'])) {
+ throw new Horde_Exception('Horde_Auth_Radius:: requires a RADIUS authentication method.');
+ }
+
+ /* RADIUS NAS Identifier. */
+ if (empty($this->_params['nas'])) {
+ $this->_params['nas'] = isset($_SERVER['HTTP_HOST'])
+ ? $_SERVER['HTTP_HOST']
+ : 'localhost';
+ }
+
+ /* Suffix to add to unqualified user names. */
+ if (empty($this->_params['suffix'])) {
+ $this->_params['suffix'] = '';
+ }
+
+ /* The RADIUS port to use. */
+ if (empty($this->_params['port'])) {
+ $this->_params['port'] = 0;
+ }
+
+ /* Maximum number of retries. */
+ if (empty($this->_params['retries'])) {
+ $this->_params['retries'] = 3;
+ }
+
+ /* RADIUS timeout. */
+ if (empty($this->_params['timeout'])) {
+ $this->_params['timeout'] = 3;
+ }
+ }
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $username The userId to check.
+ * @param array $credentials An array of login credentials.
+ * For radius, this must contain a password
+ * entry.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($username, $credentials)
+ {
+ /* Password is required. */
+ if (!isset($credentials['password'])) {
+ throw new Horde_Exception(_("Password required for RADIUS authentication."));
+ }
+
+ $res = radius_auth_open();
+ radius_add_server($res, $this->_params['host'], $this->_params['port'], $this->_params['secret'], $this->_params['timeout'], $this->_params['retries']);
+ radius_create_request($res, RADIUS_ACCESS_REQUEST);
+ radius_put_attr($res, RADIUS_NAS_IDENTIFIER, $this->_params['nas']);
+ radius_put_attr($res, RADIUS_NAS_PORT_TYPE, RADIUS_VIRTUAL);
+ radius_put_attr($res, RADIUS_SERVICE_TYPE, RADIUS_FRAMED);
+ radius_put_attr($res, RADIUS_FRAMED_PROTOCOL, RADIUS_PPP);
+ radius_put_attr($res, RADIUS_CALLING_STATION_ID, isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : '127.0.0.1');
+
+ /* Insert username/password into request. */
+ radius_put_attr($res, RADIUS_USER_NAME, $username);
+ radius_put_attr($res, RADIUS_USER_PASSWORD, $credentials['password']);
+
+ /* Send request. */
+ $success = radius_send_request($res);
+
+ switch ($success) {
+ case RADIUS_ACCESS_ACCEPT:
+ break;
+
+ case RADIUS_ACCESS_REJECT:
+ throw new Horde_Exception(_("Authentication rejected by RADIUS server."));
+
+ default:
+ throw new Horde_Exception(radius_strerror($res));
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Shibboleth class only provides transparent authentication
+ * based on the headers set by a Shibboleth SP. Note that this class does
+ * not provide any actual SP functionality, it just takes the username
+ * from the HTTP headers that should be set by the Shibboleth SP.
+ *
+ * Required Parameters:
+ * <pre>
+ * 'username_header' - (string) Name of the header holding the username of the
+ * logged in user.
+ * </pre>
+ *
+ * Optional Parameters:
+ * <pre>
+ * 'password_header' - (string) Name of the header holding the password of the
+ * logged in user.
+ * 'password_holder' - (string) Where the hordeauth password is stored.
+ * 'password_preference' - (string) Name of the Horde preference holding the
+ * password of the logged in user.
+ * </pre>
+ *
+ * Copyright 9Star Research, Inc. 2006 http://www.protectnetwork.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 Cassio Nishiguchi <cassio@protectnetwork.org>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Shibboleth extends Horde_Auth_Driver
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'transparent' => true
+ );
+
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing parameters.
+ */
+ public function __construct($params = array())
+ {
+ Horde::assertDriverConfig($params, 'auth', array('username_header'), 'Authentication Shib');
+
+ $params = array_merge(array(
+ 'password_header' => '',
+ 'password_holder' => '',
+ 'password_preference' => ''
+ ), $params);
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Automatic authentication: Check if the username is set in the
+ * configured header.
+ *
+ * @return boolean Whether or not the client is allowed.
+ * @throws Horde_Exception
+ */
+ protected function _transparent()
+ {
+ if (empty($_SERVER[$this->_params['username_header']])) {
+ throw new Horde_Exception(_("Shibboleth authentication not available."));
+ }
+
+ $username = $_SERVER[$this->_params['username_header']];
+
+ // Remove scope from username, if present.
+ $pos = strrpos($username, '@');
+ if ($pos !== false) {
+ $username = substr($username, 0, $pos);
+ }
+
+ if (!Horde_Auth::setAuth($username, array('transparent' => 1))) {
+ return false;
+ }
+
+ // Set password for hordeauth login.
+ if ($this->_params['password_holder'] == 'header') {
+ Horde_Auth::setCredential('password', $_SERVER[$this->_params['password_header']]);
+ } elseif ($this->_params['password_holder'] == 'preferences') {
+ Horde_Auth::setCredential('password', $GLOBALS['prefs']->getValue($this->_params['password_preference']));
+ }
+
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Horde_Auth_Signup:: This class provides an interface to sign up or have
+ * new users sign themselves up into the horde installation, depending
+ * on how the admin has configured Horde.
+ *
+ * Copyright 2002-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 Marko Djukic <marko@oblo.com>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Signup
+{
+ /**
+ * Attempts to return a concrete Auth_Signup instance based on $driver.
+ *
+ * @param string $driver The type of the concrete Auth_Signup subclass
+ * to return. The class name is based on the
+ * storage driver ($driver). The code is
+ * dynamically included.
+ * @param array $params A hash containing any additional configuration or
+ * connection parameters a subclass might need.
+ *
+ * @return Auth_Signup The newly created concrete Auth_Signup instance,
+ * or false on an error.
+ */
+ static public function factory($driver = null, $params = null)
+ {
+ if ($driver === null) {
+ if (!empty($GLOBALS['conf']['signup']['driver'])) {
+ $driver = $GLOBALS['conf']['signup']['driver'];
+ } else {
+ $driver = 'datatree';
+ }
+ } else {
+ $driver = basename($driver);
+ }
+
+ if ($params === null) {
+ $params = Horde::getDriverConfig('signup', $driver);
+ }
+
+ $class = 'Auth_Signup_' . $driver;
+ if (!class_exists($class)) {
+ include dirname(__FILE__) . '/Signup/' . $driver . '.php';
+ }
+ if (class_exists($class)) {
+ return new $class($params);
+ } else {
+ return PEAR::raiseError(_("You must configure a backend to use Signups."));
+ }
+ }
+
+ /**
+ * Adds a new user to the system and handles any extra fields that may have
+ * been compiled, relying on the hooks.php file.
+ *
+ * @params mixed $info Reference to array of parameteres to be passed
+ * to hook
+ *
+ * @return mixed PEAR_Error if any errors, otherwise true.
+ */
+ public function addSignup(&$info)
+ {
+ global $auth, $conf;
+
+ // Perform any preprocessing if requested.
+ if ($conf['signup']['preprocess']) {
+ $info = Horde::callHook('_horde_hook_signup_preprocess', array($info));
+ if (is_a($info, 'PEAR_Error')) {
+ return $info;
+ }
+ }
+
+ // Check to see if the username already exists.
+ if ($auth->exists($info['user_name']) ||
+ $this->exists($info['user_name'])) {
+ return PEAR::raiseError(sprintf(_("Username \"%s\" already exists."), $info['user_name']));
+ }
+
+ // Attempt to add the user to the system.
+ $result = $auth->addUser($info['user_name'], array('password' => $info['password']));
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ $result = true;
+ // Attempt to add/update any extra data handed in.
+ if (!empty($info['extra'])) {
+ $result = false;
+ $result = Horde::callHook('_horde_hook_signup_addextra',
+ array($info['user_name'], $info['extra']));
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_EMERG);
+ return $result;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Queues the user's submitted registration info for later admin approval.
+ *
+ * @params mixed $info Reference to array of parameteres to be passed
+ * to hook
+ *
+ * @return mixed PEAR_Error if any errors, otherwise true.
+ *
+ * @throws Horde_Mime_Exception
+ */
+ public function queueSignup(&$info)
+ {
+ global $auth, $conf;
+
+ // Perform any preprocessing if requested.
+ if ($conf['signup']['preprocess']) {
+ $info = Horde::callHook('_horde_hook_signup_preprocess',
+ array($info));
+ if (is_a($info, 'PEAR_Error')) {
+ return $info;
+ }
+ }
+
+ // Check to see if the username already exists.
+ if ($auth->exists($info['user_name']) ||
+ $this->exists($info['user_name'])) {
+ return PEAR::raiseError(sprintf(_("Username \"%s\" already exists."), $info['user_name']));
+ }
+
+ // If it's a unique username, go ahead and queue the request.
+ $signup = $this->newSignup($info['user_name']);
+ if (!empty($info['extra'])) {
+ $signup->data = array_merge($info['extra'],
+ array('password' => $info['password'],
+ 'dateReceived' => time()));
+ } else {
+ $signup->data = array('password' => $info['password'],
+ 'dateReceived' => time());
+ }
+
+ $result = $this->_queueSignup($signup);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if ($conf['signup']['queue']) {
+ $result = Horde::callHook('_horde_hook_signup_queued',
+ array($info['user_name'], $info));
+ }
+
+ if (!empty($conf['signup']['email'])) {
+ $link = Horde_Util::addParameter(Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/admin/signup_confirm.php', true, -1),
+ array('u' => $signup->name,
+ 'h' => hash_hmac('sha1', $signup->name, $conf['secret_key'])),
+ null, false);
+ $message = sprintf(_("A new account for the user \"%s\" has been requested through the signup form."), $signup->name)
+ . "\n\n"
+ . _("Approve the account:")
+ . "\n" . Horde_Util::addParameter($link, 'a', 'approve') . "\n"
+ . _("Deny the account:")
+ . "\n" . Horde_Util::addParameter($link, 'a', 'deny');
+ $mail = new Horde_Mime_Mail(
+ sprintf(_("Account signup request for \"%s\""), $signup->name),
+ $message,
+ $conf['signup']['email'],
+ $conf['signup']['email'],
+ NLS::getCharset());
+ $mail->send($conf['mailer']['type'], $conf['mailer']['params']);
+ }
+ }
+
+ /**
+ * Queues the user's submitted registration info for later admin approval.
+ *
+ * @params mixed $info Reference to array of parameteres to be passed
+ * to hook
+ *
+ * @return mixed PEAR_Error if any errors, otherwise true.
+ */
+ protected function &_queueSignup(&$info)
+ {
+ return PEAR::raiseError('Not implemented');
+ }
+
+ /**
+ * Get a user's queued signup information.
+ *
+ * @param string $username The username to retrieve the queued info for.
+ *
+ * @return object The bject for the requested signup.
+ */
+ public function getQueuedSignup($username)
+ {
+ return PEAR::raiseError('Not implemented');
+ }
+
+ /**
+ * Get the queued information for all pending signups.
+ *
+ * @return array An array of objects, one for each signup in the queue.
+ */
+ public function getQueuedSignups()
+ {
+ return PEAR::raiseError('Not implemented');
+ }
+
+ /**
+ * Remove a queued signup.
+ *
+ * @param string $username The user to remove from the signup queue.
+ */
+ public function removeQueuedSignup($username)
+ {
+ return PEAR::raiseError('Not implemented');
+ }
+
+ /**
+ * Return a new signup object.
+ *
+ * @param string $name The signups's name.
+ *
+ * @return object A new signup object.
+ */
+ public function newSignup($name)
+ {
+ return PEAR::raiseError('Not implemented');
+ }
+
+}
+
+/**
+ * Horde Signup Form.
+ *
+ * Copyright 2003-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 Marko Djukic <marko@oblo.com>
+ * @package Horde_Auth
+ */
+class HordeSignupForm extends Horde_Form {
+
+ var $_useFormToken = true;
+
+ function HordeSignupForm(&$vars)
+ {
+ global $registry;
+
+ parent::Horde_Form($vars, sprintf(_("%s Sign Up"), $registry->get('name')));
+
+ $this->setButtons(_("Sign up"), true);
+
+ $this->addHidden('', 'url', 'text', false);
+
+ /* Use hooks get any extra fields required in signing up. */
+ $extra = Horde::callHook('_horde_hook_signup_getextra');
+ if (!is_a($extra, 'PEAR_Error') && !empty($extra)) {
+ if (!isset($extra['user_name'])) {
+ $this->addVariable(_("Choose a username"), 'user_name', 'text', true);
+ }
+ if (!isset($extra['password'])) {
+ $this->addVariable(_("Choose a password"), 'password', 'passwordconfirm', true, false, _("type the password twice to confirm"));
+ }
+ foreach ($extra as $field_name => $field) {
+ $readonly = isset($field['readonly']) ? $field['readonly'] : null;
+ $desc = isset($field['desc']) ? $field['desc'] : null;
+ $required = isset($field['required']) ? $field['required'] : false;
+ $field_params = isset($field['params']) ? $field['params'] : array();
+
+ $this->addVariable($field['label'], 'extra[' . $field_name . ']',
+ $field['type'], $required, $readonly,
+ $desc, $field_params);
+ }
+ } else {
+ $this->addVariable(_("Choose a username"), 'user_name', 'text', true);
+ $this->addVariable(_("Choose a password"), 'password', 'passwordconfirm', true, false, _("type the password twice to confirm"));
+ }
+ }
+
+ /**
+ * Fetch the field values of the submitted form.
+ *
+ * @param Variables $vars A Variables instance, optional since Horde 3.2.
+ * @param array $info Array to be filled with the submitted field
+ * values.
+ */
+ function getInfo($vars, &$info)
+ {
+ parent::getInfo($vars, $info);
+
+ if (!isset($info['user_name']) && isset($info['extra']['user_name'])) {
+ $info['user_name'] = $info['extra']['user_name'];
+ }
+
+ if (!isset($info['password']) && isset($info['extra']['password'])) {
+ $info['password'] = $info['extra']['password'];
+ }
+ }
+
+}
--- /dev/null
+<?php
+
+require_once 'Horde/DataTree.php';
+
+/**
+ * Horde_Auth_Signup:: This class provides an interface to sign up or have
+ * new users sign themselves up into the horde installation, depending
+ * on how the admin has configured Horde.
+ *
+ * Copyright 2002-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 Marko Djukic <marko@oblo.com>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Signup_Datatree extends Horde_Auth_Signup {
+
+ /**
+ * Pointer to a DataTree instance to manage/store signups
+ *
+ * @var DataTree
+ */
+ var $_datatree;
+
+ public function _-construct()
+ {
+ global $conf;
+
+ if (empty($conf['datatree']['driver'])) {
+ Horde::fatal(_("You must configure a DataTree backend to use Signups."), __FILE__, __LINE__);
+ }
+ $driver = $conf['datatree']['driver'];
+ $this->_datatree = DataTree::singleton($driver,
+ array_merge(Horde::getDriverConfig('datatree', $driver),
+ array('group' => 'horde.signup')));
+ }
+
+ /**
+ * Stores the signup data in the backend.
+ *
+ * @params DataTreeObject_Signup $signup Signup data.
+ */
+ protected function _queueSignup($signup)
+ {
+ return $this->_datatree->add($signup);
+ }
+
+ /**
+ * Get a user's queued signup information.
+ *
+ * @param string $username The username to retrieve the queued info for.
+ *
+ * @return DataTreeObject_Signup The DataTreeObject for the requested
+ * signup.
+ */
+ public function getQueuedSignup($username)
+ {
+ return $this->_datatree->getObject($username, 'DataTreeObject_Signup');
+ }
+
+ /**
+ * Get the queued information for all pending signups.
+ *
+ * @return array An array of DataTreeObject_Signup objects, one for
+ * each signup in the queue.
+ */
+ public function getQueuedSignups()
+ {
+ $signups = array();
+ foreach ($this->_datatree->get(DATATREE_FORMAT_FLAT, DATATREE_ROOT, true) as $username) {
+ if ($username != DATATREE_ROOT) {
+ $signups[] = $this->_datatree->getObject($username);
+ }
+ }
+ return $signups;
+ }
+
+ /**
+ * Remove a queued signup.
+ *
+ * @param string $username The user to remove from the signup queue.
+ */
+ public function removeQueuedSignup($username)
+ {
+ $this->_datatree->remove($username);
+ }
+
+ /**
+ * Return a new signup object.
+ *
+ * @param string $name The signups's name.
+ *
+ * @return DataTreeObject_Signup A new signup object.
+ */
+ public function newSignup($name)
+ {
+ if (empty($name)) {
+ return PEAR::raiseError('Signup names must be non-empty');
+ }
+ return new DataTreeObject_Signup($name);
+ }
+
+}
+
+/**
+ * Extension of the DataTreeObject class for storing Signup
+ * information in the DataTree driver. If you want to store
+ * specialized Signup information, you should extend this class
+ * instead of extending DataTreeObject directly.
+ *
+ * @author Marko Djukic <marko@oblo.com>
+ * @package Horde_Auth
+ */
+class DataTreeObject_Signup extends DataTreeObject {
+
+ /**
+ * We want to see queued signups in descending order of receipt.
+ * Insert new signups at position 0 and push the rest down.
+ *
+ * @var integer
+ */
+ var $order = 0;
+
+ /**
+ * The DataTreeObject_Signup constructor. Just makes sure to call
+ * the parent constructor so that the signup's is is set
+ * properly.
+ *
+ * @param string $id The id of the signup.
+ */
+ function DataTreeObject_Signup($id)
+ {
+ parent::DataTreeObject($id);
+ if (is_null($this->data)) {
+ $this->data = array();
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Auth_Signup:: This class provides an interface to sign up or have
+ * new users sign themselves up into the horde installation, depending
+ * on how the admin has configured Horde.
+ *
+ * Copyright 2008-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 Duck <duck@obala.net>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Signup_Sql extends Horde_Auth_Signup
+{
+ /**
+ * Handle for the current database connection.
+ *
+ * @var DB
+ */
+ protected $_db;
+
+ /**
+ * Handle for the current database connection, used for writing. Defaults
+ * to the same handle as $db if a separate write database is not required.
+ *
+ * @var DB
+ */
+ protected $_write_db;
+
+ /**
+ * SQL connection parameters
+ */
+ protected $_params = array();
+
+ /**
+ * Connect to DB.
+ */
+ public function __construct($params)
+ {
+ $this->_params = $params;
+ $this->_connect();
+ }
+
+ /**
+ * Stores the signup data in the backend.
+ *
+ * @params SQLObject_Signup $signup Signup data.
+ */
+ protected function _queueSignup($signup)
+ {
+ $query = 'INSERT INTO ' . $this->_params['table']
+ . ' (user_name, signup_date, signup_host, signup_data) VALUES (?, ?, ?, ?) ';
+ $values = array($signup->name,
+ time(),
+ $_SERVER['REMOTE_ADDR'],
+ serialize($signup->data));
+ Horde::logMessage('SQL query by Auth_Signup_sql::_queueSignup(): ' . $query,
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ $stmt = $this->_write_db->prepare($query, null, MDB2_PREPARE_MANIP);
+ if (is_a($stmt, 'PEAR_Error')) {
+ Horde::logMessage($stmt, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $stmt;
+ }
+ $result = $stmt->execute($values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+ $stmt->free();
+ }
+
+ /**
+ * Checks if a user exists in the system.
+ *
+ * @param string $user The user to check.
+ *
+ * @return boolean True if the user exists.
+ */
+ public function exists($user)
+ {
+ $stmt = $this->_db->prepare('SELECT 1 FROM ' . $this->_params['table']
+ . ' WHERE user_name = ?');
+
+ if (is_a($stmt, 'PEAR_Error')) {
+ Horde::logMessage($stmt, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $stmt;
+ }
+ $result = $stmt->execute(array($user));
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ $exists = (bool)$result->fetchOne();
+ $stmt->free();
+ $result->free();
+
+ return $exists;
+ }
+
+ /**
+ * Get a user's queued signup information.
+ *
+ * @param string $username The username to retrieve the queued info for.
+ *
+ * @return SQLObject_Signup The SQLObject for the requested
+ * signup.
+ */
+ public function getQueuedSignup($username)
+ {
+ $stmt = $this->_db->prepare('SELECT * FROM ' . $this->_params['table'] . ' WHERE user_name = ?');
+ if (is_a($stmt, 'PEAR_Error')) {
+ Horde::logMessage($stmt, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $stmt;
+ }
+ $results = $stmt->execute(array($username));
+ if (is_a($results, 'PEAR_Error')) {
+ Horde::logMessage($results, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $results;
+ }
+ $data = $results->fetchRow(MDB2_FETCHMODE_ASSOC);
+ if (is_a($data, 'PEAR_Error')) {
+ Horde::logMessage($data, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $data;
+ } elseif (empty($data)) {
+ return PEAR::RaiseError(sprintf(_("User \"%s\" does not exist."), $name));
+ }
+ $stmt->free();
+ $results->free();
+
+ $object = new SQLObject_Signup($data['user_name']);
+ $object->setData($data);
+
+ return $object;
+ }
+
+ /**
+ * Get the queued information for all pending signups.
+ *
+ * @return array An array of SQLObject_Signup objects, one for
+ * each signup in the queue.
+ */
+ public function getQueuedSignups()
+ {
+ $query = 'SELECT * FROM ' . $this->_params['table'] . ' ORDER BY signup_date';
+ $result = $this->_db->query($query);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ } elseif (empty($result)) {
+ return array();
+ }
+
+ $signups = array();
+ while ($signup = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
+ $object = new SQLObject_Signup($signup['user_name']);
+ $object->setData($signup);
+ $signups[] = $object;
+ }
+
+ $result->free();
+
+ return $signups;
+ }
+
+ /**
+ * Remove a queued signup.
+ *
+ * @param string $username The user to remove from the signup queue.
+ */
+ public function removeQueuedSignup($username)
+ {
+ $stmt = $this->_write_db->prepare('DELETE FROM ' . $this->_params['table'] . ' WHERE user_name = ?', null, MDB2_PREPARE_MANIP);
+ if (is_a($stmt, 'PEAR_Error')) {
+ Horde::logMessage($stmt, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $stmt;
+ }
+ $result = $stmt->execute(array($username));
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+ $stmt->free();
+
+ return true;
+ }
+
+ /**
+ * Return a new signup object.
+ *
+ * @param string $name The signups's name.
+ *
+ * @return SQLObject_Signup A new signup object.
+ */
+ public function newSignup($name)
+ {
+ if (empty($name)) {
+ return PEAR::raiseError('Signup names must be non-empty');
+ }
+ return new SQLObject_Signup($name);
+ }
+
+ /**
+ * Attempts to open a connection to the sql server.
+ *
+ * @return boolean True on success; exits (Horde::fatal()) on error.
+ */
+ protected function _connect()
+ {
+ if (!isset($this->_params['database'])) {
+ $this->_params['database'] = '';
+ }
+ if (!isset($this->_params['username'])) {
+ $this->_params['username'] = '';
+ }
+ if (!isset($this->_params['hostspec'])) {
+ $this->_params['hostspec'] = '';
+ }
+ if (!isset($this->_params['table'])) {
+ $this->_params['table'] = 'horde_signups';
+ }
+
+ /* Connect to the sql server using the supplied parameters. */
+ $params = $this->_params;
+ unset($params['charset']);
+ $this->_write_db = MDB2::factory($params);
+ if (is_a($this->_write_db, 'PEAR_Error')) {
+ Horde::fatal($this->_write_db, __FILE__, __LINE__);
+ }
+
+ /* Set DB portability options. */
+ switch ($this->_write_db->phptype) {
+ case 'mssql':
+ $this->_write_db->setOption('field_case', CASE_LOWER);
+ $this->_write_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_RTRIM | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+ break;
+ default:
+ $this->_write_db->setOption('field_case', CASE_LOWER);
+ $this->_write_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+ }
+
+ /* Check if we need to set up the read DB connection seperately. */
+ if (!empty($this->_params['splitread'])) {
+ $params = array_merge($params, $this->_params['read']);
+ $this->_db = MDB2::factory($params);
+ if (is_a($this->_db, 'PEAR_Error')) {
+ Horde::fatal($this->_db, __FILE__, __LINE__);
+ }
+
+ /* Set DB portability options. */
+ switch ($this->_db->phptype) {
+ case 'mssql':
+ $this->_db->setOption('field_case', CASE_LOWER);
+ $this->_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_RTRIM | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+ break;
+ default:
+ $this->_db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
+ }
+ } else {
+ /* Default to the same DB handle as the writer for reading too */
+ $this->_db = $this->_write_db;
+ }
+
+ return true;
+ }
+
+}
+
+/**
+ * Extension of the SQLObject class for storing Signup
+ * information in the SQL driver. If you want to store
+ * specialized Signup information, you should extend this class
+ * instead of extending SQLObject directly.
+ *
+ * @author Duck <duck@obala.net>
+ * @package Horde_Auth
+ */
+class SQLObject_Signup {
+
+ /**
+ * Key-value hash that will be serialized.
+ *
+ * @see getData()
+ * @var array
+ */
+ var $data = array();
+
+ /**
+ * The unique name of this object.
+ * These names have the same requirements as other object names - they must
+ * be unique, etc.
+ *
+ * @var string
+ */
+ var $name;
+
+ /**
+ * The SQLObject_Signup constructor. Just makes sure to call
+ * the parent constructor so that the signup's is is set
+ * properly.
+ *
+ * @param string $id The id of the signup.
+ */
+ function SQLObject_Signup($id)
+ {
+ if (is_null($this->data)) {
+ $this->data = array();
+ }
+
+ $this->name = $id;
+ }
+
+ /**
+ * Gets the data array.
+ *
+ * @return array The internal data array.
+ */
+ function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Sets the data array.
+ *
+ * @param array The data array to store internally.
+ */
+ function setData($data)
+ {
+ $part = unserialize($data['signup_data']);
+ if (!empty($part) && is_array($part)) {
+ if (!empty($part['extra'])) {
+ $extra = $part['extra'];
+ unset($part['extra']);
+ $part = array_merge($part, $extra);
+ }
+ $this->data = array_merge($data, $part);
+ } else {
+ $this->data = $data;
+ }
+
+ unset($this->data['signup_data']);
+ $this->data['dateReceived'] = $data['signup_date'];
+ }
+
+ /**
+ * Gets the name of this object.
+ *
+ * @return string The object name.
+ */
+ function getName()
+ {
+ return $this->name;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Smb class provides a SMB implementation of the Horde
+ * authentication system.
+ *
+ * This module requires the smbauth extension for PHP:
+ * http://tekrat.com/wp/smbauth/
+ *
+ * At the time of this writing, the extension, and thus this module, only
+ * supported authentication against a domain, and pdc and bdc must be non-null
+ * and not equal to each other. In other words, to use this module you must
+ * have a domain with at least one PDC and one BDC.
+ *
+ * Required parameters:
+ * <pre>
+ * 'hostspec' - IP, DNS Name, or NetBios Name of the SMB server to
+ * authenticate with.
+ * 'domain' - The domain name to authenticate with.
+ * </pre>
+ *
+ * Optional parameters:
+ * <pre>
+ * 'group' - Group name that the user must be a member of. Will be
+ * ignored if the value passed is a zero length string.
+ * </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 Jon Parise <jon@horde.org>
+ * @author Marcus I. Ryan <marcus@riboflavin.net>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Smb extends Horde_Auth_Driver
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ *
+ * @throws Horde_Exception
+ */
+ public function __construct($params = array())
+ {
+ if (!Util::extensionExists('smbauth')) {
+ throw new Horde_Exception(_("Horde_Auth_Smb: Required smbauth extension not found."));
+ }
+
+ /* Ensure we've been provided with all of the necessary parameters. */
+ Horde::assertDriverConfig($params, 'auth',
+ array('hostspec', 'domain'),
+ 'authentication Samba');
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Find out if the given set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials.
+ *
+ * @throws Horde_Exception
+ */
+ public function _authenticate($userId, $credentials)
+ {
+ if (empty($credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ /* Authenticate. */
+ $rval = validate($this->_params['hostspec'],
+ $this->_params['domain'],
+ empty($this->_params['group']) ? '' : $this->_params['group'],
+ $userId,
+ $credentials['password']);
+
+ if ($rval === 1) {
+ throw new Horde_Exception(_("Failed to connect to SMB server."));
+ } elseif ($rval !== 0) {
+ throw new Horde_Exception(err2str());
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Smbclient class provides an smbclient implementation of
+ * the Horde authentication system.
+ *
+ * Required parameters:
+ * <pre>
+ * 'domain' The domain name to authenticate with.
+ * 'hostspec' IP, DNS Name, or NetBios Name of the SMB server to
+ * authenticate with.
+ * 'smbclient_path' The location of the smbclient(1) utility.
+ * </pre>
+ *
+ * Optional parameters:
+ * <pre>
+ * 'group' - Group name that the user must be a member of. Will be
+ * ignored if the value passed is a zero length string.
+ * </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 Jon Parise <jon@horde.org>
+ * @author Marcus I. Ryan <marcus@riboflavin.net>
+ * @package Horde_Auth
+ */
+class Horde_Auth_Smbclient extends Horde_Auth_Driver
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params A hash containing connection parameters.
+ */
+ public function __construct($params = array())
+ {
+ /* Ensure we've been provided with all of the necessary parameters. */
+ Horde::assertDriverConfig($params, 'auth',
+ array('hostspec', 'domain', 'smbclient_path'),
+ 'authentication smbclient');
+
+ parent::__construct($params);
+ }
+
+ /**
+ * Find out if the given set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials An array of login credentials.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ if (empty($credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ /* Authenticate. */
+ $cmdline = implode(' ', array($this->_params['smbclient_path'],
+ '-L',
+ $this->_params['hostspec'],
+ '-W',
+ $this->_params['domain'],
+ '-U',
+ $userId));
+
+ $sc = popen($cmdline, 'w');
+ if ($sc === false) {
+ throw new Horde_Exception(_("Unable to execute smbclient."));
+ }
+
+ fwrite($sc, $credentials['password']);
+ $rc = pclose($sc);
+
+ if ((int)($rc & 0xff) != 0) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Auth_Sql class provides a SQL implementation of the Horde
+ * authentication system.
+ *
+ * Required parameters:
+ * <pre>
+ * 'phptype' - (string) The database type (ie. 'pgsql', 'mysql', etc.).
+ * </pre>
+ *
+ * Optional parameters:
+ * <pre>
+ * 'encryption' - (string) The encryption to use to store the password in
+ * the table (e.g. plain, crypt, md5-hex, md5-base64, smd5,
+ * sha, ssha, aprmd5).
+ * DEFAULT: 'md5-hex'
+ * 'hard_expiration_field' - (string) The name of the field containing a date
+ * after which the account is no longer valid and
+ * the user will not be able to log in at all.
+ * DEFAULT: none
+ * 'password_field' - (string) The name of the password field in the auth
+ * table.
+ * DEFAULT: 'user_pass'
+ * 'show_encryption' - (boolean) Whether or not to prepend the encryption in
+ * the password field.
+ * DEFAULT: false
+ * 'soft_expiration_field' - (string) The name of the field containing a date
+ * after which the system will request the user
+ * change his or her password.
+ * DEFAULT: none
+ * 'table' - (string) The name of the SQL table to use in 'database'.
+ * DEFAULT: 'horde_users'
+ * 'username_field' - (string) The name of the username field in the auth
+ * table.
+ * DEFAULT: 'user_uid'
+ * </pre>
+ *
+ * Required by some database implementations:
+ * <pre>
+ * 'hostspec' The hostname of the database server.
+ * 'protocol' The communication protocol ('tcp', 'unix', etc.).
+ * 'database' The name of the database.
+ * 'username' The username with which to connect to the database.
+ * 'password' The password associated with 'username'.
+ * 'options' Additional options to pass to the database.
+ * 'port' The port on which to connect to the database.
+ * 'tty' The TTY on which to connect to the database.
+ * </pre>
+ *
+ * Optional values when using separate read and write servers, for example
+ * in replication settings:
+ * <pre>
+ * 'splitread' Boolean, whether to implement the separation or not.
+ * 'read' Array containing the parameters which are different for
+ * the read database connection, currently supported
+ * only 'hostspec' and 'port' parameters.
+ * </pre>
+ *
+ * The table structure for the Auth system is in
+ * horde/scripts/sql/horde_users.sql.
+ *
+ * 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
+ */
+class Horde_Auth_Sql extends Horde_Auth_Driver
+{
+ /**
+ * An array of capabilities, so that the driver can report which
+ * operations it supports and which it doesn't.
+ *
+ * @var array
+ */
+ protected $_capabilities = array(
+ 'add' => true,
+ 'list' => true,
+ 'remove' => true,
+ 'resetpassword' => true,
+ 'update' => true
+ );
+
+ /**
+ * Handle for the current database connection.
+ *
+ * @var DB
+ */
+ protected $_db;
+
+ /**
+ * Handle for the current database connection, used for writing. Defaults
+ * to the same handle as $_db if a separate write database is not required.
+ *
+ * @var DB
+ */
+ protected $_write_db;
+
+ /**
+ * Boolean indicating whether or not we're connected to the SQL server.
+ *
+ * @var boolean
+ */
+ protected $_connected = false;
+
+ /**
+ * Find out if a set of login credentials are valid.
+ *
+ * @param string $userId The userId to check.
+ * @param array $credentials The credentials to use.
+ *
+ * @throws Horde_Exception
+ */
+ protected function _authenticate($userId, $credentials)
+ {
+ try {
+ $this->_connect();
+ } catch (Horde_Exception $e) {
+ Horde::logMessage($e, __FILE__, __LINE__, PEAR_LOG_ERR);
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ /* Build the SQL query. */
+ $query = sprintf('SELECT * FROM %s WHERE %s = ?',
+ $this->_params['table'],
+ $this->_params['username_field']);
+ $values = array($userId);
+
+ Horde::logMessage('SQL Query by Auth_sql::_authenticate(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_db->query($query, $values);
+ if ($result instanceof PEAR_Error) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ throw new Horde_Exception('', Horde_Auth::REASON_FAILED);
+ }
+
+ $row = $result->fetchRow(DB_GETMODE_ASSOC);
+ if (is_array($row)) {
+ $result->free();
+ } else {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ if (!$this->_comparePasswords($row[$this->_params['password_field']],
+ $credentials['password'])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_BADLOGIN);
+ }
+
+ $now = time();
+ if (!empty($this->_params['hard_expiration_field']) &&
+ !empty($row[$this->_params['hard_expiration_field']]) &&
+ ($now > $row[$this->_params['hard_expiration_field']])) {
+ throw new Horde_Exception('', Horde_Auth::REASON_EXPIRED);
+ }
+
+ 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;
+ }
+ }
+
+ /**
+ * Add a set of authentication credentials.
+ *
+ * @param string $userId The userId to add.
+ * @param array $credentials The credentials to add.
+ *
+ * @throws Horde_Exception
+ */
+ public function addUser($userId, $credentials)
+ {
+ $this->_connect();
+
+ /* Build the SQL query. */
+ $query = sprintf('INSERT INTO %s (%s, %s) VALUES (?, ?)',
+ $this->_params['table'],
+ $this->_params['username_field'],
+ $this->_params['password_field']);
+ $values = array($userId,
+ Horde_Auth::getCryptedPassword($credentials['password'],
+ '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption']));
+
+ Horde::logMessage('SQL Query by Auth_sql::addUser(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_write_db->query($query, $values);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+ }
+
+ /**
+ * Update a set of authentication credentials.
+ *
+ * @param string $oldID The old userId.
+ * @param string $newID The new userId.
+ * @param array $credentials The new credentials
+ *
+ * @throws Horde_Exception
+ */
+ public function updateUser($oldID, $newID, $credentials)
+ {
+ $this->_connect();
+
+ /* Build the SQL query. */
+ $tuple = array();
+ $tuple[$this->_params['username_field']] = $newID;
+ $tuple[$this->_params['password_field']] =
+ Horde_Auth::getCryptedPassword($credentials['password'],
+ '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption']);
+
+ if (empty($this->_params['soft_expiration_window'])) {
+ if (!empty($this->_params['soft_expiration_field'])) {
+ $tuple[$this->_params['soft_expiration_field']] = null;
+ }
+ } else {
+ $date = time();
+ $datea = localtime($date, true);
+ $date = mktime($datea['tm_hour'], $datea['tm_min'],
+ $datea['tm_sec'], $datea['tm_mon'] + 1,
+ $datea['tm_mday'] + $this->_params['soft_expiration_window'],
+ $datea['tm_year']);
+
+ $tuple[$this->_params['soft_expiration_field']] = $date;
+
+ global $notification;
+ if (!empty($notification)) {
+ $notification->push(strftime(_("New password will expire on %s."), $date), 'horde.message');
+ }
+
+ if (empty($this->_params['hard_expiration_window'])) {
+ $tuple[$this->_params['hard_expiration_field']] = null;
+ } else {
+ $datea = localtime($date, true);
+ $date = mktime($datea['tm_hour'], $datea['tm_min'],
+ $datea['tm_sec'], $datea['tm_mon'] + 1,
+ $datea['tm_mday'] + $this->_params['soft_expiration_window'],
+ $datea['tm_year']);
+
+ $tuple[$this->_params['hard_expiration_field']] = $date;
+ }
+ }
+
+ $query = sprintf('UPDATE %s SET %s WHERE %s = ?',
+ $this->_params['table'],
+ Horde_SQL::updateValues($this->_write_db, $tuple),
+ $this->_params['username_field']);
+ $values = array($oldID);
+
+ Horde::logMessage('SQL Query by Auth_sql:updateUser(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_write_db->query($query, $values);
+ if ($result instanceof PEAR_Error) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ throw new Horde_Exception($result);
+ }
+ }
+
+ /**
+ * Reset a user's password. Used for example when the user does not
+ * remember the existing password.
+ *
+ * @param string $userId The user id for which to reset the password.
+ *
+ * @return string The new password on success.
+ * @throws Horde_Exception
+ */
+ public function resetPassword($userId)
+ {
+ $this->_connect();
+
+ /* Get a new random password. */
+ $password = Horde_Auth::genRandomPassword();
+
+ /* Build the SQL query. */
+ $query = sprintf('UPDATE %s SET %s = ? WHERE %s = ?',
+ $this->_params['table'],
+ $this->_params['password_field'],
+ $this->_params['username_field']);
+ $values = array(Horde_Auth::getCryptedPassword($password,
+ '',
+ $this->_params['encryption'],
+ $this->_params['show_encryption']),
+ $userId);
+
+ Horde::logMessage('SQL Query by Auth_sql::resetPassword(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_write_db->query($query, $values);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+
+ return $password;
+ }
+
+ /**
+ * Delete a set of authentication credentials.
+ *
+ * @param string $userId The userId to delete.
+ *
+ * @throws Horde_Exception
+ */
+ public function removeUser($userId)
+ {
+ $this->_connect();
+
+ /* Build the SQL query. */
+ $query = sprintf('DELETE FROM %s WHERE %s = ?',
+ $this->_params['table'],
+ $this->_params['username_field']);
+ $values = array($userId);
+
+ Horde::logMessage('SQL Query by Auth_sql::removeUser(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_write_db->query($query, $values);
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+
+ Horde_Auth::removeUserData($userId);
+ }
+
+ /**
+ * List all users in the system.
+ *
+ * @return array The array of userIds.
+ * @throws Horde_Exception
+ */
+ public function listUsers()
+ {
+ $this->_connect();
+
+ /* Build the SQL query. */
+ $query = sprintf('SELECT %s FROM %s',
+ $this->_params['username_field'],
+ $this->_params['table']);
+
+ Horde::logMessage('SQL Query by Auth_sql::listUsers(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $res = $this->_db->getCol($query);
+ if ($res instanceof PEAR_Error) {
+ throw new Horde_Exception($res);
+ }
+
+ return $res;
+ }
+
+ /**
+ * Checks if a userId exists in the system.
+ *
+ * @return boolean Whether or not the userId already exists.
+ */
+ public function exists($userId)
+ {
+ $this->_connect();
+
+ /* Build the SQL query. */
+ $query = sprintf('SELECT 1 FROM %s WHERE %s = ?',
+ $this->_params['table'],
+ $this->_params['username_field']);
+ $values = array($userId);
+
+ Horde::logMessage('SQL Query by Auth_sql::exists(): ' . $query, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $res = $this->_db->getOne($query, $values);
+ return ($res instanceof PEAR_Error)
+ ? false
+ : $res;
+ }
+
+ /**
+ * Compare an encrypted password to a plaintext string to see if
+ * they match.
+ *
+ * @param string $encrypted The crypted password to compare against.
+ * @param string $plaintext The plaintext password to verify.
+ *
+ * @return boolean True if matched, false otherwise.
+ */
+ protected function _comparePasswords($encrypted, $plaintext)
+ {
+ return $encrypted == Horde_Auth::getCryptedPassword($plaintext,
+ $encrypted,
+ $this->_params['encryption'],
+ $this->_params['show_encryption']);
+ }
+
+ /**
+ * Attempts to open a connection to the SQL server.
+ *
+ * @throws Horde_Exception
+ */
+ function _connect()
+ {
+ if ($this->_connected) {
+ return;
+ }
+
+ Horde::assertDriverConfig($this->_params, 'auth', array('phptype'),
+ 'authentication SQL');
+
+ if (!isset($this->_params['database'])) {
+ $this->_params['database'] = '';
+ }
+ if (!isset($this->_params['username'])) {
+ $this->_params['username'] = '';
+ }
+ if (!isset($this->_params['password'])) {
+ $this->_params['password'] = '';
+ }
+ if (!isset($this->_params['hostspec'])) {
+ $this->_params['hostspec'] = '';
+ }
+ if (empty($this->_params['encryption'])) {
+ $this->_params['encryption'] = 'md5-hex';
+ }
+ if (!isset($this->_params['show_encryption'])) {
+ $this->_params['show_encryption'] = false;
+ }
+ if (empty($this->_params['table'])) {
+ $this->_params['table'] = 'horde_users';
+ }
+ if (empty($this->_params['username_field'])) {
+ $this->_params['username_field'] = 'user_uid';
+ } else {
+ $this->_params['username_field'] = Horde_String::lower($this->_params['username_field']);
+ }
+ if (empty($this->_params['password_field'])) {
+ $this->_params['password_field'] = 'user_pass';
+ } else {
+ $this->_params['password_field'] = Horde_String::lower($this->_params['password_field']);
+ }
+
+ /* Connect to the SQL server using the supplied parameters. */
+ $this->_write_db = &DB::connect($this->_params,
+ array('persistent' => !empty($this->_params['persistent']),
+ 'ssl' => !empty($this->_params['ssl'])));
+ if ($this->_write_db instanceof PEAR_Error) {
+ throw new Horde_Exception($this->_write_db);
+ }
+
+ // Set DB portability options.
+ switch ($this->_write_db->phptype) {
+ case 'mssql':
+ $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+ break;
+ default:
+ $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+ }
+
+ /* Check if we need to set up the read DB connection
+ * seperately. */
+ if (!empty($this->_params['splitread'])) {
+ $params = array_merge($this->_params, $this->_params['read']);
+ $this->_db = &DB::connect($params,
+ array('persistent' => !empty($params['persistent']),
+ 'ssl' => !empty($params['ssl'])));
+ if ($this->_db instanceof PEAR_Error) {
+ throw new Horde_Exception($this->_db);
+ }
+
+ switch ($this->_db->phptype) {
+ case 'mssql':
+ $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+ break;
+ default:
+ $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+ }
+
+ } else {
+ /* Default to the same DB handle for reads. */
+ $this->_db =& $this->_write_db;
+ }
+
+ $this->_connected = true;
+ }
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Auth</name>
+ <channel>pear.horde.org</channel>
+ <summary>Horde Authentication API</summary>
+ <description>The Horde_Auth:: package provides a common abstracted interface into the various backends for the Horde authentication system.
+</description>
+ <lead>
+ <name>Chuck Hagenbuch</name>
+ <user>chuck</user>
+ <email>chuck@horde.org</email>
+ <active>yes</active>
+ </lead>
+ <lead>
+ <name>Jan Schneider</name>
+ <user>jan</user>
+ <email>jan@horde.org</email>
+ <active>yes</active>
+ </lead>
+ <date>2009-07-06</date>
+ <version>
+ <release>0.2.0</release>
+ <api>0.2.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license uri="http://opensource.org/licenses/lgpl-2.1.php">LGPL</license>
+ <notes>* Split Horde_Auth:: into Horde_Auth:: and Horde_Auth_Driver:: components.
+ * Initial Horde 4 package.
+ </notes>
+ <contents>
+ <dir name="/">
+ <dir name="lib">
+ <dir name="Horde">
+ <dir name="Auth">
+ <dir name="Signup">
+ <file name="Datatree.php" role="php" />
+ <file name="Sql.php" role="php" />
+ </dir> <!-- /lib/Horde/Auth/Signup -->
+ <file name="Application.php" role="php" />
+ <file name="Auto.php" role="php" />
+ <file name="Composite.php" role="php" />
+ <file name="Customsql.php" role="php" />
+ <file name="Cyrsql.php" role="php" />
+ <file name="Cyrus.php" role="php" />
+ <file name="Driver.php" role="php" />
+ <file name="Ftp.php" role="php" />
+ <file name="Http.php" role="php" />
+ <file name="HttpRemote.php" role="php" />
+ <file name="Imap.php" role="php" />
+ <file name="Imsp.php" role="php" />
+ <file name="Ipbasic.php" role="php" />
+ <file name="Kolab.php" role="php" />
+ <file name="Krb5.php" role="php" />
+ <file name="Ldap.php" role="php" />
+ <file name="Login.php" role="php" />
+ <file name="Msad.php" role="php" />
+ <file name="Pam.php" role="php" />
+ <file name="Passwd.php" role="php" />
+ <file name="Peclsasl.php" role="php" />
+ <file name="Radius.php" role="php" />
+ <file name="Shibboleth.php" role="php" />
+ <file name="Signup.php" role="php" />
+ <file name="Smb.php" role="php" />
+ <file name="Smbclient.php" role="php" />
+ <file name="Sql.php" role="php" />
+ </dir> <!-- /lib/Horde/Auth -->
+ <file name="Auth.php" role="php" />
+ </dir> <!-- /lib/Horde -->
+ </dir> <!-- /lib -->
+ <dir name="test">
+ <dir name="Horde">
+ <dir name="Auth">
+ <file name="AllTests.php" role="test" />
+ <file name="KolabScenarioTest.php" role="test" />
+ <file name="KolabTest.php" role="test" />
+ <file name="credentials.php" role="test" />
+ <file name="getCryptedPassword.phpt" role="test" />
+ <file name="getSalt.phpt" role="test" />
+ <file name="passwd.phpt" role="test" />
+ <file name="test.passwd" role="test" />
+ </dir> <!-- /test/Horde/Auth -->
+ </dir> <!-- /test/Horde -->
+ </dir> <!-- /test -->
+ </dir> <!-- / -->
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.2.0</min>
+ </php>
+ <pearinstaller>
+ <min>1.5.4</min>
+ </pearinstaller>
+ <package>
+ <name>Horde_Framework</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Secret</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Util</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ </required>
+ <optional>
+ <package>
+ <name>DataTree</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Form</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>History</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Imap_Client</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Kolab_Server</name>
+ <channel>pear.horde.org</channel>
+ <min>0.2.0</min>
+ </package>
+ <extension>
+ <name>gettext</name>
+ </extension>
+ <extension>
+ <name>pam_auth</name>
+ </extension>
+ <extension>
+ <name>sasl</name>
+ </extension>
+ </optional>
+ </dependencies>
+ <phprelease>
+ <filelist>
+ <install name="lib/Horde/Auth/Signup/Datatree.php" as="Horde/Auth/Signup/Datatree.php" />
+ <install name="lib/Horde/Auth/Signup/Sql.php" as="Horde/Auth/Signup/Sql.php" />
+ <install name="lib/Horde/Auth/Application.php" as="Horde/Auth/Application.php" />
+ <install name="lib/Horde/Auth/Auto.php" as="Horde/Auth/Auto.php" />
+ <install name="lib/Horde/Auth/Composite.php" as="Horde/Auth/Composite.php" />
+ <install name="lib/Horde/Auth/Customsql.php" as="Horde/Auth/Customsql.php" />
+ <install name="lib/Horde/Auth/Cyrsql.php" as="Horde/Auth/Cyrsql.php" />
+ <install name="lib/Horde/Auth/Cyrus.php" as="Horde/Auth/Cyrus.php" />
+ <install name="lib/Horde/Auth/Driver.php" as="Horde/Auth/Driver.php" />
+ <install name="lib/Horde/Auth/Ftp.php" as="Horde/Auth/Ftp.php" />
+ <install name="lib/Horde/Auth/Http.php" as="Horde/Auth/Http.php" />
+ <install name="lib/Horde/Auth/HttpRemote.php" as="Horde/Auth/HttpRemote.php" />
+ <install name="lib/Horde/Auth/Imap.php" as="Horde/Auth/Imap.php" />
+ <install name="lib/Horde/Auth/Imsp.php" as="Horde/Auth/Imsp.php" />
+ <install name="lib/Horde/Auth/Ipbasic.php" as="Horde/Auth/Ipbasic.php" />
+ <install name="lib/Horde/Auth/Kolab.php" as="Horde/Auth/Kolab.php" />
+ <install name="lib/Horde/Auth/Krb5.php" as="Horde/Auth/Krb5.php" />
+ <install name="lib/Horde/Auth/Ldap.php" as="Horde/Auth/Ldap.php" />
+ <install name="lib/Horde/Auth/Login.php" as="Horde/Auth/Login.php" />
+ <install name="lib/Horde/Auth/Msad.php" as="Horde/Auth/Msad.php" />
+ <install name="lib/Horde/Auth/Pam.php" as="Horde/Auth/Pam.php" />
+ <install name="lib/Horde/Auth/Passwd.php" as="Horde/Auth/Passwd.php" />
+ <install name="lib/Horde/Auth/Peclsasl.php" as="Horde/Auth/Peclsasl.php" />
+ <install name="lib/Horde/Auth/Radius.php" as="Horde/Auth/Radius.php" />
+ <install name="lib/Horde/Auth/Shibboleth.php" as="Horde/Auth/Shibboleth.php" />
+ <install name="lib/Horde/Auth/Signup.php" as="Horde/Auth/Signup.php" />
+ <install name="lib/Horde/Auth/Smb.php" as="Horde/Auth/Smb.php" />
+ <install name="lib/Horde/Auth/Smbclient.php" as="Horde/Auth/Smbclient.php" />
+ <install name="lib/Horde/Auth/Sql.php" as="Horde/Auth/Sql.php" />
+ <install name="lib/Horde/Auth.php" as="Horde/Auth.php" />
+ </filelist>
+ </phprelease>
+ <changelog>
+ <release>
+ <date>2008-10-29</date>
+ <version>
+ <release>0.1.1</release>
+ <api>0.1.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
+ <notes>* Imap driver now uses Horde_Imap_Client library.
+ * Add signup drivers to package.xml (Bug #7345).
+ * Fix the "overwriting realm info from application auth drivers" (Bug #6749)
+ * Switched Kolab auth handling from IMAP to LDAP by using Kolab_Server.
+ * Added "add user" capability to the Kolab driver.
+ * Adapted the Kolab auth driver to the changes in Kolab_Server.
+ </notes>
+ </release>
+ <release>
+ <version>
+ <release>0.1.0</release>
+ <api>0.1.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
+ <notes>* Fixed error handling when removing user data.
+* Retrieve password where necessary for salt calculations in the custom SQL driver (Bug #3739).
+* Added a query for checking existance of a user to the custom SQL driver (Request #5151).
+* Completed Cyrus virtual domain support in the cyrsql driver.
+* Fixed handling of UID logins in the Kolab driver (Bugs #1317, #4662).
+* Fixed case handling in the LDAP driver (Bug #2435).
+* Rewrote PAM driver to use PAM extension from PECL.
+* Fixed the PAM driver authentication so that it works with both pam and pam_auth extensions (Bug #6860).
+* Ensured that the LDAP driver always uses the configured filter.
+* Renamed Auth_sasl to Auth_peclsasl (Bug #4547).
+* Allow signup hooks to override the user_name and password fields (Request #2904).
+* Created an SQL driver for signups and allowing backends other than DataTree (Request #7161).
+* Added smbclient driver as an alternative to the smb driver which requires a PHP extension.
+* Fix handling of SSHA and SMD5 salts (ulrich-horde@topfen.net, Bug #2863).
+* Added readSessionData() function to get Horde authentication data from session strings.
+* Allow drivers to include their own isAdmin() implementations (Request #5521).
+* Add Active Directory extension of LDAP driver (Request #3769).
+* Hide the cyrus account in cyrsql driver (vilius@lnk.lt, Request #5626).
+* Bring the passwd driver into compliance with the Auth API.
+* Remove dependency on the mhash extension for some encryption types.
+* Add authentication against a remote HTTP Authentication endpoint (duck@obala.net).
+* CSRF token protections for logout links.
+* Call the postauthenticate hook in Horde_Auth::setAuth() instead of authenticate().
+* Modified the Kolab driver to use the new Kolab_Server package.
+* Improved handling of multiple IMAP server setups in the Kolab driver.
+ </notes>
+ </release>
+ </changelog>
+</package>
--- /dev/null
+<?php
+/**
+ * All tests for the Horde_Auth:: package.
+ *
+ * $Horde: framework/Auth/tests/Horde/Auth/AllTests.php,v 1.2 2009/01/06 17:49:08 jan Exp $
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Auth
+ * @subpackage UnitTests
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Auth
+ */
+
+/**
+ * Define the main method
+ */
+if (!defined('PHPUnit_MAIN_METHOD')) {
+ define('PHPUnit_MAIN_METHOD', 'Horde_Auth_AllTests::main');
+}
+
+require_once 'PHPUnit/Framework/TestSuite.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+
+/**
+ * Combine the tests for this package.
+ *
+ * $Horde: framework/Auth/tests/Horde/Auth/AllTests.php,v 1.2 2009/01/06 17:49:08 jan Exp $
+ *
+ * Copyright 2007-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://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package Auth
+ * @subpackage UnitTests
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Auth
+ */
+class Horde_Auth_AllTests
+{
+
+ /**
+ * Main entry point for running the suite.
+ *
+ * @return NULL
+ */
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ /**
+ * Collect the unit tests of this directory into a new suite.
+ *
+ * @return PHPUnit_Framework_TestSuite The test suite.
+ */
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite('Horde Framework - Auth');
+
+ $basedir = dirname(__FILE__);
+ $baseregexp = preg_quote($basedir . DIRECTORY_SEPARATOR, '/');
+
+ foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($basedir)) as $file) {
+ if ($file->isFile() && preg_match('/Test.php$/', $file->getFilename())) {
+ $pathname = $file->getPathname();
+ require $pathname;
+
+ $class = str_replace(DIRECTORY_SEPARATOR, '_',
+ preg_replace("/^$baseregexp(.*)\.php/",
+ '\\1', $pathname));
+ $suite->addTestSuite('Horde_Auth_' . $class);
+ }
+ }
+
+ return $suite;
+ }
+
+}
+
+if (PHPUnit_MAIN_METHOD == 'Horde_Auth_AllTests::main') {
+ Horde_Auth_AllTests::main();
+}
--- /dev/null
+<?php
+/**
+ * Kolab authentication scenarios.
+ *
+ * $Horde: framework/Auth/tests/Horde/Auth/KolabScenarioTest.php,v 1.3 2009/03/20 23:38:13 wrobel Exp $
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Auth
+ * @subpackage UnitTests
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Auth
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Storage.php';
+
+/**
+ * Kolab authentication scenarios.
+ *
+ * $Horde: framework/Auth/tests/Horde/Auth/KolabScenarioTest.php,v 1.3 2009/03/20 23:38:13 wrobel Exp $
+ *
+ * Copyright 2008-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://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Auth_KolabScenarioTest extends Horde_Kolab_Test_Storage
+{
+ /**
+ * Test loggin in after a user has been added.
+ *
+ * @scenario
+ *
+ * @return NULL
+ */
+ public function login()
+ {
+ $test_user = $this->provideBasicUserOne();
+
+ $this->given('a populated Kolab setup')
+ ->and('the Kolab auth driver has been selected')
+ ->when('logging in as a user with a password', $test_user['mail'],
+ $test_user['userPassword'])
+ ->then('the login was successful');
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Kolab authentication tests.
+ *
+ * $Horde: framework/Auth/tests/Horde/Auth/KolabTest.php,v 1.4 2009/04/01 07:59:47 wrobel Exp $
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Auth
+ * @subpackage UnitTests
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Auth
+ */
+
+/**
+ * We need the base class
+ */
+require_once 'Horde/Kolab/Test/Storage.php';
+
+/**
+ * Kolab authentication tests.
+ *
+ * $Horde: framework/Auth/tests/Horde/Auth/KolabTest.php,v 1.4 2009/04/01 07:59:47 wrobel Exp $
+ *
+ * Copyright 2008-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://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Kolab
+ * @package Kolab_Server
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Auth_KolabTest extends Horde_Kolab_Test_Storage
+{
+ /**
+ * Test loggin in after a user has been added.
+ *
+ * @return NULL
+ */
+ public function testLogin()
+ {
+ /** Create the test base */
+ $world = &$this->prepareBasicSetup();
+ $server = $world['server'];
+ $auth = $world['auth'];
+
+ /** Ensure we always use the test server */
+ $GLOBALS['conf']['kolab']['server']['driver'] = 'test';
+
+ $uid = $server->uidForIdOrMail('wrobel@example.org');
+ $this->assertEquals('cn=Gunnar Wrobel,dc=example,dc=org', $uid);
+
+ $result = $auth->authenticate('wrobel@example.org',
+ array('password' => 'none'));
+ $this->assertNoError($result);
+ $this->assertTrue($result);
+
+ $session = Horde_Kolab_Session::singleton();
+ $this->assertNoError($session->user_mail);
+ $this->assertEquals('wrobel@example.org', $session->user_mail);
+
+ $result = $auth->authenticate('wrobel@example.org',
+ array('password' => 'invalid'));
+ $this->assertFalse($result);
+
+ /** Ensure we don't use a connection from older tests */
+ $server->unbind();
+
+ $result = $auth->authenticate('wrobel',
+ array('password' => 'invalid'));
+ $this->assertNoError($result);
+ $this->assertFalse($result);
+
+ /** Ensure we don't use a connection from older tests */
+ $server->unbind();
+ $result = $auth->authenticate('wrobel',
+ array('password' => 'none'));
+ $this->assertNoError($result);
+ $this->assertTrue($result);
+
+ $session = Horde_Kolab_Session::singleton();
+ $this->assertNoError($session->user_mail);
+ $this->assertEquals('wrobel@example.org', $session->user_mail);
+
+ $this->assertEquals('wrobel@example.org', Horde_Auth::getAuth());
+ }
+}
\ No newline at end of file
--- /dev/null
+--TEST--
+Horde_Auth_Passwd:: test
+--FILE--
+<?php
+
+require_once dirname(__FILE__) . '/../../../lib/Horde/Auth.php';
+require_once dirname(__FILE__) . '/../../../lib/Horde/Auth/Driver.php';
+require_once dirname(__FILE__) . '/../../../lib/Horde/Auth/Passwd.php';
+
+$auth = Horde_Auth::factory('passwd', array('filename' => dirname(__FILE__) . '/test.passwd'));
+
+// List users
+var_dump($auth->listUsers());
+
+// Authenticate
+var_dump($auth->authenticate('user', array('password' => 'password'), false));
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ string(4) "user"
+}
+bool(true)
--- /dev/null
+user:3U6GxZSGmKPGA
$history = &$this->getHistory($guid);
if (!isset($attributes['who'])) {
- $attributes['who'] = Auth::getAuth();
+ $attributes['who'] = Horde_Auth::getAuth();
}
if (!isset($attributes['ts'])) {
$attributes['ts'] = time();
{
include_once 'Horde/Auth.php';
- $auth = Auth::singleton('kolab');
+ $auth = Horde_Auth::singleton('kolab');
return $auth;
}
global $conf;
if (empty($user)) {
- $user = Auth::getAuth();
+ $user = Horde_Auth::getAuth();
if (empty($user)) {
$user = 'anonymous';
} else if (!strpos($user, '@')) {
$params = array();
if ($this->user_uid) {
$params['uid'] = $this->user_uid;
- $params['pass'] = Auth::getCredential('password');
+ $params['pass'] = Horde_Auth::getCredential('password');
} else if (isset($user)) {
$params['user'] = $user;
if (isset($credentials['password'])) {
$params['pass'] = $credentials['password'];
} else {
- $params['pass'] = Auth::getCredential('password');
+ $params['pass'] = Horde_Auth::getCredential('password');
}
}
return Horde_Kolab_Server::singleton($params);
return $imap;
}
- $result = $imap->connect(Auth::getAuth(),
- Auth::getCredential('password'));
+ $result = $imap->connect(Horde_Auth::getAuth(),
+ Horde_Auth::getCredential('password'));
if (is_a($result, 'PEAR_Error')) {
return $result;
}
}
if (empty($user)) {
- $user = Auth::getAuth();
+ $user = Horde_Auth::getAuth();
}
if ($destruct || empty($session)
/* If this application handles Horde auth, need to add Horde tasks
* here. */
$app_list = array($this->_app);
- if (strnatcasecmp($this->_app, Auth::getProvider()) === 0) {
+ if (strnatcasecmp($this->_app, Horde_Auth::getProvider()) === 0) {
array_unshift($app_list, 'horde');
}
if (empty($need_display)) {
$lasttasks = unserialize($GLOBALS['prefs']->getValue('last_logintasks'));
$lasttasks[$this->_app] = time();
- if (strnatcasecmp($this->_app, Auth::getProvider()) === 0) {
+ if (strnatcasecmp($this->_app, Horde_Auth::getProvider()) === 0) {
$lasttasks['horde'] = time();
}
$GLOBALS['prefs']->setValue('last_logintasks', serialize($lasttasks));
$options['listeners'] = array_map('strtolower', $options['listeners']);
if ($this->_alarm && in_array('status', $options['listeners'])) {
- $this->_alarm->notify(Auth::getAuth());
+ $this->_alarm->notify(Horde_Auth::getAuth());
}
foreach ($options['listeners'] as $listener) {
* 'contacts.search',
* array(array('jan'), array('localsql'),
* array('name', 'email')),
- * array('user' => Auth::getAuth(),
- * 'pass' => Auth::getCredential('password')));
+ * array('user' => Horde_Auth::getAuth(),
+ * 'pass' => Horde_Auth::getCredential('password')));
* </code>
*
* Copyright 2002-2009 The Horde Project (http://www.horde.org/)
return true;
}
- $auth = &Auth::singleton($GLOBALS['conf']['auth']['driver']);
+ $auth = Horde_Auth::singleton($GLOBALS['conf']['auth']['driver']);
if (isset($_SERVER['PHP_AUTH_USER'])) {
$user = $_SERVER['PHP_AUTH_USER'];
}
// Be authenticated or call system.login.
- $auth = &Auth::singleton($GLOBALS['conf']['auth']['driver']);
- $authenticated = $auth->isAuthenticated() || $method== "phpgw/system/login";
+ $authenticated = Horde_Auth::isAuthenticated() || $method== "phpgw/system/login";
if ($authenticated) {
Horde::logMessage("rpc call $method allowed", __FILE__, __LINE__, PEAR_LOG_NOTICE);
$GLOBALS['__horde_rpc_PhpSoap']['lastMethodCalled'],
implode(', ', array_map(create_function('$a', 'return is_array($a) ? "Array" : $a;'),
$GLOBALS['__horde_rpc_PhpSoap']['lastMethodParams'])),
- Auth::getAuth(),
+ Horde_Auth::getAuth(),
time() - $beginTime,
ob_get_length()),
__FILE__, __LINE__, PEAR_LOG_INFO
$locktype = HORDE_LOCK_TYPE_EXCLUSIVE;
}
- $lockid = $locks->setLock(Auth::getAuth(), 'webdav', $params['path'],
+ $lockid = $locks->setLock(Horde_Auth::getAuth(), 'webdav', $params['path'],
$timeout, $locktype);
if (is_a($lockid, 'PEAR_Error')) {
}
$params['locktoken'] = $lockid;
- $params['owner'] = Auth::getAuth();
+ $params['owner'] = Horde_Auth::getAuth();
$params['timeout'] = $timeout;
return "200";
*/
function check_auth($type, $username, $password)
{
- $auth = &Auth::singleton($GLOBALS['conf']['auth']['driver']);
+ $auth = Horde_Auth::singleton($GLOBALS['conf']['auth']['driver']);
return $auth->authenticate($username, array('password' => $password));
}
* Returns a list of authenticated users and data about their session.
*
* @return array For authenticated users, the sessionid as a key and the
- * information returned from Auth::readSessionData() as
+ * information returned from Horde_Auth::readSessionData() as
* values.
* @throws Horde_Exception
*/
} catch (Horde_Exception $e) {
continue;
}
- $data = Auth::readSessionData($data, true);
+ $data = Horde_Auth::readSessionData($data, true);
if ($data !== false) {
$info[$id] = $data;
}