Resourceresolver access in services

As AEM6 had been released at the end of May 2014, we had a look at the API changes and one thing which came to our eye was that getAdministrativeResourceResolver got deprecated with the newly included Sling version which is used in AEM6. 

The documentation now states that instead of getAdministrativeResourceResolver a new method called getServiceResourceResolver should be used.

The reason for this change is mainly to improve security and prevent abuse of this method, as (pre AEM6 era) many developers just use getAdministrativeResourceResolver due to its convenience.

So how can we use getServiceResourceResolver method?

Lets have a look at the following use case:

Setting up a new project

We start off by setting up a new project with the maven archetype and changing the dependency (with the new convenient dependency which includes all AEM dependencies in one ) to the ones from AEM6.

Next, we will create two new users readuser and writeuser with the appropriate access rights. Then we will create the two services which actually use the user based resource resolvers.

      @Service
@Component(immediate=true)
public class WriteServiceImpl implements WriteService {
 
	private final Logger log = LoggerFactory.getLogger(getClass());
 
	@Reference
	private ResourceResolverFactory resolverFactory;
 
	@Activate
	public void doAWriteOperation(ComponentContext ctx) {
		Map<String, Object> param = new HashMap<String, Object>();
		param.put(ResourceResolverFactory.SUBSERVICE, "writeService");
		ResourceResolver resolver = null;
		try {
			resolver = resolverFactory.getServiceResourceResolver(param);
			log.info(resolver.getUserID());
			Resource res = resolver.getResource("/content/mydata/jcr:content");
			ValueMap readMap = res.getValueMap();
			log.info(readMap.get("jcr:primaryType", ""));
			ModifiableValueMap modMap = res.adaptTo(ModifiableValueMap.class);
			if(modMap != null){
				modMap.put("myKey", "myValue");
				resolver.commit();
				log.info("Successfully saved");
			}
		} catch (LoginException e) {
			log.error("LoginException",e);
		} catch (PersistenceException e) {
			log.error("LoginException",e);
		}finally{
			if(resolver != null && resolver.isLive()){
				resolver.close();
			}
		}
	}
}
    
      @Service
@Component(immediate=true)
public class ReadServiceImpl implements ReadService{
 
	private final Logger log = LoggerFactory.getLogger(getClass());
 
    @Reference
	private ResourceResolverFactory resolverFactory;
 
    @Activate
	public void doAReadOperation(ComponentContext ctx) {
		Map<String, Object> param = new HashMap<String, Object>();
		param.put(ResourceResolverFactory.SUBSERVICE, "readService");
		ResourceResolver resolver = null;
		try {
			resolver = resolverFactory.getServiceResourceResolver(param);
			log.info(resolver.getUserID());
			Resource res = resolver.getResource("/content/datatoreadandwrite/jcr:content");
			ValueMap readMap = res.getValueMap();
			log.info(readMap.get("jcr:primaryType", ""));
			ModifiableValueMap modMap = res.adaptTo(ModifiableValueMap.class);
			if(modMap != null){
				modMap.put("myKey", "myValue");
				resolver.commit();
				log.info("Successfully saved");
			}
		} catch (LoginException e) {
			log.error("LoginException",e);
		} catch (PersistenceException e) {
			log.error("LoginException",e);
		}finally{
			if(resolver != null && resolver.isLive()){
				resolver.close();
			}
		}
	}
}
    

The only difference between those services, is the different ResourcerResolverFactory.SUBSERVICE parameter which is passed.

Are you looking for Adobe Experience Manager developpers?

Map the users to the corresponding services

In the final step we will map the users to the corresponding services. This can be done via a configuration called “Apache Sling Service User Mapper Service” which is configurable in the OSGI configuration admin interface. There we add the following two entries:

ch.inside.cqblog-bundle:readService=readuser

ch.inside.cqblog-bundle:writeService=writeuser

As described in the description, a new configuration has to be configured like this:

‘serviceName [ “:” subServiceName ] “=” username’.

(A bundle wide configuration can be achieved when subServiceName is omitted.)

After installing the bundle, we should now see the following two entries in the log file:

      *INFO*  ch.inside.cqblog.impl.ReadServiceImpl readuser
*INFO*  ch.inside.cqblog.impl.ReadServiceImpl nt:unstructured
    
      *INFO*  ch.inside.cqblog.impl.WriteServiceImpl writeuser
*INFO* ch.inside.cqblog.impl.WriteServiceImpl nt:unstructured
*INFO* ch.inside.cqblog.impl.WriteServiceImpl Successfully saved
    

As you can see, will not even retrieve a ModifiableValueMap if the access rights aren’t sufficient.

This new configuration possibility offers an easy way to map resource resolver to users when retrieved over getServiceResourceResolver.

Looking for more great ressources?

Subscribe to our newsletter and we will send you the next article about Adobe Experience Manager.