Declarative authorization with permissons join table

Users have permission to manage articles for specific combinations of location and category.

For example, Dave is allowed to manage HR personnel for Paris. Paul may be allowed to run business articles for London.

Models and their associations are as follows:

  • user has many permissions
  • permission belongs to user, location and category
  • category contains many articles and permissions
  • layout has many articles and permissions
  • article relates to location and category

I can say:

has_permission_on :articles, :to => :manage do if_attribute :location => { :permissions => { :user_id => is {user.id} }, :category => { :permissions => { :user_id => is {user.id} } } end 

When do I call allow_to? (: delete) in an article entry (category ID 1893 and location ID 2939), the following queries are executed:

 SELECT `categories`.* FROM `categories` WHERE `categories`.`id` = 1893 LIMIT 1 SELECT `permissions`.* FROM `permissions` WHERE (`permissions`.category_id = 1893) SELECT `locations`.* FROM `locations` WHERE `locations`.`id` = 2939 LIMIT 1 SELECT `permissions`.* FROM `permissions` WHERE (`permissions`.location_id = 2939) 

What I need to run is really:

 SELECT `permissions`.* FROM `permissions` WHERE (`permissions`.category_id = 1893 AND `permissions`.location_id = 2939) 

Is there any way to achieve this?

Thanks in advance.


Update

So now I have an instance method in the article model:

 def permitted_user_ids Permission.select('user_id').where(:location_id => location_id, :category_id => category_id).map(&:user_id) end 

and my rule now:

 has_permission_on :articles, :to => :manage do if_attribute :permitted_user_ids => contains { user.id } end 

Now when do I call allow_to? (: delete) (or read / update / create / manage) in the entry of the article whose category identifier is 1893 and the location identifier is 2939, the following query is executed:

 SELECT user_id FROM `permissions` WHERE `permissions`.`category_id` = 1893 AND `permissions`.`location_id` = 2939 

... this is exactly what I want.

Except that the scope with_permissions_to behaves very weird.

 Article.with_permissions_to(:read) 

Now generated:

 SELECT `articles`.* FROM `articles` WHERE (`articles`.`id` = 9473) 

... where 9473 is the current user id!

I am very confused.

+4
source share
2 answers

You can use subclasses and manage permissions with cancan . You can have a top-class article, and then all kinds of articles can be subclasses, and in the subclasses you use cancan to manage your permissions.

0
source

This should be a comment, but I'm still at 45 :(

You say permission belongs to user , location and category . Thus, the permissions table would have the columns user_id, location_id, and category_id.

If so, why do you declare the belongs_to permission of the permission and location of the belongs_to category? Surely the relation should be has_many ?

You must use has_many in a model that does not have a foreign key (category and location) and belongs_to in the WHICH DOES model (permission).

Forgive me if all this is just a typo that you made, and what you really had in mind was true belongs to the user and has a location and has_many category.

0
source

All Articles