saran12345678 / RubyonRailsTutorial

0 stars 1 forks source link

第10章 ユーザーの更新・表示・削除 #10

Open saran12345678 opened 2 weeks ago

saran12345678 commented 2 weeks ago

target="blank"で新しいページを開くと、古いブラウザでセキュリティ上の小さな問題が生じます。それは、リンク先のサイトがHTMLドキュメントのwindowオブジェクトを扱えてしまう、という点です。具体的には、[フィッシング(Phising)](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%83%E3%82%B7%E3%83%B3%E3%82%B0(%E8%A9%90%E6%AC%BA))サイトのような、悪意のあるコンテンツを導入されてしまう可能性があります。Gravatarのような著名なサイトではこのような事態はめったに起きないと思いますが、念のため、このセキュリティ上のリスクも排除しておきましょう。対処方法は、リンク用のaタグのrel(relationship)属性に、"noopener"と設定するだけです。リスト 10.2で使ったGravatarの編集ページへのリンクでこの設定を行ってください。

      <a href="https://gravatar.com/emails" target="_blank" rel="noopener" >change</a>

リスト 10.5のパーシャルを使って、new.html.erbビュー(リスト 10.6)とedit.html.erbビュー(リスト 10.7)をリファクタリングし、コードの重複を解消してください。(ヒント: 3.4.3で使ったprovideメソッドを使うと、重複を取り除けます3 。)

記載されているソース通りにリファクタリングを行った。

saran12345678 commented 2 weeks ago

編集フォームから有効でないユーザー名やメールアドレス、パスワードを使って送信した場合、編集に失敗することを確認してみましょう。

スクリーンショット 2024-09-02 23 57 09

saran12345678 commented 2 weeks ago

リスト 10.9のテストに1行追加して、エラーメッセージが正しい個数で表示されているかテストしてみましょう。(ヒント: 表 5.2で紹介したassert_selectを使ってalertクラスのdivタグを探しだし、「The form contains 4 errors.」というテキストを精査してみましょう。)

テストに成功することを確認

    assert_select "div.alert", "The form contains 4 errors"
saran12345678 commented 2 weeks ago

実際に編集が成功するかどうか、有効な情報を送信して確かめてみましょう。

スクリーンショット 2024-09-03 0 20 16

Gravatarと紐付いていない適当なメールアドレス(foobar@example.comなど)に変更すると、プロフィール画像はどのように表示されるでしょうか? 実際に編集フォームでメールアドレスを変更して確認してみましょう。

Gravatarのロゴが表示される スクリーンショット 2024-09-03 0 23 24

saran12345678 commented 2 weeks ago

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

エラーを検知できていることを確認

  before_action :logged_in_user#, only: [:edit, :update]
  1) Failure:
UsersControllerTest#test_should_get_new [/workspaces/sample_app/test/controllers/users_controller_test.rb:11]:
Expected response to be a <2XX: success>, but was a <303: See Other> redirect to <http://www.example.com/login>
Response body: <html><body>You are being <a href="http://www.example.com/login">redirected</a>.</body></html>

  2) Failure:
UsersSignupTest#test_valid_signup_information [/workspaces/sample_app/test/integration/users_signup_test.rb:21]:
"User.count" didn't change by 1.
Expected: 2
  Actual: 1

  3) Failure:
UsersSignupTest#test_invalid_signup_information [/workspaces/sample_app/test/integration/users_signup_test.rb:13]:
Expected response to be a <422: unprocessable_entity>, but was a <303: See Other> redirect to <http://www.example.com/login>
Response body: <html><body>You are being <a href="http://www.example.com/login">redirected</a>.</body></html>.
Expected: 422
  Actual: 303

38 runs, 80 assertions, 3 failures, 0 errors, 0 skips
saran12345678 commented 2 weeks ago

editアクションとupdateアクションを両方とも保護する必要がある理由は何でしょうか?考えてみてください。

