hi christian, you can do the same (but type-safe) with my suggestion.
regards, gerhard 2012/4/22 Christian Kaltepoth <[email protected]> > 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 >
