[geeklog-cvs] Auth_Enterprise/Server/providers BasePearDBProvider.php,NONE,1.1 BaseServerProvider.php,NONE,1.1 LDAPProvider.php,NONE,1.1 PearDBProvider.php,NONE,1.1

tony at iowaoutdoors.org tony at iowaoutdoors.org
Sun Jul 4 11:28:17 EDT 2004


Update of /var/cvs/Auth_Enterprise/Server/providers
In directory www:/tmp/cvs-serv25047

Added Files:
	BasePearDBProvider.php BaseServerProvider.php LDAPProvider.php 
	PearDBProvider.php 
Log Message:
Had to rename classes and files to adhere to PEAR coding standards


--- NEW FILE: BasePearDBProvider.php ---
<?php

/**
* Auth_Enterprise
*
* This source file is subject to version 2.02 of the PHP license, that is bundled with this package
* in the file LICENSE, and is available at through the world-wide-web at
* http://www.php.net/license/2_02.txt. If you did not receive a copy of the PHP license and are
* unable to obtain it through the world-wide-web, please send a note to license at php.net so we can
* mail you a copy immediately.
*
* @author Tony Bibbs <tony at geeklog.net>
* @copyright 2004
* @version $Id: BasePearDBProvider.php,v 1.1 2004/07/04 15:28:15 tony Exp $
*
*/

/**
* Service user object
*/
require_once 'Auth/Enterprise/Server/ServiceUser.php';

/**
* Bring in the base Auth_Enterprise provider
*/
require_once 'Auth/Enterprise/Server/providers/BaseServerProvider.php';

/**
* The Auth_Enterprise group domain object
*/
require_once 'Auth/Enterprise/Common/Group.php';

/**
* The Auth_Enterprise privilege domain object
*/
require_once 'Auth/Enterprise/Common/Privilege.php';

/**
* Include the password generator
*/
require_once 'Auth/Enterprise/Server/PasswordGenerator.php';

/**
* Bring in PEAR's database abstraction layer
*/
require_once 'DB.php';

/**
* Auth_Enteprise PEAR database provider
*
* This is the abstract PEAR::DB provider.  It implements all of the methods in the
* AEServiceInterface except for the authenticate() and createAccount methods.  By doing this you
* can quickly add descendants who have custom authentication behavior (e.g. LDAP) but use the
* PEAR::DB datastore for all authorization needs.
*
* @author Tony Bibbs <tony at geeklog.net>
* @package net.geeklog.auth_enterprise.common
*
*/
abstract class Enterprise_BasePearDBProvider extends Enterprise_BaseServerProvider {
    /**
    * Instance of PEAR::DB Object
    * @access protected
    * @var object
    */
    protected $db = null;
    
    /**
    * Constructor
    *
    * Build PEAR DB database connection.  NOTE: we have turned on PEAR::DB's
    * portability feature as Auth_Enterprise should run in literally any
    * DBMS environment
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    public function __construct($appId)
    {
        global $gConf;
        
        parent::__construct($appId);
        
        $dsn = sprintf("%s://%s:%s@%s/%s"
            , $gConf[AE_PROVIDER_PEAR_DB]['dbms']
            , $gConf[AE_PROVIDER_PEAR_DB]['dbuser']
            , $gConf[AE_PROVIDER_PEAR_DB]['dbpassword']
            , $gConf[AE_PROVIDER_PEAR_DB]['dbhost']
            , $gConf[AE_PROVIDER_PEAR_DB]['dbname']);
        $options = array('portability' => DB_PORTABILITY_ALL);
        $this->db = DB::connect($dsn, $options);
        
        if (DB::isError($this->db)) {
            throw new AEUnableToConnect('Unable to connect to Auth_Enterprise PEAR::DB datasource');
        }
    }
    
    /**
    * Gets the application privileges for a given user
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $adminUserName Administrator's Username
    * @param string $adminPassword Administrator's Password
    * @param string $userName User to get privileges for
    * @return array AEPrivilege array
    *
    */
    public function getUserPrivilegesByAdmin($adminUserName, $adminPassword, $userName)
    {
        // Make sure admin is authenticated
        if (!$this->isAuthenticated) {
            try {
                $userObj = $this->authenticate($adminUserName, $adminPassword);
            } catch (AESQLException $e) {
                throw $e;
            } catch (AEAccountLocked $e) {
                throw new AEAccountLocked('Administrator\'s account is locked');
            } catch (AEPasswordExpired $e) {
                throw new AEPasswordExpired('Administrator\'s password has expired');
            }
        }
        
        try {
            return $this->getPrivileges($userName);
        } catch (AESQLException $e) {
            throw $e;
        }
    }
    
