deltaspike-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Gerhard Petracek <gerhard.petra...@gmail.com>
Subject Re: [DISCUSS] DELTASPIKE-79 Authorization API
Date Sun, 22 Apr 2012 17:50:24 GMT
hi christian,

you can do the same (but type-safe) with my suggestion.

regards,
gerhard



2012/4/22 Christian Kaltepoth <christian@kaltepoth.de>

> 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 <mposolda@redhat.com>:
> > 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
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message