Rails チュートリアル　第12章　まとめ

login(forgot password) =>
(rooting ->) password_resets controller new action -> new view
パスワード再設定のためのフォーム画面
form_forを使って、メールアドレスを入力してもらい、params[:email]に格納
params[:email]の送信先は、POST /password_resets であり、createアクションに送られる

-> create action 

```ruby:password_resets.controller
def create
    @user = User.find_by(email: params[:password_reset][:email].downcase)
#userのemailからUser情報を取得
    if @user # もしそのemailをもつUserが存在したら
      @user.create_reset_digest #token発行、tokenのハッシュ化、reset_digestに保存
      @user.send_password_reset_email #emailを送信
      flash[:info] = "Email sent with password reset instructions"
      redirect_to root_url
    else # email情報がDBになければ
      flash.now[:danger] = "Email address not found"
      render 'new' #もう一度フォームに戻ってもらう
    end
  end

```

create_reset_digestメソッドは、前章ではbefore_actionでsignup前に作動させていたが、今回はcreateアクションに組み込んでいる、場所は違うが、順序や機能は同じ

```ruby:
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
```
send_password_reset_emailメソッドは、新たにpassword_reset()メソッドを実装する必要がある

```ruby:
def send_password_reset_email
    UserMailer.password_reset(self).deliver_now
end
```

```ruby:app/mailers/user_mailer.rb
 def password_reset(user)
    @user = user
    mail to: user.email, subject: "Password reset" #送信先とサブタイトル
 end
```

メールの文面は

```app/views/user_mailer/password_reset.text.erb
To reset your password click the link below:

<%= edit_password_reset_url(@user.reset_token, email: @user.email) %>

#reset_tokenとemailをUrlに埋め込む、edit_pass-なのでedit actionへのリンク

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.
```
 => Password_resets controllerの edit action
Userに新しいpasswordを入力してもらうフォーム
edit viewに難しい内容はないが、
前章と違い、edit actionだけでなく、update actionまでUserのemail情報を渡す必要がある
Userにもう一度emailを打ち込んでもらうのは面倒なので、
`<%= hidden_field_tag :email, @user.email %>`
としてUserには見えない形で、update actionまでつなげる

before_action で正しいユーザーだけがedit,update actionへ行けるように
ここで認証を行う

```ruby:
def valid_user
# nilチェックが失敗する、有効化してなかった、認証できなかった場合はTopページへ
      unless (@user && @user.activated? &&
              @user.authenticated?(:reset, params[:id]))
        redirect_to root_url
      end
end
```

=> update action
updateアクションでしたいことは

```ruby:
def update
  @user = User.find_by(email: params[:email])
  params[:id] == self.reset_token
  params[:email] == user.email
  @user.password_digest = User.digest(params[:password]) 
#userが入力したpasswordをハッシュ化して、password_digestに保存
#passwordの再設定が完了
  @user.save
end
```
このようなことだが、再設定に際し、対策することがあるので、書き方は以下のようになる

```ruby:
def update
    if params[:user][:password].empty? #passwordが空だったら            
      @user.errors.add(:password, :blank) #allow_nilがかかる前に強制的にerror
      render 'edit'
    elsif @user.update_attributes(user_params)# user_paramsでセキュリティ対策  下
      log_in @user
      flash[:success] = "Password has been reset."
      redirect_to @user
    else
      render 'edit'                               
    end
end
```

```ruby:
    def user_params
      params.require(:user).permit(:password, :password_confirmation)
# 渡されるパラメータで適切なものはpass,confirの二つだけ
#digestへ保存されるのは、どうやってしている？
    end
```
パスワードの有効期限が切れていないかチェックをbeforeフィルターで

```ruby:
def check_expiration
      if @user.password_reset_expired?
        flash[:danger] = "Password reset has expired."
        redirect_to new_password_reset_url
      end
end
```

```ruby:user.rb
def password_reset_expired?
    reset_sent_at < 2.hours.ago
# <をより早いと読むと、2時間よりも早い、つまりまだ2時間経ってないなら
#有効期限なので、trueが返ってくる
end
```