目的は権限設定なのですが、内容はcurrent_userをmodelで使う方法になっております。。
背景
Rails+deviseで稼働中のAPIで、ユーザー毎の権限設定を追加したい。
いわゆるSaasで業務システムを開発しています。権限設定もいろいろなパターンがあると思うんですが、今回はデータに触れることが出来るかどうかを制御するパターンです(参照・更新できるかどうかではない)
実装検討
権限設定には大きく分けて2種類ある。。と思う。
- 機能を制御(主にcontrollerで制御する。参照・更新・削除できるかどうか。)
- データを制御(modelで制御? あるデータを扱えるかどうか。)
1はイメージしやすい。各APIを使う権限であり、controllerのメソッドを制御できれば良く、cancancan、pundit等のgemがある。
2は主に対象データのあるレコードが参照可能か、更新可能かを制御する。
例えば、ユーザーAは、itemモデルのid:1のデータを更新可で、id:2のデータは参照不可。ユーザーBはid:2だけ更新可のような。
2のようなデータ制御ができるgemを自分は知らない。そこで、current_userをmodelのdefault_scopeで使ってしまうことにした。
本当はcontrollerでできると思うが、controller数もかなり多いので。。
Thread.currentを使う
以下の様に書くと、modelでUser.current_idが使えるようになる。
User.current_id = nil で処理後にクリアしているので、これでも上手く動いていた。
def current_id=(id)
Thread.current[:user_id] = id
end
def current_id
Thread.current[:user_id]
end
around_action :scope_current_user
private
def scope_current_user
User.current_id = current_user.id
yield
ensure
User.current_id = nil
end
request_storeを使う
上記のThread.currentでもできるが、request_storeというgemを使うと、rack層でクリアしてくれる。
authenticated_controller.rbを以下のように変更する。
すると、RequestStore.store[:current_user_id] がmodelで使えるようになる。
around_action :scope_current_user
private
def scope_current_user
RequestStore.store[:current_user_id] = current_user.id
yield
end
以上で、modelでcurrent_user.idが使えるようになった。 current_user.idでユーザー毎に設定された権限を取得し、default_scopeに条件を追加する。これで、データを制御する権限設定が比較的かんたんに実現できる。
※ default_scopeは本来は使いたくないですね。
他に良い方法があればコメント下さい!!