##認可
ウェブアプリケーションの文脈では、
認証(authentication)はサイトのユーザーを識別することであり、
認可(authorization)はそのユーザーが実行可能な操作を管理することです。
updateアクションには大きな穴がある。
ユーザーにログインを要求し、かつ自分以外のユーザー情報を変更できないように制御してみましょう。
###ユーザーにログインを要求する
####beforeフィルターにlogged_in_userを追加する
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update]
# before_actionメソッドを使って何らかの処理が実行される直前に
# 特定のメソッドを実行する仕組み
# :editと:updateアクションだけ
.
.
.
# ログイン済みユーザーかどうか確認
def logged_in_user
unless logged_in?
# もしも、評価が偽(false)であれば○○する
flash[:danger] = "Please log in."
# ログインされていなければメッセージを表示する
redirect_to login_url
# ログインページを表示させる
end
end
end
#####テスト
ubuntu:~/environment/sample_app (updating-users) $ rails t
Running via Spring preloader in process 17649
Started with run options --seed 49225
FAIL["test_unsuccessful_edit", #<Minitest::Reporters::Suite:0x00007f5338811400 @name="UsersEditTest">, 0.08051444600096147]
test_unsuccessful_edit#UsersEditTest (0.08s)
expecting <"users/edit"> but rendering with <[]>
test/integration/users_edit_test.rb:13:in `block in <class:UsersEditTest>'
FAIL["test_successful_edit", #<Minitest::Reporters::Suite:0x0000557d37110f08 @name="UsersEditTest">, 0.0888614100003906]
test_successful_edit#UsersEditTest (0.09s)
expecting <"users/edit"> but rendering with <[]>
test/integration/users_edit_test.rb:32:in `block in <class:UsersEditTest>'
31/31: [============================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.12748s
31 tests, 70 assertions, 2 failures, 0 errors, 0 skips
13行目assert_template 'users/edit'
32行目assert_template 'users/edit'
表示されないらしい。
editアクションやupdateアクションでログインを要求するようになったため、ログインしていないユーザーだとこれらのテストが失敗するようになったため
####テストユーザーでログインする
test/integration/users_edit_test.rb
require 'test_helper'
# test_helper.rbのパラメータ群を指定
class UsersEditTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "unsuccessful edit" do
log_in_as(@user)
.
.
.
end
test "successful edit" do
log_in_as(@user)
# ログインさせる
get edit_user_path(@user)
assert_template 'users/edit'
name = "Foo Bar"
email = "foo@bar.com"
.
.
.
end
end
#####テスト
ubuntu:~/environment/sample_app (updating-users) $ rails t
Running via Spring preloader in process 18369
Started with run options --seed 35897
31/31: [============================] 100% Time: 00:00:02, Time: 00:00:02
Finished in 2.13056s
31 tests, 77 assertions, 0 failures, 0 errors, 0 skips
getリクエストを送ってもログインしていなければページが表示されない。
####セキュリティモデルを確認するためにbeforeフィルターをコメントアウトする
app/controllers/users_controller.rb
class UsersController < ApplicationController
#before_action :logged_in_user, only: [:edit, :update]
# before_actionメソッドを使って何らかの処理が実行される直前に
# 特定のメソッドを実行する仕組み
# ログインをさせる
# :editと:updateアクションだけ
.
.
.
end
#####テスト
ubuntu:~/environment/sample_app (updating-users) $ rails t
Running via Spring preloader in process 18729
Started with run options --seed 19344
31/31: [============================] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.80145s
31 tests, 77 assertions, 0 failures, 0 errors, 0 skips
####editとupdateアクションの保護に対するテストする
test/controllers/users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
# テストユーザー
end
test "should get new" do
get signup_path
assert_response :success
end
test "should redirect edit when not logged in" do
get edit_user_path(@user)
assert_not flash.empty?
# flashメッセージが空ではないかどうか?
assert_redirected_to login_url
# login_urlにリダイレクトできるか?
end
test "should redirect update when not logged in" do
patch user_path(@user), params: { user: { name: @user.name,
email: @user.email } }
# ユーザー情報を上書き
assert_not flash.empty?
assert_redirected_to login_url
end
end
#####テスト
ubuntu:~/environment/sample_app (updating-users) $ rails t
Running via Spring preloader in process 19211
Started with run options --seed 27208
FAIL["test_should_redirect_edit_when_not_logged_in", #<Minitest::Reporters::Suite:0x0000557d39d96730 @name="UsersControllerTest">, 1.666765893000047]
test_should_redirect_edit_when_not_logged_in#UsersControllerTest (1.67s)
Expected true to be nil or false
test/controllers/users_controller_test.rb:16:in `block in <class:UsersControllerTest>'
FAIL["test_should_redirect_update_when_not_logged_in", #<Minitest::Reporters::Suite:0x0000557d39dd59d0 @name="UsersControllerTest">, 1.6791209879993403]
test_should_redirect_update_when_not_logged_in#UsersControllerTest (1.68s)
Expected response to be a redirect to <http://www.example.com/login> but was a redirect to <http://www.example.com/users/762146111>.
Expected "http://www.example.com/login" to be === "http://www.example.com/users/762146111".
test/controllers/users_controller_test.rb:27:in `block in <class:UsersControllerTest>'
33/33: [============================] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.78848s
33 tests, 81 assertions, 2 failures, 0 errors, 0 skips
#####わからない
Expected true to be nil or false
16行目assert_not flash.empty?
27行目assert_redirected_to login_url
修正後
#####テスト
ubuntu:~/environment/sample_app (updating-users) $ rails t
Running via Spring preloader in process 19244
Started with run options --seed 1774
33/33: [============================] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.70367s
33 tests, 81 assertions, 0 failures, 0 errors, 0 skips
###演習
1.
デフォルトのbeforeフィルターは、すべてのアクションに対して制限を加えます。今回のケースだと、ログインページやユーザー登録ページにも制限の範囲が及んでしまうはずです(結果としてテストも失敗するはずです)。リスト 10.15のonly:オプションをコメントアウトしてみて、テストスイートがそのエラーを検知できるかどうか(テストが失敗するかどうか)確かめてみましょう。
ubuntu:~/environment/sample_app (updating-users) $ rails t
Running via Spring preloader in process 20218
Started with run options --seed 60077
FAIL["test_should_get_new", #<Minitest::Reporters::Suite:0x00007f5338f52188 @name="UsersControllerTest">, 0.09008692899988091]
test_should_get_new#UsersControllerTest (0.09s)
Expected response to be a <2XX: success>, but was a <302: Found> 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>
test/controllers/users_controller_test.rb:11:in `block in <class:UsersControllerTest>'
FAIL["test_layout_links", #<Minitest::Reporters::Suite:0x0000557d39aac218 @name="SiteLayoutTest">, 1.471755246999237]
test_layout_links#SiteLayoutTest (1.47s)
Expected at least 1 element matching "title", found 0..
Expected 0 to be >= 1.
test/integration/site_layout_test.rb:18:in `block in <class:SiteLayoutTest>'
FAIL["test_invalid_signup_information", #<Minitest::Reporters::Suite:0x0000557d39af55f8 @name="UsersSignupTest">, 1.4869099379993713]
test_invalid_signup_information#UsersSignupTest (1.49s)
expecting <"users/new"> but rendering with <[]>
test/integration/users_signup_test.rb:17:in `block in <class:UsersSignupTest>'
FAIL["test_valid_signup_information", #<Minitest::Reporters::Suite:0x0000557d39b3f568 @name="UsersSignupTest">, 1.4974412970004778]
test_valid_signup_information#UsersSignupTest (1.50s)
"User.count" didn't change by 1.
Expected: 2
Actual: 1
test/integration/users_signup_test.rb:22:in `block in <class:UsersSignupTest>'
33/33: [============================] 100% Time: 00:00:01, Time: 00:00:01
Finished in 1.56975s
33 tests, 79 assertions, 4 failures, 0 errors, 0 skips
エラーになった。