punditの質問についての回答

■Question
admin/oems_controller.rbの、
before_action :authorize_oem, only: %i[update destroy]
は、policies/oem_policy.rbのOemPolicyクラスの、
  1. index
  2. create
  3. update
  4. create_organization
  5. edit_organization
  6. destroy
の対応関係にはないのではないか?

■Answer

対応関係をOemControllerは表現できている。
具体的にはAdmin権限であるかを認証対象としており、adminのみ通る構成になる。


before_actionにおける、index create / update destroyという組み分けと、
メソッドの別定義はRESTの概念の違い、URLにidをとるかどうかの違いによって生じる差分である。


punditの認証チェックメソッドであるauthorizeは、モデルクラスもインスタンスも両方を引数として取れるので、その違いはpunditにおいては吸収される。

下記に説明。

before_action
アクセス制御の仕組み。
元々はログインユーザーと非ログインユーザーを分けるとかで使う処理。
管理者機能は特にアクセス制御が必要。
なので、当ファイルのbefore_actionで、アクションを行う前に実行するようにする。
ex)ログイン管理の簡単実装
class Admin:: < ApplicationController

before_action :authorize

...

private def authorize

  unless current.administrator

    redirect_to :admin_login

  end

end



index create / update destroyの違い

(Railsを支える基本概念の整理(RESTfulやリソースなど)より
上記の中で、URLにおいて、

  • 個別的にidを取らないのが index create
  • idを取るのが update destroy
この違いをもってして、
before_actionでの挙動の違い、
-----
authorize oem
....
@oem = oem.find(params[id])
authorize @oem
-----
という挙動の違いになっている。

(参考:挙動の対照表)
@oem = oem.find(params[id])
=>
get '/oem/:id', to: 'oem#show', as: 'oem'



Railsのルーティングについて
namespace :admin do
  resources :oems, only: [:index, :create, :update, :destroy]
は、
get "oems" => "oem#index"
post "oems" => "oem#create"
patch "oems/:id" => "oem#update"
delete "oems/:id" => "oem#destroy"

を一行で表現したもの。
only:[...]に記述のないshow,new,editは除く。
(Railsのルーティング)より


%i[index create]について
これはシンボル配列のリテラルである。
実体として、[:index :create]である。
つまり、indexメソッド と createメソッドのことを指す。

また、%w[...]は文字列配列のリテラルである。


Punditにおける関係性について
authorizeメソッドでは、、、


■punditについて
ベースファイルのApplicationPolicyの初期構成状態は、
class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end

  def show?
    false
  end

  def create?
    false
  end

  def new?
    create?
  end

  def update?
    false
  end

  def edit?
    update?
  end

  def destroy?
    false
  end

  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      @scope = scope
    end

    def resolve
      scope
    end
  end
end
これは、初期セットアップで、すべてのメソッドで認証拒否状態になっている。
クラスを拡張することで(OemPolicy < ApplicationPolicyとか)で、
設定することでホワイトリスト的に認証OK状態を定義している。

ちなみに初期セットアップの下記の部分
  # create, editアクションを実行できるかをチェックしています(newができればcreateもできる、editができればupdateもできる)
  def new?
    create?
  end

  def edit?
    update?
  end

def create?
  false
end

def update?
  false
end
なので、newとcreate/editとupdateは相互依存関係にある。


OemPolicyファイルの中身を見てみると、
....
class OemPolicy < ApplicationPolicy
def index?
  user.nttpc_administrator?
end

def create?
  index?
end

def update?
  index?
end
....
index?で、ユーザーがadminユーザーかどうかを確認している。
trueを返せば認証OK。
他のcreate?update?destroy?は、中身がindex?なので、
同様にユーザーがadminユーザーかどうかを確認している。
つまり、OemPolicyがチェックしている認証情報は、
ユーザーがadminユーザーかどうか、という一点のみであること。

authorizeメソッドについて

  • authorize Oem
  • authorize @oem
という記述があるが、クラスでもインスタンスでも両方とも値にとれる。

暗黙的には、current_user(現在のユーザー)とrecord(レコードorクラス)を引数にとって、その値を評価している。

■参考:pundit

  1. Punditを使って権限を管理する
  2. Railsでの承認にPunditを使用する-レシピとベストプラクティス
  3. 権限管理のgem、Punditの紹介
  4. punditを使ってみる
Next Post Previous Post
No Comment
Add Comment
comment url