1

I have an admin section on my site with links to 30 something features. We want to have it so that each customer service reps will be given access to these pages on a very granular, individual level (i.e. Joe will be given access to page A and B, but not C; Mary is given access to A and C, but not B).

Is Pundit or CanCan made to work like that or would I be better off creating a Permission model that relates to a User with a boolean column for each admin feature? I was hoping to avoid that as I could see it getting out of hand quick.

Ryan Grush
  • 2,076
  • 3
  • 37
  • 64
  • Pundit gives access to a `current_user` object that includes a user id and all other attributes of the user. I don't see why you couldn't use that to define permissions on a user-by-user basis. – moveson Dec 23 '16 at 18:52
  • @moveson where would I store that user-by-user permission info though? Add ~30 additional columns to the User model and do something like `current_user.page_a_access?` – Ryan Grush Dec 23 '16 at 18:55
  • 1
    You could add one integer column to the User model and store them [bitwise](http://blog.millermedeiros.com/using-integers-to-store-multiple-boolean-values/). Here's an example on [SO](http://stackoverflow.com/questions/9643491/store-multiple-bit-values-in-a-single-table-column). – moveson Dec 23 '16 at 19:09
  • I'd avoid using `current_user` in a Pundit policy. The policies are already structured to receive the object and the user in the constructor, using additional helpers should not be necessary. – Marcus Ilgner Dec 23 '16 at 19:54
  • @moveson thanks for the bitwise suggestion but like I told coreyward the 32 boolean limitation is not going to work for me. – Ryan Grush Dec 27 '16 at 18:10

1 Answers1

2

Rather than creating a column for each feature, you can use a single integer column and utilize a bit mask to store N boolean values. There are ruby libraries that will do this for you, like Bitfields, if desired.

Another approach is to create a join table (habtm relationship) between admins and features (you would add features as a db-backed model if they aren't already), then granting permission is as easy as adding a row to the join table. Here's an example:

class User < ApplicationRecord
  has_and_belongs_to_many :features
end

class Feature < ApplicationRecord
  has_and_belongs_to_many :users
end

# authorization
def authorized?(current_user, requested_feature)
  current_user.features.where(id: requested_feature.id).exists?
end
coreyward
  • 77,547
  • 20
  • 137
  • 166
  • 1
    I was excited about the Bitfields suggestion but it looks like I'm limited to 32 booleans so that's not going to work unfortunately. The latter solution is what I had in mind originally but I'd like to avoid having to create a database migration every time we added a new page. I think I'm going to try a serialized hash instead. Thanks for your help though! – Ryan Grush Dec 27 '16 at 18:08