Skip to content

Commit d291f5e

Browse files
committed
Add password reset
1 parent 61d8996 commit d291f5e

File tree

17 files changed

+284
-95
lines changed

17 files changed

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

app/helpers/password_resets_helper.rb

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 & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ def account_activation(user)
55
mail to: user.email, subject: "Account activation"
66
end
77

8-
def password_reset
9-
@greeting = "Hi"
10-
11-
mail to: "[email protected]"
8+
def password_reset(user)
9+
@user = user
10+
mail to: user.email, subject: "Password reset"
1211
end
1312
end

app/models/user.rb

Lines changed: 18 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 }
@@ -58,6 +58,23 @@ def send_activation_email
5858
UserMailer.account_activation(self).deliver_now
5959
end
6060

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

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

app/views/sessions/new.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<%= f.email_field :email, class: 'form-control' %>
1010

1111
<%= f.label :password %>
12+
<%= link_to "(forgot password)", new_password_reset_path %>
1213
<%= f.password_field :password, class: 'form-control' %>
1314

1415
<%= f.label :remember_me, class: "checkbox inline" do %>
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1-
<h1>User#password_reset</h1>
1+
<h1>Password reset</h1>
2+
3+
<p>To reset your password click the link below:</p>
4+
5+
<%= link_to "Reset password", edit_password_reset_url(@user.reset_token,
6+
email: @user.email) %>
7+
8+
<p>This link will expire in two hours.</p>
29

310
<p>
4-
<%= @greeting %>, find me in app/views/user_mailer/password_reset.html.erb
11+
If you did not request your password to be reset, please ignore this email and
12+
your password will stay as it is.
513
</p>
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1-
User#password_reset
1+
To reset your password click the link below:
22

3-
<%= @greeting %>, find me in app/views/user_mailer/password_reset.text.erb
3+
<%= edit_password_reset_url(@user.reset_token, email: @user.email) %>
4+
5+
This link will expire in two hours.
6+
7+
If you did not request your password to be reset, please ignore this email and
8+
your password will stay as it is.

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
delete "/logout", to: "sessions#destroy"
1010
resources :users
1111
resources :account_activations, only: [:edit]
12+
resources :password_resets, only: [:new, :create, :edit, :update]
1213
end

0 commit comments

Comments
 (0)