openstreetmap-website/test/controllers/passwords_controller_test.rb
Andy Allan 4e237db390 Be paranoid when sending password reset emails
This implements what is known as "paranoid" password reset flash
messages (using the terminology from Devise). It avoids revealing
whether the supplied email address is already registered.

Added an explicit test for this situation, so that the test for
email non-existance is separate from the duplicate-case tests.
2024-03-02 15:48:54 +00:00

165 lines
6.2 KiB
Ruby

require "test_helper"
class PasswordsControllerTest < ActionDispatch::IntegrationTest
##
# test all routes which lead to this controller
def test_routes
assert_routing(
{ :path => "/user/forgot-password", :method => :get },
{ :controller => "passwords", :action => "new" }
)
assert_routing(
{ :path => "/user/forgot-password", :method => :post },
{ :controller => "passwords", :action => "create" }
)
assert_routing(
{ :path => "/user/reset-password", :method => :get },
{ :controller => "passwords", :action => "edit" }
)
assert_routing(
{ :path => "/user/reset-password", :method => :post },
{ :controller => "passwords", :action => "update" }
)
end
def test_lost_password
# Test fetching the lost password page
get user_forgot_password_path
assert_response :success
assert_template :new
assert_select "div#notice", false
# Test resetting using the address as recorded for a user that has an
# address which is duplicated in a different case by another user
user = create(:user)
uppercase_user = build(:user, :email => user.email.upcase).tap { |u| u.save(:validate => false) }
# Resetting with GET should fail
assert_no_difference "ActionMailer::Base.deliveries.size" do
perform_enqueued_jobs do
get user_forgot_password_path, :params => { :email => user.email }
end
end
assert_response :success
assert_template :new
# Resetting with POST should work
assert_difference "ActionMailer::Base.deliveries.size", 1 do
perform_enqueued_jobs do
post user_forgot_password_path, :params => { :email => user.email }
end
end
assert_response :redirect
assert_redirected_to login_path
assert_match(/^If your email address exists/, flash[:notice])
email = ActionMailer::Base.deliveries.first
assert_equal 1, email.to.count
assert_equal user.email, email.to.first
ActionMailer::Base.deliveries.clear
# Test resetting using an address that does not exist
assert_no_difference "ActionMailer::Base.deliveries.size" do
perform_enqueued_jobs do
post user_forgot_password_path, :params => { :email => "nobody@example.com" }
end
end
# Be paranoid about revealing there was no match
assert_response :redirect
assert_redirected_to login_path
assert_match(/^If your email address exists/, flash[:notice])
# Test resetting using an address that matches a different user
# that has the same address in a different case
assert_difference "ActionMailer::Base.deliveries.size", 1 do
perform_enqueued_jobs do
post user_forgot_password_path, :params => { :email => user.email.upcase }
end
end
assert_response :redirect
assert_redirected_to login_path
assert_match(/^If your email address exists/, flash[:notice])
email = ActionMailer::Base.deliveries.first
assert_equal 1, email.to.count
assert_equal uppercase_user.email, email.to.first
ActionMailer::Base.deliveries.clear
# Test resetting using an address that is a case insensitive match
# for more than one user but not an exact match for either
assert_no_difference "ActionMailer::Base.deliveries.size" do
perform_enqueued_jobs do
post user_forgot_password_path, :params => { :email => user.email.titlecase }
end
end
# Be paranoid about revealing there was no match
assert_response :redirect
assert_redirected_to login_path
assert_match(/^If your email address exists/, flash[:notice])
# Test resetting using the address as recorded for a user that has an
# address which is case insensitively unique
third_user = create(:user)
assert_difference "ActionMailer::Base.deliveries.size", 1 do
perform_enqueued_jobs do
post user_forgot_password_path, :params => { :email => third_user.email }
end
end
assert_response :redirect
assert_redirected_to login_path
assert_match(/^If your email address exists/, flash[:notice])
email = ActionMailer::Base.deliveries.first
assert_equal 1, email.to.count
assert_equal third_user.email, email.to.first
ActionMailer::Base.deliveries.clear
# Test resetting using an address that matches a user that has the
# same (case insensitively unique) address in a different case
assert_difference "ActionMailer::Base.deliveries.size", 1 do
perform_enqueued_jobs do
post user_forgot_password_path, :params => { :email => third_user.email.upcase }
end
end
assert_response :redirect
assert_redirected_to login_path
assert_match(/^If your email address exists/, flash[:notice])
email = ActionMailer::Base.deliveries.first
assert_equal 1, email.to.count
assert_equal third_user.email, email.to.first
ActionMailer::Base.deliveries.clear
end
def test_reset_password
user = create(:user, :pending)
# Test a request with no token
get user_reset_password_path
assert_response :bad_request
# Test a request with a bogus token
get user_reset_password_path, :params => { :token => "made_up_token" }
assert_response :redirect
assert_redirected_to :action => :new
# Create a valid token for a user
token = user.generate_token_for(:password_reset)
# Test a request with a valid token
get user_reset_password_path, :params => { :token => token }
assert_response :success
assert_template :edit
# Test that errors are reported for erroneous submissions
post user_reset_password_path, :params => { :token => token, :user => { :pass_crypt => "new_password", :pass_crypt_confirmation => "different_password" } }
assert_response :success
assert_template :edit
assert_select "div.invalid-feedback"
# Test setting a new password
post user_reset_password_path, :params => { :token => token, :user => { :pass_crypt => "new_password", :pass_crypt_confirmation => "new_password" } }
assert_response :redirect
assert_redirected_to root_path
assert_equal user.id, session[:user]
user.reload
assert_equal "active", user.status
assert user.email_valid
assert_equal user, User.authenticate(:username => user.email, :password => "new_password")
end
end