2
3

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 5 years have passed since last update.

【初心者向け】RailsでAPI開発 Part.3

Posted at

これは何?

【初心者向け】RailsでAPI開発 Part.2の続きです。

今回は認可編になります。

概要

▼ 仕組み

前回認証をやりましたが、それは下記の2番目にあたる箇所です。
実際にクライアントからリクエストが来て複雑なアプリケーションを作っていくには下記のような順番で判別します。

  1. ヘッダーにトークンが含まれてるかどうか
  2. トークンが正しいものかどうか <- 認証
  3. 認証したユーザーがアクセスできるリソースかどうか <- 認可

▼ 使用するもの

  • Pundit

手順

① Punditの準備

まず、PunditをGemに追加してインストールしてください。

terminal
bundle exec rails g pundit:install

② ApplicationControllerの準備

ここで、ヘッダーにトークンが含まれているかどうかの判定や、
含まれている場合に認証できるのかどうかの判定などを行います。

認可については、アクセス不可の場合の記述をこちらで行います。

app/controllers/application_controller.rb
include ActionController::HttpAuthentication::Token::ControllerMethods
include Pundit

rescue_from ActiveRecord::RecordNotFound, with: :no_record
rescue_from Pundit::NotAuthorizedError, with: :no_authorization

def authenticate
 if request.headers[:HTTP_AUTHORIZATION] != nil
  current_user || no_authentication
 else
  no_header
 end
end

def current_user
 authenticate_with_http_token do |token, options|
  @current_user = User.find_by(token: token)
 end
end

def no_header
 render json: { error: 'No Header' }, status: 400
end

def no_authentication
 render json: { error: 'No Authentication' }, status: 401
end

def no_authorization
 render json: { error: 'No Authorization' }, status: 403
end

def no_record
 render json: { error: 'No Record' }, status: 404
end

③ 認可の設計

認証を突破したユーザーの中で、
ユーザー編集や削除は自分だけ(認証したユーザー自身)がアクセスできるように認可を設計します。

つまり、
indexshowアクションは認証を突破した人誰もがアクセス可能ですが、
updatedestroyアクションは認証を突破した人の中でも、
アクセスするリソースに入れる権限を持つ自分自身だけにPunditで制御します。

④ UserPolicyの作成

app/policies/user_policy.rb
class UserPolicy < ApplicationPolicy
 def show?
  true
 end

 def update?
  record == user
 end

 def destroy?
  record == user
 end
end

showメソッドは誰でもアクセスできるようにしてますが、
updatedestroyは認証ユーザーと合致しない限りはアクセスできないようにしてます。

⑤ Policyの呼び出し

app/controllers/v1/user_controller.rb
before_action :authenticate, except: [:create]
before_action :set_user, only: [:show, :update, :destroy]

..

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

..

show``update``destroyを呼び出す、set_userauthorize @userとすれば、
アクセスしようとしているリソースのユーザーと認証ユーザーが合致しているかの判断ができます。

⑥ 確認

showメソッド(認可なし)

  1. トークンなしでアクセス
    2. No Headerエラー
  2. トークンあるけど、存在しないトークン
    3. No Authenticationエラー
  3. 存在するトークンだけど、アクセスするリソースのユーザーが保有しないトークン
    4. 成功
  4. 存在するトークンかつ、アクセスするリソースのユーザーが保有するトークン
    5. 成功

showメソッドの場合は、認可は全て許可しているので認証ユーザーであれば値を返す。

destroyメソッド(認可あり)

  1. トークンなしでアクセス
    2. No Headerエラー
  2. トークンあるけど、存在しないトークン
    3. No Authenticationエラー
  3. 存在するトークンだけど、アクセスするリソースのユーザーが保有しないトークン
    4. No Authorizationエラー
  4. 存在するトークンかつ、アクセスするリソースのユーザーが保有するトークン
    5. 成功

destroyメソッドの場合は、認証ユーザーとリソースのユーザーが同一でないとアクセスできないので、
3の場合はNo Authorizationエラーが発生していればOK。

まとめ

今回はPunditを使って認可を設定してみました。
僕は以前Gemを使わず自前で認可を設計していたことがあるのですが、
Controllerの中身が結構ごちゃごちゃになってしまうので困っていました。

Punditを使うとごちゃごちゃにならずPolicyファイルに綺麗にまとめられるため非常に利便性を感じました。

これで、認証・認可周りはかなりスッキリしたのではないかなと思います。

次回は外部からの攻撃など、セキュリティ周りをしっかりとみていきたいと思います。

2
3
1

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
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?