FIX: rate limit password reset email
This commit is contained in:
parent
582ec5954f
commit
e0a82d3088
|
@ -6,25 +6,40 @@ export default DiscourseController.extend(ModalFunctionality, {
|
|||
|
||||
// You need a value in the field to submit it.
|
||||
submitDisabled: function() {
|
||||
return this.blank('accountEmailOrUsername');
|
||||
}.property('accountEmailOrUsername'),
|
||||
return this.blank('accountEmailOrUsername') || this.get('disabled');
|
||||
}.property('accountEmailOrUsername', 'disabled'),
|
||||
|
||||
actions: {
|
||||
submit: function() {
|
||||
if (!this.get('accountEmailOrUsername')) return false;
|
||||
var self = this;
|
||||
|
||||
Discourse.ajax("/session/forgot_password", {
|
||||
if (this.get('submitDisabled')) return false;
|
||||
|
||||
this.set('disabled', true);
|
||||
|
||||
var success = function() {
|
||||
// don't tell people what happened, this keeps it more secure (ensure same on server)
|
||||
var escaped = Handlebars.Utils.escapeExpression(self.get('accountEmailOrUsername'));
|
||||
if (self.get('accountEmailOrUsername').match(/@/)) {
|
||||
self.flash(I18n.t('forgot_password.complete_email', {email: escaped}));
|
||||
} else {
|
||||
self.flash(I18n.t('forgot_password.complete_username', {username: escaped}));
|
||||
}
|
||||
};
|
||||
|
||||
var fail = function(e) {
|
||||
self.flash(e.responseJSON.errors[0], 'alert-error');
|
||||
};
|
||||
|
||||
Discourse.ajax('/session/forgot_password', {
|
||||
data: { login: this.get('accountEmailOrUsername') },
|
||||
type: 'POST'
|
||||
}).then(success, fail).finally(function(){
|
||||
setTimeout(function(){
|
||||
self.set('disabled',false);
|
||||
}, 10*1000);
|
||||
});
|
||||
|
||||
// don't tell people what happened, this keeps it more secure (ensure same on server)
|
||||
var escaped = Handlebars.Utils.escapeExpression(this.get('accountEmailOrUsername'));
|
||||
if (this.get('accountEmailOrUsername').match(/@/)) {
|
||||
this.flash(I18n.t('forgot_password.complete_email', {email: escaped}));
|
||||
} else {
|
||||
this.flash(I18n.t('forgot_password.complete_username', {username: escaped}));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require_dependency 'rate_limiter'
|
||||
|
||||
class SessionController < ApplicationController
|
||||
|
||||
skip_before_filter :redirect_to_login_if_required
|
||||
|
@ -93,6 +95,9 @@ class SessionController < ApplicationController
|
|||
return
|
||||
end
|
||||
|
||||
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!
|
||||
|
||||
user = User.find_by_username_or_email(params[:login])
|
||||
if user.present?
|
||||
email_token = user.email_tokens.create(email: user.email)
|
||||
|
@ -100,6 +105,9 @@ class SessionController < ApplicationController
|
|||
end
|
||||
# always render of so we don't leak information
|
||||
render json: {result: "ok"}
|
||||
|
||||
rescue RateLimiter::LimitExceeded
|
||||
render_json_error(I18n.t("rate_limiter.slow_down"))
|
||||
end
|
||||
|
||||
def current
|
||||
|
|
|
@ -292,6 +292,7 @@ en:
|
|||
|
||||
|
||||
rate_limiter:
|
||||
slow_down: "You have performed this action too many times, try again later"
|
||||
too_many_requests: "We have a daily limit on how many times that action can be taken. Please wait %{time_left} before trying again."
|
||||
hours:
|
||||
one: "1 hour"
|
||||
|
|
|
@ -19,7 +19,7 @@ class RateLimiter
|
|||
|
||||
def initialize(user, key, max, secs)
|
||||
@user = user
|
||||
@key = "l-rate-limit:#{@user.id}:#{key}"
|
||||
@key = "l-rate-limit:#{@user && @user.id}:#{key}"
|
||||
@max = max
|
||||
@secs = secs
|
||||
end
|
||||
|
@ -71,6 +71,6 @@ class RateLimiter
|
|||
end
|
||||
|
||||
def rate_unlimited?
|
||||
!!(RateLimiter.disabled? || @user.staff?)
|
||||
!!(RateLimiter.disabled? || (@user && @user.staff?))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -360,7 +360,7 @@ describe SessionController do
|
|||
let(:user) { Fabricate(:user) }
|
||||
|
||||
it "returns a 500 if local logins are disabled" do
|
||||
SiteSetting.stubs(:enable_local_logins).returns(false)
|
||||
SiteSetting.enable_local_logins = false
|
||||
xhr :post, :forgot_password, login: user.username
|
||||
response.code.to_i.should == 500
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue