<?php

require_once dirname(__FILE__) . '/../Horde.php';

/**
 * The Auth_composite class provides a wrapper around
 * application-provided Horde authentication which fits inside the
 * Horde Auth:: API.
 *
 * Required parameters:
 * ====================
 *   'app'  --  The application which is providing authentication.
 *
 *
 * $Horde: horde/lib/Auth/composite.php,v 1.15 2003/07/10 21:42:55 slusarz Exp $
 *
 * Copyright 2002-2003 Chuck Hagenbuch <chuck@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.
 *
 * @author  Chuck Hagenbuch <chuck@horde.org>
 * @version $Revision: 1.15 $
 * @since   Horde 3.0
 * @package horde.auth
 */
class Auth_composite extends Auth {

    /**
     * Hash containing any instantiated drivers.
     *
     * @var array $_drivers
     */
    var $_drivers = array();

    /**
     * Constructs a new Composite authentication object.
     *
     * @access public
     *
     * @param optional array $params  A hash containing connection parameters.
     */
    function Auth_composite($params = array())
    {
        $this->_setParams($params);
    }

    /**
     * Set connection parameters.
     *
     * @access private
     *
     * @param array $params  A hash containing connection parameters.
     */
    function _setParams($params)
    {
        if (!is_array($params)) {
            Horde::fatal(PEAR::raiseError('No configuration information specified for Composite authentication.'), __FILE__, __LINE__);
        }

        $this->_params = $params;
    }

    /**
     * Find out if a set of login credentials are valid.
     *
     * @access private
     *
     * @param string $userID      The userID to check.
     * @param array $credentials  The credentials to use.
     *
     * @return boolean  Whether or not the credentials are valid.
     */
    function _authenticate($userID, $credentials)
    {
        if (($auth_driver = Auth::_getDriverByParam('loginscreen_switch', $this->_params)) &&
            $this->_loadDriver($auth_driver)) {
            return $this->_drivers[$auth_driver]->authenticate($userID, $credentials);
        }

        if (($auth_driver = Auth::_getDriverByParam('username_switch', $this->_params, array($userID))) &&
            $this->_loadDriver($auth_driver)) {
            return $this->_drivers[$auth_driver]->hasCapability('transparent');
        }

        $this->_setAuthError();
        return false;
    }

    /**
     * Query the current Auth object to find out if it supports the
     * given capability.
     *
     * @access public
     *
     * @param string $capability  The capability to test for.
     *
     * @return boolean  Whether or not the capability is supported.
     */
    function hasCapability($capability)
    {
        switch ($capability) {
        case 'add':
        case 'update':
        case 'remove':
        case 'list':
            if (array_key_exists('admin_driver', $this->_params) &&
                $this->_loadDriver($this->_params['admin_driver'])) {
                return $this->_drivers[$this->_params['admin_driver']]->hasCapability($capability);
            } else {
                return false;
            }
            break;

        case 'transparent':
            if (($login_driver = Auth::_getDriverByParam('loginscreen_switch', $this->_params)) &&
                $this->_loadDriver($login_driver)) {
                return $this->_drivers[$login_driver]->hasCapability('transparent');
            }
            return false;
            break;

        case 'loginscreen':
            return !empty($this->_params['loginscreen_switch']);
            break;

        default:
            return false;
        }
    }

    /**
     * Automatic authentication: Find out if the client matches an
     * allowed IP block.
     *
     * @access public
     *
     * @return boolean  Whether or not the client is allowed.
     */
    function transparent()
    {
        if (($login_driver = 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 method.
     *
     * @access public
     *
     * @return string  The login screen URI.
     */
    function getLoginScreen()
    {
        if (($login_driver = Auth::_getDriverByParam('loginscreen_switch', $this->_params)) &&
            $this->_loadDriver($login_driver)) {
            return $this->_drivers[$login_driver]->getLoginScreen();
        } else {
            return parent::getLoginScreen();
        }
    }

    /**
     * Add a set of authentication credentials.
     *
     * @param string $userID       The userID to add.
     * @param array  $credentials  The credentials to use.
     *
     * @return mixed        True on success or a PEAR_Error object on failure.
     */
    function addUser($userID, $credentials)
    {
        if (array_key_exists('admin_driver', $this->_params) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->addUser($userID, $credentials);
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * Update a set of authentication credentials.
     *
     * @param string $oldID       The old userID.
     * @param string $newID       The new userID.
     * @param array $credentials  The new credentials
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function updateUser($oldID, $newID, $credentials)
    {
        if (array_key_exists('admin_driver', $this->_params) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->updateUser($oldID, $newID, $credentials);
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * Delete a set of authentication credentials.
     *
     * @param string $userID  The userID to delete.
     *
     * @return mixed  True on success or a PEAR_Error object on failure.
     */
    function removeUser($userID)
    {
        if (array_key_exists('admin_driver', $this->_params) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->removeUser($userID);
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * List all users in the system.
     *
     * @return mixed  The array of userIDs, or a PEAR_Error object on failure.
     */
    function listUsers()
    {
        if (array_key_exists('admin_driver', $this->_params) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->listUsers();
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * Checks if a userID exists in the system.
     *
     * @access public
     *
     * @return boolean  Whether or not the userID already exists.
     */
    function exists($userID)
    {
        if (array_key_exists('admin_driver', $this->_params) &&
            $this->_loadDriver($this->_params['admin_driver'])) {
            return $this->_drivers[$this->_params['admin_driver']]->exists($userID);
        } else {
            return PEAR::raiseError('Unsupported');
        }
    }

    /**
     * Loads one of the drivers in our configuration array, if it
     * isn't already loaded.
     *
     * @access private
     *
     * @param string $driver  The name of the driver to load.
     */
    function _loadDriver($driver)
    {
        if (!array_key_exists($driver, $this->_drivers)) {
            // This is a bit specialized for Horde::getDriverConfig(),
            // so localize it here:
            global $conf;
            if (array_key_exists('params', $this->_params['drivers'][$driver])) {
                $params = $this->_params['drivers'][$driver]['params'];
            } elseif (array_key_exists($driver, $conf)) {
                $params = $conf[$driver];
            } else {
                $params = null;
            }

            $this->_drivers[$driver] = &Auth::singleton($this->_params['drivers'][$driver]['driver'], $params);
        }

        return array_key_exists($driver, $this->_drivers);
    }

}
