Skip to content
Snippets Groups Projects
Commit 48186524 authored by Peter Rotich's avatar Peter Rotich Committed by Peter Rotich
Browse files

Change AuthenticatedUser to abstract class

Add ability for backends to set authorization key (authkey) specific to the backend.
The authkey is tagged with the "id" of the backend that generated it.
parent 4dded8a8
Branches
Tags
No related merge requests found
...@@ -3,14 +3,22 @@ require(INCLUDE_DIR.'class.ostsession.php'); ...@@ -3,14 +3,22 @@ require(INCLUDE_DIR.'class.ostsession.php');
require(INCLUDE_DIR.'class.usersession.php'); require(INCLUDE_DIR.'class.usersession.php');
interface AuthenticatedUser { abstract class AuthenticatedUser {
//Authorization key returned by the backend used to authorize the user
private $authkey;
// Get basic information // Get basic information
function getId(); abstract function getId();
function getUsername(); abstract function getUsername();
function setBackend($bk); abstract function getRole();
function getBackend();
function getRole(); function setAuthKey($key) {
$this->authkey = $key;
}
function getAuthKey() {
return $this->authkey;
}
} }
interface AuthDirectorySearch { interface AuthDirectorySearch {
...@@ -72,7 +80,11 @@ abstract class AuthenticationBackend { ...@@ -72,7 +80,11 @@ abstract class AuthenticationBackend {
} }
static function getBackend($id) { static function getBackend($id) {
return static::$registry[$id];
if ($id
&& ($backends = static::allRegistered())
&& isset($backends[$id]))
return $backends[$id];
} }
static function process($username, $password=null, &$errors) { static function process($username, $password=null, &$errors) {
...@@ -94,11 +106,8 @@ abstract class AuthenticationBackend { ...@@ -94,11 +106,8 @@ abstract class AuthenticationBackend {
$result = $bk->authenticate($username, $password); $result = $bk->authenticate($username, $password);
if ($result instanceof AuthenticatedUser if ($result instanceof AuthenticatedUser
&& (static::login($result, $bk))) { && (static::login($result, $bk)))
$result->setBackend($bk);
return $result; return $result;
}
// TODO: Handle permission denied, for instance // TODO: Handle permission denied, for instance
elseif ($result instanceof AccessDenied) { elseif ($result instanceof AccessDenied) {
$errors['err'] = $result->reason; $errors['err'] = $result->reason;
...@@ -121,9 +130,8 @@ abstract class AuthenticationBackend { ...@@ -121,9 +130,8 @@ abstract class AuthenticationBackend {
if ($result instanceof AuthenticatedUser) { if ($result instanceof AuthenticatedUser) {
//Perform further Object specific checks and the actual login //Perform further Object specific checks and the actual login
if (!static::login($result, $bk)) if (!static::login($result, $bk))
continue continue;
$result->setBackend($bk);
return $result; return $result;
} }
// TODO: Handle permission denied, for instance // TODO: Handle permission denied, for instance
...@@ -181,7 +189,7 @@ abstract class AuthenticationBackend { ...@@ -181,7 +189,7 @@ abstract class AuthenticationBackend {
abstract function authenticate($username, $password); abstract function authenticate($username, $password);
abstract function login($user, $bk); abstract function login($user, $bk);
abstract function getAllowedBackends($userid); abstract function getAllowedBackends($userid);
abstract protected function getAuthKey($user);
} }
class RemoteAuthenticationBackend { class RemoteAuthenticationBackend {
...@@ -226,32 +234,39 @@ abstract class StaffAuthenticationBackend extends AuthenticationBackend { ...@@ -226,32 +234,39 @@ abstract class StaffAuthenticationBackend extends AuthenticationBackend {
return array_filter($backends); return array_filter($backends);
} }
function login($user, $bk) { function login($staff, $bk) {
global $ost; global $ost;
if (!($user instanceof Staff)) if (!$bk || !($staff instanceof Staff))
return false; return false;
// Ensure staff is allowed for realz to be authenticated via the backend. // Ensure staff is allowed for realz to be authenticated via the backend.
if (!static::isBackendAllowed($user, $bk)) if (!static::isBackendAllowed($staff, $bk)
|| !($authkey=$bk->getAuthKey($staff)))
return false; return false;
//Log debug info. //Log debug info.
$ost->logDebug('Staff login', $ost->logDebug('Staff login',
sprintf("%s logged in [%s], via %s", $user->getUserName(), sprintf("%s logged in [%s], via %s", $staff->getUserName(),
$_SERVER['REMOTE_ADDR'], get_class($bk))); //Debug. $_SERVER['REMOTE_ADDR'], get_class($bk))); //Debug.
$sql='UPDATE '.STAFF_TABLE.' SET lastlogin=NOW() ' $sql='UPDATE '.STAFF_TABLE.' SET lastlogin=NOW() '
.' WHERE staff_id='.db_input($user->getId()); .' WHERE staff_id='.db_input($staff->getId());
db_query($sql); db_query($sql);
//Tag the authkey.
$authkey = $bk::$id.':'.$authkey;
//Now set session crap and lets roll baby! //Now set session crap and lets roll baby!
$_SESSION['_staff'] = array(); //clear. $_SESSION['_auth']['staff'] = array(); //clear.
$_SESSION['_staff']['userID'] = $user->getUserName(); $_SESSION['_auth']['staff']['id'] = $staff->getId();
$_SESSION['_auth']['staff']['key'] = $authkey;
$user->refreshSession(); //set the hash. $staff->setAuthKey($authkey);
$staff->refreshSession(); //set the hash.
$_SESSION['TZ_OFFSET'] = $user->getTZoffset(); $_SESSION['TZ_OFFSET'] = $staff->getTZoffset();
$_SESSION['TZ_DST'] = $user->observeDaylight(); $_SESSION['TZ_DST'] = $staff->observeDaylight();
//Regenerate session id. //Regenerate session id.
$sid = session_id(); //Current id $sid = session_id(); //Current id
...@@ -262,12 +277,16 @@ abstract class StaffAuthenticationBackend extends AuthenticationBackend { ...@@ -262,12 +277,16 @@ abstract class StaffAuthenticationBackend extends AuthenticationBackend {
&& $sid!=session_id()) && $sid!=session_id())
$session->destroy($sid); $session->destroy($sid);
Signal::send('auth.login.succeeded', $user); Signal::send('auth.login.succeeded', $staff);
$user->cancelResetTokens(); $staff->cancelResetTokens();
return true; return true;
} }
protected function getAuthKey($staff) {
return null;
}
} }
abstract class UserAuthenticationBackend extends AuthenticationBackend { abstract class UserAuthenticationBackend extends AuthenticationBackend {
...@@ -290,19 +309,29 @@ abstract class UserAuthenticationBackend extends AuthenticationBackend { ...@@ -290,19 +309,29 @@ abstract class UserAuthenticationBackend extends AuthenticationBackend {
function login($user, $bk) { function login($user, $bk) {
global $ost; global $ost;
if (!($user instanceof TicketUser)) if (!$user || !$bk
|| !$bk::$id //Must have ID
|| !($authkey = $bk->getAuthKey($user)))
return false; return false;
$_SESSION['_client'] = array(); //clear. //Tag the authkey.
$_SESSION['_client']['userID'] = $user->getEmail(); //Email $authkey = $bk::$id.':'.$authkey;
//$_SESSION['_client']['key'] = $ticket->getExtId(); //Ticket ID --acts as password when used with email. See above. //Set the session goodies
$_SESSION['_client']['token'] = $user->getSessionToken(); $_SESSION['_auth']['user'] = array(); //clear.
$_SESSION['_auth']['user']['id'] = $user->getId();
$_SESSION['_auth']['user']['key'] = $authkey;
$_SESSION['TZ_OFFSET'] = $ost->getConfig()->getTZoffset(); $_SESSION['TZ_OFFSET'] = $ost->getConfig()->getTZoffset();
$_SESSION['TZ_DST'] = $ost->getConfig()->observeDaylightSaving(); $_SESSION['TZ_DST'] = $ost->getConfig()->observeDaylightSaving();
//The backend used decides the format of the auth key.
// XXX: encrypt to hide the bk??
$user->setAuthKey($authkey);
$user->refreshSession(); //set the hash. $user->refreshSession(); //set the hash.
//Log login info... //Log login info...
$msg=sprintf('%s/%s logged in [%s]', $msg=sprintf('%s (%s) logged in [%s]',
$user->getEmail(), $user->getId(), $_SERVER['REMOTE_ADDR']); $user->getUserName(), $user->getId(), $_SERVER['REMOTE_ADDR']);
$ost->logDebug('User login', $msg); $ost->logDebug('User login', $msg);
//Regenerate session ID. //Regenerate session ID.
...@@ -314,6 +343,11 @@ abstract class UserAuthenticationBackend extends AuthenticationBackend { ...@@ -314,6 +343,11 @@ abstract class UserAuthenticationBackend extends AuthenticationBackend {
return true; return true;
} }
protected function getAuthKey($user) {
return null;
}
} }
/** /**
...@@ -354,6 +388,10 @@ abstract class AuthStrikeBackend extends AuthenticationBackend { ...@@ -354,6 +388,10 @@ abstract class AuthStrikeBackend extends AuthenticationBackend {
return array(); return array();
} }
function getAuthKey($user) {
return null;
}
abstract function authStrike($username, $password=null); abstract function authStrike($username, $password=null);
} }
...@@ -367,35 +405,35 @@ class StaffAuthStrikeBackend extends AuthStrikeBackend { ...@@ -367,35 +405,35 @@ class StaffAuthStrikeBackend extends AuthStrikeBackend {
$cfg = $ost->getConfig(); $cfg = $ost->getConfig();
if($_SESSION['_staff']['laststrike']) { if($_SESSION['_auth']['staff']['laststrike']) {
if((time()-$_SESSION['_staff']['laststrike'])<$cfg->getStaffLoginTimeout()) { if((time()-$_SESSION['_auth']['staff']['laststrike'])<$cfg->getStaffLoginTimeout()) {
$_SESSION['_staff']['laststrike'] = time(); //reset timer. $_SESSION['_auth']['staff']['laststrike'] = time(); //reset timer.
return new AccessDenied('Max. failed login attempts reached'); return new AccessDenied('Max. failed login attempts reached');
} else { //Timeout is over. } else { //Timeout is over.
//Reset the counter for next round of attempts after the timeout. //Reset the counter for next round of attempts after the timeout.
$_SESSION['_staff']['laststrike']=null; $_SESSION['_auth']['staff']['laststrike']=null;
$_SESSION['_staff']['strikes']=0; $_SESSION['_auth']['staff']['strikes']=0;
} }
} }
$_SESSION['_staff']['strikes']+=1; $_SESSION['_auth']['staff']['strikes']+=1;
if($_SESSION['_staff']['strikes']>$cfg->getStaffMaxLogins()) { if($_SESSION['_auth']['staff']['strikes']>$cfg->getStaffMaxLogins()) {
$_SESSION['_staff']['laststrike']=time(); $_SESSION['_auth']['staff']['laststrike']=time();
$alert='Excessive login attempts by a staff member?'."\n". $alert='Excessive login attempts by a staff member?'."\n".
'Username: '.$username."\n" 'Username: '.$username."\n"
.'IP: '.$_SERVER['REMOTE_ADDR']."\n" .'IP: '.$_SERVER['REMOTE_ADDR']."\n"
.'TIME: '.date('M j, Y, g:i a T')."\n\n" .'TIME: '.date('M j, Y, g:i a T')."\n\n"
.'Attempts #'.$_SESSION['_staff']['strikes']."\n" .'Attempts #'.$_SESSION['_auth']['staff']['strikes']."\n"
.'Timeout: '.($cfg->getStaffLoginTimeout()/60)." minutes \n\n"; .'Timeout: '.($cfg->getStaffLoginTimeout()/60)." minutes \n\n";
$ost->logWarning('Excessive login attempts ('.$username.')', $alert, $ost->logWarning('Excessive login attempts ('.$username.')', $alert,
$cfg->alertONLoginError()); $cfg->alertONLoginError());
return new AccessDenied('Forgot your login info? Contact Admin.'); return new AccessDenied('Forgot your login info? Contact Admin.');
//Log every other failed login attempt as a warning. //Log every other failed login attempt as a warning.
} elseif($_SESSION['_staff']['strikes']%2==0) { } elseif($_SESSION['_auth']['staff']['strikes']%2==0) {
$alert='Username: '.$username."\n" $alert='Username: '.$username."\n"
.'IP: '.$_SERVER['REMOTE_ADDR']."\n" .'IP: '.$_SERVER['REMOTE_ADDR']."\n"
.'TIME: '.date('M j, Y, g:i a T')."\n\n" .'TIME: '.date('M j, Y, g:i a T')."\n\n"
.'Attempts #'.$_SESSION['_staff']['strikes']; .'Attempts #'.$_SESSION['_auth']['staff']['strikes'];
$ost->logWarning('Failed staff login attempt ('.$username.')', $alert, false); $ost->logWarning('Failed staff login attempt ('.$username.')', $alert, false);
} }
} }
...@@ -412,30 +450,31 @@ class UserAuthStrikeBackend extends AuthStrikeBackend { ...@@ -412,30 +450,31 @@ class UserAuthStrikeBackend extends AuthStrikeBackend {
$cfg = $ost->getConfig(); $cfg = $ost->getConfig();
$_SESSION['_auth']['user'] = array();
//Check time for last max failed login attempt strike. //Check time for last max failed login attempt strike.
if($_SESSION['_client']['laststrike']) { if($_SESSION['_auth']['user']['laststrike']) {
if((time()-$_SESSION['_client']['laststrike'])<$cfg->getClientLoginTimeout()) { if((time()-$_SESSION['_auth']['user']['laststrike'])<$cfg->getClientLoginTimeout()) {
$_SESSION['_client']['laststrike'] = time(); //renew the strike. $_SESSION['_auth']['user']['laststrike'] = time(); //renew the strike.
return new AccessDenied('You\'ve reached maximum failed login attempts allowed.'); return new AccessDenied('You\'ve reached maximum failed login attempts allowed.');
} else { //Timeout is over. } else { //Timeout is over.
//Reset the counter for next round of attempts after the timeout. //Reset the counter for next round of attempts after the timeout.
$_SESSION['_client']['laststrike'] = null; $_SESSION['_auth']['user']['laststrike'] = null;
$_SESSION['_client']['strikes'] = 0; $_SESSION['_auth']['user']['strikes'] = 0;
} }
} }
$_SESSION['_client']['strikes']+=1; $_SESSION['_auth']['user']['strikes']+=1;
if($_SESSION['_client']['strikes']>$cfg->getClientMaxLogins()) { if($_SESSION['_auth']['user']['strikes']>$cfg->getClientMaxLogins()) {
$_SESSION['_client']['laststrike'] = time(); $_SESSION['_auth']['user']['laststrike'] = time();
$alert='Excessive login attempts by a user.'."\n". $alert='Excessive login attempts by a user.'."\n".
'Login: '.$username.': '.$password."\n". 'Login: '.$username.': '.$password."\n".
'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n". 'IP: '.$_SERVER['REMOTE_ADDR']."\n".'Time:'.date('M j, Y, g:i a T')."\n\n".
'Attempts #'.$_SESSION['_client']['strikes']; 'Attempts #'.$_SESSION['_auth']['user']['strikes'];
$ost->logError('Excessive login attempts (user)', $alert, ($cfg->alertONLoginError())); $ost->logError('Excessive login attempts (user)', $alert, ($cfg->alertONLoginError()));
return new AccessDenied('Access Denied'); return new AccessDenied('Access Denied');
} elseif($_SESSION['_client']['strikes']%2==0) { //Log every other failed login attempt as a warning. } elseif($_SESSION['_auth']['user']['strikes']%2==0) { //Log every other failed login attempt as a warning.
$alert='Login: '.$username.': '.$password."\n".'IP: '.$_SERVER['REMOTE_ADDR']. $alert='Login: '.$username.': '.$password."\n".'IP: '.$_SERVER['REMOTE_ADDR'].
"\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$_SESSION['_client']['strikes']; "\n".'TIME: '.date('M j, Y, g:i a T')."\n\n".'Attempts #'.$_SESSION['_auth']['user']['strikes'];
$ost->logWarning('Failed login attempt (user)', $alert); $ost->logWarning('Failed login attempt (user)', $alert);
} }
...@@ -462,6 +501,15 @@ class osTicketAuthentication extends StaffAuthenticationBackend { ...@@ -462,6 +501,15 @@ class osTicketAuthentication extends StaffAuthenticationBackend {
return $user; return $user;
} }
} }
protected function getAuthKey($staff) {
if(!($staff instanceof Staff))
return null;
return $staff->getUsername(); //FIXME:
}
} }
StaffAuthenticationBackend::register(osTicketAuthentication); StaffAuthenticationBackend::register(osTicketAuthentication);
...@@ -470,11 +518,41 @@ class AuthTokenAuthentication extends UserAuthenticationBackend { ...@@ -470,11 +518,41 @@ class AuthTokenAuthentication extends UserAuthenticationBackend {
static $id = "authtoken"; static $id = "authtoken";
function signOn() { function signOn() {
if ($_GET['auth'] && ($user=self::__authtoken($_GET['auth']))) $user = null;
return $user; if ($_GET['auth'])
$user = self::__authtoken($_GET['auth']);
// Support old ticket based tokens.
elseif ($_GET['t'] && $_GET['e'] && $_GET['a']) {
if (($ticket = Ticket::lookupByExtId($_GET['t'], $_GET['e']))
// Using old ticket auth code algo - hardcoded here because it
// will be removed in ticket class in the upcoming rewrite
&& !strcasecmp($_GET['a'], md5($ticket->getId() . $_GET['e'] . SECRET_SALT))
&& ($client = $ticket->getClient()))
$user = new ClientSession($client);
}
return $user;
}
protected function getAuthKey($user) {
if (!$this->supportsAuthentication()
|| !$user
|| !($user instanceof EndUser))
return null;
//Generate authkey based the type of ticket user
// It's required to validate users going forward.
$authkey = sprintf('%s%dt%dh%s', //XXX: Placeholder
$user->isOwner() ? 'o':'c',
$user->getId(),
$user->getTicketID(),
md5($user->getUsername().$this->id));
return $authkey;
} }
static private function __authtoken($token) { static private function __authtoken($token) {
...@@ -482,7 +560,7 @@ class AuthTokenAuthentication extends UserAuthenticationBackend { ...@@ -482,7 +560,7 @@ class AuthTokenAuthentication extends UserAuthenticationBackend {
switch ($token[0]) { switch ($token[0]) {
case 'c': //Collaborator c+[token] case 'c': //Collaborator c+[token]
if (($c = Collaborator::lookupByAuthToken($token))) if (($c = Collaborator::lookupByAuthToken($token)))
return new TicketUser($c); //Decorator return new ClientSession($c); //Decorator
break; break;
case 'o': //Ticket owner o+[token] case 'o': //Ticket owner o+[token]
break; break;
......
...@@ -247,9 +247,8 @@ class Client { ...@@ -247,9 +247,8 @@ class Client {
* *
*/ */
class TicketUser implements AuthenticatedUser { class EndUser extends AuthenticatedUser {
protected $backend;
protected $user; protected $user;
function __construct($user) { function __construct($user) {
...@@ -261,8 +260,9 @@ class TicketUser implements AuthenticatedUser { ...@@ -261,8 +260,9 @@ class TicketUser implements AuthenticatedUser {
*/ */
function __call($name, $args) { function __call($name, $args) {
if(!$this->user if(!$this->user
|| !is_callable($this->user, $name)) || !is_callable(array($this->user, $name)))
return false; return false;
return $args return $args
...@@ -282,14 +282,6 @@ class TicketUser implements AuthenticatedUser { ...@@ -282,14 +282,6 @@ class TicketUser implements AuthenticatedUser {
return ($this->user && $this->user instanceof Client); return ($this->user && $this->user instanceof Client);
} }
function setBackend($bk) {
$this->backend = $bk;
}
function getBackend() {
return $this->backend;
}
function getUserName() { function getUserName() {
//XXX: Revisit when real usernames are introduced or when email //XXX: Revisit when real usernames are introduced or when email
// requirement is removed. // requirement is removed.
......
...@@ -22,7 +22,7 @@ include_once(INCLUDE_DIR.'class.passwd.php'); ...@@ -22,7 +22,7 @@ include_once(INCLUDE_DIR.'class.passwd.php');
include_once(INCLUDE_DIR.'class.user.php'); include_once(INCLUDE_DIR.'class.user.php');
include_once(INCLUDE_DIR.'class.auth.php'); include_once(INCLUDE_DIR.'class.auth.php');
class Staff implements AuthenticatedUser { class Staff extends AuthenticatedUser {
var $ht; var $ht;
var $id; var $id;
...@@ -97,14 +97,6 @@ class Staff implements AuthenticatedUser { ...@@ -97,14 +97,6 @@ class Staff implements AuthenticatedUser {
// AuthenticatedUser implementation... // AuthenticatedUser implementation...
// TODO: Move to an abstract class that extends Staff // TODO: Move to an abstract class that extends Staff
function setBackend($bk) {
}
function getBackend() {
}
function getRole() { function getRole() {
return 'staff'; return 'staff';
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment