hi christian,
yes
>if< you don't have a well defined set of such resources in an application,
you can still use:
public class ObjectResourceIdentifier implements ResourceIdentifier
{
private final String identifier;
public ObjectResourceIdentifier(Object o)
{
this.identifier = calculateIdentifier(o);
}
//...
@Override
public String toString()
{
return this.identifier;
}
}
or we can think about #getResource (instead of using #toString).
>but< i would only suggest it as a last resort. instead i would define such
resource-types explicitly for the application (but it's still possible to
do it the generic way).
(i would prefer ResourceDescriptor - but that's just a detail)
regards,
gerhard
2012/4/22 Christian Kaltepoth <[email protected]>
> Hey Gerhard,
>
> sorry, I missed your mail because we wrote to the list nearly at the
> same time. :)
>
> I like your idea of a more type-safe way to specify the operation. I
> think using an enum with an marker interface will work out fine. This
> is much nicer then having a String to identify the operation. Perhaps
> we could provide a default implementation for Operation so that people
> can use this one for simple cases. This would simplify it for users
> who want to get started quickly.
>
> But what about the resource? Do you want to use an interface here too?
>
> Christian
>
>
> 2012/4/22 Gerhard Petracek <[email protected]>:
> > 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
> >>
>
>
>
> --
> Christian Kaltepoth
> Blog: http://chkal.blogspot.com/
> Twitter: http://twitter.com/chkal
>