FEATURE: hide_email_address_taken forces use of email in forgot password form (#15362)
* FEATURE: hide_email_address_taken forces use of email in forgot password form This strengthens this site setting which is meant to be used to harden sites that are experiencing abuse on forgot password routes. Previously we would only deny letting people know if forgot password worked on not New change also bans usage of username for forgot password when enabled
This commit is contained in:
parent
1cdb5b7e4a
commit
b6c3e9aa03
|
@ -15,7 +15,15 @@ export default Controller.extend(ModalFunctionality, {
|
||||||
|
|
||||||
@discourseComputed("accountEmailOrUsername", "disabled")
|
@discourseComputed("accountEmailOrUsername", "disabled")
|
||||||
submitDisabled(accountEmailOrUsername, disabled) {
|
submitDisabled(accountEmailOrUsername, disabled) {
|
||||||
return isEmpty((accountEmailOrUsername || "").trim()) || disabled;
|
if (disabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.siteSettings.hide_email_address_taken) {
|
||||||
|
return (accountEmailOrUsername || "").indexOf("@") === -1;
|
||||||
|
} else {
|
||||||
|
return isEmpty((accountEmailOrUsername || "").trim());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
|
|
|
@ -3,8 +3,13 @@
|
||||||
{{#if offerHelp}}
|
{{#if offerHelp}}
|
||||||
{{html-safe offerHelp}}
|
{{html-safe offerHelp}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<label for="username-or-email">{{i18n "forgot_password.invite"}}</label>
|
{{#if siteSettings.hide_email_address_taken}}
|
||||||
{{text-field value=accountEmailOrUsername placeholderKey="login.email_placeholder" id="username-or-email" autocorrect="off" autocapitalize="off"}}
|
<label for="username-or-email">{{i18n "forgot_password.invite_no_username"}}</label>
|
||||||
|
{{text-field value=accountEmailOrUsername placeholderKey="email" id="username-or-email" autocorrect="off" autocapitalize="off"}}
|
||||||
|
{{else}}
|
||||||
|
<label for="username-or-email">{{i18n "forgot_password.invite"}}</label>
|
||||||
|
{{text-field value=accountEmailOrUsername placeholderKey="login.email_placeholder" id="username-or-email" autocorrect="off" autocapitalize="off"}}
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/d-modal-body}}
|
{{/d-modal-body}}
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
|
|
|
@ -434,7 +434,11 @@ class SessionController < ApplicationController
|
||||||
RateLimiter.new(nil, "forgot-password-hr-#{request.remote_ip}", 6, 1.hour).performed!
|
RateLimiter.new(nil, "forgot-password-hr-#{request.remote_ip}", 6, 1.hour).performed!
|
||||||
RateLimiter.new(nil, "forgot-password-min-#{request.remote_ip}", 3, 1.minute).performed!
|
RateLimiter.new(nil, "forgot-password-min-#{request.remote_ip}", 3, 1.minute).performed!
|
||||||
|
|
||||||
user = User.find_by_username_or_email(normalized_login_param)
|
if SiteSetting.hide_email_address_taken
|
||||||
|
user = User.find_by_email(Email.downcase(normalized_login_param))
|
||||||
|
else
|
||||||
|
user = User.find_by_username_or_email(normalized_login_param)
|
||||||
|
end
|
||||||
|
|
||||||
if user
|
if user
|
||||||
RateLimiter.new(nil, "forgot-password-login-day-#{user.username}", 6, 1.day).performed!
|
RateLimiter.new(nil, "forgot-password-login-day-#{user.username}", 6, 1.day).performed!
|
||||||
|
@ -449,7 +453,8 @@ class SessionController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
json = success_json
|
json = success_json
|
||||||
unless SiteSetting.hide_email_address_taken
|
|
||||||
|
if !SiteSetting.hide_email_address_taken
|
||||||
json[:user_found] = user_presence
|
json[:user_found] = user_presence
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1870,6 +1870,7 @@ en:
|
||||||
title: "Password Reset"
|
title: "Password Reset"
|
||||||
action: "I forgot my password"
|
action: "I forgot my password"
|
||||||
invite: "Enter your username or email address, and we'll send you a password reset email."
|
invite: "Enter your username or email address, and we'll send you a password reset email."
|
||||||
|
invite_no_username: "Enter your email address, and we'll send you a password reset email."
|
||||||
reset: "Reset Password"
|
reset: "Reset Password"
|
||||||
complete_username: "If an account matches the username <b>%{username}</b>, you should receive an email with instructions on how to reset your password shortly."
|
complete_username: "If an account matches the username <b>%{username}</b>, you should receive an email with instructions on how to reset your password shortly."
|
||||||
complete_email: "If an account matches <b>%{email}</b>, you should receive an email with instructions on how to reset your password shortly."
|
complete_email: "If an account matches <b>%{email}</b>, you should receive an email with instructions on how to reset your password shortly."
|
||||||
|
|
|
@ -1669,7 +1669,7 @@ en:
|
||||||
allowed_email_domains: "A pipe-delimited list of email domains that users MUST register accounts with. WARNING: Users with email domains other than those listed will not be allowed!"
|
allowed_email_domains: "A pipe-delimited list of email domains that users MUST register accounts with. WARNING: Users with email domains other than those listed will not be allowed!"
|
||||||
normalize_emails: "Check if normalized email is unique. Normalized email removes all dots from the username and everything between + and @ symbols."
|
normalize_emails: "Check if normalized email is unique. Normalized email removes all dots from the username and everything between + and @ symbols."
|
||||||
auto_approve_email_domains: "Users with email addresses from this list of domains will be automatically approved."
|
auto_approve_email_domains: "Users with email addresses from this list of domains will be automatically approved."
|
||||||
hide_email_address_taken: "Don't inform users that an account exists with a given email address during signup and from the forgot password form."
|
hide_email_address_taken: "Don't inform users that an account exists with a given email address during signup or during forgot password flow. Require full email for 'forgotten password' requests."
|
||||||
log_out_strict: "When logging out, log out ALL sessions for the user on all devices"
|
log_out_strict: "When logging out, log out ALL sessions for the user on all devices"
|
||||||
version_checks: "Ping the Discourse Hub for version updates and show new version messages on the <a href='%{base_path}/admin' target='_blank'>/admin</a> dashboard"
|
version_checks: "Ping the Discourse Hub for version updates and show new version messages on the <a href='%{base_path}/admin' target='_blank'>/admin</a> dashboard"
|
||||||
new_version_emails: "Send an email to the contact_email address when a new version of Discourse is available."
|
new_version_emails: "Send an email to the contact_email address when a new version of Discourse is available."
|
||||||
|
|
|
@ -521,7 +521,9 @@ login:
|
||||||
default: ""
|
default: ""
|
||||||
type: list
|
type: list
|
||||||
list_type: simple
|
list_type: simple
|
||||||
hide_email_address_taken: false
|
hide_email_address_taken:
|
||||||
|
client: true
|
||||||
|
default: false
|
||||||
log_out_strict: false
|
log_out_strict: false
|
||||||
pending_users_reminder_delay_minutes:
|
pending_users_reminder_delay_minutes:
|
||||||
min: -1
|
min: -1
|
||||||
|
|
|
@ -2058,6 +2058,29 @@ describe SessionController do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#forgot_password' do
|
describe '#forgot_password' do
|
||||||
|
|
||||||
|
context 'when hide_email_address_taken is set' do
|
||||||
|
before do
|
||||||
|
SiteSetting.hide_email_address_taken = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'denies for username' do
|
||||||
|
post "/session/forgot_password.json",
|
||||||
|
params: { login: user.username }
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(Jobs::CriticalUserEmail.jobs.size).to eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows for email' do
|
||||||
|
post "/session/forgot_password.json",
|
||||||
|
params: { login: user.email }
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(Jobs::CriticalUserEmail.jobs.size).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'raises an error without a username parameter' do
|
it 'raises an error without a username parameter' do
|
||||||
post "/session/forgot_password.json"
|
post "/session/forgot_password.json"
|
||||||
expect(response.status).to eq(400)
|
expect(response.status).to eq(400)
|
||||||
|
|
Loading…
Reference in New Issue