🥫

【Rails】CanCanCanで現在のユーザに紐づいているレコードのみアクセスOKにしたときcreateが権限なしになる

2021/08/06に公開
1

概要

Rails で、ユーザーへの権限付与に便利な CanCanCan という gem を使っていて、
現在のユーザーに紐づいているレコードだけアクセスOKという権限にしていたところ、createができなくなった。
原因は、新規レコードなのでuser_idがnilになっていたことです。

結論

can %i[read update], Article, user_id: user.id
can :create, Article do |article|
  article.new_record?
end

より簡潔な書き方

rubocop に注意されたので、そちらに対応した書き方も記載しておきます

can %i[read update], Article, user_id: user.id
can :create, &:new_record? # 変更

試行錯誤の履歴

エラーが出た時の設定

can %i[read update create], Article, user_id: user.id

現在のユーザーに紐づいているレコードだけアクセスOKという権限にしていたところ、createができなくなった。
原因は、新規レコードなのでuser_idがnilになっていたことです。

上記理由で動きませんでした。

条件を動きそうなものに置き換えた

can %i[read update create], Article do |article|
  article.new_record? || article.user_id == user.id
end

にしたところ、indexにアクセスした際に

The accessible_by call cannot be used with a block 'can' definition.The SQL cannot be determined for :read Article...

となり失敗。

これは、index(設定では read)ではArticle do ~ endの条件が使えないことが理由のようです

参考:https://stackoverflow.com/questions/26904542/cancan-and-using-has-many-relationships-for-create

解決法(再掲)

can %i[read update], Article, user_id: user.id
can :create, Article do |article|
  article.new_record?
end

user_id が空でも OK なのは新規レコード作成の場合だけだったので、create とその他の設定を分けることで解決しました。

Discussion

MasahiroMasahiro

以下の実装ですが、

can :create, Article do |article|
  article.new_record?
end

RuboCopの警告が出ない正しい実装は以下ではないでしょうか?

can :create, Article, &:article.new_record?