ンンンパ

ふとしです

Discourseにみる権限管理。

権限管理に興味があって、ちょろちょろ読んでいます。

qiita.com

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

そしてGuardianincludeするというシンプルな仕組みです。

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を検索してインスタンス化、そこからメソッドを探索して権限確認という実装を自分でも書いてみましたが、なかなかいい塩梅でした。

github.com

でも多分仕事でつかうならCancancanを使いますね。