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

10.2 認可

ウェブアプリケーションの文脈では、認証 (authentication) はサイトのユーザーを識別することであり、認可 (authorization) はそのユーザーが実行可能な操作を管理することです。

現状ではログインしていないユーザーでもどのユーザー情報でも編集できてしまうため
ユーザーにログインを要求し、自分以外のユーザー情報を変更できないように制御する
→セキュリティ上の制御機構をセキュリティモデルと呼ぶ

やっていくこと

ログインしていないユーザーが保護されたページにアクセスしようとした場合ログインページに転送してメッセージを表示させる
さらに、許可されていないページにアクセス済のユーザーがいたらルートURLにリダイレクトさせる

10.2.1 ユーザーにログインを要求する

Usersコントローラの中でbeforeフィルターを使ってユーザーにログインを要求する

beforeフィルターとはなんぞ

before_actionメソッドを使って何らかの処理が実行される直前に特定のメソッドを実行する仕組みです

unless文

if文が条件式の評価がtrueの場合に処理を実行するのに対して
unless文は条件式の評価がfalseの場合に処理を実行する
上記の場合
ヘルパーに定義したlogged_in?メソッドがfalse(current_userがnil)の場合に処理(エラーメッセージの表示→login_urlにリダイレクト)が行われる
↓SessionsHelperに定義されているlogged_in?メソッドとlogged_in?メソッドで呼んでるcurrent_userメソッドはこちら

現状のテストはRED

原因は、editアクションやupdateアクションでログインを要求するようになったため、ログインしていないユーザーだとこれらのテストが失敗するようになったためです。

→editアクションやupdateアクションをテストする前にログインしておく必要がある!
→log_in_asヘルパー (test_helper.rbに定義) を使う

(リスト 10.17のsetupメソッド内でログイン処理をまとめてしまうことも可能です。しかし、10.2.3で片方のテストをログインする前に編集ページにアクセスするように変更したいので、ここでまとめてしまっても結局は元に戻すことになってしまいます。)

セキュリティモデルに関するテスト

現状ではセキュリティモデルに関する実装を取り外しても(beforeフィルターをコメントアウト)テストが greenになってしまう!
これでは危険が危ないので検知できるようにテストを修正してく
→beforeフィルターは基本的にアクションごとに適用していく。よってUsersコントローラのテストもアクションごとに書いていく。

  1. 正しい種類のHTTPリクエストでeditアクション(getのリクエスト)とupdateアクション(patchのリクエスト)をそれぞれ実行
  2. flashにメッセージが代入されたか
  3. ログイン画面にリダイレクトされたか

テストが意図の通りに作動するか確認

演習

デフォルトのbeforeフィルターは、すべてのアクションに対して制限を加えます。今回のケースだと、ログインページやユーザー登録ページにも制限の範囲が及んでしまうはずです (結果としてテストも失敗するはずです)。リスト 10.15のonly:オプションをコメントアウトしてみて、テストスイートがそのエラーを検知できるかどうか (テストが失敗するかどうか) 確かめてみましょう。

これちょっと悩んで、
本文にはログインページやユーザー登録ページにも制限の範囲が及んでしまうはずってあるんだけど
ユーザー登録ページ関連にはエラーも出てるし実際動作もおかしくなる(ログインページに飛ばされるループになる)けど
ログインページではエラーも異常も出ないんだよね。
本文通りならログインページ関連にもエラーが出ないとおかしいのでは?どこか間違ってるのでは?ってなったんだけど
ログインページ関連はSessionsControllerなのでUsersControllerのbeforeフィルターの制限が及ぶわけないのよね。
と、納得した🐫

10.2.2 正しいユーザーを要求する

ログインを要求するだけではなく、ログインしたユーザーが自分だけの情報を編集できるようにする。
→現状ではログインさえしていれば他ユーザーの情報も更新できてしまう
ログインしているからと言って、他のユーザーの情報まで編集できてしまうのは🙅‍♀️
これをテスト駆動開発でやっていく!

他ユーザーの情報が編集できないかを確認するテスト

テスト用のデータに2人目のユーザーを作る

