users: refactor password reset flow.
The old password reset flow: * User requests a password reset. * Danbooru generates a password reset nonce. * Danbooru emails user a password reset confirmation link. * User follows link to password reset confirmation page. * The link contains a nonce authenticating the user. * User confirms password reset. * Danbooru resets user's password to a random string. * Danbooru emails user their new password in plaintext. The new password reset flow: * User requests a password reset. * Danbooru emails user a password reset link. * User follows link to password edit page. * The link contains a signed_user_id param authenticating the user. * User changes their own password.
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
FactoryBot.define do
|
||||
factory(:user_password_reset_nonce)
|
||||
end
|
||||
@@ -1,112 +0,0 @@
|
||||
require "test_helper"
|
||||
|
||||
module Maintenance
|
||||
module User
|
||||
class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
|
||||
context "A password resets controller" do
|
||||
setup do
|
||||
@user = create(:user, :email => "abc@com.net")
|
||||
ActionMailer::Base.delivery_method = :test
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
should "render the new page" do
|
||||
get new_maintenance_user_password_reset_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
context "create action" do
|
||||
context "given invalid parameters" do
|
||||
setup do
|
||||
post maintenance_user_password_reset_path, params: {:nonce => {:email => ""}}
|
||||
end
|
||||
|
||||
should "not create a new nonce" do
|
||||
assert_equal(0, UserPasswordResetNonce.count)
|
||||
end
|
||||
|
||||
should "redirect to the new page" do
|
||||
assert_redirected_to new_maintenance_user_password_reset_path
|
||||
end
|
||||
|
||||
should "not deliver an email" do
|
||||
assert_equal(0, ActionMailer::Base.deliveries.size)
|
||||
end
|
||||
end
|
||||
|
||||
context "given valid parameters" do
|
||||
setup do
|
||||
post maintenance_user_password_reset_path, params: {:nonce => {:email => @user.email}}
|
||||
end
|
||||
|
||||
should "create a new nonce" do
|
||||
assert_equal(1, UserPasswordResetNonce.where(:email => @user.email).count)
|
||||
end
|
||||
|
||||
should "redirect to the new page" do
|
||||
assert_redirected_to new_maintenance_user_password_reset_path
|
||||
end
|
||||
|
||||
should "deliver an email to the supplied email address" do
|
||||
assert_equal(1, ActionMailer::Base.deliveries.size)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "edit action" do
|
||||
context "with invalid parameters" do
|
||||
setup do
|
||||
get edit_maintenance_user_password_reset_path, params: {:email => "a@b.c"}
|
||||
end
|
||||
|
||||
should "succeed silently" do
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
context "with valid parameters" do
|
||||
setup do
|
||||
@user = create(:user)
|
||||
@nonce = create(:user_password_reset_nonce, :email => @user.email)
|
||||
ActionMailer::Base.deliveries.clear
|
||||
get edit_maintenance_user_password_reset_path, params: {:email => @nonce.email, :key => @nonce.key}
|
||||
end
|
||||
|
||||
should "succeed" do
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "update action" do
|
||||
context "with valid parameters" do
|
||||
setup do
|
||||
@user = create(:user)
|
||||
@nonce = create(:user_password_reset_nonce, :email => @user.email)
|
||||
ActionMailer::Base.deliveries.clear
|
||||
@old_password = @user.bcrypt_password_hash
|
||||
put maintenance_user_password_reset_path, params: {:email => @nonce.email, :key => @nonce.key}
|
||||
end
|
||||
|
||||
should "succeed" do
|
||||
assert_redirected_to new_maintenance_user_password_reset_path
|
||||
end
|
||||
|
||||
should "send an email" do
|
||||
assert_equal(1, ActionMailer::Base.deliveries.size)
|
||||
end
|
||||
|
||||
should "change the password" do
|
||||
@user.reload
|
||||
assert_not_equal(@old_password, @user.bcrypt_password_hash)
|
||||
end
|
||||
|
||||
should "delete the nonce" do
|
||||
assert_equal(0, UserPasswordResetNonce.count)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
25
test/functional/password_resets_controller_test.rb
Normal file
25
test/functional/password_resets_controller_test.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
require 'test_helper'
|
||||
|
||||
class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
|
||||
context "The passwords resets controller" do
|
||||
setup do
|
||||
@user = create(:user)
|
||||
end
|
||||
|
||||
context "show action" do
|
||||
should "work" do
|
||||
get password_reset_path
|
||||
assert_response :success
|
||||
end
|
||||
end
|
||||
|
||||
context "create action" do
|
||||
should "work" do
|
||||
post password_reset_path, params: { user: { name: @user.name } }
|
||||
|
||||
assert_redirected_to new_session_path
|
||||
assert_enqueued_email_with UserMailer, :password_reset, args: [@user]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,4 +3,9 @@ class UserMailerPreview < ActionMailer::Preview
|
||||
dmail = User.admins.first.dmails.first
|
||||
UserMailer.dmail_notice(dmail)
|
||||
end
|
||||
|
||||
def password_reset
|
||||
user = User.find(params[:id])
|
||||
UserMailer.password_reset(user)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
require 'test_helper'
|
||||
|
||||
class UserPasswordResetNonceTest < ActiveSupport::TestCase
|
||||
context "Creating a new nonce" do
|
||||
context "with a valid email" do
|
||||
setup do
|
||||
@user = FactoryBot.create(:user, :email => "aaa@b.net")
|
||||
@nonce = FactoryBot.create(:user_password_reset_nonce, :email => @user.email)
|
||||
end
|
||||
|
||||
should "validate" do
|
||||
assert_equal([], @nonce.errors.full_messages)
|
||||
end
|
||||
|
||||
should "populate the key with a random string" do
|
||||
assert_equal(24, @nonce.key.size)
|
||||
end
|
||||
|
||||
should "reset the password when reset" do
|
||||
@nonce.user.expects(:reset_password_and_deliver_notice)
|
||||
@nonce.reset_user!
|
||||
end
|
||||
end
|
||||
|
||||
context "with a blank email" do
|
||||
setup do
|
||||
@user = FactoryBot.create(:user, :email => "")
|
||||
@nonce = UserPasswordResetNonce.new(:email => "")
|
||||
end
|
||||
|
||||
should "not validate" do
|
||||
@nonce.save
|
||||
assert_equal(["Email can't be blank", "Email is invalid"], @nonce.errors.full_messages.sort)
|
||||
end
|
||||
end
|
||||
|
||||
context "with an invalid email" do
|
||||
setup do
|
||||
@nonce = UserPasswordResetNonce.new(:email => "z@z.net")
|
||||
end
|
||||
|
||||
should "not validate" do
|
||||
@nonce.save
|
||||
assert_equal(["Email is invalid"], @nonce.errors.full_messages)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -221,12 +221,6 @@ class UserTest < ActiveSupport::TestCase
|
||||
assert_equal(["Password is too short (minimum is 5 characters)"], @user.errors.full_messages)
|
||||
end
|
||||
|
||||
should "should be reset" do
|
||||
@user = FactoryBot.create(:user)
|
||||
new_pass = @user.reset_password
|
||||
assert(User.authenticate(@user.name, new_pass), "Authentication should have succeeded")
|
||||
end
|
||||
|
||||
should "not change the password if the password and old password are blank" do
|
||||
@user = FactoryBot.create(:user, :password => "67890")
|
||||
@user.update(password: "", old_password: "")
|
||||
|
||||
Reference in New Issue
Block a user