2

My question is about defining cancancan ability in this following context.

I have a common model with many to many relationship between User and Company entities

class User < ApplicationRecord
    has_many :company_users, dependent: :destroy
    has_many :companies, through: :company_users 
    enum role:[:user, :admin, :superadmin]
end

class Company < ApplicationRecord
    has_many :company_users, dependent: :destroy
    has_many :users, through: :company_users
end

class CompanyUser < ApplicationRecord
    belongs_to :company
    belongs_to :user
end

I want now to define cancan ability in order to authorized current user to manage all the users belonging to the same companies as him.

I have no problem with other many to many schemas (eg : many to many between Device and Company) like this

can :manage, Device, :companies => {:users => {:id => user.id}}

is working fine !

but

can :manage, User, :companies => {:users => {:id => user.id}}

let me see, of course, only the current user because it is on the same users table.

How can I manage easily this ability between users belonging to a same company preserving the many to many relationship ?

Thanks for your help

P.Bra
  • 264
  • 1
  • 12

2 Answers2

5

You can solve the issue using scopes: https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities-with-Blocks#block-conditions-with-scopes

and defining:

can :manage, User, User.joins(companies: :users).where(companies: { users: { id: user.id } })

please look at the full gist: https://gist.github.com/coorasse/f1174b618651fcee8c65525d38b36120

coorasse
  • 5,278
  • 1
  • 34
  • 45
  • Thanks @coorasse for your support, and example. However when applying this on my app, I got an error message: "The can? and cannot? call cannot be used with a raw sql 'can' definition. The checking code cannot be determined for :index User(id: integer, email: string, encrypted_password: string, etc..". I'm using cancan in conjonction with railsadmin, may be this explains the error ? I was trying also with scope: scope :owned_users, ->(user_id) {joins(companies: :users).where(companies: { users: { id: user_id } })} and can :manage, User, User.owned_users(user.id), but I got the same error. – P.Bra May 17 '18 at 22:57
  • I also tried with scope as above and "can :manage, User, id: User.owned_users(user.id).pluck(:id)" in the ability.rb, but I got only one record: user.id as my first trial... – P.Bra May 17 '18 at 23:12
  • I finally solve myself by using scope features upon your help. Thanks @coorasse – P.Bra May 18 '18 at 00:25
  • the reason is that you would need to define also the block part of it for single elements. – coorasse May 19 '18 at 15:40
2

Upon the @coorasse recommandations, I finally solve my problem by using 2 scopes as following in user.rb

scope :company_list, -> (user_id) {CompanyUser.where(:user_id => user_id).pluck(:company_id)}
scope :owned_users,  -> (user_id) {CompanyUser.where(:company_id => company_list(user_id)).pluck(:user_id).uniq}

and in the ability.rb

can :manage, User, id: User.owned_users(user.id)

This is working fine.

P.Bra
  • 264
  • 1
  • 12