Punditを使って権限の管理をする
Punditとは
リソースに対して、どのユーザーであれば処理が許可されるのかを定義するgemです。
参考 : GitHub - varvet/pundit: Minimal authorization through OO design and pure Ruby classes
コントローラの各アクションで authorizeリソースオブジェクト を呼ぶと対象のリソースに対して権限があるかを確認してくれます。そのポリシーをapp/policiesにあるポリシーファイルで細かく定義することができます。
Punditの使用方法
まずGemfileにPunditのgemを追加してbundle install
します。
gem "pundit"
継承元のコントローラ(application_controller.rb)にPunditをincludeします。
app/controllers/application_controller.rb class ApplicationController < ActionController::Base include Pundit end
generatorを使ってapplication_policy.rbファイルを作成します。
$ rails g pundit:install
app/policies/配下にポリシーファイルというものをオブジェクト別に作成していきます。
例題 : 記事(Article)アプリで管理者だけが記事の一覧画面、また記事の作成、削除、更新ができるように設定する。※ユーザーには管理者(admin)と一般(general)をenumで分けてある。
Policyファイルに認可を与えるユーザー(管理者)を設定していきます。
app/policies/article_policy.rb class ArticlePolicy < ApplicationPolicy def index? user.admin? end def create? user.admin? end def update? user.admin? end def destroy? user.admin? end end
articles_controller.rbの各アクションの実行前にauthorize
を実行して認可の許可を判断します。
class ArticlesController < ApplicationController def index authorize(Article) end def create authorize(Article) end def edit authorize(@article) end def update authorize(@article) end def destroy authorize(@article) end end
authorize(@article)
とauthorize(Article)
は、特定のarticleがあるかないかで使い分けます。記事一覧と記事新規作成画面では特定のarticleはないのでauthorize(Article)
を使っています。
view/articles/index.html.erbでこのようにビューファイルで権限によってリンクを表示するかしないか判定することができます。
<% if policy(Article).index? %> <%= link_to "記事一覧", articles_path %> <% end %>
403 Forbiddenのエラー画面を表示
403 Forbiddenのステータスコードは、要求されたリソースはサーバー上にありますが、アクセス権やIPアドレス制限などの問題でアクセス拒否されたことを表します。
今回はgeneralでアクセスするとこの403のエラー画面が出力されるはずです。
まずpublicディレクトリ配下に403.htmlファイルを作成しておきます。
<html> <head> <title>権限がありません(403)</title> <meta name="viewport" content="width=device-width,initial-scale=1"> </head> <body> <p>権限がありません。</p> </body> </html>
Pundit::NotAuthorizedError例外を403HTTPステータスにします。
config/application.rb config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden
development環境でアプリの動作を確認するときは、config.consider_all_requests_local
の値をtrue
から一時的にfalse
に変更します。
config/environments/development.rb
config.consider_all_requests_local = false
true
のままだと開発用のエラーページが表示されています。
false
に変えることで本番環境の画面を表示することができます。
参考 : カスタム例外を小さな exceptions_app でハンドリングする - Qiita
私は最初application_controller.rb内に以下のような権限のないユーザーがアクセスすると403エラーページが表示される実装をしていました。
class ApplicationController < ActionController::Base include Pundit rescue_from Pundit::NotAuthorizedError, with::user_not_authorized private def user_not_authorized flash[:alert] = '許可されていないユーザーです' redirect_to(request.refferrer || root_path) end end
しかしこのように記載していると開発用のエラーページが表示されないのでこの記載は開発には向いていません。
RSpecでステータスコードを確認する
ステータスコード確認のためにシステムスペック内にてdriven_by(:rack_test)
を使用します。この記述がないと、Capybara::NotSupportedByDriverError
が現れます。
before do driven_by(:rack_test) end
以下はエラーページが出力されていることを確認するテストの書き方です。
expect(page).to have_http_status(403)