Add password reset

parent 8be740be
Pipeline #1316 canceled with stages
in 0 seconds
// Place all the styles related to the PasswordResets controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/
class PasswordResetsController < ApplicationController
include SessionsHelper
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update] # Case (1)
def new
end
def create
@user = User.find_by(email: params[:password_reset][:email].downcase)
if @user
@user.create_reset_digest
@user.send_password_reset_email
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else
flash.now[:danger] = "Email address not found"
render 'new'
end
end
def edit
end
def update
if params[:user][:password].empty?
@user.errors.add(:password, "can't be empty")
render 'edit'
elsif @user.update(user_params)
log_in @user
flash[:success] = "Password has been reset."
redirect_to @user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
def get_user
@user = User.find_by(email: params[:email])
end
# Confirms a valid user.
def valid_user
unless (@user && @user.activated? &&
@user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
# Checks expiration of reset token.
def check_expiration
if @user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
...@@ -6,11 +6,12 @@ class UsersController < ApplicationController ...@@ -6,11 +6,12 @@ class UsersController < ApplicationController
before_action :admin_user, only: :destroy before_action :admin_user, only: :destroy
def show def show
@user = User.find(params[:id]) @user = User. find(params[:id] )
redirect_to root_url and return unless true
end end
def index def index
@users = User.where(activated: FILL_IN).paginate(page: params[:page]) @users = User. where(activated: true) . paginate(page: params[:page] )
end end
def new def new
...@@ -42,33 +43,33 @@ class UsersController < ApplicationController ...@@ -42,33 +43,33 @@ class UsersController < ApplicationController
end end
end end
private def destroy
User.find(params[:id]).destroy
def user_params flash[:success] = "User deleted"
params.require(:user).permit(:name, :email, :password, :password_confirmation) redirect_to users_url
end end
private
def logged_in_user def user_params
unless logged_in? params.require(:user).permit(:name, :email, :password, :password_confirmation)
store_location end
flash[:danger] = "Please log in."
redirect_to login_url
end
end
def correct_user def logged_in_user
@user = User.find(params[:id]) unless logged_in?
redirect_to(root_url) unless current_user?(@user) store_location
flash[:danger] = "Please log in."
redirect_to login_url
end end
end
def destroy def correct_user
User.find(params[:id]).destroy @user = User.find(params[:id])
flash[:success] = "User deleted" redirect_to(root_url) unless current_user?(@user)
redirect_to users_url end
end
# Confirms an admin user. # Confirms an admin user.
def admin_user def admin_user
redirect_to(root_url) unless current_user.admin? redirect_to(root_url) unless current_user.admin?
end end
end end
module PasswordResetsHelper
end
...@@ -4,9 +4,8 @@ class UserMailer < ApplicationMailer ...@@ -4,9 +4,8 @@ class UserMailer < ApplicationMailer
mail to: user.email, subject: "Account activation" mail to: user.email, subject: "Account activation"
end end
def password_reset def password_reset(user)
@greeting = "Hi" @user = user
mail to: user.email, subject: "Password reset"
mail to: "to@example.org"
end end
end end
class User < ApplicationRecord class User < ApplicationRecord
attr_accessor :remember_token, :activation_token attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email before_save :downcase_email
before_create :create_activation_digest before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 } validates :name, presence: true, length: { maximum: 50 }
...@@ -53,7 +53,21 @@ class User < ApplicationRecord ...@@ -53,7 +53,21 @@ class User < ApplicationRecord
return false if digest.nil? return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token) BCrypt::Password.new(digest).is_password?(token)
end end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private private
# Converts email to all lower-case. # Converts email to all lower-case.
def downcase_email def downcase_email
......
<% provide(:title, 'Reset password') %>
<h1>Reset password</h1>
<div class="row">
<div class="col-md-6 offset-3">
<%= form_with(model: @user, url: password_reset_path(params[:id]),
local: true) do |f| %>
<%= render 'shared/error_messages' %>
<%= hidden_field_tag :email, @user.email %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Update password", class: "btn btn-primary" %>
<% end %>
</div>
</div>
\ No newline at end of file
<% provide(:title, "Forgot password") %>
<h1>Forgot password</h1>
<div class="row">
<div class="col-md-6 offset-3">
<%= form_with(url: password_resets_path, scope: :password_reset,
local: true) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
\ No newline at end of file
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
<%= f.email_field :email, class: 'form-control' %> <%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %> <%= f.label :password %>
<%= link_to "(forgot password)", new_password_reset_path %>
<%= f.password_field :password, class: 'form-control' %> <%= f.password_field :password, class: 'form-control' %>
<%= f.label :remember_me, class: "checkbox inline" do %> <%= f.label :remember_me, class: "checkbox inline" do %>
......
<h1>User#password_reset</h1> <h1>Password reset</h1>
<p>To reset your password click the link below:</p>
<%= link_to "Reset password", edit_password_reset_url(@user.reset_token,
email: @user.email) %>
<p>This link will expire in two hours.</p>
<p> <p>
<%= @greeting %>, find me in app/views/user_mailer/password_reset.html.erb If you did not request your password to be reset, please ignore this email and
</p> your password will stay as it is.
</p>
\ No newline at end of file
User#password_reset To reset your password click the link below:
<%= @greeting %>, find me in app/views/user_mailer/password_reset.text.erb <%= edit_password_reset_url(@user.reset_token, email: @user.email) %>
This link will expire in two hours.
If you did not request your password to be reset, please ignore this email and
your password will stay as it is.
\ No newline at end of file
Rails.application.routes.draw do Rails.application.routes.draw do
get 'password_resets/new'
get 'password_resets/edit'
root 'static_pages#home' root 'static_pages#home'
get '/home', to: 'static_pages#home' get '/home', to: 'static_pages#home'
get '/help', to: 'static_pages#help' get '/help', to: 'static_pages#help'
...@@ -11,6 +13,7 @@ Rails.application.routes.draw do ...@@ -11,6 +13,7 @@ Rails.application.routes.draw do
delete '/logout', to: 'sessions#destroy' delete '/logout', to: 'sessions#destroy'
resources :users resources :users
resources :account_activations, only: [:edit] resources :account_activations, only: [:edit]
end resources :password_resets, only: [:new, :create, :edit, :update]
end
class AddResetToUsers < ActiveRecord::Migration[6.1]
def change
add_column :users, :reset_digest, :string
add_column :users, :reset_sent_at, :datetime
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_06_23_062616) do ActiveRecord::Schema.define(version: 2021_06_28_041628) do
create_table "users", force: :cascade do |t| create_table "users", force: :cascade do |t|
t.string "name" t.string "name"
...@@ -23,6 +23,8 @@ ActiveRecord::Schema.define(version: 2021_06_23_062616) do ...@@ -23,6 +23,8 @@ ActiveRecord::Schema.define(version: 2021_06_23_062616) do
t.string "activation_digest" t.string "activation_digest"
t.boolean "activated", default: false t.boolean "activated", default: false
t.datetime "activated_at" t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
end end
......
...@@ -10,7 +10,8 @@ class UserMailerPreview < ActionMailer::Preview ...@@ -10,7 +10,8 @@ class UserMailerPreview < ActionMailer::Preview
# Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset # Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset
def password_reset def password_reset
UserMailer.password_reset user = User.first
user.reset_token = User.new_token
UserMailer.password_reset(user)
end end
end end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment