15
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Rails】authorizeとは?[Pundit] / 403エラー画面の作成有り

Last updated at Posted at 2022-02-19

authorizeとは

Rubyのgem Pundeitのコントローラの各アクションでauthorizeリソースオブジェクトである。

Punditとは「認可」の仕組みを提供するもの。
ユーザーによってページの表示の許可・拒否をしたり、表示情報の範囲を変えられるgem。

分かりやすいイメージで言うと

・未ログインの場合に、ログインが必要なページをリクエストすると、ログインページへ遷移
・ログイン済みの場合に、許可されていないページをリクエストすると、ルートURLへ遷移、などが挙げられる。

Punditcurrent_user(ログイン中のユーザー)メソッドを扱うので、sorcery gemなどが「認証」の仕組みありきで、認可は認証に依存している。

認証と認可の違い

認証(Authentication)

通信の相手が誰(何)であるかを確認することが「認証」。
純粋な認証には「リソース」やそれに対する「権限」という概念は無い。

いわゆる「証明書の確認」のようなもの。
私たちの身近な証明書で言うと、マイナンバーカードが該当する。

例えばマイナンバーカードを拝見して、この人は鈴木さんと言うことが確認できました。
鈴木さんというのは確認できても、何かが許されるといった話はない。

相手が誰なのか確認するだけの状態。

認可(Authorization)

とある特定の条件に対して、リソースアクセスの権限をあたえることが「認可」。
純粋な「認可」には、「誰」という考え方は無い。

いわゆる「電車の切符」や「映画チケットの発行」のようなもの。
チケットがあるからといって誰かの身元が明らかになる話とは関係ない。

電車の切符なら乗車の許可があるだけで、誰かというのは関係ない。

認可はそれを持っているだけで何か(リソースへのアクセス)が許可される。

切符があれば電車に乗れて、持っていなければ電車に乗れない。
つまり、ただの許可証の発行だけ。

ただ多くの場合、認証(誰か確認)できないと認可(許可)できないので、認可は認証に依存していると言える。

Punditの仕組み

Punditはコントローラの各アクションでauthorizeリソースオブジェクトを呼ぶと、**対象のリソースに対して権限があるかどうかを確認してくれる。**その設定をapp/policiesにあるポリシーファイルで細かく定義できる。

Punditの使用法

Gemfileにgem "pundit"と記入し、bundle installを実施。

Punditを適用したいコントローラの継承元でincludeを記入。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include Pundit
end

認可のルールを記述するファイルを作成するために、下記コマンドを入力。

$ rails g pudit:install

するとapp/policies/application_policy.rbというファイルが生成される。

app/policies/application_policy.rb
class ApplicationPolicy
  attr_reader :user, :record #読み取りの属性を定義している

  def initialize(user, record)
    @user = user
    @record = record
  end

  def index?
    false
  end
end

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

initializeで定義されるuserは、デフォルトでcurrent_userが引数に割り当てられるようになっている。

第二引数のrecordは認可をチェックしたいモデルオブジェクト。対応するモデルのインスタンスを手動で割り当て。

これを利用することで、アクセスしているユーザーオブジェクトと、対象のリソースオブジェクトを知ることができる。

認可ファイルを作成

作り方は、モデル名_policy.rbでファイル作成し、モデル名Policyでクラス名定義。

今回は例としてPostという名前のモデルに、app/policies/post_policy.rbを作成。

app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy

  def create?
    # アクセスユーザー権限がadminまたはeditorのときのみ認可
    user.admin? || user.editor?
  end

end

def アクション名? で認可ルール(policy)を記述する。この返り値によって、認可するか否かを判断している。

上記のdef create?falseが返ってきた場合、
PostControllercreateアクション拒否されて、Pundit::NotAuthorizedErrorが発生する。

コントローラーでの呼び出す

app/controller/posts_controller.rb
def create
  @tag = Tag.find(params[:id])
  authorize(Tag)
     # インスタンスモデルを利用しない(policy側ではuserの情報のみ扱う)場合に限り
  # authorize Post という記述が可能。
end

authorize(Tag)
authorizeメソッドで、先程のpolicyファイルに記述されたdef create?が処理される。また引数には対応するモデルオブジェクトを入れる。

Pundit用静的403エラー画面の作成

各認可がfalseだった場合、Pundit::NotAuthorizedErrorが発生してしまうので、エラーを拾って403を返す仕組みを作っておく必要がある。

共通レイアウトは表示させずに、自作のエラー画面public/403.htmlを表示する方法

エラー画面のpublic/403.htmlを作成し、下記を記入。

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>

なお上記のエラー画面は、本番環境では表示されるが、開発環境ではデフォルトで非表示となっている。開発環境で403エラー画面を表示して確認するためには、config/environments/development.rbconfig.consider_all_requests_localfalseにして、サーバを再起動することで可能になる。

config/environments/development.rb
# エラー画面404と403をデバッグ用か本番用か切り替えられる
# config.consider_all_requests_local = true
    config.consider_all_requests_local = false

確認ができたらtrueに戻しておく。

本番環境で403エラー画面を表示させる

config/application.rbに設定を記述して、サーバを再起動させればOK。

config/application.rb
#例外を403HTTPステータスにする。これを付けないと500になる。
# :forbiddenというシンボルはステータスコード403と定義されている。
config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden

Pundit::NotAuthorizedErrorを補足させ、:forbiddenを指定することで、HTTPステータスコードが403になる。
このシンボルはL661で定義されている。

共通レイアウトも表示させる場合

共通レイアウトも表示させる場合は、ApplicationControllerrescue処理を記載して、app/view以下のテンプレートファイルを用意してrenderさせる。

class ApplicationController < ActionController::Base
  include Pundit

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

  private

  def user_not_authorized
    render file: Rails.root.join('public/403.html'), status: :forbidden
  end
end

参考記事

[Rails]gem Punditによる権限管理 (認可)
Pundit + Railsで認可の仕組みをシンプルに作る
Punditをなるべくやさしく解説する
よくわかる認証と認可

15
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?