436 lines
11 KiB
PHP
Executable File
436 lines
11 KiB
PHP
Executable File
<?php
|
|
|
|
/* Main (Exemple) */
|
|
|
|
session_start();
|
|
//session_destroy();
|
|
|
|
pre($_COOKIE, 'Cookie');
|
|
pre($_SESSION, 'Session');
|
|
pre($_REQUEST, 'Request');
|
|
|
|
//default values of reserved POST keys
|
|
$sLogin = array_key_exists('login', $_POST)?$_POST['login']:'';
|
|
$sPass = array_key_exists('pass', $_POST)?$_POST['pass']:'';
|
|
$sPage = array_key_exists('p', $_REQUEST)?$_REQUEST['p']:'';
|
|
$sAction = array_key_exists('a', $_POST)?$_POST['a']:'';
|
|
$sPostToken = array_key_exists('post_token', $_POST)?$_POST['post_token']:'';
|
|
|
|
$oMySql = new MySqlManager();
|
|
$oCerberus = new Cerberus($oMySql, array(Cerberus::OPTION_AUTO_LOGON=>true, Cerberus::OPTION_RENEW_TOKEN=>true));
|
|
|
|
//logging In / Out
|
|
if($sAction=='logout')
|
|
{
|
|
$oCerberus->logMeOut();
|
|
}
|
|
else/*if(($sLogin=='' && $sPass=='') || $oCerberus->checkPostToken($sPostToken))*/
|
|
{
|
|
$oCerberus->logMeIn($sLogin, $sPass);
|
|
}
|
|
/*else
|
|
{
|
|
echo 'pouet';
|
|
}*/
|
|
|
|
$sLayout = $sMenu = '';
|
|
if($oCerberus->isLogguedIn())
|
|
{
|
|
$sMenu = '<form method="post"><input type="submit" name="a" value="logout">Log out</a><hr /></form>';
|
|
$sLayout = 'Loggued In. '.$oCerberus->getNewPostToken();
|
|
}
|
|
else
|
|
{
|
|
$sLayout = '<form name="login" method="post">
|
|
Login <input type="text" name="login" />
|
|
pass <input type="text" name="pass" />
|
|
<input type="hidden" name="post_token" value="'.$oCerberus->getNewPostToken().'" />
|
|
<input type="submit" value="ok" />
|
|
</form>';
|
|
}
|
|
|
|
$sErrors = $oCerberus->getCleanMessages();
|
|
|
|
echo '<html><head><title>Cerberus</title></head><body>'.$sMenu."\n".$sLayout.'<br />'.$sErrors.'</body></html>';
|
|
pre('new session post token : '.$_SESSION[Cerberus::SESSION_POST_TOKEN]);
|
|
|
|
/* Class */
|
|
|
|
/* Requirements */
|
|
require_once 'functions.php';
|
|
require_once 'php_object.php';
|
|
require_once 'mysql_manager.php';
|
|
|
|
/**
|
|
* Cerberus
|
|
* Access control class
|
|
* @author FranzZ
|
|
*
|
|
* Requirements:
|
|
* Database with DB_TABLE_USER table and fields:
|
|
* - DB_FIELD_ID_USER
|
|
* - DB_FIELD_LOGIN
|
|
* - DB_FIELD_PASS
|
|
* - DB_FIELD_TOKEN
|
|
*
|
|
* Setup:
|
|
* - Replace required tables and fields names with the mysql manager constants
|
|
* - Set options :
|
|
* - Cerberus::OPTION_AUTO_LOGON
|
|
* - Cerberus::OPTION_RENEW_TOKEN
|
|
*/
|
|
class Cerberus extends PhpObject
|
|
{
|
|
// Database
|
|
private $oMySql;
|
|
const DB_TABLE_USER = MySqlManager::USER_TABLE;
|
|
const DB_FIELD_ID_USER = 'id_user';
|
|
const DB_FIELD_LOGIN = 'user';
|
|
const DB_FIELD_PASS = 'pass';
|
|
const DB_FIELD_TOKEN = 'token';
|
|
|
|
//Session
|
|
const SESSION_ID_USER = self::DB_FIELD_ID_USER;
|
|
const SESSION_LOGIN = self::DB_FIELD_LOGIN;
|
|
const SESSION_TOKEN = self::DB_FIELD_TOKEN;
|
|
const SESSION_POST_TOKEN = 'post_token';
|
|
|
|
//Cookie
|
|
const COOKIE_ID_USER = self::DB_FIELD_ID_USER;
|
|
const COOKIE_TOKEN = self::DB_FIELD_TOKEN;
|
|
const COOKIE_POST_TOKEN = self::SESSION_POST_TOKEN;
|
|
|
|
//Options
|
|
const OPTION_AUTO_LOGON = 'auto_logon';
|
|
const OPTION_RENEW_TOKEN = 'renew_token';
|
|
public $abOptions;
|
|
|
|
//Session Variables
|
|
private $iUserId;
|
|
private $sLogin;
|
|
private $sToken;
|
|
private $sPostToken;
|
|
|
|
public function __construct(&$oMySql, $abOptions)
|
|
{
|
|
parent::__construct();
|
|
$this->iUserId = $this->sLogin = $this->sToken = $this->sPostToken = false;
|
|
$this->oMySql = $oMySql;
|
|
$this->setOptions($abOptions);
|
|
$this->syncSession();
|
|
}
|
|
|
|
private function setOptions($abOptions)
|
|
{
|
|
//default values
|
|
$this->abOptions = array(self::OPTION_AUTO_LOGON=>false, self::OPTION_RENEW_TOKEN=>true);
|
|
$this->abOptions = array_merge($this->abOptions, $abOptions);
|
|
}
|
|
|
|
private function getOption($sOptionName)
|
|
{
|
|
return $this->abOptions[$sOptionName];
|
|
}
|
|
|
|
public function getUserId()
|
|
{
|
|
return $this->iUserId;
|
|
}
|
|
|
|
private function syncSession()
|
|
{
|
|
if(isset($_SESSION[self::SESSION_ID_USER]))
|
|
{
|
|
$this->iUserId = $_SESSION[self::SESSION_ID_USER];
|
|
}
|
|
if(isset($_SESSION[self::SESSION_LOGIN]))
|
|
{
|
|
$this->sLogin = $_SESSION[self::SESSION_LOGIN];
|
|
}
|
|
if(isset($_SESSION[self::SESSION_TOKEN]))
|
|
{
|
|
$this->sToken = $_SESSION[self::SESSION_TOKEN];
|
|
}
|
|
if(isset($_SESSION[self::SESSION_POST_TOKEN]))
|
|
{
|
|
$this->sPostToken = $_SESSION[self::SESSION_POST_TOKEN];
|
|
}
|
|
}
|
|
|
|
public function register($asData)
|
|
{
|
|
//data to register
|
|
//TODO To be customized
|
|
$sLogin = strtolower(trim($asData[self::DB_FIELD_LOGIN]));
|
|
$sPass = $asData[self::DB_FIELD_PASS];
|
|
|
|
if($sLogin=='' || $sPass=='')
|
|
{
|
|
$this->addError('Empty mandatory fields (Nickname or password)');
|
|
}
|
|
elseif(htmlspecialchars($sLogin, ENT_QUOTES)!=$sLogin)
|
|
{
|
|
$this->addError('Nickname: HTML characters are forbidden');
|
|
}
|
|
elseif($this->checkAccount($sLogin))
|
|
{
|
|
$this->addError('Nickname: There is already a user called by that name, choose a different one');
|
|
}
|
|
else
|
|
{
|
|
$asData[self::DB_FIELD_LOGIN] = $sLogin;
|
|
$asData[self::DB_FIELD_PASS] = self::encryptPassword($sPass);
|
|
$this->oMySql->insertRow(self::DB_TABLE_USER, $asData);
|
|
return $this->logMeIn($sLogin, $sPass);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function logMeIn($sLogin='', $sPass='')
|
|
{
|
|
$bResult = false;
|
|
$bFirstLogin = true;
|
|
if($sLogin=='' || $sPass=='')
|
|
{
|
|
$bFirstLogin = false;
|
|
if($this->iUserId && $this->sLogin && $this->sToken && $this->checkToken())
|
|
{
|
|
//log in with session variables
|
|
$iUserId = $this->iUserId;
|
|
$sLogin = $this->sLogin;
|
|
$sToken = $this->sToken;
|
|
$bResult = true;
|
|
}
|
|
elseif($this->getOption(self::OPTION_AUTO_LOGON) && $this->checkToken(true))
|
|
{
|
|
//log in with cookies
|
|
$iUserId = $_COOKIE[self::COOKIE_ID_USER];
|
|
$sLogin = $this->oMySql->selectValue(self::DB_TABLE_USER, self::DB_FIELD_LOGIN, $iUserId);
|
|
$sToken = $_COOKIE[self::COOKIE_TOKEN];
|
|
$bResult = true;
|
|
}
|
|
else
|
|
{
|
|
$this->addWarning('No login info (cookie / session)');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$asUser = $this->getUser($sLogin);
|
|
if(!$asUser)
|
|
{
|
|
$this->addError('Unknown user');
|
|
}
|
|
elseif(!$this->checkPassword($sPass, $asUser[self::DB_FIELD_PASS]))
|
|
{
|
|
$this->addError('Incorrect password');
|
|
}
|
|
else
|
|
{
|
|
$iUserId = $asUser[self::DB_FIELD_ID_USER];
|
|
$sToken = $asUser[self::DB_FIELD_TOKEN];
|
|
$bResult = true;
|
|
}
|
|
}
|
|
|
|
if($bResult)
|
|
{
|
|
//Class
|
|
$this->iUserId = $iUserId;
|
|
$this->sLogin = $sLogin;
|
|
$this->sToken = $sToken;
|
|
|
|
//Session
|
|
$_SESSION[self::SESSION_ID_USER] = $iUserId;
|
|
$_SESSION[self::SESSION_LOGIN] = $sLogin;
|
|
$_SESSION[self::SESSION_TOKEN] = $sToken;
|
|
|
|
//Cookie (doesn't leave any password nor login on user's computer)
|
|
self::setCookie(self::COOKIE_ID_USER, $iUserId);
|
|
self::setCookie(self::COOKIE_TOKEN, $sToken);
|
|
|
|
//reset pass
|
|
if($bFirstLogin || $this->getOption(self::OPTION_RENEW_TOKEN))
|
|
{
|
|
$this->resetToken();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$this->logMeOut();
|
|
}
|
|
|
|
return $bResult;
|
|
}
|
|
|
|
public function isLogguedIn()
|
|
{
|
|
$bLogguedIn = false;
|
|
if($this->iUserId && $this->sLogin && $this->sToken)
|
|
{
|
|
//check if token is set and valid
|
|
if($this->checkToken())
|
|
{
|
|
//Check if user got a actual account in the database
|
|
$bLogguedIn = $this->checkAccount($this->sLogin, $this->iUserId);
|
|
}
|
|
else
|
|
{
|
|
$this->addError('Authentication problem, please sign in again');
|
|
}
|
|
}
|
|
|
|
/*
|
|
echo "[TEST]<br />check token :
|
|
<br />db token ", $this->iUserId." - ".$this->getDbToken($this->iUserId),"
|
|
<br />session token ", $_SESSION[self::SESSION_TOKEN], "
|
|
<br />class token ", $this->sToken, "
|
|
<br />cookie token ", $_COOKIE[self::COOKIE_TOKEN], '<br />[/TEST]';
|
|
*/
|
|
|
|
return $bLogguedIn;
|
|
}
|
|
|
|
public function logMeOut()
|
|
{
|
|
//Database
|
|
if($this->iUserId)
|
|
{
|
|
$this->oMySql->updateRow(self::DB_TABLE_USER, $this->iUserId, array(self::DB_FIELD_TOKEN=>''));
|
|
}
|
|
|
|
//Class variables
|
|
$this->iUserId = $this->sLogin = $this->sToken = $this->sPostToken = false;
|
|
|
|
//Cookie
|
|
self::setCookie(self::COOKIE_TOKEN, '', -1);
|
|
self::setCookie(self::COOKIE_ID_USER, '', -1);
|
|
|
|
//Server session
|
|
$_SESSION = array();
|
|
return session_destroy();
|
|
}
|
|
|
|
private function checkAccount($sUserName, $iUserId=0)
|
|
{
|
|
$asConstraints = array(self::DB_FIELD_LOGIN=>$sUserName);
|
|
if($iUserId>0)
|
|
{
|
|
$asConstraints[self::DB_FIELD_ID_USER] = $iUserId;
|
|
}
|
|
return $this->oMySql->selectValue(self::DB_TABLE_USER, 'COUNT(1)', $asConstraints);
|
|
}
|
|
|
|
private function getUser($oUser)
|
|
{
|
|
$sField = is_numeric($oUser)?self::DB_FIELD_ID_USER:self::DB_FIELD_LOGIN;
|
|
return $this->oMySql->selectRow(self::DB_TABLE_USER, array($sField=>$oUser));
|
|
}
|
|
|
|
public static function encryptPassword($sPass)
|
|
{
|
|
$sRandomText = 'F_RA-1H"2{bvj)5f?0sd3r#fP,K]U|w}hGiN@(sZ.sDe!7*x/:Mq+&';
|
|
for($iIndex=0; $iIndex < strlen($sPass); $iIndex++)
|
|
{
|
|
$sPass[$iIndex] = $sRandomText[$iIndex%strlen($sRandomText)] ^ $sPass[$iIndex];
|
|
}
|
|
return md5($sPass);
|
|
}
|
|
|
|
private static function createToken()
|
|
{
|
|
return self::encryptPassword( $_SERVER['HTTP_USER_AGENT'].
|
|
$_SERVER['REMOTE_ADDR'].
|
|
$_SERVER['REQUEST_TIME'].
|
|
strstr(microtime(), ' ', true).
|
|
$_SERVER['SERVER_SIGNATURE'].
|
|
$_SERVER['SERVER_ADMIN']);
|
|
}
|
|
|
|
private function resetToken()
|
|
{
|
|
//new token
|
|
$sToken = $this->createToken();
|
|
|
|
//set database token
|
|
$this->oMySql->updateRow(self::DB_TABLE_USER, $this->iUserId, array(self::DB_FIELD_TOKEN=>$sToken));
|
|
|
|
//set session token
|
|
$_SESSION[self::SESSION_TOKEN] = $sToken;
|
|
$this->sToken = $sToken;
|
|
|
|
//set cookie token
|
|
self::setCookie(self::COOKIE_TOKEN, $sToken);
|
|
}
|
|
|
|
public static function setCookie($sCookieName, $oCookieValue, $iTime=1)
|
|
{
|
|
setcookie($sCookieName, $oCookieValue, time()+60*60*24*$iTime);
|
|
$_COOKIE[$sCookieName] = $oCookieValue;
|
|
}
|
|
|
|
private function checkToken($bCookieCheck=false)
|
|
{
|
|
$bTokenOk = $iUserId = $sToken = false;
|
|
|
|
//Cookie check
|
|
if($bCookieCheck && array_key_exists(self::COOKIE_ID_USER, $_COOKIE) && array_key_exists(self::COOKIE_TOKEN, $_COOKIE))
|
|
{
|
|
$iUserId = $_COOKIE[self::COOKIE_ID_USER];
|
|
$sToken = $_COOKIE[self::COOKIE_TOKEN];
|
|
}
|
|
//Session check
|
|
elseif(!$bCookieCheck && $this->iUserId && $this->sToken !== false)
|
|
{
|
|
$iUserId = $this->iUserId;
|
|
$sToken = $this->sToken;
|
|
}
|
|
|
|
if($iUserId && $sToken)
|
|
{
|
|
$sDbPass = $this->getDbToken($bCookieCheck?$_COOKIE[self::COOKIE_ID_USER]:$this->iUserId);
|
|
$bTokenOk = ($sDbPass == $_COOKIE[self::COOKIE_TOKEN] && ($sDbPass == $this->sToken || $bCookieCheck));
|
|
}
|
|
|
|
return $bTokenOk;
|
|
}
|
|
|
|
private function getDbToken($iUserId)
|
|
{
|
|
$sPass = false;
|
|
if($iUserId !== false)
|
|
{
|
|
$sPass = $this->oMySql->selectValue(self::DB_TABLE_USER, self::DB_FIELD_TOKEN, $iUserId);
|
|
}
|
|
return $sPass;
|
|
}
|
|
|
|
public function getNewPostToken()
|
|
{
|
|
$sToken = self::createToken();
|
|
$this->sPostToken = $sToken;
|
|
$_SESSION[self::SESSION_POST_TOKEN] = $sToken;
|
|
return $sToken;
|
|
}
|
|
|
|
public function checkPostToken($sPostToken)
|
|
{
|
|
pre(array('Posted'=>$sPostToken, 'Class'=>$this->sPostToken, 'Session'=>$_SESSION[self::SESSION_POST_TOKEN]), 'check posted token');
|
|
$bPostTokenOk = ($this->sPostToken && $sPostToken!='' && $sPostToken == $this->sPostToken);
|
|
$this->sPostToken = '';
|
|
$_SESSION[self::SESSION_POST_TOKEN] = '';
|
|
return $bPostTokenOk;
|
|
}
|
|
|
|
private static function checkPassword($sClearPass, $sEncodedPass)
|
|
{
|
|
return self::encryptPassword($sClearPass) == $sEncodedPass;
|
|
}
|
|
|
|
public function getCleanMessages($sType=parent::ALL_TAB)
|
|
{
|
|
return $this->getCleanMessageStacks(array($this->oMySql), $sType);
|
|
}
|
|
}
|
|
|
|
?>
|