editを保護しないと編集画面を他のユーザが閲覧できてしまう。 特にメールアドレスがわかってしまうため問題がある。 updateを保護しないと、他のユーザ情報を変更できてしまう。

上記のアクションのうち、どちらがブラウザで簡単にテストできますか?

edit URLを設定するだけでGetメソッドを送ることができるため

saran12345678 commented 2 weeks ago

フレンドリーフォワーディングで、渡されたURLに転送されるのが初回のみであることを、テストを書いて確認してみましょう。次回以降のログインでは、転送先のURLをデフォルトのプロフィール画面に戻しておく必要があります。(ヒント: リスト 10.30のsession[:forwarding_url]が正しい値かどうか確認するテストを追加してみましょう。)

編集画面→ログイン画面→ログイン→編集画面の順に遷移した際に、 forwardingUrlがNullになっていることを確認

    assert_nil session[:forwarding_url]

7.1.3で紹介したdebuggerメソッドをSessionsコントローラのnewアクションに置いてみましょう。その後、ログアウトして/users/1/editにアクセスしてみてください。デバッガーによって処理が途中で止まるはずです。ここでコンソール画面に移動し、session[:forwarding_url]の値が正しいかどうか確認してみましょう。また、newアクションにアクセスしたときのrequest.get?の値も確認してみましょう。なお、デバッガーを使っている最中に、ターミナルが不意に動かなくなったり挙動がおかしくなったりすることもあります。そのようなときは、熟練開発者の精神を思い出しながら落ち着いて対処しましょう(コラム 1.2)。

