tapitapi’s blog

1日1杯タピオカ!エンジニア

【Ruby on Rails: cancancan】自分のレコードのみ操作できるようにする

cancancanで権限設定をしようとしたとき、つまづいた部分があったので忘備録として今回の記事を書きます。

 

だいたいの使い方は、下記の公式リファレンスをみたり、「cancancan how to use」などで検索すると出てきます

github.com

 

ただ今回、

「自分のプロフィールだけを編集できるようにしたい(他人のは編集不可)」、

「自分に所属するお店だけを編集できるようにしたい(他人のお店は編集不可)」、

「自分に所属するお店に所属する商品だけを編集できるようにしたい(他人のお店の商品は編集不可)」

というときに、どうやって設定しようか?というところでつまづきました。

 

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.new

if user.new_record?
 customer_user(user)
elsif user.shop?
 shop_user(user)
end

end

 

def shop_user(user)
can :manage, User, id: user.id
can :manage, Shop, user_id: user.id

can:manage, Product, shop: {user_id:user.id}
end

def customer_user(user)
can [:show, :edit, :update, :new, :create], User, id: user.id

can :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のソースコードはとても勉強になりそうなので、今後解析していければと思います。

 

 

おやすみなさいぃぃぃぃ