パスワードの再設定 | rails チュートリアル 18

Ruby on Rails チュートリアル:実例を使って Rails を学ぼう第12章 パスワードの再設定の勉強メモです。

ここでは、パスワードを忘れた時のパスワードの再設定ができるようにする。

PasswordResetsリソース

Pw再設定用コントローラーを作成する。

$ bundle exec rails generate controller PasswordResets new edit --no-test-framework

「–no-test-framework」で、テストを生成しないというオプションを指定している

次に、routeを修正

Rails.application.routes.draw do
  get 'password_resets/new'

  get 'password_resets/edit'

  root 'static_pages#home'

  get    '/help', to: 'static_pages#help'
  get    '/about', to: 'static_pages#about'
  get    '/contact', to: 'static_pages#contact'
  get    '/signup',  to: 'users#new'
  post   '/signup',  to: 'users#create'
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'

  resources :users
  patch  '/users/:id/edit', to: 'users#update'

  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]
end

続いて、パスワード再設定画面へのリンクを追加する。

<p><%= link_to "forgot password", new_password_reset_path %> / <%= link_to "Sign up now!", signup_path %></p>

Userモデルに属性を追加する。

reset_digest属性とreset_sent_at属性をUserモデルに追加する。

$ bundle exec rails generate migration add_reset_to_users reset_digest:string reset_sent_at:datetime
$ bundle exec rails db:migrate

新しいパスワード再設定画面ビューを作成

$ touch app/views/password_resets/new.html.erb
<% provide(:title, "Forgot password") %>
<div class="container">
  <div class="ta_center">
    <br>
    <br>
    <br>
    <h1 class="title_signup">Forgot password</h1>

    <p>登録済みのメールアドレスを入力してください</p>
    <%= form_with(scope: :password_reset, url: password_resets_path, html: {class: ''}, local: true) do |f| %>
      <table class="mr_auto ml_auto">
        <tr>
          <th><%= f.label :email %></th>
          <td><%= f.email_field :email, class: 'form-control' %></td>
        </tr>
      </table>
      <%= f.submit "Submit", class: "btn btn-primary" %>
    <% end %>
    <br>
    <br>
    <br>
  </div>
</div>

その後、password_resets_controller.rbを修正、createアクションを追加する。

class PasswordResetsController < ApplicationController

  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] = "登録されているされているメールアドレスにパスワードリセット用のメールをお送りしました。"
      redirect_to root_url
    else
      flash.now[:danger] = "このメールアドレスは登録されていません"
      render 'new'
    end
  end

  def edit
  end
end

つぎに、Userモデルにパスワード再設定用メソッドを追加する

class User < ApplicationRecord
  attr_accessor :remember_token, :activation_token, :reset_token
.
.
.
  # パスワード再設定の属性を設定する
  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

  private
.
.
.
end

これで、登録されていないアドレスに対してのエラーメッセージが表示できるようになった。

パスワード再設定のメール送信

パスワード再設定のリンクをメール送信できるように、mailerを修正する。

class UserMailer < ApplicationMailer

  def account_activation(user)
    @user = user
    mail to: user.email, subject: "Account activation"
  end

  def password_reset(user)
    @user = user
    mail to: user.email, subject: "Password reset"
  end
end

つづいて、メール用のテキストとhtmlのviewを修正

To reset your password click the link below:

<%= 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.
<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>
If you did not request your password to be reset, please ignore this email and
your password will stay as it is.
</p>

最後に、パスワード再設定のプレビューメソッドを修正

# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview

  # Preview this email at http://localhost:3000/rails/mailers/user_mailer/account_activation
  def account_activation
    user = User.first
    user.activation_token = User.new_token
    UserMailer.account_activation(user)
  end

  # Preview this email at
  # http://localhost:3000/rails/mailers/user_mailer/password_reset
  def password_reset
    user = User.first
    user.reset_token = User.new_token
    UserMailer.password_reset(user)
  end
end

これで、登録されているアドレスを入力しても、エラーにならないようになった。

パスワードを再設定する

パスワード再設定のフォーム用のviewを修正

<% provide(:title, 'Reset password') %>

<div class="container">
  <div class="ta_center">
    <br>
    <br>
    <br>
    <h1 class="title_signup">Forgot password</h1>

    <p>新しいパスワードを入力してください。</p>

    <%= form_with(model: @user, url: password_reset_path(params[:id]), html: {class: ''}, local: true) do |f| %>
      <%= render '/users/error_messages', user: @user %>
      <%= hidden_field_tag :email, @user.email %>
      <table class="mr_auto ml_auto">
        <tr>
          <th><%= f.label :password %></th>
          <td><%= f.password_field :password, class: 'form-control' %></td>
        </tr>
        <tr>
          <th><%= f.label :password_confirmation, "Confirmation" %></th>
          <td><%= f.password_field :password_confirmation, class: 'form-control' %></td>
        </tr>
      </table>
      <%= f.submit "Update password", class: "btn btn-primary" %>
    <% end %>
    <br>
    <br>
    <br>
  </div>
</div>

コントローラーも合わせて修正

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] = "登録されているされているメールアドレスにパスワードリセット用のメールをお送りしました。"
      redirect_to root_url
    else
      flash.now[:danger] = "このメールアドレスは登録されていません"
      render 'new'
    end
  end

  def edit
  end

  def update
    if params[:user][:password].empty?
      @user.errors.add(:password, :blank)
      render 'edit'
    elsif @user.update_attributes(user_params)
      log_in @user
      @user.update_attribute(:reset_digest, nil)
      flash[:success] = "パスワードがリセットされました。"
      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

メモ

うーん、(´ε`;)ウーン…

heroku環境だとうまくいくけど、ローカルだとうまくいかない。。

valid_userメソッドが、うまく動いていない。。

どうやら、valid_userメソッドはうまく動いていた。

うまくいかないと思い込んでいたのは、「http://localhost:3000/rails/mailers/user_mailer/password_reset」で生成されているリンクがうまく動いていないかもと思っていたこと。しかし、正しいローカルのリンクは、rails sでlogに表示されている文字列だった。

コメント

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

内容に問題なければ、下記の「コメントを送信する」ボタンを押してください。

同じカテゴリの前後の記事


  1. KATOON.NET
  2. TRASH
  3. パスワードの再設定 | rails チュートリアル 18