Open marekdlugos opened 5 years ago
Would it help to just use the wiki_user_role(role)
method you defined on User, in your policies?
Like @jrochkind said, you should check for specific role inside can
block, because those are no longer static roles
Many to many relationships with this are tough, and by default was extremely non-DRY.
In my case, my User model has rights to perform actions on other users, projects, etc. (about 12 different classes), with a bunch of conditional cases. So, extracting it to a clean pattern was challenging.
So, what i did was create a Concern that I included into the AccessGranted::Role
a class that helps me make things easier to query. It gives me nice methods for own_record?(user) etc.
I thought about forking and letting you pass a helper method, but that seemed like overkill. It seems to be working now, but I thought I'd share what I did in case future version want to make it all easier.
Here's the important part/example of the mixin. No point in pasting 100s of lines.
module PolicyBase
extend ActiveSupport::Concern
def user_manager
@user_manager ||= ::UserAccess.new(user: user)
end
def admin_role?(role)
admin_roles.include?(role)
end
def admin_roles
%w[admin owner]
end
# SNIP about 200 lines of code here
end
My UserAccess class has a single method that wraps other "relational check" methods using a case statement to find the right way to extract the relational roles, so I can make the language in the "can" statement super simple.
# This is a method in UserAccess
# Checks the role of the current_user for another object
# Returns a nil if the role_for cannot be found
def role_for(klass_instance)
case klass_instance
when Company
company_role_for(klass_instance)
when Project
project_role_for(klass_instance)
when Team
team_role_for(klass_instance)
when Accessor
project_role_for(klass_instance.project)
when Relationship
company_role_for(klass_instance.company)
when User
user_role_for(klass_instance)
else
nil
end
Then, I mixed the PolicyBase into the class, as I said.. and then added it to the top of the policy file.
class AccessGranted::Role
include PolicyBase
end
class UserRights
include AccessGranted::Policy
def configure
role :admin, proc { |user| user.admin? } do
can :manage, Accessor # Access to a project directly
can :manage, Company # owner or rights
can :manage, Invite # their invites, only for ones that they have rights to
can :manage, Project # can we manage this projects?
can :manage, Relationship # Access in a company
can :manage, User # the user themself
# SNIP and others
end
role :manager, proc { |user| user.manager? } do
can :manage, Accessor do |accessor, user|
own_accessor?(accessor) || manageable_role?(user_manager.role_for(accessor))
end
end
# SNIP a bunch of lines here
end
So you can see that the can
statement super clean, and readable/manageable by a simple label (reader, user, manager, admin, owner). So, doing all this DRY'd up the thing pretty nicely.
It's a great gem and does really simplify doing this by hand. Thanks for the work! Please let me know if there will be major unintended consequences to what I did. :-)
I'm pretty new to Rails, however, I work on the app where I have this trio:
users, wikis, wiki_users
. Many users can be assigned to many wikis and vice versa.I struggle with connecting my
access_policy.rb
file with Rails. How could I let the gem know that it should look for user, assigned to a specific wiki with a specific role on that specific wiki?My project_users table contains columns
id
(of the relationship),user_id
,project_id
, androle
. Myaccess_policy.rb
file looks like this so far, with some effort to make it work inrole :reader
block. However, it all feels just like a workaround and I am wondering whether this scenario can't be handeled in easier manner?