らくだ🐫にもできるRailsチュートリアル|12.3

※毎回書いたほうがいい気がするので書いておきます※
平文の部分をちょこちょこI18nで日本語化しているのですが
その部分に関しては特に記述していなくてもたまにコードが違ったりしています。
t(‘.hogehoge’)って部分です
どうぞお気になさらず。

12.3 パスワードを再設定する

passwordResetsコントローラのeditアクションを実装していく
テストは認証メールの時と同じで、統合テストで行っていく

12.3.1 editアクションで再設定

パスワード再設定メールに含まれるリンクを機能させるために、パスワード再設定フォームを表示するビューが必要

このビューはユーザーの編集フォーム (リスト 10.2) と似ていますが、今回はパスワード入力フィールドと確認用フィールドだけで十分です。

パスワード再設定フォーム

前項でのパスワード再設定フォームとこの項で言うパスワード再設定フォームは別のもので
ここで言うパスワード再設定フォームは再設定を実行するためのフォーム
前項までのパスワード再設定フォームは再設定を申し込むためのフォーム

面倒な問題

メールアドレスをキーとしてユーザーを検索する為にeditアクションとupdateアクションの両方でメールアドレスが必要

メールアドレス入りリンクのおかげで、editアクションでメールアドレスを取り出すことは問題ありません。しかしフォームを一度送信してしまうと、この情報は消えてしまいます

→再設定を実行するためのフォームにはパスワード入力フィールドと確認用フィールドしか作っていない為
→隠しフィールドを作ってメールアドレスを保存
→フォームの情報を送信するときにメールアドレスの情報も一緒に送信できる

異なるフォームタグヘルパー

今までの書き方はこう

これだとparams[:user][:email] にメールアドレスが保存される

form_withやform_forで渡すインスタンスがある場合(もしくはそれらのヘルパーを使っている場合)は、f.hidden_fieldを使います。

今回はparams[:email]にメールアドレスを保存させる↓

一個だけパラメータを他のアクションへ単体で渡したい時は、hidden_field_tagを使います。

引用元はこちら↓


本文読んでて「hidden_field初めて出てきたけどこれまでの書き方とは🤔」ってなったけど
「これまでの(フォームの)書き方」って事か。

PasswordResetsコントローラのeditアクション

params[:email]のメールアドレスに対応するユーザーを保存するための@user変数を定義する
続けて、params[:id]の再設定用トークンとauthenticated?メソッドを使って、このユーザーが正当なユーザーであることを確認する
正当なユーザー=(ユーザーが存在する、有効化されている、認証済みである)
更にこれ↓

editアクションとupdateアクションのどちらの場合も正当な@userが存在する必要があるので、いくつかのbeforeフィルタを使って@userの検索とバリデーションを行います

→再設定用のURLにアクセスすると再設定を実行するためのフォームが表示されるようになった!

演習

  1. 12.2.1.1で示した手順に従って、Railsサーバーのログから送信メールを探し出し、そこに記されているリンクを見つけてください。そのリンクをブラウザから表示してみて、図 12.11のように表示されるか確かめてみましょう。
  2. 先ほど表示したページから、実際に新しいパスワードを送信してみましょう。どのような結果になるでしょうか?
  1. 表示される
  2. Unknown action
    The action ‘update’ could not be found for PasswordResetsController
    (updateアクションがないよなエラー)

12.3.2 パスワードを更新する

フォームからの送信に対応するupdateアクションが必要!

  1. パスワード再設定の有効期限が切れていないか
  2. 無効なパスワードであれば失敗させる (失敗した理由も表示する)
  3. 新しいパスワードが空文字列になっていないか (ユーザー情報の編集ではOKだった)
  4. 新しいパスワードが正しければ、更新する

パスワード再設定の有効期限が切れていないか

editとupdateアクションに期限切れかどうかを確認するメソッドとbeforeフィルターを用意する

無効なパスワードは失敗、有効なパスワードは更新

有効期限が切れていないかをチェックするbeforeフィルターで保護したupdateアクションを使うことで2.4のケースに対応できそう
→パスワードが無効で更新に失敗する際はeditのビューが再描画される(flashメッセージも出す)
→パスワードが有効で更新に成功する際は、パスワードを再設定し、ログイン成功と同様の処理を進める(ユーザー詳細ページにリダイレクト、かな?)

新しいパスワードが空文字列になっていないか

Userモデルではパスワードは空でもよいという実装をしている
Railsチュートリアル|リスト10.13Railsチュートリアル|リスト10.13
しかし再設定ではパスワードのフィールドが空では再設定にならないので
明示的に(パスワードのフィールドが空であることを)キャッチするコードを追加する!
(確認フィールドが空の場合は、確認フィールドのバリデーションで検出されてエラーメッセージが表示される)
→@userオブジェクトにエラーメッセージを追加

オブジェクト.errors.add(対象のカラム, ‘エラーの内容’)
エラーの内容にblankオプションを指定することでI18nで多言語している場合でも言語に合わせた適切なメッセージを表示してくれる

パスワード再設定のupdateアクション

