* Copyright (c) 2001-2007 Platon Group, http://platon.sk/ * All rights reserved. * * See README file for more information about this software. * See COPYING file for license information. * * Download the latest version from * http://platon.sk/projects/phpPlatonLib/ */ /* $Platon: Metafox/lib/Auth/AuthDB.php,v 1.9 2009-10-14 11:56:09 igor Exp $ */ /* * History: * * 2002-03-31 - initial release * 2002-05-13 - headers update * 2002-05-29 - code cleanup, little improvements * (username and password moved to props array) * 2002-09-05 - expiration & timeout implementation * - better HTTP_X_FORWARDED_FOR handling in authentification * - extensive code cleanup * 2003-07-27 - support for authentification via another cooperative * authentification interface * 2007-09-22 - configurable cookie name, path, domain * - configurable username/password CGI form variable name * 2009-10-14 - disable domain in cookie if domain is "localhost" */ require_once 'DB.php'; define('AUTH_DB_COOKIE_NAME', 'AuthDB_session_id_cookie'); define('AUTH_DB_VAR_USERNAME', 'username'); define('AUTH_DB_VAR_PASSWORD', 'password'); define('AUTH_DB_UNDEFINED', 0); define('AUTH_DB_NOT_LOGGED', -1); define('AUTH_DB_LOGGED_IN', 1); define('AUTH_DB_LOGGED_OUT', -2); define('AUTH_DB_EXPIRED', -3); define('AUTH_DB_TIMEOUT', -4); define('AUTH_DB_FAILED', -5); //define('AUTH_DB_ERROR', -6); class AuthDB { var $_status = AUTH_DB_UNDEFINED; var $_options = null; var $_db = null; var $_logged_fnc = null; var $_props = array( 'username' => null, 'password' => null, 'session_id' => null, 'user_agent' => null, 'remote_ip' => null ); function __construct($options) /* {{{ */ { $this->_options = $options; $this->_checkOptions(); } /* }}} */ function _checkOptions() /* {{{ */ { if (! isset($this->_options['var']['username'])) { $this->_options['var']['username'] = AUTH_DB_VAR_USERNAME; } if (! isset($this->_options['var']['password'])) { $this->_options['var']['password'] = AUTH_DB_VAR_PASSWORD; } if (! isset($this->_options['cookie']['name'])) { $this->_options['cookie']['name'] = AUTH_DB_COOKIE_NAME; } if (! strcasecmp(trim($this->_options['cookie']['domain']), 'localhost')) { $this->_options['cookie']['domain'] = null; } } /* }}} */ /** * Tells AuthDB, that particular user is already authentificated * via another cooperative authetification interface. * * @param fncname name of the function, which returns * username of already logged in user * @return status */ function setLoggedFunction($fncname) /* {{{ */ { $this->_logged_fnc = $fncname; } /* }}} */ function assignData() /* {{{ */ { $username_var = $this->_options['var']['username']; $password_var = $this->_options['var']['password']; /* Empty passwords are allowed. Empty usernames not. */ if (isset($_POST[$username_var]) && $_POST[$username_var] != '') { $this->_props['username'] = $_POST[$username_var]; if (isset($_POST[$password_var])) { $this->_props['password'] = $_POST[$password_var]; } } } /* }}} */ /* PEAR Auth class compatibility */ function getAuth() /* {{{ */ { return $this->getStatus() == AUTH_DB_LOGGED_IN; } /* }}} */ function getStatus() /* {{{ */ { if ($this->_status != AUTH_DB_UNDEFINED) return $this->_status; $this->_status = $this->_processStatus(); return $this->_status; } /* }}} */ function _processStatus() /* {{{ */ { if ($this->_logged_fnc != null && function_exists($this->_logged_fnc)) { $this->_props['username'] = call_user_func($this->_logged_fnc); } $this->_checkDB(); $this->_DBupdateTimeout(); $status = $this->_DBcheckLogin(); /* If there is a defined login status returns it. Exception is, if we are in "logged" mode, which means we are authentificated via another cooperative authentification interface. In this case only LOGGED_IN and FAILED are returned. Other statuses purpously force login function again. */ if ($status != AUTH_DB_UNDEFINED && ($this->_logged_fnc == null || $status == AUTH_DB_FAILED || $status == AUTH_DB_LOGGED_IN)) { if ($status != AUTH_DB_LOGGED_IN) { setcookie($this->_options['cookie']['name'], '', /* empty string */ time() - 100000, /* time in the past */ $this->_options['cookie']['path'], $this->_options['cookie']['domain']); } return $status; } /* If not in "logged" mode, assign login data into variables. */ $this->_logged_fnc != null || $this->assignData(); $status = $this->login(); if ($status == AUTH_DB_LOGGED_IN) { $this->_DBinsert(); setcookie($this->_options['cookie']['name'], $this->_getProperty('session_id'), 0, /* expires at the end of session */ $this->_options['cookie']['path'], $this->_options['cookie']['domain']); } elseif ($status == AUTH_DB_FAILED) { $this->_DBlog(); } return $status; } /* }}} */ function login() /* {{{ */ { $status = AUTH_DB_UNDEFINED; if (! empty($this->_props['username'])) { $password = $this->_DBgetPassword(); if (isset($password)) { if ($this->_logged_fnc || ! strcmp($password, md5($this->_props['password']))) { $status = AUTH_DB_LOGGED_IN; } else { $status = AUTH_DB_FAILED; } } else { $status = AUTH_DB_FAILED; } } else { $status = AUTH_DB_NOT_LOGGED; } return $status; } /* }}} */ function logout() /* {{{ */ { if ($this->getAuth()) { $this->_DBlogout(); $this->_status = AUTH_DB_LOGGED_OUT; } setcookie($this->_options['cookie']['name'], '', /* empty string */ time() - 100000, /* time in the past */ $this->_options['cookie']['path'], $this->_options['cookie']['domain']); } /* }}} */ function getUsername() /* {{{ */ { return $this->_DBgetUsername(); } /* }}} */ function getUserInfo() /* {{{ */ { if ($this->getAuth()) { $this->_props['username'] = $this->getUsername(); return $this->_DBgetUserInfo(); } return false; } /* }}} */ // Private methods function _checkDB() /* {{{ */ { if (! isset($this->_db)) { $this->_db = DB::connect($this->_options['dsn']); DB::isError($this->_db) && $this->_error($this->_db); $this->_db->setFetchMode(DB_FETCHMODE_ASSOC); } } /* }}} */ function _error($error) /* {{{ */ { die('AuthDB error: '.$error->getMessage()); // sensitive! // $error->getDebugInfo(); } /* }}} */ function _getProperty($property) /* {{{ */ { if (isset($this->_props[$property])) return $this->_props[$property]; switch ($property) { case 'user_agent': $ret = @$_SERVER['HTTP_USER_AGENT']; $ret = substr(stripslashes($ret), 0, 255); break; case 'remote_ip': /* Sending HTTP_X_FORWARDED_FOR? OK, send anything you want, but it must persist for the whole session. */ $ret = array(); foreach (array('REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP') as $key) { if (isset($_SERVER[$key])) { $ret[] = $_SERVER[$key]; } } $ret = array_unique($ret); $ret = join(' / ', $ret); break; default: case 'session_id': if (isset($_COOKIE[$this->_options['cookie']['name']])) { $ret = $_COOKIE[$this->_options['cookie']['name']]; $ret = intval(stripslashes($ret)); } else { $ret = ''; } break; } $this->_props[$property] = $ret; return $ret; } /* }}} */ function _DBgetPassword() /* {{{ */ { $query = sprintf('SELECT %s FROM %s WHERE %s = %s', $this->_options['users']['cols']['password'], $this->_options['users']['table'], $this->_options['users']['cols']['username'], $this->_db->quote($this->_props['username'])); if (DB::isError($password = $this->_db->getOne($query))) $this->_error($password); return $password; } /* }}} */ function _DBinsert() /* {{{ */ { mt_srand((double) microtime() * 1000000); do { $session_id = mt_rand(); $query = sprintf('SELECT session_id FROM %s' . ' WHERE session_id = %s', $this->_options['sessions']['table'], $this->_db->quote($session_id)); DB::isError($res = $this->_db->getOne($query)) && $this->_error($res); } while (isset($res)); $query = sprintf('INSERT INTO %s' .' (status, login, logout,' .' username, session_id, ip, user_agent)' .' VALUES' .' ("online", sysdate(), sysdate(),' .' %s, %s, %s, %s)', $this->_options['sessions']['table'], $this->_db->quote($this->_props['username']), $this->_db->quote($session_id), $this->_db->quote($this->_getProperty('remote_ip')), $this->_db->quote($this->_getProperty('user_agent'))); DB::isError($res = $this->_db->query($query)) && $this->_error($res); $this->_props['session_id'] = $session_id; } /* }}} */ function _DBlog() /* {{{ */ { $query = sprintf('INSERT INTO %s' .' (status, login, logout,' .' username, password, ip, user_agent)' .' VALUES' .' ("failed", sysdate(), sysdate(),' .' %s, %s, %s, %s)', $this->_options['sessions']['table'], $this->_db->quote($this->_props['username']), $this->_db->quote($this->_props['password']), $this->_db->quote($this->_getProperty('remote_ip')), $this->_db->quote($this->_getProperty('user_agent'))); DB::isError($res = $this->_db->query($query)) && $this->_error($res); } /* }}} */ function _DBupdateTimeout() /* {{{ */ { $timeout = intval($this->_options['timeout']); $expired = intval($this->_options['expired']); if ($timeout > 0) { $query = sprintf('UPDATE %s' .' SET status = "timeout"' .' WHERE status = "online"' .' AND UNIX_TIMESTAMP(SYSDATE())' .' - UNIX_TIMESTAMP(logout) >= %s', $this->_options['sessions']['table'], $this->_db->quote($timeout)); DB::isError($res = $this->_db->query($query)) && $this->_error($res); } if ($expired > 0) { $query = sprintf('UPDATE %s' .' SET status = "expired"' .' WHERE status = "online"' .' AND UNIX_TIMESTAMP(SYSDATE())' .' - UNIX_TIMESTAMP(login) >= %s', $this->_options['sessions']['table'], $this->_db->quote($expired)); DB::isError($res = $this->_db->query($query)) && $this->_error($res); } } /* }}} */ // This method needs ISDN fastfix function _DBlogout() /* {{{ */ { $query = sprintf('UPDATE %s' .' SET status = "logout", logout = sysdate() ' .' WHERE status = "online"' .' AND session_id = %s' .' AND ip = %s' .' AND user_agent = %s', $this->_options['sessions']['table'], $this->_db->quote($this->_getProperty('session_id')), $this->_db->quote($this->_getProperty('remote_ip')), $this->_db->quote($this->_getProperty('user_agent'))); DB::isError($res = $this->_db->query($query)) && $this->_error($res); } /* }}} */ // This method needs ISDN fastfix function _DBupdateLogin() /* {{{ */ { $query = sprintf('UPDATE %s' .' SET logout = sysdate()' .' WHERE status = "online"' .' AND session_id = %s' .' AND ip = %s' .' AND user_agent = %s', $this->_options['sessions']['table'], $this->_db->quote($this->_getProperty('session_id')), $this->_db->quote($this->_getProperty('remote_ip')), $this->_db->quote($this->_getProperty('user_agent'))); DB::isError($res = $this->_db->query($query)) && $this->_error($res); return $this->_db->affectedRows() > 0 ? true : false; } /* }}} */ // This method needs ISDN fastfix function _DBcheckLogin() /* {{{ */ { if ($this->_DBupdateLogin() == true) return true; $query = sprintf('SELECT status FROM %s' .' WHERE session_id = %s' .' AND ip = %s' .' AND user_agent = %s', $this->_options['sessions']['table'], $this->_db->quote($this->_getProperty('session_id')), $this->_db->quote($this->_getProperty('remote_ip')), $this->_db->quote($this->_getProperty('user_agent'))); DB::isError($res = $this->_db->getCol($query)) && $this->_error($res); $res = array_unique($res); /* Keep this in priority order. */ if (in_array('logout', $res)) return AUTH_DB_LOGGED_OUT; if (in_array('failed', $res)) return AUTH_DB_FAILED; if (in_array('timeout', $res)) return AUTH_DB_TIMEOUT; if (in_array('expired', $res)) return AUTH_DB_EXPIRED; if (in_array('online', $res)) return AUTH_DB_LOGGED_IN; return AUTH_DB_UNDEFINED; } /* }}} */ // This method needs ISDN fastfix function _DBgetUsername() /* {{{ */ { $query = sprintf('SELECT username FROM %s' .' WHERE status = "online"' .' AND session_id = %s' .' AND ip = %s' .' AND user_agent = %s', $this->_options['sessions']['table'], $this->_db->quote($this->_getProperty('session_id')), $this->_db->quote($this->_getProperty('remote_ip')), $this->_db->quote($this->_getProperty('user_agent'))); DB::isError($res = $this->_db->getCol($query)) && $this->_error($res); return $res[0]; } /* }}} */ function _DBgetUserInfo() /* {{{ */ { $select_cols = array(); $select_tables = array(); $select_where = array(); foreach (array('sessions', 'users', 'groups') as $key) { $prefix = $this->_options[$key]['prefix']; $table = $this->_options[$key]['table']; $cols = $this->_options[$key]['info_cols']; if (! is_array($cols) || ! isset($table)) continue; if (in_array('*', $cols) == true) { if (DB::isError($info = $this->_db->tableInfo($table))) $this->_error($info); $cols = array(); foreach ($info as $info_val) $cols[] = $info_val['name']; } $select_tables[] = $table; foreach ($cols as $col_val) $select_cols[] = "$table.$col_val AS ".$prefix.$col_val; } if (count($select_tables) == 0 || count($select_cols) == 0) return false; if (is_array($this->_options['users']['info_cols']) && is_array($this->_options['groups']['info_cols'])) { $select_where[] = sprintf('%s.%s = %s.%s', $this->_options['users']['table'], $this->_options['users']['cols']['group_id'], $this->_options['groups']['table'], $this->_options['groups']['cols']['id']); } if (is_array($this->_options['users']['info_cols']) && is_array($this->_options['sessions']['info_cols'])) { $select_where[] = sprintf('%s.username = %s.%s', $this->_options['sessions']['table'], $this->_options['users']['table'], $this->_options['users']['cols']['username']); } if (is_array($this->_options['users']['info_cols'])) { $select_where[] = sprintf('%s.username = %s', $this->_options['users']['table'], $this->_db->quote($this->_props['username'])); } if (is_array($this->_options['sessions']['info_cols'])) { $select_where[] = sprintf('%s.session_id = %s', $this->_options['sessions']['table'], $this->_db->quote($this->_getProperty('session_id'))); } $query = sprintf('SELECT %s FROM %s', join(', ', $select_cols), join(', ', $select_tables)); if (count($select_where)) $query .= ' WHERE ' . join(' AND ', $select_where); DB::isError($res = $this->_db->getRow($query)) && $this->_error($res); return $res; } /* }}} */ } /* Modeline for ViM {{{ * vim: set ts=4: * vim600: fdm=marker fdl=0 fdc=0: * }}} */ ?>