←Rails 6で認証認可入り掲示板APIを構築する #16 policyの設定
管理者権限を用意する
前回までの実装で、投稿の編集や削除は投稿者本人だけできるようになりました。
ですがそこに拡張し、管理者としてログインしている時は誰の投稿でも編集・削除できるようにしてみます。
そのためにはuserモデルにadminカラムを追加しましょう。
$ rails g migration AddAdminToUsers admin:boolean
# frozen_string_literal: true
class AddAdminToUsers < ActiveRecord::Migration[6.0]
def change
add_column :users, :admin, :boolean, default: false, null: false
end
end
さて、今までは機能を実装してからテストを書いていましたが、punditの基本挙動は理解できたと思うので、先にテストから書いてみます。
管理者権限のない別ユーザーでログインしていた際に編集削除できないテストはすでに書かれているので、
- 管理者権限のある際に別ユーザーの投稿を編集できること
- 管理者権限のある際に別ユーザーの投稿を削除できること
を実装してみます。
厳密にはpolicyテストとrequestテスト両方実装すると万全ですが、ここはpolicyだけ実装してみます。
もし良ければここから書いてあるサンプルを見ずに、まずは自力で実装してみて見比べてみると、コードを書く練習になるのでやってみてください。
post_policy_spec.rbの編集
...
RSpec.describe PostPolicy, type: :policy do
let(:user) { create(:user) }
+ let(:admin_user) { create(:user, admin: true) }
let(:post) { create(:post) }
...
it "ログインしているが別ユーザーの時に不許可" do
expect(subject).not_to permit(user, post)
end
+ it "adminユーザーでログインしている時に許可" do
+ expect(subject).to permit(admin_user, post)
+ end
...
...
remember_created_at { nil }
name { "MyString" }
tokens { nil }
+ admin { false }
end
...
ここまで実装したらrubocopとrspecを動かして確認。
まだpolicyファイルを実装していないのでテストはコケます。
policyの実装
まずは管理者であることを判定するprivateメソッドをapplication_policy.rbに生やします。
...
private
def mine?
@record.user == @user
end
+
+ def admin?
+ @user.present? && @user.admin?
+ end
+
あとはpost_policy.rbのupdate?destroy?の2つの判定を変えるだけですね。
def update?
- mine?
+ mine? || admin?
end
def destroy?
- mine?
+ mine? || admin?
end
なんとこれだけです。
traitを使ってadminを簡単に作る
これだけだと少し中身の薄い記事なので、factoryBotを触ってadminユーザーをもっと簡単に作れるようにします。
name { "MyString" }
tokens { nil }
admin { false }
+
+ trait :admin do
+ admin { true }
+ end
end
RSpec.describe PostPolicy, type: :policy do
let(:user) { create(:user) }
- let(:admin_user) { create(:user, admin: true) }
+ let(:admin_user) { create(:user, :admin) }
let(:post) { create(:post) }
これでrspecを動かしてみるとどうでしょうか。
ミスなく書けていればテスト通過するはずです。
traitはご覧の通り、factoryBotのcreate
等の第2引数に別名として渡すことで使うことができます。
adminフラグを立てるだけなら恩恵も少なく意味もあまりないのですが、例えばadminユーザーの場合に必ずセットで初期値を持っておきたいカラムがあったりすれば、いちいちcreate
のたびに複数カラムの初期値セットをしなくて済みます。
ぜひご活用ください。