/** * @param int $userId * @param string $newPassword * @param null $resetPasswordToken token to expire if call is from password reset * @return bool * @throws PasswordChangeException */ public function updatePassword($userId, $newPassword, $resetPasswordToken = null) { $transactionManager = new TransactionManager(); try { //use a transaction as we would be updating more than one table $transaction = $transactionManager->get(); $this->setTransaction($transaction); $user = User::findFirst($userId); if ($user == false) { $transaction->rollback(ErrorMessages::PASSWORD_UPDATE_FAILED); } $previousPassword = $user->password; $user->password = Utils::encryptPassword($newPassword); if (!$user->save()) { $transaction->rollback(ErrorMessages::PASSWORD_UPDATE_FAILED); } $userPasswordChange = new UserPasswordChange(); $userPasswordChange->setTransaction($transaction); $userPasswordChange->setDateChanged(date("Y-m-d H:i:s")); $userPasswordChange->setUserId($userId); $userPasswordChange->setPasswordHash($previousPassword); if (!$userPasswordChange->save()) { $transaction->rollback(ErrorMessages::PASSWORD_UPDATE_FAILED); } if (!empty($resetPasswordToken) && !(new UserPasswordReset())->expireToken($resetPasswordToken)) { $transaction->rollback(ErrorMessages::TOKEN_EXPIRY_FAILED); } $transaction->commit(); return true; } catch (TransactionFailed $e) { throw new PasswordChangeException($e->getMessage()); } }
/** * check if the new password does not correspond to the previous max passwords * We use max-1 in the query because we are assuming that the user's current password is * inclusive of the last max passwords used and this has already been checked above * * @param int $userId * @param string $newPassword * @param int $max * @throws PasswordChangeException */ public static function validateNewPassword($userId, $newPassword, $max = self::MAX_PASSWORD_CHANGES_BEFORE_REUSE) { $recentPasswords = UserPasswordChange::query()->where("user_id = :user_id:")->bind(["user_id" => $userId])->orderBy("date_changed DESC")->limit($max - 1)->execute()->toArray(); foreach ($recentPasswords as $aRecentPassword) { if (Utils::verifyPassword($newPassword, $aRecentPassword['password_hash'])) { throw new PasswordChangeException("You cannot use any of your last {$max} passwords"); } } }