I like this proposal as it is really generic and allows for some very
interesting use cases like denying access to resources for a given
time or allowing only access if the user has paid for the content or
whatever :)

And it's totally transparent and doesn't need any changes to the core.

As we're about to finalize the API release, I think we should try to
get this in as well. Could you maybe come up with a patch implementing
this?

I'm not 100% sure on the names and if we should have a fixed set of
methods or rather a more generic approach where the action (read,
write, execute...) is passed as a string (or enum). But I think we can
flesh this out easily.

Regards
Carsten

2012/10/26 Mike Müller <mike...@mysign.ch>:
> Hi
>
> The main goal of this proposal is to provide a easy to use service in
> Sling to restrict (or grant) access to resources for special use cases
> (like giving access to some resources only between 8am and 5pm). The
> service should be very lightweight and should NOT be a replacement of ACLs
> - and of course should not harm performance. The service should be
> decoupled from the ResourceProvider to make it easy to define an access
> gate which applies to resources from different ResourceProvider services.
> Actually this last point is important to make it very easy to use.
>
> I propose a new service interface ResourceAccessGateService:
>
> interface ResourceAccessGateService
> {
>         enum GateResult { GRANTED, DENIED, NOTRESPONSIBLE };
>
>         GateResult canRead( Resource, authenticationInfo );
>         GateResult canCreate( absPathName, authenticationInfo );
>         GateResult canUpdate( Resource, authenticationInfo );
>         GateResult canDelete( Resource, authenticationInfo );
>         GateResult canExecute( Resource, authenticationInfo );
>
>         GateResult canReadValue( Resource, valueName, authenticationInfo );
>         GateResult canCreateValue( Resource, valueName, authenticationInfo );
>         GateResult canUpdateValue( Resource, valueName, authenticationInfo );
>         GateResult canDeleteValue( Resource, valueName, authenticationInfo );
>
>         String sanitizeQuery( query, language, authenticationInfo ) throws
> AccessGateException;
>
>         /* for convenience (and performance) */
>         boolean hasReadRestrictions( authenticationInfo );
>         boolean hasCreateRestrictions( authenticationInfo );
>         boolean hasUpdateRestrictions( authenticationInfo );
>         boolean hasDeleteRestrictions( authenticationInfo );
>         boolean hasExecuteRestrictions( authenticationInfo );
>
>         boolean canReadAllValues( Resource, authenticationInfo );
>         boolean canCreateAllValues( Resource, authenticationInfo );
>         boolean canUpdateAllValues( Resource, authenticationInfo );
>         boolean canDeleteAllValues( Resource, authenticationInfo );
> }
>
> Implementations of this service interface must be registered like
> ResourceProvider with a path (like provider.roots). If different
> ResourceAccessGateService services match a path, not only the
> ResourceAccessGateService with the longest path should be called, but all
> of them, that's in contrast to the ResourceProvider, but in this case more
> logical (and secure!).
>
> service properties:
> path: regexp to define on which paths the service should be called
> (default .*)
> operations: set of operations on which the service should be called
> ("read,create,update,delete", default all of them)
> finaloperations: set of operations on which the service answer is final an
> no other service should be called (default none of them)
>
> Okay, how should it work: First of all, if there's no registered
> ResourceAccessGateService, nothing changes.
> If one ore more ResourceAccessGateServices are registered Sling takes them
> in account as follows:
>
> Sling registers a ResourceDecorator and wraps with that service all
> resources into a AccessGateResourceWrapper.
>
> In general:
> All servcies are called in the order of the service ranking. If one
> service defines its answer as "final" (service property finaloperations),
> then Sling takes the answer of this service and no other service is called
> despite the service returns NOTRESPONSIBLE.
>
> For READ:
> After resolving and getting a resource from the ResourceProvider all
> matching ResourceAccessGateService are called (canRead) in the order of
> the service ranking and the resource only will be returned if one services
> return GRANTED, otherwise a NonExistingResource is returned. This also
> applies to findResources and queryResources. And yes in this case the
> number of returned results can differ dependent on the implementations of
> the ResourceAccessGateService. I don't think that's a big issue and users
> of the ResourceAccessGateService have to be aware of this.
> If there's a query to find resources, first of all the resource resolver
> calls method sanitizeQuery on all ResourceAccessGateServices (because
> there's no matching path). The ResourceAccessGateService either returns a
> sanitized query (or the original query if everything is fine) or an
> AccessGateException if the query is not allowed or illegal.
>
> For UPDATE:
> AccessGateResourceWrapper overwrites adaptTo an returns instead a
> ModifiableValueMap (if requested) a ModifiableValueMapWrapper. If the
> resource can't be modified (all ResourceAccessGateService#canUpdate
> returns
> DENIED) an AccessGateException will be throwed. The
> ModifiableValueMapWrapper overwrites the methods put, putAll, remove and
> clear and calls the appropriate methods (canUpdateValue and
> canDeleteValue) on the registered ResourceAccessGateServices. The first
> check will always be a call to canUpdateAllValues/canDeleteAllValues, if
> this method returns true (from one service at least), no further call to
> canUpdateValue/canDeleteValue has to be made. An implementation which does
> not care about access to values is not slowed down. That's why this
> additional (convenience) methods are important as well.
>
> For CREATE:
> This case is easy to implement: On ResourceResolver#create we call
> ResourceAccessGateService#canCreateAllResources, if one returns GRANTED,
> we
> do nothing else. Otherwise we return an AccessGateResourceWrapper which is
> aware, that the resource is just created. As long as the resource is not
> committed (through ResourceResolver#commit) the returned
> ModifiableValueMap (which is also aware of the "just created" through a
> "pointer" to the AccessGateResourceWrapper) does call the appropriate
> canCreateAllValues/canCreateValue. After calling commit the state "just
> created" on the AccessGateResourceWrapper will be erased and therefore the
> appropriate canUpdate methods will be called on the
> ResourceAccessGateService.
>
> For DELETE:
> ResourceResolver#delete will take care to call the canDelete methods on
> all matching ResourceAccessGateService. If one service returns GRANTED the
> resource can be deleted.
>
> Alternatively, to make the interface more flexible, we could combine the
> canXXX() methods in a method named hasPermission with a parameter
> "operation" as String.
>
> So WDYT?
>
> best regards
> Mike
>



-- 
Carsten Ziegeler
cziege...@apache.org

Reply via email to