I prefer the method signature Shane suggested in his initial mail. Providing a single method with an object parameter makes most sense to me:
Identity.hasPermission(Object resource, String operation) This way the developer is completely free to choose how to identify the resource. I think in most cases the object itself will be used. And the JSF example Shane showed is a very good example for this. Of cause people may need other ways to refer to resources like in the ResourceIdentifier concept Marek showed. But actually I see something like this more as an usage pattern and I don't think it belongs directly into the API. Perhaps we could provide something like ResourceIdentifier as an utility class that people can use if they want to refer to resources this way. But the API of Identity should be as simple as possible. And the object parameter will allow people to use any object they want. Even something like ResourceIdentifier. Christian 2012/4/19 Marek Posolda <[email protected]>: > On 19.4.2012 13:56, Shane Bryzak wrote: >> >> On 19/04/12 20:15, Marek Posolda wrote: >>> >>> On 19.4.2012 00:56, Shane Bryzak wrote: >>>> >>>> On 19/04/12 08:03, Marek Posolda wrote: >>>>> >>>>> Hi all, >>>>> >>>>> I am Marek Posolda and i am working on GateIn portal project together >>>>> wth Bolek Dawidowicz. Some of my notes inline: >>>>> >>>>> On 18.4.2012 00:52, Shane Bryzak wrote: >>>>>> >>>>>> I'd like to kick off a discussion around the Authorization API, >>>>>> specifically object permissions. This API is used to determine whether >>>>>> the >>>>>> currently authenticated user has the necessary privileges to perform an >>>>>> operation on a particular domain object. I'll start by outlining my >>>>>> proposal covering the simple use cases of this API and we can then >>>>>> proceed >>>>>> from there. >>>>>> >>>>>> First of all, the developer needs a gateway into the permission API so >>>>>> that they can perform permission checks within their own application. >>>>>> This >>>>>> is provided by the hasPermission() method: >>>>>> >>>>>> >>>>>> Identity.hasPermission(Object resource, String operation) >>>>> >>>>> How about having additional method like: >>>>> >>>>> >>>>> Identity.hasPermission(String resourceType, Serializable resourceId, >>>>> String operation); >>>>> >>>>> This have one advantage that you don't need to obtain instance of >>>>> Object from DB when you have only ID of this resource. Use case example >>>>> can >>>>> be: >>>>> >>>>> public Customer loadCustomer(Long customerId) >>>>> { >>>>> if (!identity.hasPermission("org.myorganization.Customer", customerId, >>>>> "READ") >>>>> { >>>>> throw new AuthorizationException("Insufficient privileges to load >>>>> this customer"); >>>>> } >>>>> >>>>> // snip code >>>>> } >>>>> >>>> >>>> This is a good use case, I'm wondering though if we could still wrap >>>> this up as a resource object to keep the API clean. Perhaps something like >>>> this: >>>> >>>> public Customer loadCustomer(Long customerId) >>>> { >>>> if (!identity.hasPermission(new >>>> ResourceIdentifier("org.myorganization.Customer", customerId), "READ") >>>> { >>>> throw new AuthorizationException("Insufficient privileges to load >>>> this customer"); >>>> } >>>> >>>> // snip code >>>> } >>> >>> Yes, that should work too. Question is if we still have two methods then >>> or only single method with signature: >>> Identity.hasPermission(ResourceIdentifier identifier, String action) throws >>> AuthorizationException; ? >>> >>> Seems that one method may be sufficient as long as you can always obtain >>> ResourceIdentifier for particular object when you have Type and ID of this >>> object. So your edit method can then looks like: >>> >>> @Inject Identity identity; >>> >>> public void editCustomer(Customer customer) >>> { >>> if (!identity.hasPermission(new >>> ResourceIdentifier(customer.getClass(), customer.getId()), "EDIT")) >>> { >>> throw new AuthorizationException("Insufficient privileges to edit >>> this customer"); >>> } >>> >>> // snip code >>> } >>> >>> >>> Marek >> >> >> >> Except that this makes it harder to consume - for example, think of a JSF >> page with a table of records. For each row in the table, you may wish to >> display action buttons etc depending on the privileges that the user has for >> that row. >> >> <h:dataTable value="#{customerSearch.searchResults}" var="customer"> >> <h:column> >> <h:outputText value="#{customer.name}"/> >> </h:column> >> <h:column> >> <ui:fragment rendered="#{identity.hasPermission(customer, 'EDIT')}"> >> <!-- action buttons go here --> >> </ui:fragment> >> </h:column> >> </h:dataTable> >> >> This wouldn't be possibly if Identity.hasPermission() required a >> ResourceIdentifier parameter. I think an Object parameter is going to be >> the most versatile for our requirements, and we can just have special >> handling when the resource type is a ResourceIdentifier. > > True. So having two methods: > - Identity.hasPermission(Object resource, String action) throws > AuthorizationException; > - Identity.hasPermission(ResourceIdentifier resourceIdentifier, String > action) throws AuthorizationException; > > should work better then. > > >> >>> >>> >>>> >>>> >>>> >>>>>> >>>>>> >>>>>> A permission has three aspects; 1) The application resource for which >>>>>> the permission is granted, 2) The operation that has been granted for >>>>>> that >>>>>> resource, and 3) The recipient of the permission, which may be either a >>>>>> User, Group or Role. >>>>>> >>>>>> For example, if we wish to check whether the user has permission to >>>>>> edit a Customer instance the code might look like this: >>>>>> >>>>>> @Inject Identity identity; >>>>>> >>>>>> public void editCustomer(Customer customer) >>>>>> { >>>>>> if (!identity.hasPermission(customer, "EDIT")) >>>>>> { >>>>>> throw new AuthorizationException("Insufficient privileges to >>>>>> edit this customer"); >>>>>> } >>>>>> >>>>>> // snip code >>>>>> } >>>>>> >>>>>> We could potentially also do some clever stuff with method-level >>>>>> annotations here. Off the top of my head, something like this might >>>>>> work: >>>>>> >>>>>> @CheckPermissions >>>>>> public void editCustomer(@CheckPermission("EDIT") Customer customer) >>>>>> >>>>>> The @CheckPermissions annotation would be a security binding type with >>>>>> a matching authorizer that would scan the parameter list and perform any >>>>>> checks on parameters annotated with @CheckPermission. Anyway we can >>>>>> refine >>>>>> this idea in ongoing discussions. >>>>>> >>>>>> In the default implementation, the Identity.hasPermission() method >>>>>> essentially contains no code, instead it delegates the permission check >>>>>> to >>>>>> the PermissionMapper bean, an implementation-only bean which contains a >>>>>> list >>>>>> of PermissionResolver instances that are used to perform the permission >>>>>> check. >>>>>> >>>>>> public class DefaultIdentity implements Identity >>>>>> { >>>>>> // snip code >>>>>> >>>>>> public boolean hasPermission(Object resource, String operation) >>>>>> { >>>>>> return permissionMapper.resolvePermission(resource, operation); >>>>>> } >>>>>> } >>>>>> >>>>>> The PermissionMapper bean provides the resolvePermission() method, >>>>>> which basically iterates through the known PermissionResolvers, and if >>>>>> one >>>>>> of them returns true for the permission check, then a true result is >>>>>> returned. >>>>>> >>>>>> public class PermissionMapper >>>>>> { >>>>>> @Inject Instance<PermissionResolver> resolvers; >>>>>> >>>>>> public boolean resolvePermission(Object resource, String operation) >>>>>> { >>>>>> for (PermissionResolver resolver : resolvers) >>>>>> { >>>>>> if (resolver.hasPermission(resource, operation)) return >>>>>> true; >>>>>> } >>>>>> return false; >>>>>> } >>>>>> } >>>>>> >>>>>> We can do some clever stuff here, like caching which permission >>>>>> resolver returns a true result for a particular class of resource, and >>>>>> then >>>>>> always using that resolver for that class, etc. >>>>>> >>>>>> PermissionResolver is an API interface, the implementations of which >>>>>> do the actual work of checking the permission. >>>>>> >>>>>> public interface PermissionResolver >>>>>> { >>>>>> boolean hasPermission(Object resource, String operation); >>>>>> } >>>>> >>>>> I think that it might be useful if PermissionResolver can also deny >>>>> permissions? So it may look like: >>>>> >>>>> public interface PermissionResolver >>>>> { >>>>> PermissionType hasPermission(Object resource, String operation); >>>>> } >>>>> >>>>> where PermissionType can be: >>>>> >>>>> public enum PermissionType >>>>> { >>>>> PERMIT, DENY, NOT_APPLICABLE >>>>> } >>>>> >>>>> and PermissionMapper implementation can look like: >>>>> >>>>> public class PermissionMapper >>>>> { >>>>> @Inject Instance<PermissionResolver> resolvers; >>>>> >>>>> public boolean resolvePermission(Object resource, String operation) >>>>> { >>>>> for (PermissionResolver resolver : resolvers) >>>>> { >>>>> PermissionType permissionType = >>>>> resolver.hasPermission(resource, operation); >>>>> if (permissionType == PERMIT) >>>>> { >>>>> return true; >>>>> } >>>>> else if (permissionType == DENY) >>>>> { >>>>> return false; >>>>> } >>>>> } >>>>> >>>>> return false; >>>>> } >>>>> } >>>>> >>>>> And order of PermissionResolvers inside PermissionMapper can be ordered >>>>> according to some priorities. This design may be more flexible. For >>>>> example, >>>>> it will allow you to quickly or temporarily revoke all permissions for >>>>> particular user. >>>> >>>> >>>> This is also a good use case. We would need to iterate through all >>>> PermissionResolvers though and if any of them returned a DENY then our >>>> permission check would return a final result of false. >>>> >>>>> >>>>>> >>>>>> We would provide one PermissionResolver implementation out of the box >>>>>> with DeltaSpike; PersistentPermissionResolver would provide permission >>>>>> checks for ACL-style object permissions and provide a key piece of >>>>>> functionality required by a complete permission management API. >>>>>> Developers >>>>>> can easily provide their own PermissionResolver implementations with >>>>>> custom >>>>>> business logic by simply deploying a PermissionResolver bean in their own >>>>>> application. >>>>>> >>>>>> This pretty much covers the basics of the object permission API, at >>>>>> least on the consuming side. We can discuss this area first before >>>>>> moving >>>>>> onto the permission management API shortly. >>>>>> >>>>>> >>>>> >>>> >>>> >>> >> >> > -- Christian Kaltepoth Blog: http://chkal.blogspot.com/ Twitter: http://twitter.com/chkal
