【Ruby on Rails: cancancan】自分のレコードのみ操作できるようにする
cancancanで権限設定をしようとしたとき、つまづいた部分があったので忘備録として今回の記事を書きます。
だいたいの使い方は、下記の公式リファレンスをみたり、「cancancan how to use」などで検索すると出てきます
ただ今回、
「自分のプロフィールだけを編集できるようにしたい(他人のは編集不可)」、
「自分に所属するお店だけを編集できるようにしたい(他人のお店は編集不可)」、
「自分に所属するお店に所属する商品だけを編集できるようにしたい(他人のお店の商品は編集不可)」
というときに、どうやって設定しようか?というところでつまづきました。
table構成は以下の通り
user table: id, role, profile
shop table: id, name, user_id
product table: id, name, shop_id
userが複数のshopを持てるようにしてます。
shopも複数のproductを持てるようにしてます。
(User modelは、has_many :shops,
Shop modelは、 belongs_to :user, has_many:products,
Product modelは、belongs_to:shop)
roleはショップ(shop)と顧客(customer)があり、
shopのroleを持つユーザのみshop の処理全て(show, edit, updateなど)と、
productの処理全て(show, edit, updateなど)を実行可能。
customerはshopのindexとproductのshow&indexのみ実行可能にしたい。
そのときの
ability.rb は下記のように書いてうまくいきました!
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.newif user.new_record?
customer_user(user)
elsif user.shop?
shop_user(user)
endend
def shop_user(user)
can :manage, User, id: user.id
can :manage, Shop, user_id: user.idcan:manage, Product, shop: {user_id:user.id}
enddef customer_user(user)
can [:show, :edit, :update, :new, :create], User, id: user.idcan :index, Shop
can [:index, :show], Product
end
end
shop userは、
can :manage, User, id: user.id
で、id == user.id つまり自分のuserレコードのときだけ操作可能
can :manage, Shop, user_id: user.id
で、user_id == user.id つまり自分のshopレコードのときだけ操作可能
can:manage, Product, shop: {user_id:user.id}
で、親レコード(shop)のuser_id == user.id のレコードのときだけ操作可能
customer userは、
can [:show, :edit, :update, :new, :create], User, id: user.id
で、id == user.id つまり自分のuserレコードのときだけ操作可能(index以外)
can :index, Shop
で、つまりshopのindexのときだけ操作可能
can [:index, :show], Product
で、つまりproductのindexとshowのときだけ操作可能
usersとshopsとproductsのコントローラに
load_and_authorize_resourceを 記述するのを忘れないように注意です!
※load_resourseやauthorize_resourceだと、なぜかうまくいかない時があります。
これで結構色々なパターンの権限管理ができると思います!
下記、一歩進んだ解説
cancancanが内部でどんな処理をしているのか?
Shop, user_id: user.idや、Product, shop: {user_id:user.id}をみて、お気づきの方もいるかと思いますが、、、、
Shop, user_id: user.idなら、Shop.where(user_id: user.id)で、条件に合ったアクティブレコードを取ってきている。
Product, shop: {user_id:user.id}なら、Product.joins(:shops).where(shop: {user_id:user.id})で、条件に合ったアクティブレコードを取ってきている。
cancancanのソースコードはとても勉強になりそうなので、今後解析していければと思います。
おやすみなさいぃぃぃぃ