(ruby@puma: cluster worker 3: 34562 [sample_app]#34586) session[:forwarding_url]
"https://curly-fiesta-gvx96rqr57vc99p6-3000.app.github.dev/users/1/edit"
(ruby@puma: cluster worker 3: 34562 [sample_app]#34586) request.get?
true
saran12345678 commented 2 weeks ago

レイアウトにあるすべてのリンクに対して統合テストを書いてみましょう。ログイン済みユーザーとそうでないユーザーのそれぞれに対して、正しい振る舞いを考えてください。(ヒント: log_in_asヘルパーを使ってリスト 5.31にテストを追加してみましょう。)

require "test_helper"

class SiteLayoutTest < ActionDispatch::IntegrationTest

  test "layout links" do
    get root_path
    assert_template 'static_pages/home'
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path
    assert_select "a[href=?]", signup_path
    assert_select "a[href=?]", login_path
  end

  def setup
    @user = users(:michael)
  end

  test "layout links after logged in" do
    log_in_as(@user)
    get root_path
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path
    assert_select "a[href=?]", signup_path
    assert_select "a[href=?]", users_path
    assert_select "a[href=?]", user_path(@user)
    assert_select "a[href=?]", edit_user_path(@user)
    assert_select "a[href=?]", logout_path
  end
end
saran12345678 commented 2 weeks ago

試しに他のユーザーの編集ページにアクセスしてみて、10.2.2で実装した通りにリダイレクトされるかどうかを確かめてみましょう。

メイン画面に飛ぶことを確認

saran12345678 commented 2 weeks ago

Railsコンソールを開き、pageオプションにnilをセットして実行すると、1ページ目のユーザーが取得できることを確認してみましょう。

1ページ目の情報が取得できることを確認

irb(main):004> User.paginate(page: nil)
  User Load (0.2ms)  SELECT "users".* FROM "users" LIMIT ? OFFSET ?  [["LIMIT", 30], ["OFFSET", 0]]
=> 
[#<User:0x000077360630b048
  id: 1,
  name: "Example User",
  email: "example@railstutorial.org",
  created_at: Tue, 03 Sep 2024 02:39:53.880092000 UTC +00:00,
  updated_at: Tue, 03 Sep 2024 02:39:53.880092000 UTC +00:00,
  password_digest: "[FILTERED]",
  remember_digest: nil>,
 #<User:0x000077360630afa8
  id: 2,
  name: "Dr. Adrianne Willms",
  email: "example-1@railstutorial.org",
  created_at: Tue, 03 Sep 2024 02:39:55.114312000 UTC +00:00,
  updated_at: Tue, 03 Sep 2024 02:39:55.114312000 UTC +00:00,
  password_digest: "[FILTERED]",
  remember_digest: nil>,
 #<User:0x000077360630af08
  id: 3,
  name: "Mrs. Yasmine Schaefer",
  email: "example-2@railstutorial.org",
  created_at: Tue, 03 Sep 2024 02:39:55.391942000 UTC +00:00,
  updated_at: Tue, 03 Sep 2024 02:39:55.391942000 UTC +00:00,
  password_digest: "[FILTERED]",

先ほどの演習課題で取得したpaginationオブジェクトは、どのクラスでしょうか? また、User.allのクラスとどこが違うかを比較してみてください。

違う点がない

irb(main):006> page = User.paginate(page: nil)
  User Load (0.2ms)  SELECT "users".* FROM "users" LIMIT ? OFFSET ?  [["LIMIT", 30], ["OFFSET", 0]]
=> 
[#<User:0x000077360658c9e8
...
irb(main):007> page.class
=> User::ActiveRecord_Relation
irb(main):008> User.all.class
=> User::ActiveRecord_Relation
saran12345678 commented 2 weeks ago

試しにリスト 10.46にあるページネーションのリンク(will_paginateの部分)を2つともコメントアウトしてみて、リスト 10.49のテストが red に変わるかどうか確かめてみましょう。

テストに失敗することを確認

  1) Failure:
UsersIndexTest#test_index_including_pagination [/workspaces/sample_app/test/integration/users_index_test.rb:13]:
Expected at least 1 element matching "div.pagination", found 0..
Expected 0 to be >= 1.

44 runs, 112 assertions, 1 failures, 0 errors, 0 skips

先ほどは2つともコメントアウトしましたが、1つだけコメントアウトした場合、テストが green のままであることを確認してみましょう。will_paginateのリンクが2つとも存在していることをテストしたい場合は、どのようなテストを追加すれば良いでしょうか?(ヒント: 表 5.2を参考にして、個数をカウントするテストを追加してみましょう。)

2行のうち、1行だけコメントアウトを解除したところ、 テストに成功してしまうことを確認

44 runs, 142 assertions, 0 failures, 0 errors, 0 skips

countを追加し、テストに成功することを確認

    assert_select 'div.pagination', count: 2
saran12345678 commented 2 weeks ago

リスト 10.53にあるrenderの行をコメントアウトし、テストの結果が red に変わることを確認してみましょう。

テストに失敗することを確認

  1) Failure:
UsersIndexTest#test_index_including_pagination [/workspaces/sample_app/test/integration/users_index_test.rb:15]:
Expected at least 1 element matching "a[href="/users/14035331"]", found 0..
Expected 0 to be >= 1.

44 runs, 113 assertions, 1 failures, 0 errors, 0 skips
saran12345678 commented 2 weeks ago

管理者ユーザーとしてログインし、試しにサンプルユーザを2〜3人削除してみましょう。ユーザーを削除すると、Railsサーバーのログにはどのような情報が表示されるでしょうか?

全ユーザ情報取得のログでターミナルが埋まる

saran12345678 commented 2 weeks ago

試しにリスト 10.60にある管理者ユーザーのbeforeフィルターをコメントアウトしてみて、テストの結果が red に変わることを確認してみましょう。

  1) Failure:
UsersControllerTest#test_should_redirect_destroy_when_logged_in_as_a_non-admin [/workspaces/sample_app/test/controllers/users_controller_test.rb:68]:
"User.count" didn't change by 0.
Expected: 34
  Actual: 33

48 runs, 181 assertions, 1 failures, 0 errors, 0 skips