上記をまとめるとパスワード再設定のupdateアクションが完成する
(ただし、password_reset_expired?の実装はまだなのでした)

user_paramsメソッドを使ってpasswordとpassword_confirmation属性を精査している

password_reset_expired?の実装

今回は先回りして、始めからUserモデルに移譲する前提で次のようにコードを書いていました。

と、いう事なのでpassword_reset_expired?メソッドをUserモデルに定義していく
→2時間以上パスワードが再設定されなかった場合は期限切れになる処理
これをRubyで書くとこうなる

「<」の記号は「~より少ない」ではなく「〜より早い時刻」と読む

→updateアクションが動作する!

演習

  1. 12.2.1.1で得られたリンク (Railsサーバーのログから取得) をブラウザで表示し、passwordとconfirmationの文字列をわざと間違えて送信してみましょう。どんなエラーメッセージが表示されるでしょうか?
  2. コンソールに移り、パスワード再設定を送信したユーザーオブジェクトを見つけてください。見つかったら、そのオブジェクトのpassword_digestの値を取得してみましょう。次に、パスワード再設定フォームから有効なパスワードを入力し、送信してみましょう (図 12.13)。パスワードの再設定は成功したら、再度password_digestの値を取得し、先ほど取得した値と異なっていることを確認してみましょう。ヒント: 新しい値はuser.reloadを通して取得する必要があります。
  1. Password confirmation doesn’t match Password
    (パスワードと確認用パスワードが一致しないよ的なやつ)
  2. 更新後のpassword_digestは更新前の値と異なっている

12.3.3 パスワードの再設定をテストする

送信に成功した場合と失敗した場合の統合テストを作成する
まずはテストファイルを作成

アカウント有効化のテストとも共通点が多い
以下手順

最初に「forgot password」フォームを表示して無効なメールアドレスを送信し、次はそのフォームで有効なメールアドレスを送信します。後者ではパスワード再設定用トークンが作成され、再設定用メールが送信されます。続いて、メールのリンクを開いて無効な情報を送信し、次にそのリンクから有効な情報を送信して、それぞれが期待どおりに動作することを確認します。

みっちりお読みくださいとの事なのでみっちりコメントをつけてみました長い

上記ハイライト部分、59~72行目
入力したパスワードと確認用パスワードが異なっている場合も、入力欄が空白だった場合も
チェックする内容が同じ(エラー表示用のdivについているidの名前)になってるけど
より詳しくテストする場合には
それぞれのエラーメッセージでチェックするとどっちで引っかかっているのか、両方引っかかっているのかなどがわかって良い。となる。

演習

  1. リスト 12.6にあるcreate_reset_digestメソッドはupdate_attributeを2回呼び出していますが、これは各行で1回ずつデータベースへ問い合わせしていることになります。リスト 12.20に記したテンプレートを使って、update_attributeの呼び出しを1回のupdate_columns呼び出しにまとめてみましょう (これでデータベースへの問い合わせが1回で済むようになります)。また、変更後にテストを実行し、 greenになることも確認してください。ちなみにリスト 12.20にあるコードには、前章の演習 (リスト 11.39) の解答も含まれています。
  2. リスト 12.21のテンプレートを埋めて、期限切れのパスワード再設定で発生する分岐 (リスト 12.16) を統合テストで網羅してみましょう (12.21 のコードにあるresponse.bodyは、そのページのHTML本文をすべて返すメソッドです)。期限切れをテストする方法はいくつかありますが、リスト 12.21でオススメした手法を使えば、レスポンスの本文に「expired」という語があるかどうかでチェックできます (なお、大文字と小文字は区別されません)。
  3. 2時間経ったらパスワードを再設定できなくする方針は、セキュリティ的に好ましいやり方でしょう。しかし、もっと良くする方法はまだあります。例えば、公共の (または共有された) コンピューターでパスワード再設定が行われた場合を考えてみてください。仮にログアウトして離席したとしても、2時間以内であれば、そのコンピューターの履歴からパスワード再設定フォームを表示させ、パスワードを更新してしまうことができてしまいます (しかもそのままログイン機構まで突破されてしまいます!)。この問題を解決するために、リスト 12.22のコードを追加し、パスワードの再設定に成功したらダイジェストをnilになるように変更してみましょう
  4. リスト 12.18に1行追加し、1つ前の演習課題に対するテストを書いてみましょう。ヒント: リスト 9.25のassert_nilメソッドとリスト 11.33のuser.reloadメソッドを組み合わせて、reset_digest属性を直接テストしてみましょう。

1.

2.

3.

4.

まとめとか感想

パスワードの再設定機能が作動するようになりました!
あれこれの確認のために言語設定をenにしたりjaにしたり面d略お

らくだ🐫にもできるRailsチュートリアルとは

「ド」が付く素人のらくだ🐫が勉強するRailsチュートリアルの学習記録です。
自分用に記録していますが、お役に立つことがあれば幸いです。

調べたとはいえらくだ🐫なりの解釈や説明が含まれます。間違っている部分もあるかと思います。そんな所は教えて頂けますと幸いなのですが、このブログにはコメント機能がありません💧お手数おかけしますがTwitterなどでご連絡いただければ幸いです