4
1

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.

【Ruby on Rails】CanCanCan の authorize_resource はレコードの権限まではチェックしてくれない

Posted at

CanCanCan

Rails でいい感じにアクセス権を確認したり、作成・更新できるかを確認したり、そんなことができる gem です。

以下のような記事が参考になると思います。

この gem を導入すると、controller で権限認証が楽にできます。

authorize!

まず、本題のauthorize_resourceの前に、元となるauthorize!を見てみます。

#books_controller.rb
def show
  @book = Book.find(params[:id])
  authorize! :read, @book
end

ability.rb でよしなに設定している場合に、これで「現在のユーザはこの @book の情報にアクセスできるか?」を判定できます。

しかし、アクションごとに authorize! を書くのはダルいので普通は次に示すようなやり方でチェックすると思います。

authorize_resource

今回フォーカスする authorize_resourceです。
コントローラに

class UsersController < ApplicationController
  authorize_resource
  ...
end

こんな感じで書いておくと、各アクションでauthorize!をチェックしてくれます。

しかし、ちょっと勘違いしてました。

次のように ability.rb を設定しているときを考えます。

# ability.rb
class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new
    
    can :read, Book
    can :manage, Book, user_id: user.id
    ...
  end
end

Book の一覧は見れるけど、自分の所有物の Book しか編集できないよ、的な設定ですね。

このとき、

#books_controller.rb
def update
  # ここで取得するのは current_user 以外のユーザが所有している @book とする
  @book = Book.find(id) 
  @book.title = "foo"
  @book.save
end

これを阻止してくれるかと思っていたのですが、そうではないようです。

authorize_resource でやってくれるのはあくまでモデルに対する権限チェックなので

authorize! :update, Book

をやっているのと同じことになります。
ここで、

can :manage, Book, user_id: user.id

と設定しているので、Bookモデルに対しての:update権限はアリと判定され、CanCan::AccessDenied は発生しません。

当然ながら

authorize! :update, @book

は権限なしで例外になります。
※ここでは@bookは自分の所有物ではない前提としているので

これを自動で判定したければ次の load_and_authorize_resource を使うのが良いです。

load_and_authorize_resource

今回触れてないですが、よしなにレコードを検索してインスタンス変数にセットしてくれる load_resource というヤツもあります。

このload_and_authorize_resource はそのload_resourceauthorize_resourceの合せ技として使えるようです。

class BooksContoller < ApplicationController
  load_and_authorize_resource

  ...
  def update
    # 暗黙にレコードを検索し、権限も判定してくれる
    # @book = Book.find(params[:id])
    # authorize! :update, @book
    # これで @book が他のユーザの所有物であれば、例外が発生する
  end

これでインスタンス変数に勝手に値をセットしてくれるし、その権限も見てくれるし、ということでかなり楽しながら安全も担保できます。

Rails のレールに沿った作り方をしていれば上手に使えると思います。

まとめ

  • authorize_resourceはモデルへの権限をチェックするものなので、レコードへの権限は見てくれない
  • レコードの権限まで見たいならload_and_authorize_resourceを使うと便利
4
1
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
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?