Railsでデータの権限設定:Thread.currentを使ってみる

Techrails, ruby

Rails+deviseで稼働中のAPIに、ユーザー毎の権限設定を追加したい。

実装方法の背景

権限設定には大きく分けて2種類ある。と思う。
1.機能を制御するもの(主にcontrollerで制御する)
2.データを制御するもの(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は100あるし、modelも200あるし、、

Thread.currentを使う

以下の様に書くと、modelでUser.current_idが使えるようになる。
User.current_id = nil で処理後にクリアしているので、これでも上手く動いていた。

request_storeを使う

request_storeというgemを使うと、rack層でクリアしてくれる。

authenticated_controller.rbを以下のように変更する。
すると、RequestStore.store[:current_user_id] がmodelで使えるようになる。

以上で、modelでcurrent_user.idが使えるようになった。 current_user.idでユーザー毎に設定された権限を取得し、default_scopeに条件を追加する。これで、データを制御する権限設定が比較的かんたんに実現できる。
※ default_scopeは本来は使いたくないですね。

他に良い方法があればコメント下さい!!

なお、権限設定自体はrollモデル、userモデル、制御したいデータのモデルのmapを作成し実装しています。