diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index 7a11a8ca9..3b18334ae 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -1094,9 +1094,13 @@ class LoginWebPage extends NiceWebPage if (isset($_SESSION['auth_user'])) { $sAuthUser = $_SESSION['auth_user']; + $sIssue = $_SESSION['pwd_issue'] ?? null; + unset($_SESSION['pwd_issue']); + $bFailedLogin = ($sIssue != null); // Force the "failed login" flag to display the "issue" message + UserRights::Login($sAuthUser); // Set the user's language $oPage = self::NewLoginWebPage(); - $oPage->DisplayChangePwdForm(); + $oPage->DisplayChangePwdForm($bFailedLogin, $sIssue); $oPage->output(); exit; } diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index d80bc64c1..505e9eae4 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -604,7 +604,7 @@ abstract class User extends cmdbAbstractObject * @throws \OQLException * @since 3.0.0 */ - private function IsCurrentUser(): bool + protected function IsCurrentUser(): bool { if (is_null(UserRights::GetUserId())) { return false; diff --git a/datamodels/2.x/authent-local/dictionaries/de.dict.authent-local.php b/datamodels/2.x/authent-local/dictionaries/de.dict.authent-local.php index 38119aa68..1fdaed540 100644 --- a/datamodels/2.x/authent-local/dictionaries/de.dict.authent-local.php +++ b/datamodels/2.x/authent-local/dictionaries/de.dict.authent-local.php @@ -36,6 +36,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '', 'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'abgelaufen', 'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '', + 'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'einmaliges Passwort', + 'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '', 'Class:UserLocal/Attribute:password_renewed_date' => 'Letzte Passworterneuerung', 'Class:UserLocal/Attribute:password_renewed_date+' => 'Letztes Änderungsdatum', diff --git a/datamodels/2.x/authent-local/dictionaries/en.dict.authent-local.php b/datamodels/2.x/authent-local/dictionaries/en.dict.authent-local.php index c0bb5cda1..2cc418dcb 100644 --- a/datamodels/2.x/authent-local/dictionaries/en.dict.authent-local.php +++ b/datamodels/2.x/authent-local/dictionaries/en.dict.authent-local.php @@ -49,10 +49,13 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '', 'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expired', 'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '', - 'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal', + 'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'One-time Password', + 'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.', + 'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on', 'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed', 'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.', - 'UserLocal:password:expiration' => 'The fields below require an extension' + 'UserLocal:password:expiration' => 'The fields below require an extension', + 'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User', )); diff --git a/datamodels/2.x/authent-local/dictionaries/fr.dict.authent-local.php b/datamodels/2.x/authent-local/dictionaries/fr.dict.authent-local.php index 533ceb867..c85f68889 100644 --- a/datamodels/2.x/authent-local/dictionaries/fr.dict.authent-local.php +++ b/datamodels/2.x/authent-local/dictionaries/fr.dict.authent-local.php @@ -27,16 +27,19 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:UserLocal/Attribute:expiration' => 'Validité du mot de passe', 'Class:UserLocal/Attribute:expiration+' => 'Statut du mot de passe (nécessite une extension pour avoir un effet)', - 'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'à durée limitée', + 'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Durée limitée', 'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '', - 'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'à validité permanente', + 'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Permanente', 'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '', - 'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'à changer', + 'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'A changer à la prochaine connexion', 'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '', + 'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'Usage unique', + 'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '', 'Class:UserLocal/Attribute:password_renewed_date' => 'Mot de passe changé le', 'Class:UserLocal/Attribute:password_renewed_date+' => 'Dernière date à laquelle le mot de passe a été changé', 'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 8 caractères, avec minuscule, majuscule, nombre et caractère spécial.', - 'UserLocal:password:expiration' => 'Les champs ci-dessous nécessitent une extension' + 'UserLocal:password:expiration' => 'Les champs ci-dessous nécessitent une extension', + 'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Impossible de mettre "Usage unique" comme validité du mot de passe pour son propre utilisateur.', )); diff --git a/datamodels/2.x/authent-local/model.authent-local.php b/datamodels/2.x/authent-local/model.authent-local.php index ed62ae40f..23e8cf333 100755 --- a/datamodels/2.x/authent-local/model.authent-local.php +++ b/datamodels/2.x/authent-local/model.authent-local.php @@ -68,7 +68,8 @@ class UserLocal extends UserInternal const EXPIRE_CAN = 'can_expire'; const EXPIRE_NEVER = 'never_expire'; const EXPIRE_FORCE = 'force_expire'; - + const EXPIRE_ONE_TIME_PWD = 'otp_expire'; + /** @var UserLocalPasswordValidity|null */ protected $m_oPasswordValidity = null; @@ -90,8 +91,8 @@ class UserLocal extends UserInternal MetaModel::Init_AddAttribute(new AttributeOneWayPassword("password", array("allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - $sExpireEnum = implode(',', array(self::EXPIRE_CAN, self::EXPIRE_NEVER, self::EXPIRE_FORCE)); - MetaModel::Init_AddAttribute(new AttributeEnum("expiration", array("allowed_values"=>new ValueSetEnum($sExpireEnum), "sql"=>"expiration", "default_value"=>'never_expire', "is_null_allowed"=>false, "depends_on"=>array()))); + $sExpireEnum = implode(',', array(self::EXPIRE_CAN, self::EXPIRE_NEVER, self::EXPIRE_FORCE, self::EXPIRE_ONE_TIME_PWD)); + MetaModel::Init_AddAttribute(new AttributeEnum("expiration", array("allowed_values"=>new ValueSetEnum($sExpireEnum), "sql"=>"expiration", "default_value"=>self::EXPIRE_NEVER, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("password_renewed_date", array("allowed_values"=>null, "sql"=>"password_renewed_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); // Display lists @@ -136,6 +137,10 @@ class UserLocal extends UserInternal { return false; } + if($this->Get('expiration') == self::EXPIRE_ONE_TIME_PWD) + { + return false; + } return true; } @@ -184,6 +189,8 @@ class UserLocal extends UserInternal protected function OnInsert() { parent::OnInsert(); + $sToday = date(\AttributeDate::GetInternalFormat()); + $this->Set('password_renewed_date', $sToday); $this->OnWrite(); } @@ -202,6 +209,15 @@ class UserLocal extends UserInternal $sNow = date(\AttributeDate::GetInternalFormat()); $this->Set('password_renewed_date', $sNow); + + // Reset the "force" expiration flag when the user updates her/his own password! + if ($this->IsCurrentUser()) + { + if (($this->Get('expiration') == self::EXPIRE_FORCE)) + { + $this->Set('expiration', self::EXPIRE_CAN); + } + } } public function IsPasswordValid() @@ -278,7 +294,13 @@ class UserLocal extends UserInternal { $this->m_aCheckIssues[] = $this->m_oPasswordValidity->getPasswordValidityMessage(); } - + + // A User cannot force a one-time password on herself/himself + if ($this->IsCurrentUser()) { + if (array_key_exists('expiration', $this->ListChanges()) && ($this->Get('expiration') == self::EXPIRE_ONE_TIME_PWD)) { + $this->m_aCheckIssues[] = Dict::S('Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed'); + } + } parent::DoCheckToWrite(); }