Skip to content

Commit 4a847ec

Browse files
committed
Add password reset
1 parent f051561 commit 4a847ec

File tree

15 files changed

+242
-13
lines changed

15 files changed

+242
-13
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
class PasswordResetsController < ApplicationController
2+
before_action :get_user, only: [:edit, :update]
3+
before_action :valid_user, only: [:edit, :update]
4+
before_action :check_expiration, only: [:edit, :update]
5+
6+
def new
7+
end
8+
9+
def create
10+
@user = User.find_by(email: params[:password_reset][:email].downcase)
11+
if @user
12+
@user.create_reset_digest
13+
14+
@user.send_password_reset_email
15+
flash[:info] = "Email sent with password reset instructions"
16+
redirect_to root_url
17+
else
18+
flash.now[:danger] = "Email address not found"
19+
render "new", status: :unprocessable_entity
20+
end
21+
end
22+
23+
def edit
24+
end
25+
26+
def update
27+
if params[:user][:password].empty?
28+
@user.errors.add(:password, "can't be empty")
29+
render "edit", status: :unprocessable_entity
30+
elsif @user.update(user_params)
31+
reset_session
32+
log_in @user
33+
flash[:success] = "Password has been reset."
34+
redirect_to @user
35+
else
36+
render "edit", status: :unprocessable_entity
37+
end
38+
end
39+
40+
private
41+
42+
def user_params
43+
params.require(:user).permit(:password,
44+
:password_confirmation)
45+
end
46+
47+
# Before filters
48+
def get_user
49+
@user = User.find_by(email: params[:email])
50+
end
51+
52+
# Confirms a valid user.
53+
def valid_user
54+
unless (@user && @user.activated? &&
55+
@user.authenticated?(:reset, params[:id]))
56+
redirect_to root_url
57+
end
58+
end
59+
60+
# Checks expiration of reset token.
61+
def check_expiration
62+
if @user.password_reset_expired?
63+
flash[:danger] = "Password reset has expired."
64+
redirect_to new_password_reset_url
65+
end
66+
end
67+
end
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
module PasswordResetsHelper
2+
end

app/mailers/user_mailer.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ def account_activation(user)
99
mail to: user.email, subject: "Account activation"
1010
end
1111

12-
def password_reset
13-
@greeting = "Hi"
14-
mail to: "[email protected]"
12+
def password_reset(user)
13+
@user = user
14+
mail to: user.email, subject: "Password reset"
1515
end
1616
end

app/models/user.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class User < ApplicationRecord
2-
attr_accessor :remember_token, :activation_token
2+
attr_accessor :remember_token, :activation_token, :reset_token
33
before_save :downcase_email
44
before_create :create_activation_digest
55
validates :name, presence: true, length: { maximum: 50 }
@@ -56,6 +56,22 @@ def send_activation_email
5656
UserMailer.account_activation(self).deliver_now
5757
end
5858

59+
def create_reset_digest
60+
self.reset_token = User.new_token
61+
update_attribute(:reset_digest, User.digest(reset_token))
62+
update_attribute(:reset_sent_at, Time.zone.now)
63+
end
64+
65+
# Sends password reset email.
66+
def send_password_reset_email
67+
UserMailer.password_reset(self).deliver_now
68+
end
69+
70+
# Returns true if a password reset has expired.
71+
def password_reset_expired?
72+
reset_sent_at < 2.hours.ago
73+
end
74+
5975
private
6076

6177
# Converts email to all lowercase.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<% provide(:title, 'Reset password') %>
2+
<h1>Reset password</h1>
3+
<div class="row">
4+
<div class="col-md-6 offset-md-3">
5+
<%= form_with(model: @user, url: password_reset_path(params[:id])) do |f| %>
6+
<%= render 'shared/error_messages' %>
7+
<%= hidden_field_tag :email, @user.email %>
8+
<%= f.label :password %>
9+
<%= f.password_field :password, class: 'form-control' %>
10+
<%= f.label :password_confirmation, "Confirmation" %>
11+
<%= f.password_field :password_confirmation, class: 'form-control' %>
12+
<%= f.submit "Update password", class: "btn btn-primary" %>
13+
<% end %>
14+
</div>
15+
</div>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<% provide(:title, "Forgot password") %>
2+
<h1>Forgot password</h1>
3+
<div class="row">
4+
<div class="col-md-6 offset-md-3">
5+
<%= form_with(url: password_resets_path, scope: :password_reset) do |f| %>
6+
<%= f.label :email %>
7+
<%= f.email_field :email, class: 'form-control' %>
8+
<%= f.submit "Submit", class: "btn btn-primary" %>
9+
<% end %>
10+
</div>
11+
</div>

app/views/sessions/new.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<%= f.label :email %>
77
<%= f.email_field :email, class: 'form-control' %>
88
<%= f.label :password %>
9+
<%= link_to "(forgot password)", new_password_reset_path %>
910
<%= f.password_field :password, class: 'form-control' %>
1011
<%= f.label :remember_me, class: "checkbox inline" do %>
1112
<%= f.check_box :remember_me %>
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
<h1>User#password_reset</h1>
2-
1+
<h1>Password reset</h1>
2+
<p>To reset your password click the link below:</p>
3+
<%= link_to "Reset password", edit_password_reset_url(@user.reset_token,
4+
email: @user.email) %>
5+
<p>This link will expire in two hours.</p>
36
<p>
4-
<%= @greeting %>, find me in app/views/user_mailer/password_reset.html.erb
5-
</p>
7+
If you did not request your password to be reset, please ignore this email and
8+
your password will stay as it is.
9+
</p>
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
User#password_reset
2-
3-
<%= @greeting %>, find me in app/views/user_mailer/password_reset.text.erb
1+
To reset your password click the link below:
2+
<%= edit_password_reset_url(@user.reset_token, email: @user.email) %>
3+
This link will expire in two hours.
4+
If you did not request your password to be reset, please ignore this email and
5+
your password will stay as it is.

config/routes.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
Rails.application.routes.draw do
2+
get "password_resets/new"
3+
get "password_resets/edit"
24
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
35
root "static_pages#home"
46
get "/help", to: "static_pages#help"
@@ -20,4 +22,5 @@
2022
# root "posts#index"
2123
resources :users
2224
resources :account_activations, only: [:edit]
25+
resources :password_resets, only: [:new, :create, :edit, :update]
2326
end

0 commit comments

Comments
 (0)