Skip to content
Snippets Groups Projects
Commit 57845b7f authored by Jared Hancock's avatar Jared Hancock
Browse files

auth: Add concept of bk passwd update and policy

This adds the concept of a PasswordPolicy registration system, which
provides an extensible way of administering and configuring password
complexity policies.

It also adds a setPassword() method for the authentication backends which
will allow for the respective backend up update the password according to
whatever method is suitable for the respective backend (such as remote
updates for LDAP).
parent 1c590065
No related branches found
No related tags found
No related merge requests found
......@@ -347,6 +347,38 @@ abstract class AuthenticationBackend {
return false;
}
/**
* Request the backend to update the password for a user. This method is
* the main entry for password updates so that password policies can be
* applied to the new password before passing the new password to the
* backend for updating.
*
* Throws:
* BadPassword — if password does not meet policy requirement
* PasswordUpdateFailed — if backend failed to update the password
*/
function setPassword($user, $password, $current=false) {
PasswordPolicy::checkPassword($password, $current);
$rv = $this->syncPassword($user, $password);
if ($rv) {
$info = array('password' => $password, 'current' => $current);
Signal::send('auth.pwchange', $user, $info);
}
return $rv;
}
/**
* Request the backend to update the user's password with the password
* given. This method should only be used if the backend advertises
* supported password updates with the supportsPasswordChange() method.
*
* Returns:
* true if the password was successfully updated and false otherwise.
*/
protected function syncPassword($user, $password) {
return false;
}
function supportsPasswordReset() {
return false;
}
......@@ -946,6 +978,14 @@ class osTicketAuthentication extends StaffAuthenticationBackend {
}
}
function supportsPasswordChange() {
return true;
}
function syncPassword($staff, $password) {
$staff->passwd = Passwd::hash($password);
}
}
StaffAuthenticationBackend::register('osTicketAuthentication');
......@@ -1228,4 +1268,56 @@ class ClientAcctConfirmationTokenBackend extends UserAuthenticationBackend {
}
}
UserAuthenticationBackend::register('ClientAcctConfirmationTokenBackend');
// ----- Password Policy --------------------------------------
class BadPassword extends Exception {}
class PasswordUpdateFailed extends Exception {}
abstract class PasswordPolicy {
static protected $registry = array();
/**
* Check a password and throw BadPassword with a meaningful message if
* the password cannot be accepted.
*/
abstract function processPassword($new, $current);
static function checkPassword($new, $current) {
foreach (static::allActivePolicies() as $P) {
$P->processPassword($new, $current);
}
}
static function allActivePolicies() {
$policies = array();
foreach (static::$registry as $P) {
if (is_string($P) && class_exists($P))
$P = new $P();
if ($P instanceof PasswordPolicy)
$policies[] = $P;
}
return $policies;
}
static function register($policy) {
static::$registry[] = $policy;
}
}
class osTicketPasswordPolicy
extends PasswordPolicy {
function processPassword($passwd, $current) {
if (strlen($passwd) < 6) {
throw new BadPassword(
__('Password must be at least 6 characters'));
}
// XXX: Changing case is technicall changing the password
if (0 === strcasecmp($passwd, $current)) {
throw new BadPassword(
__('New password MUST be different from the current password!'));
}
}
}
PasswordPolicy::register('osTicketPasswordPolicy');
?>
......@@ -96,7 +96,13 @@ implements AuthenticatedUser, EmailContact {
}
function getAuthBackend() {
list($authkey, ) = explode(':', $this->getAuthKey());
list($bk, ) = explode(':', $this->getAuthKey());
// If administering a user other than yourself, fallback to the
// agent's declared backend, if any
if (!$bk && $this->backend)
$bk = $this->backend;
return StaffAuthenticationBackend::getBackend($authkey);
}
......@@ -157,6 +163,34 @@ implements AuthenticatedUser, EmailContact {
&& $this->passwd_change>($cfg->getPasswdResetPeriod()*30*24*60*60));
}
function setPassword($new, $current=false) {
// Allow the backend to update the password. This is the preferred
// method as it allows for integration with password policies and
// also allows for remotely updating the password where possible and
// supported.
if (!($bk = $this->getAuthBackend())
|| !$bk instanceof AuthBackend
) {
// Fallback to osTicket authentication token udpates
$bk = new osTicketAuthentication();
}
// And now for the magic
if (!$bk->supportsPasswordChange()) {
throw new PasswordUpdateFailed(
__('Authentication backend does not support password updates'));
}
if (!$bk->setPassword($this, $new, $current)) {
// Backend should throw PasswordUpdateFailed directly
return false;
}
// Successfully updated authentication tokens
$this->change_passwd = 0;
$this->cancelResetTokens();
$this->passwdreset = SqlFunction::NOW();
}
function canAccess($something) {
if ($something instanceof RestrictedAccess)
return $something->checkStaffPerm($this);
......@@ -492,8 +526,6 @@ implements AuthenticatedUser, EmailContact {
if(!$vars['passwd1'])
$errors['passwd1']=__('New password is required');
elseif($vars['passwd1'] && strlen($vars['passwd1'])<6)
$errors['passwd1']=__('Password must be at least 6 characters');
elseif($vars['passwd1'] && strcmp($vars['passwd1'], $vars['passwd2']))
$errors['passwd2']=__('Passwords do not match');
......@@ -511,13 +543,24 @@ implements AuthenticatedUser, EmailContact {
$errors['cpasswd']=__('Current password is required');
elseif(!$this->cmp_passwd($vars['cpasswd']))
$errors['cpasswd']=__('Invalid current password!');
elseif(!strcasecmp($vars['passwd1'], $vars['cpasswd']))
$errors['passwd1']=__('New password MUST be different from the current password!');
}
if($vars['default_signature_type']=='mine' && !$vars['signature'])
$errors['default_signature_type'] = __("You don't have a signature");
// Update the user's password if requested
if ($vars['passwd1']) {
try {
$this->setPassword($vars['passwd1'], $vars['cpasswd']);
}
catch (BadPassword $ex) {
$errors['passwd1'] = $ex->getMessage();
}
catch (PasswordUpdateFailed $ex) {
// TODO: Add a warning banner or crash the update
}
}
if($errors) return false;
$_SESSION['staff:lang'] = null;
......@@ -539,15 +582,6 @@ implements AuthenticatedUser, EmailContact {
$this->default_paper_size = $vars['default_paper_size'];
$this->lang = $vars['lang'];
if ($vars['passwd1']) {
$this->change_passwd = 0;
$this->passwdreset = SqlFunction::NOW();
$this->passwd = Passwd::hash($vars['passwd1']);
$info = array('password' => $vars['passwd1']);
Signal::send('auth.pwchange', $this, $info);
$this->cancelResetTokens();
}
return $this->save();
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment