/** * @param integer $userId The user's id * @param string $passwordHash The passsword's hash */ public static function createAndSave($userId, $passwordHash) { $passwordHistory = new PasswordHistory(); $passwordHistory->user_id = $userId; $passwordHistory->password_hash = $passwordHash; $passwordHistory->save(); }
/** * Checks whether the user is allowed to change his password. * * If the password is the same as one of his $lastPasswordChangesCount previous passwords, the user is not allowed. * * @param Event $event */ public function allowPasswordChange(Event $event) { $form = $event->sender; // The ChangePasswordForm if ($form->oldPassword == $form->newPassword) { Yii::info("The system doesn't allow a password change because the current " . "password of the user is the same as the new one!", __CLASS__); $form->addError('newPassword', Yii::t(Module::I18N_CATEGORY, 'Passwords must not be the same!')); return; } $security = Yii::$app->security; $userModel = $form->getUser(); $previousPasswords = PasswordHistory::findAllByUserId($userModel->id, $this->lastPasswordChangesCount); if (!count($previousPasswords)) { // The password is changed for a first time Yii::info("Save the first password of the user to the password history table", __CLASS__); PasswordHistory::createAndSave($userModel->id, $userModel->password_hash); // Save the first password } else { foreach ($previousPasswords as $passwordHistory) { if ($security->validatePassword($form->newPassword, $passwordHistory->password_hash)) { Yii::info("The system doesn't allow a password change, because " . "the current password is the same as one of the previous passwords of the user!", __CLASS__); $form->addError('newPassword', Yii::t(Module::I18N_CATEGORY, 'Your password is the same as a previous password of yours. For security reasons, please add another password.')); return; } } } Yii::info("Save the new password of the user to the password history table", __CLASS__); PasswordHistory::createAndSave($userModel->id, $security->generatePasswordHash($form->newPassword)); // Save the new password }
public function getPasswordHistories() { return $this->hasMany(PasswordHistory::className(), ['user_id' => 'id']); }
public function testPasswordHistoryPolicy() { // Asure that everything is configured properly verify('Check that the advanced directory exists', is_dir(Commons::ADVANCED_MIGRATIONS_DIR))->true(); $files = scandir(Commons::ADVANCED_MIGRATIONS_DIR); $result = preg_grep('/' . self::ATTR_PASSWORD_HISTORY . '/', $files); verify('Check that the migration exists', $result)->notEmpty(); verify('Check that the table is added to the database (the migration is run)', Yii::$app->db->schema->getTableSchema('password_history'))->notNull(); verify('Asure that the model is created', Yii::createObject(self::PASSWORD_HISTORY_MODEL))->notNull(); // Behavior validations $changePasswordForm = Yii::createObject(Yii::$app->user->changePasswordForm); $behavior = $changePasswordForm->attachBehavior('passwordHistoryPolicy', 'nkostadinov\\user\\behaviors\\PasswordHistoryPolicyBehavior'); verify('Check that the behavior exists', $behavior)->notNull(); verify('Check that lastPasswordChangesCount field exists', isset($behavior->lastPasswordChangesCount))->true(); verify('Check that the default value of lastPasswordChangesCount is set to 5', $behavior->lastPasswordChangesCount)->equals(5); // Try to change the password for a first time by adding the same password Commons::createUser(); $changePasswordForm->email = Commons::TEST_EMAIL; $changePasswordForm->oldPassword = '******'; $changePasswordForm->newPassword = '******'; $changePasswordForm->newPasswordRepeat = 'Ni$test123'; verify('Assure that the password cannot be changed, because it is the same as the previous one', $changePasswordForm->changePassword())->false(); verify('Assure that exactly the new password field has errors', $changePasswordForm->hasErrors('newPassword'))->true(); verify('Assure that the error on the new password is the error we expect', $changePasswordForm->getErrors('newPassword')[0])->equals('Passwords must not be the same!'); // Change the password this time for real $userId = $changePasswordForm->getUser()->id; $changePasswordForm->newPassword = '******'; $changePasswordForm->newPasswordRepeat = 'Ni$test1233'; verify('Assure that the password is successfuly changed', $changePasswordForm->changePassword())->true(); $previousPasswords = PasswordHistory::findAllByUserId($userId); verify('Assure that both - the first and the new passwords are added to the history table', count($previousPasswords))->equals(2); // Try to change the password by adding a password that has already been used in the past $changePasswordForm->oldPassword = '******'; $changePasswordForm->newPassword = '******'; $changePasswordForm->newPasswordRepeat = 'Ni$test123'; verify('Assure that the password cannot be changed, because it is the same as a password added in the past', $changePasswordForm->changePassword())->false(); verify('Assure that exactly the new password field has errors', $changePasswordForm->hasErrors('newPassword'))->true(); verify('Assure that the error on the new password is the error we expect', $changePasswordForm->getErrors('newPassword')[0])->equals('Your password is the same as a previous password of yours. For security reasons, please add another password.'); // Change the password for a second time (this time for real) $changePasswordForm->oldPassword = '******'; $changePasswordForm->newPassword = '******'; $changePasswordForm->newPasswordRepeat = 'Ni$test!23'; verify('Assure that the password is successfuly changed', $changePasswordForm->changePassword())->true(); $previousPasswords = PasswordHistory::findAllByUserId($userId); verify('Assure that the new password is added to the history table', count($previousPasswords))->equals(3); }