log_in_asメソッドを使って、editアクションとupdateアクションをテストする

このとき、既にログイン済みのユーザーを対象としているため、ログインページではなくルートURLにリダイレクトしている点に注意してください。

別のユーザーのプロフィールを編集しようとしたらリダイレクトさせる

beforeフィルターで管理
→correct_userというメソッドを作成し、ログインユーザーが別のユーザーのプロフィールを編集しようとしたらリダイレクトさせる

beforeフィルターのcorrect_userで@user変数を定義しているため、リスト 10.25ではeditとupdateの各アクションから、@userへの代入文を削除している点にも注意してください。

リファクタリング

一般的な慣習に倣ってcurrent_user?という論理値を返すメソッドを実装します

correct_userの中で使えるようにする為にSessionsヘルパーの中に追加する

Usersコントローラーに反映させる

演習

  1. 何故editアクションとupdateアクションを両方とも保護する必要があるのでしょうか? 考えてみてください。
  2. 上記のアクションのうち、どちらがブラウザで簡単にテストできるアクションでしょうか?
  1. 他のユーザーのユーザー情報編集ページを表示させない為にeditアクション、データを更新させないためにupdateアクションをそれぞれ保護する必要がある
  2. viewが定義されているeditアクション

10.2.3 フレンドリーフォワーディング

各種リダイレクト先はユーザーが開こうとしていたページに設定するのが親切というもの!

ログインしていないユーザーが編集ページにアクセスしようとしていたなら、ユーザーがログインした後にはその編集ページにリダイレクトされるようにするのが望ましい動作です。

フレンドリーフォワーディングのテスト

ログインのテスト(ログインした後に編集ページへアクセスするという順序)を逆にする

  1. 編集ページにアクセス
  2. ログイン
  3. プロフィールページではなく編集ページにリダイレクト

ん?
successful editのテストを編集するのででいいんだよね?(?

フレンドリーフォワーディングの実装

ユーザーを希望のページに転送するには、リクエスト時点のページをどこかに保存しておき、その場所にリダイレクトさせる必要があります。

Sessionヘルパーにstore_location(リクエスト時点のページを保存)とredirect_back_or(ユーザーを希望のページに転送)の二つのメソッドを定義する

2つのヘルパーメソッドの実装

store_locationとredirect_back_orの2つのメソッドを定義していく

requestオブジェクト

ブラウザから返されるリクエストに関する情報が多数含まれている


request.original_url → 現在のリクエストURLをStringとして返す
request.get? → HTTPメソッドがGETの時trueを返す

beforeフィルターを修正

store_locationメソッドを使って、早速beforeフィルターのlogged_in_userメソッドを修正

createアクションを修正

createアクションにredirect_back_orメソッドを組み込んでリダイレクト先を呼び出す

演習

  1. フレンドリーフォワーディングで、渡されたURLに初回のみ転送されていることを、テストを書いて確認してみましょう。次回以降のログインのときには、転送先のURLはデフォルト (プロフィール画面) に戻っている必要があります。ヒント: リスト 10.29のsession[:forwarding_url]が正しい値かどうか確認するテストを追加してみましょう。
  2. 7.1.3で紹介したdebuggerメソッドをSessionsコントローラのnewアクションに置いてみましょう。その後、ログアウトして /users/1/edit にアクセスしてみてください (デバッガーが途中で処理を止めるはずです)。ここでコンソールに移り、session[:forwarding_url]の値が正しいかどうか確認してみましょう。また、newアクションにアクセスしたときのrequest.get?の値も確認してみましょう (デバッガーを使っていると、ときどき予期せぬ箇所でターミナルが止まったり、おかしい挙動を見せたりします。熟練の開発者になった気になって (コラム 1.1)、落ち着いて対処してみましょう)。

1.

2.
session[:forwarding_url]の値 → ~/users/1/editとなるはず

request.get?の値 → trueとなるはず

まとめとか感想

本文にちょこちょこ引っかかりまくって思いのほか時間がかかったりだけど、その分しっかり調べたから!と、思う🐫
フレンドリーフォワーディングたいせつ

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

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

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