What makes these problems challenging is that there are multiple reasonable approaches to the issues. Here are options, none better than the other, for dealing with inheriting permissions from a more 'powerful' resource/zone.
For Resources:
- Write it into the policy 1a. Check a property of the parent resource 1b. Check for a grant on the parent resource 1c. Delegate to a policy for the parent resource
- Write the inheritance rules into the Resource Resolver such that it returns a token for the resource as well as the parent resource
Note that writing it into the policy allows a different permission to determine the inheritance, but that mismatch is not necessarily good.
For Zones, the options are essentially the same:
- Check for a grant in a broader zone by name in the policy
- Build the inheritance rules into the ResourceResolver such that the token is checked in multiple zones
As an example:
Say that a Listing belongs to a Category. There is one specific zone, "umich.edu" and a system zone. Content can only be created in the "umich.edu" zone, as "system" serves only as a way to grant global permissions. Suppose some complex rules for whether a user may delete a Listing, where any one of these conditions would permit it:
- The user created/owns the Listing
- The user has been granted the "manage" permission on the Listing's Category
- The user has been granted the "admin" role in the "umich.edu" zone
- The user has been granted the "admin" role in the "system" zone
There are many combinations of the above options that could meet this particular scenario. Only rule number 2 would require specialization in the policy (by direct check or delegation), because it is not the same permission as being checked at the Listing level. The other aspects could be implemented within the rules of Resource resolution.
For now, my position is that these are design decisions for each project, but that that direct inheritance should be implemented in the Resource Resolver to avoid incidentally different implementations across policies. However, a small, standalone policy to implement the sub-rule would be an acceptable alternative, and may be preferable in some cases.
class ListingPolicy
def initialize(authority: nil)
@authority = authority
end
def destroy?
return true if can_delete_own? && user == listing.owner
return true if authority.permitted?(user, :destroy, listing)
false
end
private
def can_delete_own?
Settings.delete_own
end
def authority
@authority ||= Services.permit_authority
end
end