Implement Policy#has_ability?
Problem
Currently, there's no way of checking policy's ability.
Proposed solution
Similar to Policy#has_policy?
implement Policy#has_ability?
to check if the an ability is available on this policy and its delegations.
user = User.find_by(username: 'john')
alert = AlertManagement::Alert.first
policy = AlertManagement::AlertPolicy.new(user, alert)
# Direct access
policy.has_ability?(:read_alert_management_alert) # => true
policy.has_ability?(:foo_bar) # => false
# Delegate access off by default
policy.has_ability?(:read_project) # => false
policy.has_ability?(:read_project, delegate: true) # => true
# OR
# Delegate access on by default
policy.has_ability?(:read_project) # => true
policy.has_ability?(:read_project, direct: true) # => false
Proposed implementation
Taken from gitlab-org/gitlab!94135 (comment 1064716469).
def has_ability?(ability, delegate: false)
# NilPolicy has no abilities. Ignore it.
return true if is_a?(DeclarativePolicy::NilPolicy)
# Search in current policy first
return true if self.class.ability_map.map.key?(ability)
return false unless delegate
# Search recursively in all delegations otherwise.
# This is potentially slow.
# Stolen from:
# https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/blob/d691e/lib/declarative_policy/base.rb#L360-369
self.class.delegations.any? do |_, block|
new_subject = instance_eval(&block)
new_policy = policy_for(new_subject)
new_policy.has_ability?(ability, delegate: delegate)
end
end
Open question
- How do we deal with
NilPolicy
? - What the performance impact of this lookup?
- Especially with delegations.
- Is the lookup cacheable?
- Delegations are dynamic and impossible to cache.
- How do we handle polymorphism, both at the root policy level and for delegates?
Discussion
The following discussion from gitlab-org/gitlab!94135 (merged) should be addressed:
-
@alexkalderimis started a discussion: These look like good candidates for upstreaming into the library.
Edited by Alex Kalderimis