cvs: pear /Perm_LiveUser Liveuser.php LoginManager.php backend_db.php backend_mdb.php backend_mysql.php demoConf.xml

From: Date: Wed, 21 Aug 2002 22:42:48 +0000
Subject: cvs: pear /Perm_LiveUser Liveuser.php LoginManager.php backend_db.php backend_mdb.php backend_mysql.php demoConf.xml
Groups: php.pear.cvs 
Request: Send a blank email to pear-cvs+get-5870@lists.php.net to get a copy of this message
mw21st Wed Aug 21 18:42:48 2002 EDT Added files: /pear/Perm_LiveUser backend_db.php backend_mdb.php backend_mysql.php demoConf.xml Liveuser.php LoginManager.php Log: Initial commit, second try with the now correct package name Perm_LiveUser

Index: pear/Perm_LiveUser/backend_db.php +++ pear/Perm_LiveUser/backend_db.php <?php /** * Class LiveUser_DB * * Description: * This is a PEAR::DB backend driver for the LiveUser class. * A PEAR::DB connection object can be passed to the constructor to reuse an * existing connection. Alternatively, a DSN can be passed to open a new one. * * Requirements: * - File "Liveuser.php" (contains the parent class "LiveUser") * - Array of connection options must be passed to the constructor. * Example: array("server" => "localhost", "user" => "root", * "password" => "pwd", "database" => "AllMyPreciousData") * * @author Markus Wolff <wolff@21st.de> * @version $Id: backend_db.php,v 1.1 2002/08/21 22:42:47 mw21st Exp $ **/ // Include the parent class definition require_once("luTNG/Liveuser.php"); // Include PEAR::DB require_once("DB.php"); class LiveUser_DB extends LiveUser { /** * PEAR::DB connection object * @var object * @access private **/ var $dbc=null; /** * Indicates if backend module initialized correctly. If yes, * TRUE, if not FALSE. Backend module won't initialize if the * init value (usually an object or resource handle that * identifies the backend to be used) is not of the required * type. * @var boolean * @access public **/ var $init_ok=FALSE; var $currentlogin; var $lastlogin; /** * liveUser_DB::liveUser_DB() * * Class constructor. **/ function liveUser_DB(&$connectOptions) { if (is_subclass_of($connectOptions, "db_common")) { // Reuse existing PEAR::DB connection $this->dbc = &$connectOptions; $this->init_ok = TRUE; } elseif (is_array($connectOptions)) { // Open new MySQL connection $this->dbc = DB::connect($connectOptions["dsn"]); if (!DB::isError($this->dbc)) { $this->init_ok = TRUE; } } } /** * LiveUser_mysql::updateUserData() * * Writes current values for user back to the database. * This method does nothing in the base class and is supposed to * be overridden in subclasses according to the supported backend. * **/ function updateUserData() { $sql = "UPDATE users SET handle='$this->handle', "; $sql .= "password='$this->passwd', valid=$this->is_valid, "; $sql .= "currentlogin=$this->currentLogin, lastlogin=$this->lastLogin "; $sql .= "WHERE user_id=$this->user_id"; $result = $this->dbc->query($sql); return $result; } /** * LiveUser_mysql::readUserData() * * Reads user_id, password, validity flag, current * and last login timestamp from the database * If only $handle is given, it will read the data * from the first user with that handle and return * TRUE on success. * If $handle and $passwd are given, it will try to * find the first user with both handle and password * matching and return TRUE on success (this allows * multiple users having the same handle but different * passwords - yep, some people want this). * If no match is found, FALSE is being returned. * * @param $handle * @param boolean $passwd * @return **/ function readUserData($handle, $passwd=FALSE) { // initialize return value $success = FALSE; if ($passwd != FALSE) { // If $passwd is set, try to find the first user with the given // handle and password. $sql = "SELECT user_id,handle,password,valid,currentlogin,lastlogin "; $sql .= "FROM users WHERE handle='$handle' AND password='"; $sql .= $this->encryptPW($passwd)."'"; } else { // If only $handle is set, try to find the first matching user $sql = "SELECT user_id,handle,password,valid,currentlogin,lastlogin "; $sql .= "FROM users WHERE handle='$handle'"; } // Query database $result = $this->dbc->query($sql); // If a user was found, read data into class variables and set // return value to TRUE if (!DB::isError($result) && $result->numRows() > 0) { $udata = $result->fetchRow(DB_FETCHMODE_ASSOC); $this->handle = $udata["handle"]; $this->passwd = $this->decryptPW($udata["password"]); $this->is_valid = $udata["valid"]; $this->user_id = $udata["user_id"]; $this->lastLogin = $udata["lastlogin"]; $this->currentLogin = $udata["currentlogin"]; $result->free(); $success = TRUE; } return $success; } /** * LiveUser_mysql::readUserRights() * * Reads all individual rights of current user into * a two-dimensional array of this format: * AreaName => RightName -> Value **/ function readUserRights() { $sql = "SELECT U.user_id,U.authright_id as UARID, U.authright_value, "; $sql .= "R.authright_id, R.authright_name, R.autharea_id, "; $sql .= "A.autharea_id as AID, A.area_name "; $sql .= "FROM userrights as U, authrights as R, authareas as A "; $sql .= "WHERE U.user_id = '$this->user_id' AND R.authright_id = U.authright_id "; $sql .= "AND R.autharea_id = A.autharea_id "; $sql .= "ORDER BY A.area_name"; $result = $this->dbc->query($sql); if (!DB::isError($result)) { $allRights = array(); while($rdata = $result->fetchRow(DB_FETCHMODE_ASSOC)) { $currentArea = $rdata["area_name"]; $currentRight = $rdata["authright_name"]; $allRights[$currentArea][$currentRight] = $rdata["authright_value"]; } $this->userRights = $allRights; $result->free(); } } /** * LiveUser_mysql::readGroupRights() * * Reads all individual rights of current user into * a two-dimensional array of this format: * "GroupName" => "AreaName" -> "RightName" **/ function readGroupRights() { $sql ="SELECT GU.user_id, GU.group_id, G.group_id, G.group_name, "; $sql .="GR.group_id, GR.authright_id, AR.authright_id, AR.authright_name, "; $sql .="AR.autharea_id, A.autharea_id, A.area_name "; $sql .="FROM groupusers AS GU, groups AS G, grouprights AS GR, "; $sql .="authrights AS AR, authareas AS A "; $sql .="WHERE GU.user_id = '$this->user_id' AND GU.group_id = G.group_id AND "; $sql .="GR.group_id = GU.group_id AND GR.authright_id = AR.authright_id AND "; $sql .="AR.autharea_id = A.autharea_id"; $result = $this->dbc->query($sql); if (!DB::isError($result)) { $allRights = array(); while($grdata = $result->fetchRow(DB_FETCHMODE_ASSOC)) { $currentGroup = $grdata["group_name"]; $currentArea = $grdata["area_name"]; $allRights[$currentGroup][$currentArea][] = $grdata["authright_name"]; } $this->groupRights = $allRights; } } // End of function: readGroupRights /** * LiveUser_mysql::userExists() * * Helper function that checks if there is a user in * the database who's matching the given parameters. * If $checkHandle is given and $checkPW is set to * FALSE, it only checks if a user with that handle * exists. If only $checkPW is given and $checkHandle * is set to FALSE, it will check if there exists a * user with that password. If both values are set to * anything but FALSE, it will find the first user in * the database with both values matching. * Please note: * - If no match was found, the return value is FALSE * - If a match was found, the user_id from the database * is being returned * Whatever is returned, please keep in mind that this * function only searches for the _first_ occurence * of the search values in the database. So when you * have multiple users with the same handle, only the * ID of the first one is returned. Same goes for * passwords. Searching for both password and handle * should be pretty safe, though - having more than * one user with the same handle/password combination * in the database would be pretty stupid anyway. * * @param boolean $checkHandle The handle (username) to search * @param boolean $checkPW The password to check against * @return mixed **/ function userExists($checkHandle=FALSE,$checkPW=FALSE) { if ($checkHandle != FALSE && $checkPW == FALSE) { // only search for the first user with the given handle $sql = "SELECT user_id,handle "; $sql .= "FROM users WHERE handle='$checkHandle'"; $result = $this->dbc->query($sql); if (!DB::isError($result) && $result->numRows() >= 1) { $udata = $result->fetchRow(DB_FETCHMODE_ASSOC); return $udata["user_id"]; } else { return FALSE; } } elseif ($checkHandle == FALSE && $checkPW != FALSE) { // only search for the first user with the given password $sql = "SELECT user_id,password "; $sql .= "FROM users WHERE password='".$this->encryptPW($checkPW)."'"; $result = $this->dbc->query($sql); if (!DB::isError($result) && $result->numRows() >= 1) { $udata = $result->fetchRow(DB_FETCHMODE_ASSOC); return $udata["user_id"]; } else { return FALSE; } } else { // check for a user with both handle and password matching $sql = "SELECT user_id,password,handle "; $sql .= "FROM users WHERE handle='$checkHandle' AND "; $sql .= "password='".$this->encryptPW($checkPW)."'"; $result = $this->dbc->query($sql); if (!DB::isError($result) && $result->numRows() >= 1) { $udata = $result->fetchRow(DB_FETCHMODE_ASSOC); return $udata["user_id"]; } else { return FALSE; } } } } ?> Index: pear/Perm_LiveUser/backend_mdb.php +++ pear/Perm_LiveUser/backend_mdb.php <?php /** * Class LiveUser_MDB * * Description: * This is a PEAR::MDB backend driver for the LiveUser class. * A PEAR::MDB connection object can be passed to the constructor to reuse an * existing connection. Alternatively, a DSN can be passed to open a new one. * * Requirements: * - File "Liveuser.php" (contains the parent class "LiveUser") * - Array of connection options must be passed to the constructor. * Example: array("server" => "localhost", "user" => "root", * "password" => "pwd", "database" => "AllMyPreciousData") * * @author Markus Wolff <wolff@21st.de> * @version $Id: backend_mdb.php,v 1.1 2002/08/21 22:42:48 mw21st Exp $ **/ // Include the parent class definition require_once("Liveuser.php"); // Include PEAR::MDB require_once("MDB/MDB.php"); require_once("MDB/MDB_date.php"); class LiveUser_MDB extends LiveUser { /** * DSN * @var string * @access private **/ var $dsn = NULL; /** * PEAR::MDB connection object * @var object * @access private **/ var $dbc = NULL; /** * Indicates if backend module initialized correctly. If yes, * TRUE, if not FALSE. Backend module won't initialize if the * init value (usually an object or resource handle that * identifies the backend to be used) is not of the required * type. * @var boolean * @access public **/ var $init_ok = FALSE; var $currentlogin; var $lastlogin; /** * liveUser_MDB::liveUser_MDB() * * Class constructor. **/ function &liveUser_MDB(&$connectOptions) { if (is_subclass_of($connectOptions, "mdb_common")) { // Reuse existing PEAR::MDB connection $this->dbc = &$connectOptions; $this->init_ok = TRUE; } elseif (is_array($connectOptions)) { // Open new connection $this->dsn = $connectOptions['dsn']; $this->dbc =& MDB::connect($connectOptions['dsn']); if (!MDB::isError($this->dbc)) { $this->init_ok = TRUE; } } } function unfreeze() { // Open new connection $this->dbc =& MDB::connect($this->dsn); } function freeze($loginManager) { // Open new connection if($this->dsn) { $this->dbc->disconnect(); } $this->dbc = NULL; } /** * LiveUser_MDB::updateUserData() * * Writes current values for user back to the database. * This method does nothing in the base class and is supposed to * be overridden in subclasses according to the supported backend. * **/ function updateUserData() { $sql = 'UPDATE liveUser_users SET handle='.$this->dbc->getTextValue($this->handle); $sql .= ', password='.$this->dbc->getTextValue($this->passwd); $sql .= ', currentlogin='.MDB_date::unix2Mdbstamp($this->dbc->getTimestampValue($this->currentLogin)); $sql .= ', lastlogin='.MDB_date::unix2Mdbstamp($this->dbc->getTimestampValue($this->lastLogin)); $sql .= ', is_active='.$this->dbc->getBooleanValue($this->is_active); $sql .= ' WHERE user_id='.$this->user_id; $result = $this->dbc->query($sql); return $result; } /** * LiveUser_MDB::readUserData() * * Reads user_id, password, type, current and last * login timestamp, is_active flag from the database * If only $handle is given, it will read the data * from the first user with that handle and return * TRUE on success. * If $handle and $passwd are given, it will try to * find the first user with both handle and password * matching and return TRUE on success (this allows * multiple users having the same handle but different * passwords - yep, some people want this). * If no match is found, FALSE is being returned. * * @param $handle * @param boolean $passwd * @return **/ function readUserData($handle, $passwd=FALSE) { // initialize return value $success = FALSE; if ($passwd != FALSE) { // If $passwd is set, try to find the first user with the given // handle and password. $sql = 'SELECT user_id, handle, password, type, currentlogin, lastlogin, is_active'; $sql .= ' FROM liveUser_users WHERE handle='.$this->dbc->getTextValue($handle); $sql .= ' AND password='.$this->dbc->getTextValue($this->encryptPW($passwd)); } else { // If only $handle is set, try to find the first matching user $sql = 'SELECT user_id, handle, password, type, currentlogin, lastlogin, is_active'; $sql .= ' FROM liveUser_users WHERE handle='.$this->dbc->getTextValue($handle); } // Query database $result = $this->dbc->queryRow($sql, NULL, DB_FETCHMODE_ASSOC); // If a user was found, read data into class variables and set // return value to TRUE if (!MDB::isError($result) && is_array($result)) { $udata = $result; $this->handle = $udata['handle']; $this->passwd = $this->decryptPW($udata['password']); $this->user_id = $udata['user_id']; $this->user_type = $udata['type']; $this->lastLogin = $udata['lastlogin']; $this->currentLogin = $udata['currentlogin']; $this->is_active = $this->dbc->convertResult($udata['is_active'], MDB_TYPE_BOOLEAN); if ($this->user_type == LIVEUSER_AREAADMIN_TYPE_ID) { // get all areas in which the user is area admin $sql = 'SELECT autharea_id'; $sql .= ' FROM liveUser_area_admin_areas WHERE user_id='.$this->user_id; $result = $this->dbc->queryCol($sql); if (MDB::isError($result)) { $success = $result; } $this->area_admin = $result; } $success = TRUE; } return $success; } /** * LiveUser_MDB::readUserRights() * * Reads all individual rights of current user into * a multi-dimensional array of this format: * AreaName => RightName => Level **/ function readUserRights() { $sql = 'SELECT A.autharea_id, R.authright_id,'; $sql .= ' U.authright_level, R.has_implied'; $sql .= ' FROM liveUser_authareas as A, liveUser_userrights as U, liveUser_authrights as R'; $sql .= ' WHERE U.user_id = '.$this->user_id; $sql .= ' AND R.authright_id = U.authright_id AND R.autharea_id = A.autharea_id'; $sql .= ' ORDER BY U.authright_level'; $result = $this->dbc->query($sql); if (!MDB::isError($result)) { $allRights = array(); while (is_array($rdata = $this->dbc->fetchInto($result, DB_FETCHMODE_ASSOC))) { $currentArea = $rdata['autharea_id']; $currentRight = $rdata['authright_id']; $currentlevel = $rdata['authright_level']; $currentImplied = $this->dbc->convertResult($rdata['has_implied'], MDB_TYPE_BOOLEAN); $allRights[$currentArea][$currentRight] = $currentlevel; if ($currentImplied) { $queue = array($currentRight); while(count($queue) > 0) { $currentRight = array_shift($queue); $sql = 'SELECT A.implied_authright_id, '.$currentlevel; $sql .= ' FROM liveUser_authright_implied AS A'; $sql .= ' WHERE A.authright_id = '.$currentRight; $result2 = $this->dbc->queryAll($sql, NULL, DB_FETCHMODE_ASSOC, TRUE, FALSE); if (MDB::isError($result2)) { break; } elseif(is_array($result2)) { $impliedRights[$currentGroup][$currentArea] = $result2; $rights = array_keys($result2); foreach($rights as $right) { $sql = 'SELECT has_implied'; $sql .= ' FROM liveUser_authrights'; $sql .= ' WHERE authright_id = '.$right; $implied = $this->dbc->queryOne($sql); if($this->dbc->convertResult($implied, MDB_TYPE_BOOLEAN)) { $queue[] = $right; } } } } } } if (isset($impliedRights) && is_array($impliedRights)) { foreach ($impliedRights as $groupAreas => $areaRights) { foreach ($areaRights as $right => $level) { if (isset($allRights[$currentArea][$right])) { $allRights[$currentArea][$right] = max($allRights[$currentArea][$right], $level); } else { $allRights[$currentArea][$right] = $level; } } } } $this->userRights = $allRights; $this->dbc->freeResult($result); } } /** * LiveUser_MDB::readGroupRights() * * Reads all individual rights of current user into * a multi-dimensional array of this format: * GroupName => AreaName => RightName => Level **/ function readGroupRights() { $sql = 'SELECT GU.group_id, A.autharea_id, AR.authright_id,'; $sql .= ' GR.authright_level, AR.has_implied'; $sql .= ' FROM liveUser_groupusers AS GU, liveUser_authareas AS A,'; $sql .= ' liveUser_authrights AS AR, liveUser_grouprights AS GR'; $sql .= ' WHERE GU.group_id IN ('.implode(', ', $this->group_ids).')'; $sql .= ' AND GR.group_id = GU.group_id AND GR.authright_id = AR.authright_id'; $sql .= ' AND AR.autharea_id = A.autharea_id ORDER BY GR.authright_level ASC'; $result = $this->dbc->query($sql); if (!MDB::isError($result)) { $allRights = array(); while (is_array($grdata = $this->dbc->fetchInto($result, DB_FETCHMODE_ASSOC))) { $currentGroup = $grdata['group_id']; $currentArea = $grdata['autharea_id']; $currentRight = $grdata['authright_id']; $currentlevel = $grdata['authright_level']; $currentImplied = $this->dbc->convertResult($grdata['has_implied'], MDB_TYPE_BOOLEAN); $allRights[$currentGroup][$currentArea][$currentRight] = $currentlevel; if ($currentImplied) { $queue = array($currentRight); while(count($queue) > 0) { $currentRight = array_shift($queue); $sql = 'SELECT A.implied_authright_id, '.$currentlevel; $sql .= ' FROM liveUser_authright_implied AS A'; $sql .= ' WHERE A.authright_id = '.$currentRight; $result2 = $this->dbc->queryAll($sql, NULL, DB_FETCHMODE_ASSOC, TRUE, FALSE); if (MDB::isError($result2)) { break; } elseif(is_array($result2)) { $impliedRights[$currentGroup][$currentArea] = $result2; $rights = array_keys($result2); foreach($rights as $right) { $sql = 'SELECT has_implied'; $sql .= ' FROM liveUser_authrights'; $sql .= ' WHERE authright_id = '.$right; $implied = $this->dbc->queryOne($sql); if($this->dbc->convertResult($implied, MDB_TYPE_BOOLEAN)) { $queue[] = $right; } } } } } } if (isset($impliedRights) && is_array($impliedRights)) { foreach ($impliedRights as $currentGroup => $groupAreas) { foreach ($groupAreas as $areaRights => $groupRright) { foreach ($groupRright as $right => $level) { if (isset($allRights[$currentGroup][$currentArea][$right])) { $allRights[$currentGroup][$currentArea][$right] = max($allRights[$currentGroup][$currentArea][$right], $level); } else { $allRights[$currentGroup][$currentArea][$right] = $level; } } } } } $this->groupRights = $allRights; $this->dbc->freeResult($result); } } /** * LiveUser_MDB::readGroups() * * Reads all the group ids in that the user is also a member of * (all groups that are subgroups of these are also added recursively) and * reads all user ids that are a memember of these (sub)groups **/ function readGroups() { $sql = 'SELECT group_id'; $sql .= ' FROM liveUser_groupusers'; $sql .= ' WHERE user_id='.$this->user_id; $result = $this->dbc->queryCol($sql); if (!MDB::isError($result)) { $this->group_ids = $result; } else { $this->group_ids = array(); } // get all subgroups recursively // WARNING: this will explode if there are any evil loops // in the group-subgroup structure while(count($result) > 0) { $sql = 'SELECT DISTINCT subgroup_id'; $sql .= ' FROM liveUser_group_subgroups'; $sql .= ' WHERE group_id IN ('.implode(', ', $result).')'; $sql .= ' AND subgroup_id NOT IN ('.implode(', ', $this->group_ids).')'; $result = $this->dbc->queryCol($sql); if (MDB::isError($result)) { break; } else { $this->group_ids = array_merge($result, $this->group_ids); } } if(count($this->group_ids) > 0) { $sql = 'SELECT DISTINCT user_id'; $sql .= ' FROM liveUser_groupusers'; $sql .= ' WHERE group_id IN ('.implode(', ', $this->group_ids).')'; // I assume that this decreases performance more than having a fairly // meaningless value ($this->user_id) in the array. So its commented out. // $sql .= ' AND user_id != '.$this->user_id).')'; $result = $this->dbc->queryCol($sql); if (!MDB::isError($result)) { $this->group_user_ids = $result; } } } /** * LiveUser_MDB::userExists() * * Helper function that checks if there is a user in * the database who's matching the given parameters. * If $checkHandle is given and $checkPW is set to * FALSE, it only checks if a user with that handle * exists. If only $checkPW is given and $checkHandle * is set to FALSE, it will check if there exists a * user with that password. If both values are set to * anything but FALSE, it will find the first user in * the database with both values matching. * Please note: * - If no match was found, the return value is FALSE * - If a match was found, the user_id from the database * is being returned * Whatever is returned, please keep in mind that this * function only searches for the _first_ occurence * of the search values in the database. So when you * have multiple users with the same handle, only the * ID of the first one is returned. Same goes for * passwords. Searching for both password and handle * should be pretty safe, though - having more than * one user with the same handle/password combination * in the database would be pretty stupid anyway. * * @param boolean $checkHandle The handle (username) to search * @param boolean $checkPW The password to check against * @return mixed **/ function userExists($checkHandle = FALSE, $checkPW = FALSE) { if ($checkHandle != FALSE && $checkPW == FALSE) { // only search for the first user with the given handle $sql = 'SELECT user_id FROM liveUser_users'; $sql .= ' WHERE handle='.$this->dbc->getTextValue($checkHandle); $result = $this->dbc->query($sql); if (!MDB::isError($result) && $result->numRows() >= 1) { $udata = $this->dbc->fetchRow($result, DB_FETCHMODE_ASSOC); return $udata['user_id']; } else { return FALSE; } } elseif ($checkHandle == FALSE && $checkPW != FALSE) { // only search for the first user with the given password $sql = 'SELECT user_id FROM liveUser_users'; $sql .= ' WHERE password='.$this->dbc->getTextValue($this->encryptPW($checkPW)); $result = $this->dbc->query($sql); if (!MDB::isError($result) && $result->numRows() >= 1) { $udata = $this->dbc->fetchRow($result, DB_FETCHMODE_ASSOC); return $udata['user_id']; } else { return FALSE; } } else { // check for a user with both handle and password matching $sql = 'SELECT user_id FROM liveUser_users'; $sql .= ' WHERE handle='.$this->dbc->getTextValue($checkHandle).' AND'; $sql .= ' password='.$this->dbc->getTextValue($this->encryptPW($checkPW)); $result = $this->dbc->query($sql); if (!MDB::isError($result) && $result->numRows() >= 1) { $udata = $this->dbc->fetchRow($result, DB_FETCHMODE_ASSOC); return $udata['user_id']; } else { return FALSE; } } } } ?> Index: pear/Perm_LiveUser/backend_mysql.php +++ pear/Perm_LiveUser/backend_mysql.php <?php /** * Class LiveUser_mysql * * Description: * This is a MySQL backend driver for the LiveUser class. * It requires a MySQL connection handle to be passed to the * factory method of the parent class and makes all database * calls using PHP's native MySQL functions for speed optimization. * * Requirements: * - File "Liveuser.php" (contains the parent class "LiveUser") * - Array of connection options must be passed to the constructor. * Example: array("server" => "localhost", "user" => "root", * "password" => "pwd", "database" => "AllMyPreciousData") * * @author Markus Wolff <wolff@21st.de> * @version $Id: backend_mysql.php,v 1.1 2002/08/21 22:42:48 mw21st Exp $ **/ // Include the parent class definition require_once("luTNG/Liveuser.php"); class LiveUser_mysql extends LiveUser { /** * MySQL database connection handle * @var resource * @access private **/ var $dbc=null; /** * Indicates if backend module initialized correctly. If yes, * TRUE, if not FALSE. Backend module won't initialize if the * init value (usually an object or resource handle that * identifies the backend to be used) is not of the required * type. * @var boolean * @access public **/ var $init_ok=FALSE; var $currentlogin; var $lastlogin; /** * liveUser_mysql::liveUser_mysql() * * Class constructor. **/ function liveUser_mysql(&$connectOptions) { if (is_resource($connectOptions)) { // Reuse existing MySQL connection $this->dbc = &$connectOptions; $this->init_ok = TRUE; } elseif (is_array($connectOptions)) { // Open new MySQL connection $this->dbc = @mysql_connect($connectOptions["server"], $connectOptions["user"], $connectOptions["password"]); if (is_resource($this->dbc)) { $success = @mysql_select_db($connectOptions["database"]); if($success == TRUE) { $this->init_ok = TRUE; } } } } /** * LiveUser_mysql::updateUserData() * * Writes current values for user back to the database. * This method does nothing in the base class and is supposed to * be overridden in subclasses according to the supported backend. * **/ function updateUserData() { $sql = "UPDATE users SET handle='$this->handle', "; $sql .= "password='$this->passwd', valid='$this->is_valid', "; $sql .= "currentlogin='$this->currentLogin', lastlogin='$this->lastLogin' "; $sql .= "WHERE user_id='$this->user_id'"; $result = mysql_query($sql, $this->dbc) or die("<pre>Query:\n $sql \n".mysql_error()); return $result; } /** * LiveUser_mysql::readUserData() * * Reads user_id, password, validity flag, current * and last login timestamp from the database * If only $handle is given, it will read the data * from the first user with that handle and return * TRUE on success. * If $handle and $passwd are given, it will try to * find the first user with both handle and password * matching and return TRUE on success (this allows * multiple users having the same handle but different * passwords - yep, some people want this). * If no match is found, FALSE is being returned. * * @param $handle * @param boolean $passwd * @return **/ function readUserData($handle, $passwd=FALSE) { // initialize return value $success = FALSE; if ($passwd != FALSE) { // If $passwd is set, try to find the first user with the given // handle and password. $sql = "SELECT user_id,handle,password,valid,currentlogin,lastlogin "; $sql .= "FROM users WHERE handle='$handle' AND password='"; $sql .= $this->encryptPW($passwd)."'"; } else { // If only $handle is set, try to find the first matching user $sql = "SELECT user_id,handle,password,valid,currentlogin,lastlogin "; $sql .= "FROM users WHERE handle='$handle'"; } // Query database $result = mysql_query($sql, $this->dbc) or die(mysql_error()); // If a user was found, read data into class variables and set // return value to TRUE if (mysql_num_rows($result) > 0) { $udata = mysql_fetch_array($result); $this->handle = $udata["handle"]; $this->passwd = $this->decryptPW($udata["password"]); $this->is_valid = $udata["valid"]; $this->user_id = $udata["user_id"]; $this->lastLogin = $udata["lastlogin"]; $this->currentLogin = $udata["currentlogin"]; $success = TRUE; } return $success; } /** * LiveUser_mysql::readUserRights() * * Reads all individual rights of current user into * a two-dimensional array of this format: * AreaName => RightName -> Value **/ function readUserRights() { $sql = "SELECT U.user_id,U.authright_id as UARID, U.authright_value, "; $sql .= "R.authright_id, R.authright_name, R.autharea_id, "; $sql .= "A.autharea_id as AID, A.area_name "; $sql .= "FROM userrights as U, authrights as R, authareas as A "; $sql .= "WHERE U.user_id = '$this->user_id' AND R.authright_id = U.authright_id "; $sql .= "AND R.autharea_id = A.autharea_id "; $sql .= "ORDER BY A.area_name"; $result = mysql_query($sql,$this->dbc) or die(mysql_error()); $allRights = array(); while($rdata = mysql_fetch_array($result)) { $currentArea = $rdata["area_name"]; $currentRight = $rdata["authright_name"]; $allRights[$currentArea][$currentRight] = $rdata["authright_value"]; } $this->userRights = $allRights; } /** * LiveUser_mysql::readGroupRights() * * Reads all individual rights of current user into * a two-dimensional array of this format: * "GroupName" => "AreaName" -> "RightName" **/ function readGroupRights() { $sql ="SELECT GU.user_id, GU.group_id, G.group_id, G.group_name, "; $sql .="GR.group_id, GR.authright_id, AR.authright_id, AR.authright_name, "; $sql .="AR.autharea_id, A.autharea_id, A.area_name "; $sql .="FROM groupusers AS GU, groups AS G, grouprights AS GR, "; $sql .="authrights AS AR, authareas AS A "; $sql .="WHERE GU.user_id = '$this->user_id' AND GU.group_id = G.group_id AND "; $sql .="GR.group_id = GU.group_id AND GR.authright_id = AR.authright_id AND "; $sql .="AR.autharea_id = A.autharea_id"; $result = mysql_query($sql,$this->dbc) or die(mysql_error()); $allRights = array(); while($grdata = mysql_fetch_array($result)) { $currentGroup = $grdata["group_name"]; $currentArea = $grdata["area_name"]; $allRights[$currentGroup][$currentArea][] = $grdata["authright_name"]; } $this->groupRights = $allRights; } // End of function: readGroupRights /** * LiveUser_mysql::userExists() * * Helper function that checks if there is a user in * the database who's matching the given parameters. * If $checkHandle is given and $checkPW is set to * FALSE, it only checks if a user with that handle * exists. If only $checkPW is given and $checkHandle * is set to FALSE, it will check if there exists a * user with that password. If both values are set to * anything but FALSE, it will find the first user in * the database with both values matching. * Please note: * - If no match was found, the return value is FALSE * - If a match was found, the user_id from the database * is being returned * Whatever is returned, please keep in mind that this * function only searches for the _first_ occurence * of the search values in the database. So when you * have multiple users with the same handle, only the * ID of the first one is returned. Same goes for * passwords. Searching for both password and handle * should be pretty safe, though - having more than * one user with the same handle/password combination * in the database would be pretty stupid anyway. * * @param boolean $checkHandle The handle (username) to search * @param boolean $checkPW The password to check against * @return mixed **/ function userExists($checkHandle=FALSE,$checkPW=FALSE) { if ($checkHandle != FALSE && $checkPW == FALSE) { // only search for the first user with the given handle $sql = "SELECT user_id,handle "; $sql .= "FROM users WHERE handle='$checkHandle'"; $result = mysql_query($sql, $this->dbc) or die(mysql_error()); if (mysql_num_rows($result) >= 1) { $udata = mysql_fetch_array($result); return $udata["user_id"]; } else { return FALSE; } } elseif ($checkHandle == FALSE && $checkPW != FALSE) { // only search for the first user with the given password $sql = "SELECT user_id,password "; $sql .= "FROM users WHERE password='".$this->encryptPW($checkPW)."'"; $result = mysql_query($sql, $this->dbc) or die(mysql_error()); if (mysql_num_rows($result) >= 1) { $udata = mysql_fetch_array($result); return $udata["user_id"]; } else { return FALSE; } } else { // check for a user with both handle and password matching $sql = "SELECT user_id,password,handle "; $sql .= "FROM users WHERE handle='$checkHandle' AND "; $sql .= "password='".$this->encryptPW($checkPW)."'"; $result = mysql_query($sql, $this->dbc) or die(mysql_error()); if (mysql_num_rows($result) >= 1) { $udata = mysql_fetch_array($result); return $udata["user_id"]; } else { return FALSE; } } } } ?> Index: pear/Perm_LiveUser/demoConf.xml +++ pear/Perm_LiveUser/demoConf.xml <?xml version="1.0" encoding="ISO-8859-1" ?> <liveuserConfig> <sessionName>PHPSESSION</sessionName> <sessionVarName>loginInfo</sessionVarName> <handleParamName>handle</handleParamName> <passwdParamName>passwd</passwdParamName> <loginInputType>POST</loginInputType> <loginParamName>what</loginParamName> <logoutParamName>logout</logoutParamName> <cookieName>loginInfo</cookieName> <allowCookies>1</allowCookies> <cookieParamName>rememberMe</cookieParamName> <cookieLifetime>30</cookieLifetime> <cookieDomain>localhost</cookieDomain> <cookiePath>/</cookiePath> <logoutRedirectURL>http://www.21st.de/</logoutRedirectURL> <backendList> <!-- <backend type='mysql' server='localhost' user='root' password='root' database='live' /> --> <backend type='MDB' dsn='mysql://metapear:funky@localhost/liveUser_MDB' /> <!-- <backend type='ldap' server='localhost' baseDN='' /> <backend type='soap' server='localhost' user='soapclient' password='pleaseanswerme' /> --> </backendList> </liveuserConfig> Index: pear/Perm_LiveUser/Liveuser.php +++ pear/Perm_LiveUser/Liveuser.php <?php // Usertypes // lowest user type id define('LIVEUSER_ANONYMOUS_TYPE_ID', 0); // higest user type id define('LIVEUSER_USER_TYPE_ID', 1); // lowest admin type id define('LIVEUSER_ADMIN_TYPE_ID', 2); define('LIVEUSER_AREAADMIN_TYPE_ID', 3); define('LIVEUSER_SUPERADMIN_TYPE_ID', 4); // higest admin type id define('LIVEUSER_MASTERADMIN_TYPE_ID', 5); // The higest possible right level define('LIVEUSER_MAX_LEVEL', 3); /** * Class LiveUser * * Description: * This class provides a set of functions for implementing a user * authorisation system on live websites. * * Requirements: * - When using "DB" backend: * PEAR::DB database abstraction layer * - LiveUser admin GUI for easy user administration and setup of * authorisation areas and rights * * Authors: * - Markus Wolff, 21st Media GbR: base and backend classes * - Florian Herlitschke, 21st Media GbR: admin GUI * * Thanks to: * Björn Schotte (www.rent-a-phpwizard.de) for his ideas on creating * a single-signon-blackbox (which we haven't yet fully achieved, but * we're working on it ;-)) * * @author Markus Wolff <wolff@21st.de> * @author Florian Herlitschke <f.herlitschke@21st.de> * @version $Id: Liveuser.php,v 1.1 2002/08/21 22:42:48 mw21st Exp $ **/ class LiveUser { /** * The handle (username) of the current user * @var string **/ var $handle = ""; /** * The password of the current user as given to the * login() method. * @var string **/ var $passwd = ""; /** * Current user's database record id * @var integer **/ var $user_id = 0; /** * One-Dimensional array containing current user's AuthAreas * and his rights in each of these areas. This already includes * grouprights and possible overrides by individual right * settings. * * Format: "AreaName" => "RightName" * * @var mixed **/ var $rights = FALSE; /** * Two-dimensional array containing only the individual * rights for the actual user. * * Format: "AreaName" => "RightName" => "Value" * * @var array **/ var $userRights = array(); /** * Two-dimensional array containing all groups that * the user belongs to and the grouprights defined for * each area. * * Format: "GroupName" => "AreaName" => "RightName" * * @var array * @see $userRights * @see $rights **/ var $groupRights = array(); /** * Is the current user allowed to login at all? If false, * a call to login() will not set $logged_in to TRUE, even * if handle and password were submitted correctly. This is * useful when you want your users to be activated by an * administrator before they can actually use your application. * Default: FALSE * @var boolean * @see $logged_in **/ var $is_active = FALSE; /** * Has the current user successfully logged in? * Default: FALSE * @var boolean * @see $is_active **/ var $logged_in = FALSE; /** * Timestamp of last login (previous to currentLogin) * @var integer **/ var $lastLogin = 0; /** * Timestamp of current login (last to be written) * @var integer **/ var $currentLogin = 0; /** * Number of hours that must pass between two logins * to be counted as a new login. Comes in handy in * some situations. Default: 12 * @var integer **/ var $loginTimeout = 12; /** * Allow multiple users in the database to have the same * login handle. Default: FALSE. * @var boolean **/ var $allowDuplicateHandles = FALSE; /** * Defines the algorhythm used for encrypting/decrypting * passwords. Default: "MD5". * @var string **/ var $passwordEncryptionMode = "PLAIN"; /** * Defines the user type. * @var integer **/ var $user_type = LIVEUSER_ANONYMOUS_TYPE_ID; /** * Defines if the user is an area admin. * If the user is an area admin this variable will be filled * with an array of all areas in which the user is area admin * @var mixed **/ var $area_admin = FALSE; /** * Defines if the user rights should be retrieved ondemand. * @var boolean **/ var $ondemand = FALSE; /** * Defines the (sub)groups in which the user is a memember * @var mixed **/ var $group_ids = FALSE; /** * Defines the members of all (sub)groups in which the user is a memember * @var mixed **/ var $group_user_ids = FALSE; /** * LiveUser::liveUser() * * Class constructor. Feel free to override in backend subclasses. **/ function liveUser() { // I do nothing, override me plenty ;-) } function unfreeze() { // I do nothing, override me plenty ;-) } function freeze() { // I do nothing, override me plenty ;-) } /** * LiveUser::decryptPW() * * Decrypts a password so that it can be compared with the user * input. Uses the algorhythm defined in the passwordEncryptionMode * property. * * @param string $encryptedPW * @return string The decrypted password **/ function decryptPW ($encryptedPW) { $decryptedPW = "Encryption type not supported."; switch ($this->passwordEncryptionMode) { case "PLAIN": $decryptedPW = $encryptedPW; break; case "MD5": // MD5 can't be decoded, so return the string unmodified $decryptedPW = $encryptedPW; break; } return $decryptedPW; } /** * LiveUser::encryptPW() * * Encrypts a password for storage in a backend container. * Uses the algorhythm defined in the passwordEncryptionMode * property. * * @param string $plainPW * @return string The encrypted password **/ function encryptPW($plainPW) { $encryptedPW = "Encryption type not supported."; switch ($this->passwordEncryptionMode) { case "PLAIN": $encryptedPW = $plainPW; break; case "MD5": $encryptedPW = md5($plainPW); break; } return $encryptedPW; } /** * LiveUser::isNewLogin() * * Checks if there's enough time between lastLogin * and currentLogin to count as a new login. * * @return boolean **/ function isNewLogin () { $meantime = $this->loginTimeout * 3600; if (time() >= $this->currentLogin + $meantime) { return TRUE; } else { return FALSE; } } /** * LiveUser::login() * * Tries to make a login with the given handle and password. * If $checkpw is set to FALSE, the password won't be * validated and the user will be logged in anyway. Set this * option if you want to allow your users to be * authenticated by a simple cookie... however, this is * NOT RECOMMENDED !!! * In any case, a user can't login if he's not active. * * @param $handle * @param $passwd * @param boolean $checkpw * @param boolean $updateLastLogin **/ function login($handle, $passwd, $checkpw = TRUE, $updateLastLogin = TRUE) { // Init value: Has user data successfully been read? $success = FALSE; // Init value: Is user logged in? $this->logged_in = FALSE; // Read user data from database if ($this->allowDuplicateHandles == TRUE || $checkpw == TRUE) { // If duplicate handles are allowed or the password _has_ // to be checked, only read in data if a matching user is found $success = $this->readUserData($handle, $passwd); } else { // If duplicate handles are not allowed or the password // doesn't need to be checked, just read in the data based // on the handle $success = $this->readUserData($handle); } // If login is successful (user data has been read) if ($success == TRUE) { $pwCheck = FALSE; // Init value // ...check again if we have to check the password... if ($checkpw == TRUE) { // If yes, does the password from the database match the given one? if ($this->passwd == $this->encryptPW($passwd)) { // If yes, set pwCheck Flag $pwCheck = TRUE; } } else { // We don't have to check for the password, so set the pwCheck Flag // regardless of the user's input $pwCheck = TRUE; } // ...we still need to check if this user is declared active and // if the pwCheck Flag is set to TRUE... if ($this->is_active != FALSE && $pwCheck == TRUE) { // ...and if so, we have a successful login (hooray)! $this->logged_in = TRUE; } } if ($updateLastLogin == TRUE && $this->logged_in == TRUE) { // In case Login was successful, check if this can be counted // as a _new_ login by definition... if ($this->isNewLogin() == TRUE) { $this->lastLogin = $this->currentLogin; $this->currentLogin = time(); $this->updateUserData(); } } } /** * LiveUser::readRights() * * Reads all rights of current user into a * two-dimensional associative array, having the * area names as the key of the 1st dimension. * Group rights and invididual rights are being merged * in the process. **/ function readRights() { $this->readUserRights(); $this->readGroups(); $this->readGroupRights(); // Flatten group rights if (is_array($this->groupRights)) { foreach ($this->groupRights as $currentGroup => $groupAreas) { foreach ($groupAreas as $currentArea => $areaRights) { foreach ($areaRights as $rightName => $level) { $tmpRights[$currentArea][$rightName] = $level; } } } } else { $this->groupRights = FALSE; } // Check if user has individual rights... if (is_array($this->userRights)) { // Overwrite values from temporary array with values from userrights foreach ($this->userRights as $currentArea => $areaRights) { foreach ($areaRights as $rightName => $level) { if(isset($tmpRights[$currentArea][$rightName])) { if($level < 0) { $tmpRights[$currentArea][$rightName] = $tmpRights[$currentArea][$rightName] - $level; } else { $tmpRights[$currentArea][$rightName] = max($tmpRights[$currentArea][$rightName], $level); } } else { $tmpRights[$currentArea][$rightName] = $level; } } } } else { $this->userRights = FALSE; } // Strip values from array if level is not greater than zero if (isset($tmpRights) && is_array($tmpRights)) { foreach ($tmpRights as $currentArea => $areaRights) { foreach ($areaRights as $rightName => $level) { if ($level > 0) { $cRights[$currentArea][$rightName] = $level; } } } } if (isset($cRights) && is_array($cRights)) { $this->rights = $cRights; } else { $this->rights = array(); } } /** * LiveUser::checkArea() * * Checks if the current user has a rights in a * given area. If no third parameter is given, this also * checks if the user is logged in and will only return * TRUE if he has the right in question AND is currently * logged in. Set the third parameter to FALSE if you * just want to check wheter the user has a certain * right or not while ignoring the login status. * * @param string $area_name The name of the AuthArea to use * @param boolean $checkLogin Flag: Shall the login status also be checked? * @return boolean **/ function checkArea($area_name, $checkLogin = TRUE) { $hasright = FALSE; if($this->rights) { if ($this->user_type > LIVEUSER_AREAADMIN_TYPE_ID || ($this->user_type == LIVEUSER_AREAADMIN_TYPE_ID && $this->area_admin && in_array($area_name, $this->area_admin))) { $hasright = TRUE; // Check if user has rights in this area at all. } else if(is_array($this->rights[$area_name])) { // Now let's see if we shall check the login status, too if ($checkLogin == TRUE) { // Yep, login has to be checked, so we'll only return TRUE // if the user is currently logged in if ($this->logged_in == TRUE) { $hasright = TRUE; } } else { // We know the user has the right and we don't want to check // the login status, so TRUE will be returned. $hasright = TRUE; } } } elseif ($this->ondemand) { $this->readRights(); checkArea($area_name, $checkLogin); } return $hasright; } /** * LiveUser::checkRight() * * Checks if the current user has a certain right in a * given area. If no third parameter is given, this also * checks if the user is logged in and will only return * TRUE if he has the right in question AND is currently * logged in. Set the third parameter to FALSE if you * just want to check wheter the user has a certain * right or not while ignoring the login status. * * @param string $area_name The name of the AuthArea to use * @param string $right_name The name of the right within the AuthArea to check for * @param boolean $checkLogin Flag: Shall the login status also be checked? * @return integer level of the right **/ function checkRight($area_name, $right_name, $checkLogin = TRUE) { $hasright = FALSE; if($this->rights) { if ($this->user_type > LIVEUSER_AREAADMIN_TYPE_ID || ($this->user_type == LIVEUSER_AREAADMIN_TYPE_ID && $this->area_admin && in_array($area_name, $this->area_admin))) { $hasright = LIVEUSER_MAX_LEVEL; // Check if user has rights in this area at all. } else if(is_array($this->rights[$area_name])) { // If he does, look for the right in question if (in_array($right_name, array_keys($this->rights[$area_name]))) { // Now let's see if we shall check the login status, too if ($checkLogin == TRUE) { // Yep, login has to be checked, so we'll only return the right level // if the user is currently logged in if ($this->logged_in == TRUE) { $hasright = $this->rights[$area_name][$right_name]; } } else { // We know the user has the right and we don't want to check // the login status, so the right level will be returned. $hasright = $this->rights[$area_name][$right_name]; } } } } elseif ($this->ondemand) { $this->readRights(); checkRight($area_name, $right_name, $checkLogin); } return $hasright; } /** * LiveUser::checkRightLevel() * * Checks if the current user has a certain right in a * given area at the necessary level. * If no third parameter is given, this also * checks if the user is logged in and will only return * TRUE if he has the right in question AND is currently * logged in. Set the third parameter to FALSE if you * just want to check wheter the user has a certain * right or not while ignoring the login status. * * Level 1: requires that owner_user_id matches $this->user_id * Level 2: requires that the $owner_group_id matches the id one of * the (sub)groups that $this->user_id is a memember of * or requires that the $owner_user_id matches a user_id of a memeber of one of $this->user_id's (sub)groups * Level 3: no requirements * * Important note: * Every ressource MAY be owned by a user and/or by a group. Therefore, $owner_user_id and/or $owner_group_id can * either be an integer or NULL. * * @param integer $level level value as returned by checkRight() * @param mixed $owner_user_id id of the owner of the ressource for * which the right is requested * @param mixed $owner_group_id id of the group of the ressource for * which the right is requested * @return boolean TRUE if the level is sufficient to grant access * else FALSE **/ function checkLevel($level, $owner_user_id, $owner_group_id) { $hasright = FALSE; // highest level (that is level 3) if ($level == LIVEUSER_MAX_LEVEL) { $hasright = TRUE; } elseif ($level >= 1) { // level 1 or higher if (is_int($owner_user_id) && $this->user_id == $owner_user_id) { $hasright = TRUE; // level 2 or higher } elseif ($level >= 2) { // check if the ressource is owned by a (sub)group that the user is part of // or if the ressource is owned by a member of a (sub)group of the user // not sure if this feature is worth the performance cost // as this would need a possibly very large array of user id's if ((is_int($owner_group_id) && in_array($owner_group_id, $this->group_ids)) || (is_int($owner_user_id) && in_array($owner_user_id, $this->group_user_ids))) { $hasright = TRUE; } } } return $hasright; } /** * LiveUser::checkRightLevel() * * Checks if the current user has a certain right in a * given area with the sufficient level to gain access * to a specific ressource in that area. * If no fith parameter is given, this also * checks if the user is logged in and will only return * TRUE if he has the right in question AND is currently * logged in. Set the third parameter to FALSE if you * just want to check wheter the user has a certain * right or not while ignoring the login status. * * @param string $area_name The name of the AuthArea to use * @param string $right_name The name of the right within the AuthArea to check for * @param mixed $owner_user_id id of the owner of the ressource for * which the right is requested * @param mixed $owner_group_id id of the group of the ressource for * which the right is requested * @param boolean $checkLogin Flag: Shall the login status also be checked? * @return boolean TRUE if the level is sufficient to grant access * else FALSE **/ function checkRightLevel($area_name, $right_name, $owner_user_id, $owner_group_id, $checkLogin = TRUE) { $level = $this->checkRight($area_name, $right_name, $checkLogin); $hasright = $this->checkLevel($level, $owner_user_id, $owner_group_id); return $hasright; } /** * LiveUser::readUserRights() * * Reads all individual rights of current user into * a two-dimensional array of this format: * AreaName => RightName -> Value * * Again, this does nothing in the base class. The * described functionality must be implemented in a * subclass overriding this method. **/ function readUserRights() { // Override me plenty ;-) } /** * LiveUser::readGroupRights() * * Reads all individual rights of current user into * a two-dimensional array of this format: * "GroupName" => "AreaName" -> "RightName" * * Again, this does nothing in the base class. The * described functionality must be implemented in a * subclass overriding this method. **/ function readGroupRights() { // Override me plenty ;-) } /** * LiveUser::updateUserData() * * Writes current values for user back to the database. * This method does nothing in the base class and is supposed to * be overridden in subclasses according to the supported backend. * **/ function updateUserData () { // Override me plenty ;-) } /** * LiveUser::readUserData() * * Reads user_id, password, is_active flag, current * and last login timestamp from the database * If only $handle is given, it will read the data * from the first user with that handle and return * TRUE on success. * If $handle and $passwd are given, it will try to * find the first user with both handle and password * matching and return TRUE on success (this allows * multiple users having the same handle but different * passwords - yep, some people want this). * If no match is found, FALSE is being returned. * * Again, this does nothing in the base class. The * described functionality must be implemented in a * subclass overriding this method. * * @param $handle * @param boolean $passwd * @return **/ function readUserData($handle, $passwd=FALSE) { // Override me plenty ;-) } /** * LiveUser::userExists() * * Helper function that checks if there is a user in * the database who's matching the given parameters. * If $checkHandle is given and $checkPW is set to * FALSE, it only checks if a user with that handle * exists. If only $checkPW is given and $checkHandle * is set to FALSE, it will check if there exists a * user with that password. If both values are set to * anything but FALSE, it will find the first user in * the database with both values matching. * Please note: * - If no match was found, the return value is FALSE * - If a match was found, the user_id from the database * is being returned * Whatever is returned, please keep in mind that this * function only searches for the _first_ occurence * of the search values in the database. So when you * have multiple users with the same handle, only the * ID of the first one is returned. Same goes for * passwords. Searching for both password and handle * should be pretty safe, though - having more than * one user with the same handle/password combination * in the database would be pretty stupid anyway. * * Again, this does nothing in the base class. The * described functionality must be implemented in a * subclass overriding this method. * * @param boolean $checkHandle * @param boolean $checkPW * @return mixed **/ function userExists($checkHandle=FALSE,$checkPW=FALSE) { // Override me plenty ;-) } } ?> Index: pear/Perm_LiveUser/LoginManager.php +++ pear/Perm_LiveUser/LoginManager.php <?php // this will probably moved to a seperate file that can be generated ondemand // area defines define('LIVEUSER_AREA_COFFEEMAKER', 3); // right defines define('LIVEUSER_RIGHT_MAKE_COFFEE', 6); // Include PEAR::XML_Tree class definition require_once("XML/Tree.php"); /** * Class LoginManager * * Description: * This is a manager class for a user login system using the LiveUser * class. It creates a LiveUser object, takes care of the whole login * process and stores the LiveUser object in a session. * The whole process is completely customizable via an XML configuration * file. * You can also configure this class to try to connect to more than * one server that can store user information - each server requiring * a different backend class. This way you can for example create a login * system for a live website that first queries the local database and * if the requested user is not found, it tries to find im in your * company's LDAP server. That way you don't have to create lots of * user accounts for your employees so that they can access closed * sections of your website - everyone can use his existing account. * * NOTE: No browser output may be made before using this class, because * it will try to send HTTP headers such as cookies and redirects. * * Requirements: * - Package PEAR::XML_Tree * - Should run on PHP version 4.1.0 or higher, tested only on 4.2.1 so far * * Authors: * - Markus Wolff, 21st Media GbR: base and backend classes * - Florian Herlitschke, 21st Media GbR: admin GUI * * Contributors: * Antonio Guerra * * Thanks to: * Björn Schotte (www.rent-a-phpwizard.de) for his ideas on creating * a single-signon-blackbox (which we haven't yet fully achieved, but * we're working on it ;-)) * * @author Markus Wolff <wolff@21st.de> * @version $Id: LoginManager.php,v 1.1 2002/08/21 22:42:48 mw21st Exp $ **/ class LoginManager { /** * The LiveUser object * @var object **/ var $user=NULL; /** * The session name to be used for all sessions * @var string **/ var $sessionName; /** * The name of the session variable in which the user object * will be stored * @var string **/ var $sessionVarName; /** * The variable name for the user handle that comes from a * HTML login form * @var string **/ var $handleParamName; /** * The variable name for the password that comes from a * HTML login form * @var string **/ var $passwdParamName; /** * The input channel in which to look for login information. * Can be POST for form input or GET for URL input. * @var string [GET|POST] **/ var $loginInputType; /** * Name of the GET-Parameter that is being monitored for logging * out. If present, the user object in the session will be * destroyed. * @var string **/ var $logoutParamName; /** * The name for the cookie to store user information in (used * for the "remember me" function). * @var string **/ var $cookieName; /** * This flag determines if "remember me" cookies are allowed. If * not, the LoginManager won't even look for a cookie, so login * information has to be re-entered for every session. * This is the recommended behaviour!!! * Set to 0 for not allowing cookies, set to 1 for allowing them. * @var integer [0|1] **/ var $allowCookies; /** * The name of the "remember me" checkbox on a HTML form. If this * is provided via the channel defined in loginInputType (GET or POST), * and allowCookies has been set to 1, a cookie with user information * will be set so the user won't have to login using his username and * password again as long as the cookie exists. * @var string **/ var $cookieParamName; /** * The lifetime of the "remember me" cookie in days. Note that the * cookie lifetime will be refreshed every time the user logs in. * @var integer **/ var $cookieLifetime; /** * The domain for the "remember me" cookie. Example: "21st.de" * @var string **/ var $cookieDomain; /** * The path for the "remember me" cookie. Example: "/" * @var string **/ var $cookiePath; /** * The URL to redirect to when the user logs out * @var string **/ var $logoutRedirectURL; /** * Nested array with the backends that shall be queried for user * information. Format: array(1 => array("option1" => "value", ...)) * Typical options are: * - server: The adress of the server being queried (ie. "localhost") * - user: The user name used to login for the server * - password: The password used to login for the server * - database: Name of the database containing user information (this is * usually used only by RDBMS) * - baseDN: Obviously, this is what you need when using an LDAP server * - connection: Present only if an existing connection shall be used. This * contains a reference to an already existing connection resource or object. * - type: The backend type. This option must always be present, otherwise * the LoginManager can't include the correct backend class definition. * @var array **/ var $backends = array(); /** * Name of the session variable that is used to store information on the * backend in which the user data resides. This is needed to include the * correct backend class definition on demand. * @var string **/ var $backendTypeVar; /** * Determines whether or not to destroy the entire session on logout * @var boolean **/ var $destroySessionOnLogout; /** * LoginManager::LoginManager() * * Class constructor. Optionally, a valid filepath to an XML configuration * file can be passed. * * @param string $configFile * @param string $cacheXML * @access public **/ function LoginManager($configFile = "", $cacheXML = FALSE) { // Make some basic default settings (in case the config file doesn't // cover everything or doesn't exist $this->sessionVarName = "ludata"; $this->sessionName = session_name(); $this->backendTypeVar = "mysql"; $this->handleParamName = "handle"; $this->passwdParamName = "passwd"; $this->loginInputType = "POST"; $this->logoutParamName = "logout"; $this->cookieName = "loginData"; $this->allowCookies = FALSE; $this->cookieParamName = "useCookie"; $this->cookieLifetime = 30; $this->cookieDomain = "localhost"; $this->cookiePath = "/"; $this->logoutRedirectURL = ""; $this->destroySessionOnLogout = TRUE; // Read config file (eventually overrides default settings if ($configFile != "") { $this->readConfigFile($configFile, $cacheXML); } } /** * LoginManager::init() * * Tries to retrieve user object from session. * If this fails, the class attempts a login based on cookie or form * information (depends on class settings). * Returns TRUE if a user object was successfully retrieved or created. * Otherwise, FALSE is returned. * * @return boolean * @access public **/ function init() { // Set the name of the current session session_name($this->sessionName); // If there's no session yet, start it now session_start(); // Determine if user wanted to log out $this->processLogout(); // Try to fetch user object from session if (!$this->retrieveUserObject()) { // If unsuccessful, see if there was a login attempt: // First check for a cookie-based login - if allowed! if (isset($_COOKIE[$this->cookieName]) && $this->allowCookies) { echo "Trying cookie login...\n"; $this->tryLoginByCookie(); } // If cookie login failed, the user object still is NULL. In // this case, and if a user name was submitted, try standard login: if ($this->user == NULL && $_POST[$this->handleParamName]) { echo "Trying standard login...\n"; $this->tryStandardLogin(); } } // Return boolean that indicates whether a user object has been created // or retrieved from session if ($this->user != NULL && $this->user->handle != "") { return FALSE; } else { return TRUE; } } /** * LoginManager::tryLoginByCookie() * * @return **/ function tryLoginByCookie() { $this->user->login($_COOKIE[$this->cookieName],"",FALSE); } /** * LoginManager::tryStandardLogin() * **/ function tryStandardLogin() { $counter = 0; $userFound = FALSE; // Continue trying to login using each defined backend until // the user is logged in or all backends have been tried: $backend_cnt = count($this->backends); while ($userFound == FALSE && $backend_cnt > $counter) { $classname = "LiveUser_".$this->backends[$counter]['type']; include_once("backend_".$this->backends[$counter]['type'].".php"); if (isset($this->backends[$counter]['connection'])) { $this->user = new $classname($this->backends[$counter]['connection']); } else { $this->user = new $classname($this->backends[$counter]); } $this->user->login(stripslashes($_POST[$this->handleParamName]), stripslashes($_POST[$this->passwdParamName]), TRUE); if ($this->user->logged_in) { // Read user's rights from database if (!$this->user->ondemand) { $this->user->readRights(); $this->user->freeze($this); } $userFound = TRUE; } elseif ($this->user->handle != "") { // The user was found in the database, but couldn't login, so // he's most likely not activated or entered a wrong password. // It is not required to query any other servers. $userFound = TRUE; } $counter++; } } function unfreeze() { $this->user = unserialize($_SESSION[$this->sessionVarName]); $this->user->unfreeze(); } function freeze() { $this->user->freeze(); // Bind object to session $_SESSION[$this>sessionVarName] = serialize($this->user); $_SESSION[$this->backendTypeVar] = $this->backends[$counter]['type']; $this->setRememberCookie(); } /** * LoginManager::setRememberCookie() * **/ function setRememberCookie() { // If cookies are allowed, check if user wanted a cookie to be set... if ($this->allowCookies && isset($_POST[$this->cookieParamName])) { // Calculate cookie timeout in days $cookieTimeout = time()+(86400*$this->cookieLifetime); setcookie($this->cookieName,$this->user->handle,$cookieTimeout, $this->cookiePath,$this->cookieDomain); } } /** * LoginManager::retrieveUserObject() * * Determines the backend type of the user object stored in the session, * includes the proper class definition and then imports the object from * the session. * * @return boolean **/ function retrieveUserObject() { $success = FALSE; if (isset($_SESSION[$this->sessionVarName])) { $classFile = "backend_".$_SESSION[$this->backendTypeVar].".php"; include_once($classFile); if (class_exists("liveuser_".$_SESSION[$this->backendTypeVar])) { $success = TRUE; $this->unfreeze(); } } return $success; } /** * LoginManager::processLogout() * * If the logout parameter has been set via GET, this destroys the * session object. **/ function processLogout() { // Has the user tried to logout? if (isset($_GET[$this->logoutParamName]) || isset($_POST[$this->logoutParamName])) { // Yes indeed, destroy the session object now! unset($_SESSION[$this->sessionVarName]); // If there's a cookie, kill that one too... if (isset($_COOKIE[$this->cookieName])) { $cookieKillTime = time()-86400; setcookie($this->cookieName,"",$cookieKillTime, $this->cookiePath,$this->cookieDomain); } // If the session should be destroyed, do so now... if ($this->destroySessionOnLogout == TRUE) { session_destroy(); } // Finally, set the LoginManager's internal user object back to NULL $this->user = NULL; // If there's a URL to redirect to on logout, do so if ($this->logoutRedirectURL != "") { header("Location: $this->logoutRedirectURL"); } } } /** * LoginManager::readConfigFile() * * Reads an XML configuration file specified in $configFile and sets * the class properties accordingly. Not only does this save a lot of * typing, but also allows several applications that use this class to * have the same configuration by using one central XML file. * * @param string $configFile * @param boolean $enableCache * @return boolean * @access public **/ function readConfigFile($configFile, $enableCache=FALSE) { // In the future, this will be cached using PEAR::Cache if // $enableCache is set to TRUE. if (is_file($configFile)) { $tree = new XML_Tree($configFile); $t = &$tree->getTreeFromFile(); // Iterate through all nodes below the root node foreach ($t->children as $node) { // Check the node name and set the appropriate class property switch($node->name) { case "sessionName": $this->sessionName = $node->content; break; case "sessionVarName": $this->sessionVarName = $node->content; break; case "handleParamName": $this->handleParamName = $node->content; break; case "passwdParamName": $this->passwdParamName = $node->content; break; case "loginInputType": $this->loginInputType = $node->content; break; case "loginParamName": $this->loginParamName = $node->content; break; case "logoutParamName": $this->logoutParamName = $node->content; break; case "cookieName": $this->cookieName = $node->content; break; case "allowCookies": $this->allowCookies = $node->content; break; case "cookieParamName": $this->cookieParamName = $node->content; break; case "cookieLifetime": $this->cookieLifetime = $node->content; break; case "cookieDomain": $this->cookieDomain = $node->content; break; case "cookiePath": $this->cookiePath = $node->content; break; case "logoutRedirectURL": $this->logoutRedirectURL = $node->content; break; case "destroySessionOnLogout": $this->destroySessionOnLogout = $node->content; case "backendList": foreach($node->children as $backend) { $this->backends[] = $backend->attributes; } break; } } return TRUE; } else { return FALSE; } } /** * LoginManager::setApplication() * * Sets the name of the current application as a default for * all checkRight() calls * * @param string $appName **/ function setApplication($appName) { $this->application = $appName; if (is_object($this->user)) { $this->user->application = $appName; } } /** * LoginManager::checkRight() * * Wrapper method for the user object's own checkRight method. * * @param string $areaName * @param string $rightName * @return boolean **/ function checkRight($areaName, $rightName) { return $this->user->checkRight($areaName, $rightName); } /** * LoginManager::isLoggedIn() * * Checks if a user is logged in. * * @return boolean **/ function isLoggedIn() { if (!is_object($this->user)) { return FALSE; } elseif ($this->user->logged_in == TRUE) { return TRUE; } else { return FALSE; } } /** * LoginManager::userNotActive() * * Function that determines if the user exists but hasn't yet been declared * "active" by an administrator. Use this to check if this was the reason * why a user was not able to login. * TRUE == Not active * FALSE == active * * @return boolean **/ function userNotActive() { if (is_object($this->user) && $this->user->is_active == 0 && $this->user->handle != "") { return TRUE; } else { return FALSE; } } } ?>
« previous php.pear.cvs (#5870) next »