Punditについて
gem "pundit"
bundle install後以下入力
rails g pundit:install
これで、app/policies/配下にapplication_policy.rbというファイルが作成されます。 policies配下のファイルは「認可のルールを記述するファイル」と認識して差し支えないと思います。
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 end
このファイルで定義されるクラスApplicationPolicyを継承して、コントローラーごとの認可ルールを記述していきます。
Punditはデフォルトで current_user メソッドを呼んで user として自動的に引数に渡しています。
「Policy」という単語が接尾辞として付いているだけです。
taxonomy_policy.rb
class TaxonomyPolicy < ApplicationPolicy def index? true end def create? user.admin? || user.editor? end def update? true end def destroy? true end end
このように書き方として
- モデル名_policy.rbでファイルを作成
- モデル名Policyでクラス名を定義
- def アクション名?で認可ルール(policy)を記述
taxonomy_policy.rbを見てみましょう。createだけadminとeditorのみ権限があり、他は全ユーザーに権限があります。 generatorによって作成されたApplicationPolicyから継承するか、継承する独自の基本クラスを設定する必要があります。
仮にindex?にfalseを記載するとどうなるでしょう?category_controller、tag_controller、author_controllerのindexアクションは拒否されて、Pundit::NotAuthorizedErrorが投げられます。
category_controller
class Admin::CategoriesController < ApplicationController layout 'admin' before_action :set_categories, only: %i[index] before_action :set_category, only: %i[edit update destroy] def index authorize(Category) @category = Category.new end def create authorize(Category) @category = Category.new(category_params) if @category.save redirect_to admin_categories_path else set_categories render :index end end def edit authorize(@category) end def update authorize(@category) if @category.update(category_params) redirect_to admin_categories_path else render :edit end end end
authorizeメソッドは、Taxonomyが一致するTaxonomyPolicyクラスを持っていることを自動的に推測し、このクラスをインスタンス化して、現在のユーザと指定されたレコードを渡します。
例外を403エラーにする
application_controller.rb
class ApplicationController < ActionController::Base include Pundit rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized private def user_not_authorized render file: Rails.root.join('public/403.html'), status: forbidden end
config/application.rb
以下を追加
config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden
publicファイルに403エラーをの時に表示させるhtmlを作成
public/403.html
<!DOCTYPE html> <html> <head> <title>Forbidden(403)</title> <meta name="viewport" content="width=device-width,initial-scale=1"> </head> <body> <p>このページへのアクセス権限がありません。</p> </body> </html>
参考URL