    /**
    * Sets the application privileges for a given user
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $adminUserName Administrator's Username
    * @param string $adminPassword Administrator's Password
    * @param string $userName User to set privilege for
    * @param array $privArray Array of AEPrivilege objects
    *
    */
    public function setUserPrivilegesByAdmin($adminUserName, $adminPassword, $userName, $privArray)
    {
        // Make sure admin is authenticated
        if (!$this->isAuthenticated) {
            try {
                $userObj = $this->authenticate($adminUserName, $adminPassword);
            } catch (AESQLException $e) {
                throw $e;
            } catch (AEAccountLocked $e) {
                throw new AEAccountLocked('Administrator\'s account is locked');
            } catch (AEPasswordExpired $e) {
                throw new AEPasswordExpired('Administrator\'s password has expired');
            }
        }
     
        // Verify privileges given even exist
        foreach ($privArray as $curPriv) {
            try {
                if (!$this->privilegeExists($curPriv)) {
                    throw new AEInvalidPrivilege(sprintf('Privilege %s does not exist
                        for application %s', $curPriv->getPrivilegeCode(), $this->appId));
                }
            } catch (AESQLException $e) {
                throw $e;
            }
        }
        
        $userName = strtoupper($userName);
        
        // First delete any privileges the user has (and start SQL transaction)
        if ($this->db->provides('transactions')) {
            $this->db->autoCommit(false);
        }
        
        $prepStmt = $this->db->prepare('DELETE FROM ae_privilege_access WHERE pa_user_name = ?');
        $result = $this->db->execute($prepStmt, array($userName));
        if (DB::isError($result)) {
            $this->db->autoCommit(true);
            throw new AESQLException($result->toString());
        }
        
        // Now add privileges for the user
        $prepStmt = $this->db->prepare('INSERT INTO ae_privilege_access
            (pa_priv_cd, pa_app_id, pa_user_name, pa_grp_id)
            VALUES (?,?,?,NULL)');
        foreach ($privArray as $curPriv) {
            $result = $this->db->execute($prepStmt, array($curPriv->getPrivilegeCode(),
                $this->appId, $userName));
            if (DB::isError($result)) {
                if ($this->db->provides('transactions')) {
                    $this->db->rollback();
                    $this->db->autoCommit(true);
                }
                throw new AESQLException($result->toString());
            }
        }
        
        if ($this->db->provides('transactions')) {
            $this->db->commit();
            $this->db->autoCommit(true);
        }
    }
    
    /**
    * Lists all available privileges for a given application
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    public function listAppPrivilegesByAdmin($adminUserName, $adminPassword)
    {
        // Make sure admin is authenticated
        if (!$this->isAuthenticated) {
            try {
                $userObj = $this->authenticate($adminUserName, $adminPassword);
            } catch (AESQLException $e) {
                throw $e;
            } catch (AEAccountLocked $e) {
                throw new AEAccountLocked('Administrator\'s account is locked');
            } catch (AEPasswordExpired $e) {
                throw new AEPasswordExpired('Administrator\'s password has expired');
            }
        }
        
        if (!$userObj->authorize('AE_ACCOUNT_MGR')) {
            throw new AEUserNotAuthorized('This user is not an account manager and is unable to
                list the privileges for this application');
        }
        
        // Get privileges 
        $prepStmt = $this->db->prepare('SELECT ap_priv_cd, ap_priv_desc
            FROM ae_app_privileges 
            WHERE ap_app_id = ?');
        $result = $this->db->execute($prepStmt, array($this->appId));
        //print $result->numRows(); exit;
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        
        return $this->dbResultToPrivilege($result);
    }
    
    /**
    * Gets the groups for a given user on behalf of an administrator
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $adminUserName Administrator's username
    * @param string $adminPassword Administrator's password
    * @param string $userName User to get groups for
    * @return array Array of AEGroup objects
    *
    */
    public function getUserGroupsByAdmin($adminUserName, $adminPassword, $userName)
    {
        // Make sure admin is authenticated
        if (!$this->isAuthenticated) {
            try {
                $userObj = $this->authenticate($adminUserName, $adminPassword);
            } catch (AESQLException $e) {
                throw $e;
            } catch (AEAccountLocked $e) {
                throw new AEAccountLocked('Administrator\'s account is locked');
            } catch (AEPasswordExpired $e) {
                throw new AEPasswordExpired('Administrator\'s password has expired');
            }
        }
     
        $userName = strtoupper($userName);

        if (!$userObj->authorize('AE_ACCOUNT_MGR')) {
            throw new AEUserNotAuthorized('This user is not an account manager and is unable to
                get the groups for the given user');
        }
        
        try {   
            return $this->getGroups($userName);
        } catch (AESQLException $e) {
            throw $e;
        }
    }
    
    /**
    * Gets the groups for a given user on behalf of an administrator
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $adminUserName Administrator's username
    * @param string $adminPassword Administrator's password
    * @param string $userName User to set groups for
    * @param array $groupArray Array of AEGroup objects
    *
    */
    public function setUserGroupsByAdmin($adminUserName, $adminPassword, $userName, $groupArray)
    {
        // Make sure admin is authenticated
        if (!$this->isAuthenticated) {
            try {
                $userObj = $this->authenticate($adminUserName, $adminPassword);
            } catch (AESQLException $e) {
                throw $e;
            } catch (AEAccountLocked $e) {
                throw new AEAccountLocked('Administrator\'s account is locked');
            } catch (AEPasswordExpired $e) {
                throw new AEPasswordExpired('Administrator\'s password has expired');
            }
        }
     
        // Verify privileges given even exist
        foreach ($groupArray as $curGroup) {
            try {
                if (!$this->groupExists($curGroup)) {
                    throw new AEInvalidGroup(sprintf('Group %s does not exist
                        for application %s', $curGroup->getGroupLogicalName(), $this->appId));
                }
            } catch (AESQLException $e) {
                throw $e;
            }
        }
        
        $userName = strtoupper($userName);
        
        // First delete any privileges the user has (and start SQL transaction)
        if ($this->db->provides('transactions')) {
            $this->db->autoCommit(false);
        }
        
        $prepStmt = $this->db->prepare('DELETE FROM ae_group_assignment
            WHERE ga_assigned_user_name = ?');
        $result = $this->db->execute($prepStmt, array($userName));
        if (DB::isError($result)) {
            $this->db->autoCommit(true);
            throw new AESQLException($result->toString());
        }
        
        // Now add groups for the user
        $prepStmt = $this->db->prepare('INSERT INTO ae_group_assignment
            (ga_main_grp_id, ga_assigned_user_name, ga_assigned_grp_id)
            VALUES (?,?,NULL)');
        foreach ($groupArray as $curGroup) {
            $result = $this->db->execute($prepStmt, array($curGroup->getGroupId(),$userName));
            if (DB::isError($result)) {
                if ($this->db->provides('transactions')) {
                    $this->db->rollback();
                    $this->db->autoCommit(true);
                }
                throw new AESQLException($result->toString());
            }
        }
        
        if ($this->db->provides('transactions')) {
            $this->db->commit();
            $this->db->autoCommit(true);
        }
    }
    
    /**
    * Determines if a given group exists for the current application
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param object $groupObj AEGroup instance
    * @return boolean
    *
    */
    private function groupExists($groupObj)
    {
        $prepStmt = $this->db->prepare('SELECT count(*)
            FROM ae_group
            WHERE grp_app_id = ?
            AND grp_id = ?');
        $result = $this->db->execute($prepStmt, array($this->appId, $groupObj->getGroupId()));
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        $row = $result->fetchRow($this->fetchMode);
        
        if ($row[0] == 1) {
            return true;
        }
        
        return false;
    }
    
    /**
    * Determines if a given privilege exists for the current application
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param object $privObj AEPrivilege instance
    * @return boolean
    *
    */
    private function privilegeExists($privObj)
    {
        $prepStmt = $this->db->prepare('SELECT count(*)
            FROM ae_app_privileges
            WHERE ap_app_id = ?
            AND ap_priv_cd = ?');
        $result = $this->db->execute($prepStmt, array($this->appId, $privObj->getPrivilegeCode()));
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        $row = $result->fetchRow($this->fetchMode);
        
        if ($row[0] == 1) {
            return true;
        }
        
        return false;
    }
    
    /**
    * Gets the privileges in an application for the given user
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access protected
    * @param string $userName User to get privileges for
    * @return array Array of AEPrivilege objects
    *
    */
    protected function getPrivileges($userName)
    {
        $username = strtoupper($userName);
        
        $prepStmt = $this->db->prepare('SELECT ap_priv_cd, ap_priv_desc
            FROM ae_privilege_access, ae_app_privileges 
            WHERE ap_app_id = pa_app_id
            AND pa_app_id = ?
            AND pa_user_name = ?');
        $result = $this->db->execute($prepStmt, array($this->appId, $userName));
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        
        return $this->dbResultToPrivilege($result);
    }
    
    /**
    * Gets the groups a user belongs to
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access protected
    * @param string $appId Application ID
    * @param string $userName Username
    * @param array $userGroups Groups collected for user
    * @param int $currentGroupId The group ID we are currently working on
    * @return array Array of groups user belongs to
    *
    */
    protected function getGroups($userName, $userGroups = '', $currentGroupId = '')
    {
        if (empty($userGroups)) {
            $userGroups = array();
        }
        
        if (empty($currentGroupId)) {
            $prepStmt = $this->db->prepare('SELECT ga_main_grp_id,grp_display_name,grp_logical_name,grp_descr
                                            FROM ae_group_assignment,ae_group,ae_user
                                            WHERE grp_app_id = ?
                                            AND grp_id = ga_main_grp_id
                                            AND user_name = ga_assigned_user_name
                                            AND ga_assigned_user_name = ?');
            $result = $this->db->execute($prepStmt, array($this->appId, $userName));
        } else {
            $prepStmt = $this->db->prepare('SELECT ga_main_grp_id,grp_display_name,grp_logical_name,grp_descr
                                            FROM ae_group_assignment,ae_group
                                            WHERE grp_id = ga_main_grp_id
                                            AND ga_assigned_grp_id = ?');
            $result = $this->db->execute($prepStmt, array($currentGroupId));
        }
        
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        
        if (!$result->numRows()) {
            return $userGroups;
        }
        $curGroup = new Enterprise_Group();
        while ($row = $result->fetchRow($this->_fetchMode)) {
            $curGroup->setGroupId($row[0]);
            $curGroup->setGroupLogicalName($row[2]);
            $curGroup->setGroupDisplayName($row[1]);
            $curGroup->setGroupDesc($row[3]);
            $userGroups[] = $curGroup;
            $userGroups = $this->getGroups($userName,$userGroups,$row[0]);
        }
        
        if (is_array($userGroups)) {
            ksort($userGroups);
        }
        
        return $userGroups;
    }
    
    /**
    * Checks to see if the given password has been used recently
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access protected
    * @param string $password Password to check against the history
    * @return boolean
    * @todo need to encrypte password once testing is over
    *
    */
    protected function passwordInHistory($userName, $password)
    {
        global $gConf;
        
        //$encryptedPass = MD5($password);
        $encryptedPass = $password;
        
        $prepStmt = $this->db->prepare('SELECT COUNT(*)
            FROM ae_user_old_password WHERE uop_password = ? AND uop_user_name = ?');
        $result = $this->db->execute($prepStmt, array($encryptedPass, $userName));
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        $row = $result->fetchRow($this->fetchMode);
        if ($row[0] > 0) {
            return true;
        } else {
            return false;
        }
    }
    
    /**
    * Adds a password to a user's history
    *
    * @author Tony Bibbs <tony at geeklog.net
    * @access protected
    * @param string $userName User to add password to history for
    * @param string $password unencrypted password (we encrypt it on behalf of caller)
    * @todo Need to actually encrypte password
    * 
    */
    protected function addPasswordToHistory($userName, $password)
    {
        global $gConf;
        
        // I use this to test transactions
        //throw new AESQLException('test');
    
        $userName = strtoupper($userName);
        
        //$encryptedPassword = MD5($password);
        $encryptedPass = $password;
        $prepStmt = $this->db->prepare('INSERT INTO ae_user_old_password
            (uop_user_name, uop_password, uop_insert_date)
            VALUES (?,?,?)');
        $result = $this->db->execute($prepStmt, array($userName, $encryptedPass, time()));
        
        // Check for SQL error
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        
        // Purge history for user if needed
        $prepStmt = $this->db->prepare('SELECT uop_insert_date
            FROM ae_user_old_password
            WHERE uop_user_name = ? ORDER BY uop_insert_date');
        $result = $this->db->execute($prepStmt, array($userName));
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        if ($result->numRows() > $gConf[AE_PROVIDER_PEAR_DB]['numoldpasswords']) {
            // Note the hack to the starting value of $i.  This is done this way so array_merge
            // will work right
            $prepFinish = '';
            $rowsToDelete = array();
            for ($i = 1; $i <= ($result->numRows() - $gConf[AE_PROVIDER_PEAR_DB]['numoldpasswords']); $i++) {
                $row = $result->fetchRow($this->fetchMode);
                $rowsToDelete[$i] = $row[0];
                // Build ?-string to add to prepare statement
                if ($i == ($result->numRows() - $gConf[AE_PROVIDER_PEAR_DB]['numoldpasswords'])) {
                    $prepFinish .= '?';
                } else {
                    $prepFinish .= '?,';
                }
            }
            $row = $result->fetchRow($this->fetchMode);
            $prepStmt = $this->db->prepare("DELETE FROM ae_user_old_password
                WHERE uop_user_name = ?
                AND uop_insert_date IN ($prepFinish)");
            $result = $this->db->execute($prepStmt,
                array_merge(array(0 => $userName), $rowsToDelete));
            if (DB::isError($result)) {
                throw new AESQLException($result->toString());
            }
        }
    }
    
    /**
    * Increments the number of failed authentication attempts for a user
    *
    * NOTE: if the username doesn't exist we simply return.  If you get a SQL exception then some
    * extraordinary error occured and you should handle it
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access protected
    * @param string $userName User to update failed authentication attempts for
    *
    */
    protected function incrementFailedAttempts($userName)
    {
        global $gConf;
        
        $userName = strtoupper($userName);
        
        $prepStmt = $this->db->prepare('SELECT user_failed_attempts
            FROM ae_user
            WHERE user_name = ?');
        $result = $this->db->execute($prepStmt, array($userName));
        if (DB::isError($result)) {
            // Just bail because the username could be invalid
            return;
        }
        $row = $result->fetchRow($this->fetchMode);
        
        // Auth_Enterpise sets a thresholed for failed authentication attempts. If we hit it we should
        // lock the account
        if (($row[0] + 1) >= $gConf[AE_PROVIDER_PEAR_DB]['maxauthattempts']) {
            $prepStmt = $this->db->prepare('UPDATE ae_user
                SET user_account_locked = 1 WHERE user_name = ?');
            $result = $this->db->execute($prepStmt, array($userName));
            if (DB::isError($result)) {
                throw new AESQLException($result->toString());
            }
        }
        
        $prepStmt = $this->db->prepare('UPDATE ae_user
            SET user_failed_attempts = ?
            WHERE user_name = ?');
        $result = $this->db->execute($prepStmt, array($row[0] + 1, $userName));
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
    }
    
    /**
    * Converts a PEAR::DB SQL result to privilege objects
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param object $sqlResult PEAR::DB_Result object
    * @return array Array of AEPrivilege objects
    *
    */
    protected function dbResultToPrivilege($sqlResult)
    {
        $numRows = $sqlResult->numRows();
        $tmpPriv = new Enterprise_Privilege();
        $privArray = array();
        
        for ($i = 1; $i <= $numRows; $i++) {
            $row = $sqlResult->fetchRow($this->fetchMode);
            $tmpPriv = new Enterprise_Privilege();
            $tmpPriv->setPrivilegeCode($row[0]);
            $tmpPriv->setPrivilegeDesc($row[1]);

            $privArray[] = $tmpPriv;
        }
        return $privArray;
    }
    
    /**
    * Builds AAServiceUser object from SQL result from authenticate
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param object $sqlResult Valid database resource object
    * @return AAServiceUser User object
    *
    */
    protected function mapResultToUserObject($sqlResult)
    {
        // Pull data into array
        $user = new Enterprise_ServiceUser();
        $row = $sqlResult->fetchRow($this->fetchMode);
        
        // Set user attributes
        $user->setUserName($row[0]);
        $user->setPassword($row[1]);

        if ($row[2] == 1) {
            $user->setAccountLocked(true);
        } else {
            $user->setAccountLocked(false);
        }
        $user->setFailedAttempts($row[3]);
        $user->setLastPWChange($row[4]);
        
        return $user;
    }

}

?>
--- NEW FILE: PearDBProvider.php ---
<?php

/**
* Auth_Enterprise
*
* This source file is subject to version 2.02 of the PHP license, that is bundled with this package
* in the file LICENSE, and is available at through the world-wide-web at
* http://www.php.net/license/2_02.txt. If you did not receive a copy of the PHP license and are
* unable to obtain it through the world-wide-web, please send a note to license at php.net so we can
* mail you a copy immediately.
*
* @author Tony Bibbs <tony at geeklog.net>
* @copyright 2004
* @version $Id: PearDBProvider.php,v 1.1 2004/07/04 15:28:15 tony Exp $
*
*/

/**
* Bring in the base Auth_Enterprise provider
*/
require_once 'Auth/Enterprise/Server/providers/BasePearDBProvider.php';

/**
* The Auth_Enterprise server configuration file
*/
require_once 'Auth/Enterprise/Server/ServerConfig.php';

/**
* Auth_Enteprise PEAR database provider
*
* This extends the base PEAR::DB server provider and simply implements the authenticate() and
* createAccount() methods against the PEAR::DB datasource.  This one can be implemented directly
*
* @author Tony Bibbs <tony at geeklog.net>
* @package net.geeklog.auth_enterprise.server
* 
*/
class Enterprise_PearDBProvider extends Enterprise_BasePearDBProvider {
    /**
    * Constructor
    *
    * Has parent set the application ID
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $appId Auth_Enterprise assigned application ID
    *
    */
    public function __construct($appId)
    {
        parent::__construct($appId);
    }
    
    /**
    * Authenticates a user
    *
    * There is a long calling chain from the client end to this point but,
    * finally, this is where the real work gets done.  This method will authenticate
    * a user against a DBMS that is supported by PEAR::DB
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $appId App user is authenticating to
    * @param string $userName ID of user trying to log in
    * @param string $password Password to try logging in with
    * @return object AEServiceUser Object *reference* or PEAR::Error
    *
    */
    public function authenticate($userName, $password)
    {
        //$encryptedPass = MD5($password);
        $encryptedPass = $password;
        
        $prepStmt = $this->db->prepare('SELECT user_name,user_password,user_account_locked,
            user_failed_attempts,user_pwd_last_set FROM ae_user
            WHERE user_name = ? AND user_password = ?');
        $result = $this->db->execute($prepStmt, array($userName, $encryptedPass));
        
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        
        if ($result->numRows() > 0) {
            $user = $this->mapResultToUserObject($result);
            
            // Ensure the account hasn't been locked
            if ($user->getAccountLocked()) {
                throw new AEAccountLocked();
            }
            
            // Check to see if password has expired.
            if ($user->isPasswordExpired()) {
                throw new AEPasswordExpired();
            }
            $user->setAppId($appId);
            
            try {
                // We get the groups as those will be sent back in user object
                $user->setGroups($this->getGroups($user->getUserName()));
                $user->setPrivileges($this->getPrivileges($user->getUserName()));
            } catch (AESQLException $e) {
                throw $e;
            } catch (Exception $e) {
                throw $e;
            }
            
            $this->isAuthenticated = true;
            
            return $user;
        } else {
            // Invalid credentials, try to update the failed attempts for the user. I say try
            // because the username could be invalid
            try {
                $this->incrementFailedAttempts($userName);
            } catch (AESQLException $e) {
                throw $e;
            }
            
            throw new AEInvalidUserCredentials();
        }
    }
    
    /**
    * Creates a user account
    *
    * Allows calling applications to create a new user account. NOTE: username's are case
    * insensitive.  This is achieved by converting them all to uppercase.  Passwords are case
    * sensitive.
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $appId App user is authenticating to
    * @param string $userName Username of user trying to log in
    * @param string $password Password for new user
    *
    */
    public function createAccountByAdmin($adminUserName, $adminUserPass, $userName, $password)
    {
        // Make sure admin is authenticated
        if (!$this->isAuthenticated) {
            try {
                $userObj = $this->authenticate($adminUserName, $adminUserPass);
            } catch (AESQLException $e) {
                throw $e;
            } catch (AEAccountLocked $e) {
                throw new AEAccountLocked('Administrator\'s account is locked');
            } catch (AEPasswordExpired $e) {
                throw new AEPasswordExpired('Administrator\'s password has expired');
            }
        }
        
        // Begin a SQL transaction if we can
        if ($this->db->provides('transactions')) {
            print 'starting transaction...<br>';
            $this->db->autoCommit(false);
        }
        
        // Don't forget to encrypt the password at some point
        //$encryptedPass = MD5($password);
        $encryptedPass = $password;
        $prepStmt = $this->db->prepare('INSERT INTO ae_user (user_name, user_password,
            user_account_locked, user_failed_attempts, user_pwd_last_set, user_creation_date,
            user_change_date, user_change_user_name)
            VALUES (?,?,?,?,?,?,?,?)');
        $curTime = time();
        $result = $this->db->execute($prepStmt, array(strtoupper($userName), $encryptedPass, 0, 0
            , $curTime, $curTime, $curTime, strtoupper($adminUserName)));
        if (DB::isError($result)) {
            // Rollback transaction
            if ($this->db->provides('transactions')) {
                $this->db->rollback();
                $this->db->autoCommit(true);
            }
            
            throw new AESQLException($result->toString());
        }
        
        // Add password to history
        try {
            $this->addPasswordToHistory($userName, $password);
        } catch (AESQLException $e) {
            // Rollback transaction
            if ($this->db->provides('transactions')) {
                print 'rolling back transaction...<br>';
                $this->db->rollback();
            }
            throw $e;
        }
        
        // Commit transaction
        if ($this->db->provides('transactions')) {
            $this->db->commit();
            $this->db->autoCommit(true);
        }
    }
    
    /**
    * Changes a user's password
    *
    * NOTE: verification of the old password and confirmation of the new password should be taken
    * care of by the calling application.  This method will use a transaction if DBMS supports it.
    * 
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $userName User for whom to change the password for
    * @param string $newPassword The user's new password.
    * @todo need to add ability to limit a certain number of password changes within 24 hours.
    * 
    */
    public function changePassword($userName, $newPassword)
    {
        // Make sure new password is a valid format
        if (!Enterprise_PasswordGenerator::isValidPassword($newPassword)) {
            throw new AEPasswordInvalid();
        }
        
        // Verify the password isn't in our password history
        if ($this->passwordInHistory($userName, $newPassword)) {
            throw new AEPasswordInHistory();
        }
        
        // Begin a SQL transaction if we can
        if ($this->db->provides('transactions')) {
            $this->db->autoCommit(false);
        }
            
        // Change the password
        try {
            $this->doPasswordChange($userName, $newPassword, $userName);
        } catch (AESQLException $e) {
            // rollback transaction
            if ($this->db->provides('transactions')) {
                $this->db->rollback();
                $this->db->autoCommit(true);
            }
            throw $e;
        }
        
        // Add password to history
        try {
            $this->addPasswordToHistory($userName, $newPassword);
        } catch (AESQLException $e) {
            // Rollback transaction
            if ($this->db->provides('transactions')) {
                $this->db->rollback();
                $this->db->autoCommit(true);
            }
            
            // Throw exception
            throw $e;
        }
        
        // Commit it all
        if ($this->db->provides('transactions')) {
            $this->db->commit();
            $this->db->autoCommit(true);
        }
    }
    
    /**
    * Allows an application administrator to change a user's password
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $adminUserName Administrator's username
    * @param string $adminPassword Administrator's password
    * @param string $userName User to change password for
    * @param string $newPassword Password to give to a user
    * 
    */
    public function changePasswordByAdmin($adminUserName, $adminPassword, $userName, $newPassword)
    {
        // Make sure admin is authenticated
        if (!$this->isAuthenticated) {
            try {
                $userObj = $this->authenticate($adminUserName, $adminPassword);
            } catch (AESQLException $e) {
                throw $e;
            } catch (AEAccountLocked $e) {
                throw new AEAccountLocked('Administrator\'s account is locked');
            } catch (AEPasswordExpired $e) {
                throw new AEPasswordExpired('Administrator\'s password has expired');
            }
        }
        
        // Need to verify the they have right Auth_Enterprise privilege
        if (!$userObj->authorize('AE_ACCOUNT_MGR')) {
            throw new AEUserNotAuthorized("User $adminUserName does not have sufficient privileges
                to change the password for $userName");
        }
        
        // Make sure password adhere's to our password rules
        if (!AEPasswordGenerator::isValidPassword($newPassword)) {
            throw new AEPasswordInvalid();
        }
        
        // Verify the password isn't in our password history
        if ($this->passwordInHistory($userName, $newPassword)) {
            throw new AEPasswordInHistory();
        }
        
        // Begin a SQL transaction if we can
        if ($this->db->provides('transactions')) {
            $this->db->autoCommit(false);
        }
            
        // Change the password
        try {
            $this->doPasswordChange($userName, $newPassword, $adminUserName);
        } catch (AESQLException $e) {
            // rollback transaction
            if ($this->db->provides('transactions')) {
                $this->db->rollback();
                $this->db->autoCommit(true);
            }
            throw $e;
        }
        
        // Add password to history
        try {
            $this->addPasswordToHistory($userName, $newPassword);
        } catch (AESQLException $e) {
            // Rollback transaction
            if ($this->db->provides('transactions')) {
                $this->db->rollback();
                $this->db->autoCommit(true);
            }
            
            // Throw exception
            throw $e;
        }
        
        // Commit it all
        if ($this->db->provides('transactions')) {
            $this->db->commit();
            $this->db->autoCommit(true);
        }
    }
    
    /**
    * Gives the user a randomly generator password
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $userName User to reset password for
    * @return string New password
    *
    */
    public function resetPassword($userName)
    {
        $newPassword = Enterprise_PasswordGenerator::generatePassword();
        
        // Begin a SQL transaction if we can
        if ($this->db->provides('transactions')) {
            $this->db->autoCommit(false);
        }
        
        try {
            $this->doPasswordChange($userName, $newPassword, $userName);
        } catch (AESQLException $e) {
            // rollback transaction
            if ($this->db->provides('transactions')) {
                $this->db->rollback();
                $this->db->autoCommit(true);
            }
            throw $e;
        }
        
        try {
            $this->addPasswordToHistory($userName, $newPassword);
        } catch (AESQLException $e) {
            // Rollback transaction
            if ($this->db->provides('transactions')) {
                $this->db->rollback();
                $this->db->autoCommit(true);
            }
            
            // Throw exception
            throw $e;
        }
        
        // Commit it all
        if ($this->db->provides('transactions')) {
            $this->db->commit();
            $this->db->autoCommit(true);
        }
        
        return $newPassword;
    }
    
    /**
    * Atomic function that only does password change
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access private
    * @param string $userName User to change password for
    * @param string $newPassword Password to give to the user
    * @param string $changeUserName User that requested the password change
    * @todo need to encrypt the password once we are done testing
    * 
    */
    private function doPasswordChange($userName, $newPassword, $changeUserName)
    {
        //$encryptedPass = MD5($newPassword);
        $encryptedPass = $newPassword;
        $curTime = time();
        $prepStmt = $this->db->prepare('UPDATE ae_user
            SET user_password = ?, user_change_date = ?, user_change_user_name = ?,
            user_pwd_last_set = ?
            WHERE user_name = ?');
        $result = $this->db->execute($prepStmt, array($encryptedPass, $curTime, $changeUserName, $curTime,
            $userName));
            
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
    }
    
}

?>

--- NEW FILE: LDAPProvider.php ---
<?php

/**
* Auth_Enterprise
*
* This source file is subject to version 2.02 of the PHP license, that is bundled with this package
* in the file LICENSE, and is available at through the world-wide-web at
* http://www.php.net/license/2_02.txt. If you did not receive a copy of the PHP license and are
* unable to obtain it through the world-wide-web, please send a note to license at php.net so we can
* mail you a copy immediately.
*
* @author Ozzyie Chen <ozzyie at doit.wisc.edu>
* @author Tony Bibbs <tony at geeklog.net>
* @copyright 2004
* @version $Id: LDAPProvider.php,v 1.1 2004/07/04 15:28:15 tony Exp $
*
*/

/**
* Bring in the base Auth_Enterprise provider
*/
require_once 'Auth/Enterprise/Server/providers/BasePearDBProvider.php';

/**
* The Auth_Enterprise server configuration file
*/
require_once 'Auth/Enterprise/Server/ServerConfig.php';

/**
* Auth_Enteprise LDAP database provider
*
* This extends the base PEAR::DB server provider and simply implements the authenticate() and
* createAccount() and change password methods against the LDAP datasource.
*
* @author Ozzyie Chen <ozzyie at doit.wisc.edu>
* @author Tony Bibbs <tony at geeklog.net>
* @package net.geeklog.auth_enterprise.server
* 
*/
class Enterprise_LDAPProvider extends Enterprise_BasePearDBProvider {
    /**
    * Handle to an LDAP connection
    */
    protected $ldapConn = null;
    
    /**
    * Sets app ID and establish LDAP connection
    *
    * @author Ozzyie Chen <ozzyie at doit.wisc.edu>
    * @param string $appId Application ID
    *
    */
    public function __construct($appId)
    {
        global $gConf;
        
        // Call constructor on parent first
        parent::__construct($appId);
        
        // Try connecting to the LDAP server
        $this->ldapConn = ldap_connect($gConf[AE_PROVIDER_LDAP]['ldapHost'],
            $gConf[AE_PROVIDER_LDAP]['ldapPort']);
            
        if (!$this->ldapConn) {
           // Throw LDAP connection exception
           throw new AEUnableToConnect('Unable to connect to LDAP server');
        }
        
        // Set LDAP protocol version
        ldap_set_option($this->ldapConn, LDAP_OPT_PROTOCOL_VERSION, $gConf[AE_PROVIDER_LDAP]['ldapProtocolVersion']);
    }

    /**
    * Binds to the LDAP server with a specific set of credentials
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access protected
    * @param string $dn
    * @param string $userPassword Password to bind with
    */
    protected function doBind($dn, $userPassword)
    {
        if (empty($userPassword)) {
           if (!ldap_bind($this->ldapConn, $dn)) {
              // Throw LDAP bind exception
              throw new AELDAPBindError('LDAP bind failed');
           }
        } else {
           //print "$dn"; exit;
           //print_r($this->ldapConn); exit;
           if (!ldap_bind($this->ldapConn, $dn, $userPassword)) {
              // Throw LDAP bind exception
              throw new AELDAPBindError('LDAP bind failed');
           }
        }
    }

    /**
    * Authenticates user against an LDAP repository
    *
    * @author Ozzyie Chen <ozzyie at doit.wisc.edu>
    * @access protected
    * @param string $userName User to authenticate
    * @param string $password Password to authenticate with
    * 
    */
    public function authenticate($userName, $password)
    {
        global $gConf;
        
        try {
           $this->doBind("cn=$userName,ou=people,{$gConf[AE_PROVIDER_LDAP]['ldapDC']}", $password);
        } catch (AELDAPBindError $e) {
           throw new AEInvalidUserCredentials($e->getMessage());
        } catch (Exception $e) {
           throw $e;
        }
        
        // OK, authentication worked, get data for the user
        $prepStmt = $this->db->prepare('SELECT user_name,user_password,user_account_locked,
            user_failed_attempts,user_pwd_last_set FROM ae_user
            WHERE user_name = ?');
        $result = $this->db->execute($prepStmt, array($userName));
        
        if (DB::isError($result)) {
            throw new AESQLException($result->toString());
        }
        
        $this->isAuthenticated = true;
        
        if ($result->numRows() > 0) {
            $user = $this->mapResultToUserObject($result);
            
            // Ensure the account hasn't been locked
            // Do we need to do this for LDAP or can LDAP
            // report this?
            if ($user->getAccountLocked()) {
                throw new AEAccountLocked();
            }
            
            // Check to see if password has expired.
            if ($user->isPasswordExpired()) {
                throw new AEPasswordExpired();
            }
            
            $user->setAppId($appId);
            
            try {
                // We get the groups as those will be sent back in user object
                $user->setGroups($this->getGroups($user->getUserName()));
                $user->setPrivileges($this->getPrivileges($user->getUserName()));
            } catch (AESQLException $e) {
                throw $e;
            } catch (Exception $e) {
                throw $e;
            }
               
            return $user;
        } else {
            // Hrm no data for the user exists.  This is an obvious error
            throw new AEUnknownException('LDAP authenticated fine but the user is not in the database');
        }
    }
}
?>

--- NEW FILE: BaseServerProvider.php ---
<?php

/**
* Auth_Enterprise
*
* This source file is subject to version 2.02 of the PHP license, that is bundled with this package
* in the file LICENSE, and is available at through the world-wide-web at
* http://www.php.net/license/2_02.txt. If you did not receive a copy of the PHP license and are
* unable to obtain it through the world-wide-web, please send a note to license at php.net so we can
* mail you a copy immediately.
*
* @author Tony Bibbs <tony at geeklog.net>
* @copyright 2004
* @version $Id: BaseServerProvider.php,v 1.1 2004/07/04 15:28:15 tony Exp $
*
*/

/**
* The Auth_Enterprise service inteface
*/
require_once 'Auth/Enterprise/Common/ServiceInterface.php';

/**
* The base provider class.
*
* The base provider is an abstract class from which all
* Auth_Enterprise providers inherit from.  A provider is
* instantiated by the service at run time.  Which provider
* an application uses depends on what it wants to authenticate
* against.  For example, you could have an LDAP provider, a
* an IMAP provider, a /etc/passwd provider, etc.
*
* @author Tony Bibbs <tony at geeklog.net>
* @package net.geeklog.auth_enterprise.server
*
*/
abstract class Enterprise_BaseServerProvider implements Enterprise_ServiceInterface {
    /**
    * Application ID
    * @access protected
    * @var string
    */
    protected $appId = null;
    
    /**
    * User authentication can be fired multiple times for the same user which would be inefficient.
    * This attribute indicates if the user has been authenticated to save on these extra calls
    * @access protected
    * @var boolean
    */
    protected $isAuthenticated = false;
    
    /**
    * Constructor
    *
    * Sets the Application ID for the provider
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $appId Application ID assigned by Auth_Enterprise server.
    *
    */
    public function __construct($appId)
    {
        $this->appId = $appId;
    }
    
    /**
    * Authenticates a user to an application
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    public function authenticate($userName, $password)
    {
    }
    
    /**
    * Registers a new account with the service
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    public function createAccountByAdmin($adminUserName, $adminPassword, $userName, $userPassword)
    {
    }
    
    /**
    * Changes a user's password
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    public function changePassword($userName, $newPassword)
    {
    }
    
    /**
    * Allows an application level admin to change a
    * user's password
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    public function changePasswordByAdmin($adminUserName, $adminPassword, $userName, $newPassword)
    {
    }
    
    /**
    * Resets a user's password
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    * @param string $userName User to reset password for
    * @return string New password
    * 
    */
    public function resetPassword($userName)
    {
    }
    
    /**
    * Gets the application privileges for a given user
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    public function getUserPrivileges($adminUserName, $adminPassword, $userName)
    {
    }
    
    /**
    * Sets the application privileges for a given user
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    public function setUserPrivilegesByAdmin($adminUserName, $adminPassword, $userName, $privArray)
    {
    }
    
    /**
    * Lists all available privileges for a given application
    *
    * @author Tony Bibbs <tony at geeklog.net>
    * @access public
    *
    */
    public function listAppPrivilegesByAdmin($adminUserName, $adminPassword)
    {
    }
    
    public function getUserGroupsByAdmin($adminUserName, $adminPassword, $userName)
    {
    }
    
    public function setUsterGroupsByAdmin($adminUserName, $adminPassword, $userName, $groupArray)
    {
    }
    
}

?>



More information about the geeklog-cvs mailing list