Discourseにみる権限管理。
権限管理に興味があって、ちょろちょろ読んでいます。
Spreeを読む前はDiscourseを読んでいました。Discourseでは権限管理にこれといったGemは用いず、独自の権限管理機能を実装しています。
Guardian
というクラスです。
ただの感想みたいになったのでこっちのブログにメモとして残します。
Guardian
用法はCancancanと大きくかわるところはありません。
まずcurrent_user
に類するものでGuardian
のインスタンスを作成します。
app/controllers/application_controller.rb
def guardian @guardian ||= Guardian.new(current_user) end
そしてそのインスタンスに処理内容を冠したメソッドに処理対象のなんらかのオブジェクを渡すと権限の確認が行われます。
app/controllers/users_controller.rb
guardian.ensure_can_edit_username!(user)
許可された動作ならそのまま処理はすすみ、許可されていない動作であればDiscourse::InvalidAccess
例外を発生します。
設定
Spreeにおいて、Cancancanはユーザー単位で権限を設定していました。
Guardian
は処理対象のオブジェクトのクラスごとにモジュールを用意します。
lib/guardian/user_guardian.rb
module UserGuardian # 略 def can_edit_username?(user) return false if (SiteSetting.sso_overrides_username? && SiteSetting.enable_sso?) return true if is_staff? return false if SiteSetting.username_change_period <= 0 is_me?(user) && (user.post_count == 0 || user.created_at > SiteSetting.username_change_period.days.ago) end # 略 end
そしてGuardian
にinclude
するというシンプルな仕組みです。
lib/guardian.rb
class Guardian include EnsureMagic include CategoryGuardian include PostGuardian include TopicGuardian include UserGuardian include PostRevisionGuardian include GroupGuardian #略 end
実行
前述の例のように接頭辞としてensure_
をつけた場合、設定されたメソッドがありません。
app/controllers/users_controller.rb
guardian.ensure_can_edit_username!(user)
そこでEnsureMagic
というmodule
に実装されたmethod_missing
があらためてメソッドを探索し、その結果をうけてtrue
を返すか例外を発生します。
感想
最初は単一のクラスで、その後どんどん増改築した結果、基本となるGuardian
にもわりとコードがありつつ、include
するmodule
にもコードがもりもりあるという感じになっています。
それを除けば、method_missing
から定義済みメソッドを探索して実行、権限を確認するという動作は、メソッドにより権限をドバっと設定するCancancanより好ましさを感じます。
自分でも
引数でわたされるオブジェクトのクラスを見て該当のGuardian
を検索してインスタンス化、そこからメソッドを探索して権限確認という実装を自分でも書いてみましたが、なかなかいい塩梅でした。
でも多分仕事でつかうならCancancanを使いますね。