Add password reset functionality
This commit is contained in:
parent
f17b5046c4
commit
2f940fab13
1
Gemfile
1
Gemfile
|
@ -27,6 +27,7 @@ group :development, :test do
|
|||
end
|
||||
|
||||
group :development do
|
||||
gem "letter_opener"
|
||||
gem "rubocop-rails-omakase"
|
||||
gem "web-console"
|
||||
end
|
||||
|
|
|
@ -99,6 +99,7 @@ GEM
|
|||
rack-test (>= 0.6.3)
|
||||
regexp_parser (>= 1.5, < 3.0)
|
||||
xpath (~> 3.2)
|
||||
childprocess (5.0.0)
|
||||
concurrent-ruby (1.2.3)
|
||||
connection_pool (2.4.1)
|
||||
crass (1.0.6)
|
||||
|
@ -134,6 +135,11 @@ GEM
|
|||
activesupport (>= 5.0.0)
|
||||
json (2.7.2)
|
||||
language_server-protocol (3.17.0.3)
|
||||
launchy (3.0.1)
|
||||
addressable (~> 2.8)
|
||||
childprocess (~> 5.0)
|
||||
letter_opener (1.10.0)
|
||||
launchy (>= 2.2, < 4)
|
||||
loofah (2.22.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
|
@ -327,6 +333,7 @@ DEPENDENCIES
|
|||
image_processing (~> 1.2)
|
||||
importmap-rails
|
||||
jbuilder
|
||||
letter_opener
|
||||
mission_control-jobs
|
||||
pg
|
||||
propshaft
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
:root {
|
||||
--background-color: #111;
|
||||
--background-color: #333;
|
||||
--main-background-color:
|
||||
--header-color: #15345b;
|
||||
--header-text-color: #fff;
|
||||
--header-height: 150px;
|
||||
--header-height: 200px;
|
||||
|
||||
--inset-bg-color: #eee;
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ header:before {
|
|||
}
|
||||
|
||||
header h1 {
|
||||
padding: .5em 0;
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
header h1 a:link, header h1 a:visited {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PasswordResetsController < ApplicationController
|
||||
skip_before_action :authenticate
|
||||
|
||||
def new
|
||||
reset_session
|
||||
end
|
||||
|
||||
def create
|
||||
user = User.find_by(username: params[:username])
|
||||
if user
|
||||
token = user.generate_token_for(:password_reset)
|
||||
UserMailer.with(user: user, token: token).password_reset.deliver_later
|
||||
redirect_to new_session_path, notice: t(".success") and return
|
||||
end
|
||||
|
||||
redirect_to :root, notice: t(".error")
|
||||
end
|
||||
|
||||
def edit
|
||||
reset_session
|
||||
@user = User.find_by(username: params[:id])
|
||||
@token = params[:token]
|
||||
unless @user == User.find_by_token_for(:password_reset, params[:token])
|
||||
redirect_to :root, notice: t(".invalid_token") and return
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
user = User.find_by(username: params[:id])
|
||||
unless user == User.find_by_token_for(:password_reset, params[:token])
|
||||
redirect_to :root, notice: t(".invalid_token") and return
|
||||
end
|
||||
|
||||
if user.update(password: params[:password], password_confirmation: params[:password_confirmation])
|
||||
redirect_to new_session_path, notice: t(".success")
|
||||
else
|
||||
redirect_to :root, notice: t(".error")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,12 +6,12 @@ class TableInviteMailer < ApplicationMailer
|
|||
def invite_user
|
||||
@table_invite = params[:table_invite]
|
||||
|
||||
mail(to: @table_invite.email, subject: "[Tabletop Companion] Invite to join a table")
|
||||
mail(to: @table_invite.email, subject: t(".invite_user.subject"))
|
||||
end
|
||||
|
||||
def invite_new_user
|
||||
@table_invite = params[:table_invite]
|
||||
|
||||
mail(to: @table_invite.email, subject: "[Tabletop Companion] You’ve been invited to a game!")
|
||||
mail(to: @table_invite.email, subject: t(".invite_new_user.subject"))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,12 +7,19 @@ class UserMailer < ApplicationMailer
|
|||
@user = params[:user]
|
||||
@token = params[:token]
|
||||
|
||||
mail(to: @user.email, subject: "[Tabletop Companion] Verify your email")
|
||||
mail(to: @user.email, subject: t(".email_verification.subject"))
|
||||
end
|
||||
|
||||
def email_verified
|
||||
@user = params[:user]
|
||||
|
||||
mail(to: @user.email, subject: "[Tabletop Companion] Your email has been verified")
|
||||
mail(to: @user.email, subject: t(".email_verified.subject"))
|
||||
end
|
||||
|
||||
def password_reset
|
||||
@user = params[:user]
|
||||
@token = params[:token]
|
||||
|
||||
mail(to: @user.email, subject: t(".password_reset.subject"))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<%= content_for :title, t(".reset_password") %>
|
||||
|
||||
<h2><%= t(".reset_password") %></h2>
|
||||
|
||||
<section class="inset">
|
||||
<%= form_with url: password_reset_path(id: @user.username), method: :patch do |f| %>
|
||||
<%= f.hidden_field :token, value: @token %>
|
||||
|
||||
<%= f.label :password %>
|
||||
<%= f.password_field :password %>
|
||||
|
||||
<%= f.label :password_confirmation %>
|
||||
<%= f.password_field :password_confirmation %>
|
||||
|
||||
<%= f.submit t(".reset_password_button") %>
|
||||
<% end %>
|
||||
</section>
|
|
@ -0,0 +1,15 @@
|
|||
<%= content_for :title, t(".reset_password") %>
|
||||
|
||||
<h2><%= t(".reset_password") %></h2>
|
||||
<section class="inset">
|
||||
<p><%= t(".forgotten_password_intro") %></p>
|
||||
|
||||
<%= form_with url: password_resets_path do |f| %>
|
||||
<%= f.label :username %>
|
||||
<%= f.text_field :username %>
|
||||
|
||||
<%= f.submit t(".reset_password_button") %>
|
||||
<% end %>
|
||||
|
||||
</section>
|
||||
|
|
@ -14,3 +14,4 @@
|
|||
<% end %>
|
||||
</section>
|
||||
|
||||
<%= link_to(t(".forgot_password"), new_password_reset_path) %>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<%= htmlify_email(t(".content")) %>
|
||||
|
||||
<%= link_to t(".reset_password"), edit_password_reset_url(id: @user, token: @token) %>
|
|
@ -0,0 +1,3 @@
|
|||
<%= t(".content") %>
|
||||
|
||||
<%= edit_password_reset_url(id: @user, token: @token) %>
|
|
@ -40,6 +40,7 @@ Rails.application.configure do
|
|||
|
||||
# Don't care if the mailer can't send.
|
||||
config.action_mailer.raise_delivery_errors = false
|
||||
config.action_mailer.delivery_method = :letter_opener
|
||||
|
||||
config.action_mailer.perform_caching = false
|
||||
|
||||
|
|
|
@ -54,22 +54,42 @@ en:
|
|||
destroy:
|
||||
success: Successfully deleted “%{name}”.
|
||||
error: “%{name}” could not be deleted.
|
||||
password_resets:
|
||||
new:
|
||||
reset_password: Reset your password
|
||||
forgotten_password_intro: |-
|
||||
If you have forgotten your password, enter your information below to reset it.
|
||||
reset_password_button: Reset password
|
||||
create:
|
||||
success: Please check your email to reset your password.
|
||||
error: Could not reset your password.
|
||||
edit:
|
||||
reset_password: Reset your password
|
||||
invalid_token: That token seems to have expired, please try resettting your password again.
|
||||
reset_password_button: Update password
|
||||
update:
|
||||
invalid_token: That token seems to have expired, please try resettting your password again.
|
||||
success: Your password has been reset, you may now log in.
|
||||
error: Failed to reset password. Please try again or contact us for help.
|
||||
sessions:
|
||||
create:
|
||||
success: "Hello, %{name}!"
|
||||
error: "Could not sign in. Please check your username and password."
|
||||
new:
|
||||
log_in: Log in
|
||||
forgot_password: Forgotten your password?
|
||||
destroy:
|
||||
log_out: Log out
|
||||
success: "You have signed out."
|
||||
table_invite_mailer:
|
||||
invite_new_user:
|
||||
subject: You’ve been invited to join a game on Tabletop Companion!
|
||||
content: |-
|
||||
You’ve been invited to join a game on Tabletop Companion. To start playing, sign up at the
|
||||
link below and accept your invitation!
|
||||
sign_up: Sign up now
|
||||
invite_user:
|
||||
subject: You’ve been invited to a new game!
|
||||
content: |-
|
||||
You have been invited to join the table “%{table_name}” on Tabletop Companion. To
|
||||
respond, visit the link below.
|
||||
|
@ -145,13 +165,22 @@ en:
|
|||
error: Failed to update profile
|
||||
user_mailer:
|
||||
email_verified:
|
||||
subject: Email verified on Tabletop Companion
|
||||
content: |-
|
||||
Thanks for verifying your email address, and welcome to Tabletop Companion!
|
||||
|
||||
You can now go ahead and log in. Enjoy!
|
||||
email_verification:
|
||||
subject: Verify your email on Tabletop Companion
|
||||
content: |-
|
||||
If you did not sign up for Tabletop Companion, please ignore this email.
|
||||
|
||||
Otherwise, please visit the link below to verify your email address.
|
||||
password_reset:
|
||||
subject: Reset your password on Tabletop Companion
|
||||
reset_password: Reset your password
|
||||
content: |-
|
||||
If you did not request a password reset, please ignore this email.
|
||||
|
||||
Otherwise, please visit the link below to reset your password.
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ Rails.application.routes.draw do
|
|||
|
||||
resources :users, only: [ :new, :create, :show, :edit, :update ]
|
||||
resources :account_verifications, only: [ :show ]
|
||||
resources :password_resets, only: [ :new, :create, :edit, :update ]
|
||||
resources :sessions, only: [ :new, :create, :destroy ]
|
||||
|
||||
resources :table_invites, only: [ :index, :edit, :update ]
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
|
||||
test "should get new" do
|
||||
get new_password_reset_path
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should send a password reset email" do
|
||||
user = users(:trevor)
|
||||
assert_emails(+1) do
|
||||
post password_resets_path, params: { username: user.username }
|
||||
assert_redirected_to new_session_path
|
||||
end
|
||||
end
|
||||
|
||||
test "should get edit" do
|
||||
user = users(:trevor)
|
||||
token = user.generate_token_for(:password_reset)
|
||||
get edit_password_reset_path(token: token, id: user.username)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should update password" do
|
||||
user = users(:trevor)
|
||||
token = user.generate_token_for(:password_reset)
|
||||
put password_reset_path(id: user.username, token: token),
|
||||
params: { password: "password", password_confirmation: "password" }
|
||||
assert_redirected_to new_session_path
|
||||
assert user.reload.authenticate("password")
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue