これは何?
【初心者向け】RailsでAPI開発 Part.2の続きです。
今回は認可編になります。
概要
▼ 仕組み
前回認証をやりましたが、それは下記の2番目にあたる箇所です。
実際にクライアントからリクエストが来て複雑なアプリケーションを作っていくには下記のような順番で判別します。
- ヘッダーにトークンが含まれてるかどうか
- トークンが正しいものかどうか <- 認証
- 認証したユーザーがアクセスできるリソースかどうか <- 認可
▼ 使用するもの
- Pundit
手順
① Punditの準備
まず、PunditをGemに追加してインストールしてください。
bundle exec rails g pundit:install
② ApplicationControllerの準備
ここで、ヘッダーにトークンが含まれているかどうかの判定や、
含まれている場合に認証できるのかどうかの判定などを行います。
認可については、アクセス不可の場合の記述をこちらで行います。
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
③ 認可の設計
認証を突破したユーザーの中で、
ユーザー編集や削除は自分だけ(認証したユーザー自身)がアクセスできるように認可を設計します。
つまり、
index
やshow
アクションは認証を突破した人誰もがアクセス可能ですが、
update
やdestroy
アクションは認証を突破した人の中でも、
アクセスするリソースに入れる権限を持つ自分自身だけにPunditで制御します。
④ UserPolicyの作成
class UserPolicy < ApplicationPolicy
def show?
true
end
def update?
record == user
end
def destroy?
record == user
end
end
show
メソッドは誰でもアクセスできるようにしてますが、
update
とdestroy
は認証ユーザーと合致しない限りはアクセスできないようにしてます。
⑤ Policyの呼び出し
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_user
でauthorize @user
とすれば、
アクセスしようとしているリソースのユーザーと認証ユーザーが合致しているかの判断ができます。
⑥ 確認
show
メソッド(認可なし)
- トークンなしでアクセス
2.No Header
エラー - トークンあるけど、存在しないトークン
3.No Authentication
エラー - 存在するトークンだけど、アクセスするリソースのユーザーが保有しないトークン
4. 成功 - 存在するトークンかつ、アクセスするリソースのユーザーが保有するトークン
5. 成功
show
メソッドの場合は、認可は全て許可しているので認証ユーザーであれば値を返す。
destroy
メソッド(認可あり)
- トークンなしでアクセス
2.No Header
エラー - トークンあるけど、存在しないトークン
3.No Authentication
エラー - 存在するトークンだけど、アクセスするリソースのユーザーが保有しないトークン
4.No Authorization
エラー - 存在するトークンかつ、アクセスするリソースのユーザーが保有するトークン
5. 成功
destroy
メソッドの場合は、認証ユーザーとリソースのユーザーが同一でないとアクセスできないので、
3の場合はNo Authorization
エラーが発生していればOK。
まとめ
今回はPunditを使って認可を設定してみました。
僕は以前Gemを使わず自前で認可を設計していたことがあるのですが、
Controllerの中身が結構ごちゃごちゃになってしまうので困っていました。
Punditを使うとごちゃごちゃにならずPolicyファイルに綺麗にまとめられるため非常に利便性を感じました。
これで、認証・認可周りはかなりスッキリしたのではないかなと思います。
次回は外部からの攻撃など、セキュリティ周りをしっかりとみていきたいと思います。