Reputation: 9385
I'm wondering the best/simplest way to give a user "super_admin" access using the Pundit gem -- or, what's the simplest way to give a user access to all controller actions across the site?
I realize I can edit the policy file for each controller, then add something like
def delete?
user.is_super_admin?
end
...to each and every action, in each and every controller. But is there a single place where I could define this?
I've taken a look at the application policy, which all of my other controller policies inherit from, but I believe these actions are all overridden in my controllers anyway so I don't think I could define it there.
I'm sure others have needed to implement this feature? What's the simplest way? Thanks!
Upvotes: 3
Views: 1069
Reputation: 35370
For the (in reality) minimal annoyance that comes from retyping a method name a couple dozen times through the life of the project, I think the maybe not-so-obvious benefit is worth it.
First, you could save a few keystrokes by defining a super?
method on your ApplicationPolicy
def super?
user.is_super_admin?
end
And then your delete?
and other methods could be simplified just a bit.
def delete?
super? || user.id == record.user_id # super admin or owner of the resource
end
Pundit policies are simple enough. If I have never worked in your code before, I can look at the above method and know exactly who can perform the deletion.
Sure, it might save you a couple keystrokes to have some meta-programmed functionality to call super?
automagically, with delete?
appearing as
def delete?
user.id == record.user_id # super admin or owner of the resource
end
but now the clarity is lost. Also, what if there is a method down the road you do not want the super admin to have access to? Reworking your already more complicated solution would make things even more prone to bugs and more difficult to understand. All so ~10 keystrokes per method could be saved.
Opinions on code readability aside, here is one implementation that does what you seem to be after. You might also look into hooking the method calls instead of removing the ?
from the method names as I've done below to make sure method_missing
picks them up.
#
# Classes
#
User = Struct.new(:id, :name)
Foo = Struct.new(:id, :title, :user_id)
class SuperUser < User; end
ApplicationPolicy = Struct.new(:user, :record) do
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def method_missing(name)
raise NoMethodError, "No method `#{name}` for #{self}" if (name =~ /\?$/).nil?
user.is_a?(SuperUser) || send(name.to_s.sub(/\?$/, ""))
end
end
class FooPolicy < ApplicationPolicy
private
def edit
user.id == record.user_id
end
end
#
# Fixtures
#
user = User.new(1, "Deefour")
foo = Foo.new(1, "A Sample Foo", 1)
super_user = SuperUser.new(2, "FireDragon")
#
# Examples
#
FooPolicy.new(user, foo).edit? #=> true
FooPolicy.new(super_user, foo).edit? #=> true
Upvotes: 2