<?php
/*
* phpPlatonLib - Platon Group PHP library
*
* Auth/AuthDB.php - powerful authentification interface
* ____________________________________________________________
*
* Developed by Ondrej Jombik <nepto@platon.sk>
* 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: phpPlatonLib/Auth_AuthDB/AuthDB.php,v 1.17 2008-05-01 14:15:57 nepto 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 AuthDB($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:
* }}} */
?>
Platon Group <platon@platon.sk> http://platon.sk/
|