Commit 292bca05 authored by Robert Speicher's avatar Robert Speicher

Only allow password reset emails once per minute

Addresses internal https://dev.gitlab.org/gitlab/gitlabhq/issues/2611
parent 3a4274e1
...@@ -2,18 +2,19 @@ class PasswordsController < Devise::PasswordsController ...@@ -2,18 +2,19 @@ class PasswordsController < Devise::PasswordsController
def create def create
email = resource_params[:email] email = resource_params[:email]
resource_found = resource_class.find_by_email(email) self.resource = resource_class.find_by_email(email)
if resource_found && resource_found.ldap_user?
if resource && resource.ldap_user?
flash[:alert] = "Cannot reset password for LDAP user." flash[:alert] = "Cannot reset password for LDAP user."
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name)) and return respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name)) and return
end end
self.resource = resource_class.send_reset_password_instructions(resource_params) unless can_send_reset_email?
if successfully_sent?(resource) flash[:alert] = "Instructions about how to reset your password have already been sent recently. Please wait a few minutes to try again."
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name)) respond_with({}, location: new_password_path(resource_name)) and return
else
respond_with(resource)
end end
super
end end
def edit def edit
...@@ -35,4 +36,11 @@ class PasswordsController < Devise::PasswordsController ...@@ -35,4 +36,11 @@ class PasswordsController < Devise::PasswordsController
end end
end end
end end
private
def can_send_reset_email?
resource && (resource.reset_password_sent_at.blank? ||
resource.reset_password_sent_at < 1.minute.ago)
end
end end
require 'spec_helper' require 'spec_helper'
feature 'Password reset', feature: true do feature 'Password reset', feature: true do
describe 'with two-factor authentication' do describe 'throttling' do
let(:user) { create(:user, :two_factor) } it 'sends reset instructions when not previously sent' do
visit root_path
forgot_password(create(:user))
expect(page).to have_content(I18n.t('devise.passwords.send_instructions'))
expect(current_path).to eq new_user_session_path
end
it 'sends reset instructions when previously sent more than a minute ago' do
user = create(:user)
user.send_reset_password_instructions
user.update_attribute(:reset_password_sent_at, 5.minutes.ago)
visit root_path
forgot_password(user)
expect(page).to have_content(I18n.t('devise.passwords.send_instructions'))
expect(current_path).to eq new_user_session_path
end
it "throttles multiple resets in a short timespan" do
user = create(:user)
user.send_reset_password_instructions
visit root_path
forgot_password(user)
expect(page).to have_content("Instructions about how to reset your password have already been sent recently. Please wait a few minutes to try again.")
expect(current_path).to eq new_user_password_path
end
end
describe 'with two-factor authentication' do
it 'requires login after password reset' do it 'requires login after password reset' do
visit root_path visit root_path
forgot_password forgot_password(create(:user, :two_factor))
reset_password reset_password
expect(page).to have_content("Your password was changed successfully.") expect(page).to have_content("Your password was changed successfully.")
...@@ -17,12 +48,10 @@ feature 'Password reset', feature: true do ...@@ -17,12 +48,10 @@ feature 'Password reset', feature: true do
end end
describe 'without two-factor authentication' do describe 'without two-factor authentication' do
let(:user) { create(:user) }
it 'requires login after password reset' do it 'requires login after password reset' do
visit root_path visit root_path
forgot_password forgot_password(create(:user))
reset_password reset_password
expect(page).to have_content("Your password was changed successfully.") expect(page).to have_content("Your password was changed successfully.")
...@@ -30,7 +59,7 @@ feature 'Password reset', feature: true do ...@@ -30,7 +59,7 @@ feature 'Password reset', feature: true do
end end
end end
def forgot_password def forgot_password(user)
click_on 'Forgot your password?' click_on 'Forgot your password?'
fill_in 'Email', with: user.email fill_in 'Email', with: user.email
click_button 'Reset password' click_button 'Reset password'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment