[Rails] Punditを使ったアクション権限の管理

学習記録

はじめに

ブログアプリでタグ・著者・カテゴリーを作成する機能があります。ユーザー権限の種類は下記の3つです。

  • admin
  • editor
  • writer

現状、writer権限のユーザーだけ各項目の作成権限が付与されていません。しかし各項目の一覧表示や編集・削除はできてしまいますので、それらもできないように変更しましょう。 また、writer権限ユーザーが権限を許可されていないページにアクセスした場合、403エラー画面が出るように変更しましょう。

Punditの導入

$ gem "pundit"

bundle installし、

class ApplicationController < ActionController::Base
  include Pundit
end

generatorを使います。

$ rails g pundit:install

app/policies/application_policy.rb が生成されました。

class ApplicationPolicy
  attr_reader :user, :record

  def initialize(user, record)         # こちらのuserはデフォルトでcurrent_userが引数に割り当てられるようになっている。recordの方には対応するモデルのインスタンスを手動で割り当てる。
    @user = user
    @record = record
  end

  def index?
    false
  end

 ------一部省略-------

  
end

このファイルで定義されるクラスApplicationPolicyを継承して、コントローラーごとの認可ルールを記述していきます。


policyファイルの設定

今回権限を調整したいTag, Author, CategoryモデルはSTI(単一テーブル継承)なので、継承元のTaxonomyモデルに対してpolicyを作成しましょう。

class TaxonomyPolicy < ApplicationPolicy
  def index?
    user.admin? || user.editor?
  end

  def create?
    user.admin? || user.editor?
  end

  def update?
    user.admin? || user.editor?
  end

  def destroy?
    user.admin? || user.editor?
  end
end

  • モデル名_policy.rbでファイルを作成
  • モデル名Policyでクラス名を定義
  • def アクション名? で認可ルール(policy)を記述
  • def アクション名? の返り値によって、認可するか否かを判断しています。 falseが返ってくれば、対応するコントローラのアクションは拒否されて、Pundit::NotAuthorizedErrorが投げられます。

    policyファイルを適用するには、コントローラ側からPunditを呼び出しましょう。

    class Admin::TagsController < ApplicationController
    
      def index
        @tags = Tag.all.order(:slug)
        authorize(Tag)               # インスタンスモデルを利用しない(policy側ではuserの情報のみ扱う)場合に限り、authorize(Tag) という記述が可能。
        
        @tag = Tag.new
      end
      
      -----------一部省略------------------
     
    end

    エラー画面設定

    任意の403エラーページpublic/403.htmlを作成します。

    開発環境で403エラーページを表示させる

    config/application.rbに追記

    config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden

    この記述により、Production環境でユーザー向けの静的ページを表示させることができます。
    なぜなら、config/envirronments/production.rbの中のconfig.consider_all_requests_localfalseに設定されているため、エラーの詳細情報ではなく静的ページを表示する挙動になります。
    ローカル開発環境で静的ページを確認する場合は、config/environments/development.rbのconfig.consider_all_requests_localfalseにして、サーバー再起動してください。

    [コラム] 共通レイアウトをエラーページに表示させる場合

    共通ヘッダーなどのレイアウトも表示させる場合は、Applicationcontrollerでrescue処理を記載して、app/view以下にテンプレートファイルを用意してrenderを使って表示させます。

    class ApplicationController < ActionController::Base
      include Pundit
    
      rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
    
      private
    
      def user_not_authorized
        render 'error/403', status: :forbidden
      end
    end

    参考記事

    GitHub - varvet/pundit: Minimal authorization through OO design and pure Ruby classes
    Minimal authorization through OO design and pure Ruby classes - GitHub - varvet/pundit: Minimal authorization through OO design and pure Ruby classes
    Action Controller の概要 - Railsガイド
    本ガイドでは、コントローラの動作と、アプリケーションのリクエストサイクルでコントローラがどのように使われるかについて説明します。セッション、フィルタ、cookie、データストリーミング、リクエストによって発生する例外、その他多くの話題を取り扱っています。

    コメント

    タイトルとURLをコピーしました