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