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