mirror of
https://github.com/discourse/discourse.git
synced 2025-05-24 03:36:18 +08:00
FEATURE: Allow site admin to mark a user's password as expired (#27314)
This commit adds the ability for site administrators to mark users' passwords as expired. Note that this commit does not add any client side interface to mark a user's password as expired. The following changes are introduced in this commit: 1. Adds a `user_passwords` table and `UserPassword` model. While the `user_passwords` table is currently used to only store expired passwords, it will be used in the future to store a user's current password as well. 2. Adds a `UserPasswordExpirer.expire_user_password` method which can be used from the Rails console to mark a user's password as expired. 3. Updates `SessionsController#create` to check that the user's current password has not been marked as expired after confirming the password. If the password is determined to be expired based on the existence of a `UserPassword` record with the `password_expired_at` column set, we will not log the user in and will display a password expired notice. A forgot password email is automatically send out to the user as well.
This commit is contained in:

committed by
GitHub

parent
30f55cd64b
commit
e97ef7e9af
@ -15,6 +15,7 @@ class SessionController < ApplicationController
|
||||
allow_in_staff_writes_only_mode :email_login
|
||||
|
||||
ACTIVATE_USER_KEY = "activate_user"
|
||||
FORGOT_PASSWORD_EMAIL_LIMIT_PER_DAY = 6
|
||||
|
||||
def csrf
|
||||
render json: { csrf: form_authenticity_token }
|
||||
@ -332,8 +333,10 @@ class SessionController < ApplicationController
|
||||
rate_limit_second_factor!(user)
|
||||
|
||||
if user.present?
|
||||
# If their password is correct
|
||||
unless user.confirm_password?(params[:password])
|
||||
password = params[:password]
|
||||
|
||||
# If their password is incorrect
|
||||
if !user.confirm_password?(password)
|
||||
invalid_credentials
|
||||
return
|
||||
end
|
||||
@ -347,6 +350,18 @@ class SessionController < ApplicationController
|
||||
# User signed on with username and password, so let's prevent the invite link
|
||||
# from being used to log in (if one exists).
|
||||
Invite.invalidate_for_email(user.email)
|
||||
|
||||
# User's password has expired so they need to reset it
|
||||
if user.password_expired?(password)
|
||||
begin
|
||||
enqueue_password_reset_for_user(user)
|
||||
rescue RateLimiter::LimitExceeded
|
||||
# Just noop here as user would have already been sent the forgot password email more than once
|
||||
end
|
||||
|
||||
render json: { error: I18n.t("login.password_expired") }
|
||||
return
|
||||
end
|
||||
else
|
||||
invalid_credentials
|
||||
return
|
||||
@ -622,15 +637,7 @@ class SessionController < ApplicationController
|
||||
end
|
||||
|
||||
if user
|
||||
RateLimiter.new(nil, "forgot-password-login-day-#{user.username}", 6, 1.day).performed!
|
||||
email_token =
|
||||
user.email_tokens.create!(email: user.email, scope: EmailToken.scopes[:password_reset])
|
||||
Jobs.enqueue(
|
||||
:critical_user_email,
|
||||
type: "forgot_password",
|
||||
user_id: user.id,
|
||||
email_token: email_token.token,
|
||||
)
|
||||
enqueue_password_reset_for_user(user)
|
||||
else
|
||||
RateLimiter.new(
|
||||
nil,
|
||||
@ -897,4 +904,23 @@ class SessionController < ApplicationController
|
||||
|
||||
allowed_domains.split("|").include?(hostname)
|
||||
end
|
||||
|
||||
def enqueue_password_reset_for_user(user)
|
||||
RateLimiter.new(
|
||||
nil,
|
||||
"forgot-password-login-day-#{user.username}",
|
||||
FORGOT_PASSWORD_EMAIL_LIMIT_PER_DAY,
|
||||
1.day,
|
||||
).performed!
|
||||
|
||||
email_token =
|
||||
user.email_tokens.create!(email: user.email, scope: EmailToken.scopes[:password_reset])
|
||||
|
||||
Jobs.enqueue(
|
||||
:critical_user_email,
|
||||
type: "forgot_password",
|
||||
user_id: user.id,
|
||||
email_token: email_token.token,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user