diff --git a/SeedDMS_Core/Core/inc.ClassUser.php b/SeedDMS_Core/Core/inc.ClassUser.php index 5d8aa10d4..ca1634e03 100644 --- a/SeedDMS_Core/Core/inc.ClassUser.php +++ b/SeedDMS_Core/Core/inc.ClassUser.php @@ -333,6 +333,13 @@ class SeedDMS_Core_User { /* {{{ */ */ var $_isDisabled; + /** + * @var string date till when the user is disabled + * + * @access protected + */ + var $_disabledUntil; + /** * @var int number of login failures * @@ -402,7 +409,7 @@ class SeedDMS_Core_User { /* {{{ */ * @param string $secret * @param string $webauthn */ - function __construct($id, $login, $pwd, $fullName, $email, $language, $theme, $comment, $role, $isHidden=0, $isDisabled=0, $pwdExpiration='', $loginFailures=0, $quota=0, $homeFolder=null, $secret='', $webauthn='') { + function __construct($id, $login, $pwd, $fullName, $email, $language, $theme, $comment, $role, $isHidden=0, $isDisabled=0, $pwdExpiration='', $loginFailures=0, $quota=0, $homeFolder=null, $secret='', $webauthn='', $disabledUntil=null) { $this->_id = $id; $this->_login = $login; $this->_pwd = $pwd; @@ -414,6 +421,7 @@ class SeedDMS_Core_User { /* {{{ */ $this->_role = $role; $this->_isHidden = $isHidden; $this->_isDisabled = $isDisabled; + $this->_disabledUntil = $disabledUntil; $this->_pwdExpiration = $pwdExpiration; $this->_loginFailures = $loginFailures; $this->_quota = $quota; @@ -463,7 +471,7 @@ class SeedDMS_Core_User { /* {{{ */ $classname = $dms->getClassname('role'); $role = $classname::getInstance($resArr['role'], $dms); - $user = new self($resArr["id"], $resArr["login"], $resArr["pwd"], $resArr["fullName"], $resArr["email"], $resArr["language"], $resArr["theme"], $resArr["comment"], $role, $resArr["hidden"], $resArr["disabled"], $resArr["pwdExpiration"], $resArr["loginfailures"], $resArr["quota"], $resArr["homefolder"], $resArr["secret"], $resArr["webauthn"]); + $user = new self($resArr["id"], $resArr["login"], $resArr["pwd"], $resArr["fullName"], $resArr["email"], $resArr["language"], $resArr["theme"], $resArr["comment"], $role, $resArr["hidden"], $resArr["disabled"], $resArr["pwdExpiration"], $resArr["loginfailures"], $resArr["quota"], $resArr["homefolder"], $resArr["secret"], $resArr["webauthn"], $resArr["disabledUntil"]); $user->setDMS($dms); return $user; } /* }}} */ @@ -491,7 +499,7 @@ class SeedDMS_Core_User { /* {{{ */ for ($i = 0; $i < count($resArr); $i++) { /** @var SeedDMS_Core_User $user */ $role = $classname::getInstance($resArr[$i]['role'], $dms); - $user = new self($resArr[$i]["id"], $resArr[$i]["login"], $resArr[$i]["pwd"], $resArr[$i]["fullName"], $resArr[$i]["email"], (isset($resArr[$i]["language"])?$resArr[$i]["language"]:NULL), (isset($resArr[$i]["theme"])?$resArr[$i]["theme"]:NULL), $resArr[$i]["comment"], $role, $resArr[$i]["hidden"], $resArr[$i]["disabled"], $resArr[$i]["pwdExpiration"], $resArr[$i]["loginfailures"], $resArr[$i]["quota"], $resArr[$i]["homefolder"]); + $user = new self($resArr[$i]["id"], $resArr[$i]["login"], $resArr[$i]["pwd"], $resArr[$i]["fullName"], $resArr[$i]["email"], (isset($resArr[$i]["language"])?$resArr[$i]["language"]:NULL), (isset($resArr[$i]["theme"])?$resArr[$i]["theme"]:NULL), $resArr[$i]["comment"], $role, $resArr[$i]["hidden"], $resArr[$i]["disabled"], $resArr[$i]["pwdExpiration"], $resArr[$i]["loginfailures"], $resArr[$i]["quota"], $resArr[$i]["homefolder"], $resArr[$i]["disabledUntil"]); $user->setDMS($dms); $users[$i] = $user; } @@ -827,6 +835,31 @@ class SeedDMS_Core_User { /* {{{ */ return true; } /* }}} */ + /** + * @param $disabledUntil ''|integer with seconds from now on|date time string + * @return bool + */ + function setDisabledUntil($disabledUntil) { /* {{{ */ + $db = $this->_dms->getDB(); + + if(trim($disabledUntil) == '' || trim($disabledUntil) == 'never') + $queryStr = "UPDATE `tblUsers` SET `disabledUntil` = NULL WHERE `id` = " . $this->_id; + elseif(is_numeric($disabledUntil) && $disabledUntil < 10000) + $queryStr = "UPDATE `tblUsers` SET `disabledUntil` = " . $db->qstr(date('Y-m-d H:i:s', time()+(int)$disabledUntil)) . " WHERE `id` = " . $this->_id; + else + $queryStr = "UPDATE `tblUsers` SET `disabledUntil` = " . $db->qstr($disabledUntil) . " WHERE `id` = " . $this->_id; + if (!$db->getResult($queryStr)) + return false; + + $this->_disabledUntil = $disabledUntil; + return true; + } /* }}} */ + + /** + * @return null|string + */ + function getDisabledUntil() { return $this->_disabledUntil; } + /** * @return bool|int */ @@ -848,13 +881,18 @@ class SeedDMS_Core_User { /* {{{ */ $db = $this->_dms->getDB(); $this->_loginFailures = 0; - $queryStr = "UPDATE `tblUsers` SET `loginfailures` = " . $this->_loginFailures . " WHERE `id` = " . $this->_id; + $queryStr = "UPDATE `tblUsers` SET `loginfailures` = " . $this->_loginFailures . ", `disabledUntil` = NULL WHERE `id` = " . $this->_id; if (!$db->getResult($queryStr)) return false; return true; } /* }}} */ + /** + * @return integer + */ + function getLoginFailures() { return $this->_loginFailures; } + /** * Calculate the disk space for all documents owned by the user * diff --git a/controllers/class.Login.php b/controllers/class.Login.php index f33aae161..563c371ed 100644 --- a/controllers/class.Login.php +++ b/controllers/class.Login.php @@ -96,6 +96,12 @@ class SeedDMS_Controller_Login extends SeedDMS_Controller_Common { return false; } + // Check if account is temporarily disabled + if($settings->_loginDelay && $user->getDisabledUntil() > date('Y-m-d H:i:s')) { + $this->setErrorMsg("login_disabled_until_text"); + return false; + } + // control admin IP address if required if ($user->isAdmin() && ($_SERVER['REMOTE_ADDR'] != $settings->_adminIP ) && ( $settings->_adminIP != "") ){ $this->setErrorMsg("invalid_user_id"); diff --git a/inc/inc.ClassDbAuthentication.php b/inc/inc.ClassDbAuthentication.php index 81948137d..ff427ccbb 100644 --- a/inc/inc.ClassDbAuthentication.php +++ b/inc/inc.ClassDbAuthentication.php @@ -60,11 +60,16 @@ class SeedDMS_DbAuthentication extends SeedDMS_Authentication { // (and dangerous) for passwords to be sent via GET. if (md5($password) != $user->getPwd() && !password_verify($password, $user->getPwd())) { /* if counting of login failures is turned on, then increment its value */ + $failures = $user->addLoginFailure(); if($settings->_loginFailure) { - $failures = $user->addLoginFailure(); if($failures >= $settings->_loginFailure) $user->setDisabled(true); } + if($settings->_loginDelay) { + if($failures > 1) { + $user->setDisabledUntil(($failures-1)*($failures-1)*3); + } + } $user = false; } } diff --git a/inc/inc.ClassLdapAuthentication.php b/inc/inc.ClassLdapAuthentication.php index a3d4a4427..f3e2a43ca 100644 --- a/inc/inc.ClassLdapAuthentication.php +++ b/inc/inc.ClassLdapAuthentication.php @@ -150,11 +150,16 @@ class SeedDMS_LdapAuthentication extends SeedDMS_Authentication { } } elseif($user) { $userid = $user->getID(); + $failures = $user->addLoginFailure(); if($settings->_loginFailure) { - $failures = $user->addLoginFailure(); if($failures >= $settings->_loginFailure) $user->setDisabled(true); } + if($settings->_loginDelay) { + if($failures > 1) { + $user->setDisabledUntil(($failures-1)*($failures-1)*3); + } + } $user = false; } ldap_close($ds); diff --git a/inc/inc.ClassSettings.php b/inc/inc.ClassSettings.php index 77b154b87..d5686d790 100644 --- a/inc/inc.ClassSettings.php +++ b/inc/inc.ClassSettings.php @@ -54,6 +54,8 @@ class Settings { /* {{{ */ var $_passwordHistory = 10; // Number of failed logins before account is disabled var $_loginFailure = 0; + // increase the login delay between logins after each failed login + var $_loginDelay = false; // User id that is automatically logged if nobody is logged in var $_autoLoginUser = 0; // maximum amount of bytes a user may consume, 0 = unlimited @@ -599,6 +601,7 @@ class Settings { /* {{{ */ $this->_passwordExpiration = intval($tab["passwordExpiration"]); $this->_passwordHistory = intval($tab["passwordHistory"]); $this->_loginFailure = intval($tab["loginFailure"]); + $this->_loginDelay = Settings::boolVal($tab["loginDelay"]); $this->_autoLoginUser = intval($tab["autoLoginUser"]); $this->_quota = intval($tab["quota"]); $this->_undelUserIds = strval($tab["undelUserIds"]); @@ -965,6 +968,7 @@ class Settings { /* {{{ */ $this->setXMLAttributValue($node, "passwordExpiration", $this->_passwordExpiration); $this->setXMLAttributValue($node, "passwordHistory", $this->_passwordHistory); $this->setXMLAttributValue($node, "loginFailure", $this->_loginFailure); + $this->setXMLAttributValue($node, "loginDelay", $this->_loginDelay); $this->setXMLAttributValue($node, "autoLoginUser", $this->_autoLoginUser); $this->setXMLAttributValue($node, "quota", $this->_quota); $this->setXMLAttributValue($node, "undelUserIds", $this->_undelUserIds); diff --git a/op/op.Settings.php b/op/op.Settings.php index 6f397e760..d0e6dcb56 100644 --- a/op/op.Settings.php +++ b/op/op.Settings.php @@ -154,6 +154,7 @@ if ($action == "saveSettings") $settings->_passwordExpiration = intval($_POST["passwordExpiration"]); $settings->_passwordHistory = intval($_POST["passwordHistory"]); $settings->_loginFailure = intval($_POST["loginFailure"]); + $settings->_loginDelay = getBoolValue("loginDelay"); $settings->_autoLoginUser = intval($_POST["autoLoginUser"]); $settings->_quota = intval($_POST["quota"]); $settings->_undelUserIds = strval($_POST["undelUserIds"]); diff --git a/views/bootstrap/class.Settings.php b/views/bootstrap/class.Settings.php index 99537fb14..f77816af3 100644 --- a/views/bootstrap/class.Settings.php +++ b/views/bootstrap/class.Settings.php @@ -382,6 +382,7 @@ $this->showStartPaneContent('site', (!$currenttab || $currenttab == 'site')); showConfigText('settings_passwordExpiration', 'passwordExpiration'); ?> showConfigText('settings_passwordHistory', 'passwordHistory'); ?> showConfigText('settings_loginFailure', 'loginFailure'); ?> +showConfigCheckbox('settings_loginDelay', 'loginDelay'); ?> showConfigText('settings_autoLoginUser', 'autoLoginUser'); ?> showConfigText('settings_quota', 'quota'); ?> showConfigText('settings_undelUserIds', 'undelUserIds'); ?>