punditとは?
「ユーザーによってページ表示の許可・拒否をしたり、表示情報の範囲を変えたりすることができるgem」のことです。
Punditはcurrent_user(ログイン中のユーザー)メソッドを扱うので、sorcery gem などが「認証」の仕組みありきです。
実装の目的:
①権限設定の記述を見つけ、ライターにタグ・著者・カテゴリーの一覧表示・編集・削除を出来ないようにする。
②権限エラーが発生した際に、403エラーのページを表示させる(403.htmlは、public配下に新たに作成する)
現状:
writer権限のユーザーだけ各項目の作成権限が付与されていません。しかし各項目の一覧表示や編集・削除はできてしまっている。
また、writer権限ユーザーが権限を許可されていないページにアクセスした場合、403エラー画面に遷移しない。
ユーザー権限の種類は下記の3つです。
punditの導入手順:
gemfile
$ gem "pundit"
gemfileに記載後、bndle install
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include Pundit
〜〜〜〜〜〜 途中省略
end
$ rails g pundit:install
これで、app/policies/配下にapplication_policy.rbというファイルが作成されます。
app/polices/application_policyrb
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
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope
end
end
end
[checkポイント]
**このファイルで定義されるクラスApplicationPolicyを継承して、コントローラーごとの認可ルールを記述していきます。**
policyファイルの設定
今回権限を調整したいTag, Author, CategoryモデルはSTI(単一テーブル継承)なので、継承元のTaxonomyモデルに対してpolicyを作成しましょう。
app/polices/taxonomy_policy.rb
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
[checkポイント]
・モデル名_policy.rbでファイルを作成
・モデル名Policyでクラス名を定義
・def アクション名? で認可ルール(policy)を記述
def アクション名? の返り値によって、認可するか否かを判断しています。
falseが返ってくれば、対応するコントローラのアクションは拒否されて、Pundit::NotAuthorizedErrorが投げられます。
app/controllers/admin/tags_controller.rb
class Admin::TagsController < ApplicationController
def index@tags = Tag.all.order(:slug)
authorize(Tag) # インスタンスモデルを利用しない(policy側ではuserの情報のみ扱う)場合に限り、authorize(Tag) という記述が可能。
@tag = Tag.new
end
-----------一部省略------------------
end
============================================================================
config/application.rb
config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden
共通レイアウトをエラーページに表示させる
共通ヘッダーなどのレイアウトも表示させる場合は、Applicationcontrollerでrescue処理を記載して、
app/view以下にテンプレートファイルを用意してrenderを使って表示させます。
app/controllers/application_controller.rb
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
public/403.html
<!DOCTYPE html>
<html>
<head>
<title>権限がありません(401)</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<p>権限がありません。</p>
</body>
</html>