初心者による、Railsの認可GemであるPunditの備忘録です。
導入
インストールします。
gem "pundit"
application_controllerにincludeしときます。
class ApplicationController < ActionController::Base
include Pundit
end
Punditで使うファイルを生成します。
$ rails g pundit:install
これで app/policies/application_policy.rbができます。ほぼ準備完了です。
概要
Punditとはご存知の通り、リソースベースで認可の仕組みを提供します。
私は多対多の関係をうまく動作させるのに利用させていただきました。
以下、拙いですが勘弁してください。
ユーザは複数のプロジェクトに参加し、プロジェクトは複数のユーザに参加されるというのを中間テーブルを利用し実現します。
ユーザ 1-* プロジェクト参加ユーザ *-1 プロジェクト
のようなイメージです。
例えば、「プロジェクトの詳細ページはプロジェクトに参加しているユーザしか閲覧することができない」という要件があったとします。
きっとProjectコントローラのshowメソッドらへんでうまいことやれば良いのでしょうね。
Punditではこれをコントローラとポリシーファイルで制御します。
class ProjectsController < ApplicationController
def show
@project = Project.find(params[:id])
authorize @project
end
end
class ProjectPolicy < ApplicationPolicy
def show?
!!@record.users.find_by(id: @user.id)
end
end
上記コードの動作はこんな感じです。
- projects_controllerのshowが呼ばれる
- show内で引数@projectを渡してauthorizeメソッドを呼ぶ
- 自動的にproject_policyのshow?メソッドが呼ばれる
- 検証を行い、boolを返す
- falseだった場合、NotAuthorizedErrorが発生する
動作
autorizeメソッドはauthorize(user, record, query)
と定義されていますが、上記コードでは@projectのみ渡しています。userには暗黙でcurrent_userが渡されているので渡さなくて大丈夫です。
queryには、policy内で呼びたいメソッドを指定する時のみシンボルを指定しますが、詳しくはgithubを参照してください。
これらはそれぞれ@user, @recordに入ります。
今回はprojectモデルに関連した認可を扱いたいので、application_policyを継承したproject_policyを作成しました。
ここに「どんな時そのコントローラのメソッド実行を許可するのか」ということを定義します。
class ProjectPolicy < ApplicationPolicy
def show?
!!@record.users.find_by(id: @user.id)
end
end
あとは先述の通りです。project_controller#showのauthorize(@project)は自動的にproject_policyのshow?メソッドを呼びます。show?では、渡された@record(=@project)と@user(=current_user)を用いて検証を行います。
上記コードではcurrent_userが@projectに(中間テーブルを通して)ひもづくユーザかどうか検索し、存在すればtrueを返します。つまり、プロジェクト参加者かどうか検証しているわけです。
検証失敗時
もし参加者でないユーザがプロジェクトをみようとしたら、falseが返ります。
するとauthorizeはNotAuthorizedErrorを投げます。拾いましょう。
class ApplicationController < ActionController::Base
include Pundit
rescue_from NotAuthorizedError, with: :render_404
def render_404(exception = nil)
if exception
logger.info "Rendering 404 with exception: #{exception.message}"
end
render file: "#{Rails.root}/public/404.html", status: 404, content_type: 'text/html'
end
end
404に飛ぶようにしました。
(参考)
るびま http://magazine.rubyist.net/articles/0047/0047-IntroductionToPundit.html
おしまい
本当にざっくりとこんな感じです。書き間違いとか追記したいこととかあったらまた書きます。
参考URLもあとで載せます。