Commit a0954f3a by phuctmZigexn

Add password reset

parent 1990642f
...@@ -61,7 +61,7 @@ Style/RaiseArgs: ...@@ -61,7 +61,7 @@ Style/RaiseArgs:
Enabled: false Enabled: false
# Hashの最後のカンマを許容 # Hashの最後のカンマを許容
Style/TrailingCommaInLiteral: Style/TrailingCommaInHashLiteral:
Enabled: false Enabled: false
# Hashの最後のカンマを許容 # Hashの最後のカンマを許容
......
//= require jquery //= require jquery
//= require bootstrap //= require bootstrap
//= require rails-ujs //= require rails-ujs
//= require activestorage
//= require_tree . //= require_tree .
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
// Place all the styles related to the password_resets controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update]
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_attributes(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
def valid_user
unless @user && @user.activated? && @user.authenticated?(:reset, params[:id])
redirect_to root_url
end
end
def check_expiration
if @user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
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
...@@ -49,6 +49,20 @@ class User < ApplicationRecord ...@@ -49,6 +49,20 @@ class User < ApplicationRecord
UserMailer.account_activation(self).deliver_now UserMailer.account_activation(self).deliver_now
end end
def create_reset_digest
self.reset_token = User.new_token
update_attribute :reset_digest, User.digest(reset_token)
update_attribute :reset_sent_at, Time.zone.now
end
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private private
def downcase_email def downcase_email
self.email = email.downcase self.email = email.downcase
......
<% provide(:title, "Reset password") %>
<h1>Reset password</h1>
<div class="row">
<div class="col-md-6 col-md-offet-3">
<%= form_for @user, url: password_reset_path(params[:id]) do |f| %>
<%= render 'shared/error_messages', user: @user %>
<%= hidden_field_tag :email, @user.email %>
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
<%= f.label :password_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 col-md-offset-3">
<%= form_for :password_reset, url: password_resets_path 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 your password will stay as it is.
</p> </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
require_relative 'boot' require_relative 'boot'
require 'rails/all' require 'rails'
# Include each railties manually, excluding `active_storage/engine`
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
# require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
require "sprockets/railtie"
require "rails/test_unit/railtie"
# Require the gems listed in Gemfile, including any gems # Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production. # you've limited to :test, :development, or :production.
......
...@@ -27,9 +27,6 @@ Rails.application.configure do ...@@ -27,9 +27,6 @@ Rails.application.configure do
config.cache_store = :null_store config.cache_store = :null_store
end end
# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :local
# Don't care if the mailer can't send. # Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = true config.action_mailer.raise_delivery_errors = true
config.action_mailer.delivery_method = :smtp config.action_mailer.delivery_method = :smtp
......
...@@ -38,9 +38,6 @@ Rails.application.configure do ...@@ -38,9 +38,6 @@ Rails.application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :local
# Mount Action Cable outside main process or domain # Mount Action Cable outside main process or domain
# config.action_cable.mount_path = nil # config.action_cable.mount_path = nil
# config.action_cable.url = 'wss://example.com/cable' # config.action_cable.url = 'wss://example.com/cable'
......
...@@ -29,7 +29,7 @@ Rails.application.configure do ...@@ -29,7 +29,7 @@ Rails.application.configure do
config.action_controller.allow_forgery_protection = false config.action_controller.allow_forgery_protection = false
# Store uploaded files on the local file system in a temporary directory # Store uploaded files on the local file system in a temporary directory
config.active_storage.service = :test # config.active_storage.service = :test
config.action_mailer.perform_caching = false config.action_mailer.perform_caching = false
......
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 '/help', to: 'static_pages#help' get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about' get '/about', to: 'static_pages#about'
...@@ -13,6 +15,7 @@ Rails.application.routes.draw do ...@@ -13,6 +15,7 @@ Rails.application.routes.draw do
resources :users resources :users
resources :account_activations, only: [:edit] resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
end end
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
local:
service: Disk
root: <%= Rails.root.join("storage") %>
# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# amazon:
# service: S3
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
# region: us-east-1
# bucket: your_own_bucket
# Remember not to checkin your GCS keyfile to a repository
# google:
# service: GCS
# project: your_project
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
# bucket: your_own_bucket
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
# microsoft:
# service: AzureStorage
# storage_account_name: your_account_name
# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
# container: your_container_name
# mirror:
# service: Mirror
# primary: local
# mirrors: [ amazon, google, microsoft ]
class AddResetToUsers < ActiveRecord::Migration[5.2]
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: 2018_08_12_093253) do ActiveRecord::Schema.define(version: 2018_08_13_074348) 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: 2018_08_12_093253) do ...@@ -23,6 +23,8 @@ ActiveRecord::Schema.define(version: 2018_08_12_093253) 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
......
require 'test_helper'
class AccountActivationsControllerTest < ActionDispatch::IntegrationTest
# test "the truth" do
# assert true
# end
end
...@@ -10,7 +10,9 @@ class UserMailerPreview < ActionMailer::Preview ...@@ -10,7 +10,9 @@ 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
...@@ -12,4 +12,15 @@ class UserMailerTest < ActionMailer::TestCase ...@@ -12,4 +12,15 @@ class UserMailerTest < ActionMailer::TestCase
assert_match user.activation_token, mail.body.encoded assert_match user.activation_token, mail.body.encoded
assert_match CGI.escape(user.email), mail.body.encoded assert_match CGI.escape(user.email), mail.body.encoded
end end
test "password_reset" do
user = users(:michael)
user.reset_token = User.new_token
mail = UserMailer.password_reset(user)
assert_equal "Password reset", mail.subject
assert_equal [user.email], mail.to
assert_equal ["noreply@example.com"], mail.from
assert_match user.reset_token, mail.body.encoded
assert_match CGI.escape(user.email), mail.body.encoded
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