pundit Gemの使い方

pundit Gemの使い方
[pundit]Gemは、ユーザーの権限の条件によって表示できるビューを変えるもの

[pundit]Gemの使い方は、以下のようになる

[Gemfile]に以下のように記述

[Gemfile]

gem "pundit"


[application_controller.rb]に以下を記述する

[app/controllers/application_controller.rb]

include Pundit


以下コマンドで[policy]ファイルを生成する

$ rails g pundit:install


上記で生成された[app/policies/application_policy.rb]を承継するように新規ファイルを作成し以下のようなクラスを定義する

[app/policies/user_policy.rb]

class UserPolicy < ApplicationPolicy
  def index?
    true
  end

  def show?
    true
  end

  def create?
    user.admin?
  end

  def update?
    user.admin? || user == record
  end

  def destroy?
    user != record && create?
  end

end

下記のようなコントローラーがある場合

[app/controllers/admin/users_controller.rb]

class Admin::UsersController < ApplicationController
  layout 'admin'

  before_action :set_user, only: %i[show update destroy]

  def index
    authorize(User)     # authorizeに引数で渡すモデルのオブジェクトが無い場合は、モデルをそのまま渡す

    @users = User.all.order(:id)
  end

  def show
    authorize(@user)   # authorizeに引数で渡すモデルのオブジェクトがある場合は、そのオブジェクトを渡す
  end

  def create
    authorize(User)

    @user = User.new(user_params)

    if @user.save
      redirect_to admin_user_path(@user)
    else
      render :new
    end
  end

  def update
    authorize(@user)

    if @user.update(user_params)
      redirect_to admin_user_path(@user)
    else
      render :show
    end
  end

  def destroy
    authorize(@user)

    @user.destroy!

    redirect_to admin_users_path
  end

  private

  def user_params
    params.require(:user).permit(policy(:user).permitted_attributes)
  end

  def set_user
    @user = User.find(params[:id])
  end
end

createアクションは、authorize(User)により[app/policies/user_policy.rb]のcreate?アクションの(true or false)を確認する
今回は、userが管理者の場合はtrueを返しそのままcreateアクションが実行されるが管理者以外の場合はfalseになりcreateアクションが実行されず、通常のビューがブラウザに返らずPundit::NotAuthorizedErrorの例外が出る


以下の記述をする事で、[Pundit::NotAuthorizedError]例外を403のHTTPステータスにできる

[config/application.rb]

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

上記を記述せずに権限が無いページにアクセスすると[categories]htmlファイルが500エラーになる

Image from Gyazo

上記を記述すると権限が無いページにアクセスすると[categories]htmlファイルが403エラーになる

Image from Gyazo


下記のように記述することで、[user_not_authorized]メソッドで403のエラーページになった際の動きを定義できる

[app/controllers/application_controller.rb]

class ApplicationController < ActionController::Base
  include Pundit

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  private

  def user_not_authorized
    flash[:alert] = "You are not authorized to perform this action."
    redirect_to(request.referrer || root_path)
  end
end

また、画面全部ではなくユーザーの権限の状況によって表示したい部分を変えたい場合(権限の条件によって表示したいリンクと表示したくないリンクなど)がある場合は、ビュー側でpolicyヘルパーを使って以下のように記述できる

[app/views ファイル]

<% if policy(user).index? %>
  <%= link_to users_path, "ユーザー一覧"%>
<% end %>

上記は、UserPolicyのindex?メソッドの(true or false)の結果でリンクを表示したり表示しなかったりなどできる

※ポイント
[policy]ファイルは、クラスの最後にPolicyをつける
[pundit]の[policy]ファイルの中のuserという記述は、current_userメソッドが呼び出される
[app/policies/application_policy.rb]のrecordは、認可をチェックしたいモデルオブジェクトの事。

参考記事:

Punditを使って権限を管理する - Qiita

【Ruby on Rails】「pundit」について | プログラミングマガジン

Pundit + Railsで認可の仕組みをシンプルに作る - Qiita