Guide · updated

Service users and repoinit: replacing admin sessions properly

Calls such as getAdministrativeResourceResolver and loginAdministrative were deprecated in Sling long ago, and your code cannot use administrative sessions on AEM as a Cloud Service. The replacement has three parts that ship with your code: a service user created by repoinit, a mapping from your bundle to that user, and a call that asks for a resolver by sub-service name.

1. Decide what each service needs

List every place that used an admin session and write down the paths it reads or writes. One service user per purpose (for example, one that reads /content/dam statistics, another that writes reports under /var) keeps permissions small and audit logs readable.

2. Create the user with repoinit

Repoinit scripts run at startup and are deployed as an OSGi configuration of org.apache.sling.jcr.repoinit.RepositoryInitializer. Adobe's WKND example creates the user and grants read access like this:

  • create service user wknd-examples-statistics-service with forced path system/cq:services/wknd-examples
  • a set principal ACL for wknd-examples-statistics-service block containing allow jcr:read on /content/dam, closed with end

Adobe notes that not every repoinit directive is supported in the older .config format; use .cfg.json for advanced directives. Factory configuration files on Cloud Service follow the <factoryPID>-<name>.cfg.json naming convention.

3. Map your bundle to the user

Add an amended service user mapping, for example org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-wknd-examples.cfg.json, with a user.mapping entry in the form bundle-symbolic-name:subservice=[service-user]. Adobe's example maps wknd-examples.core:wknd-examples-statistics to [wknd-examples-statistics-service].

4. Open the resolver by sub-service, and always close it

Pass ResourceResolverFactory.SUBSERVICE with your sub-service name to getServiceResourceResolver, inside try-with-resources so the resolver is closed even when an exception is thrown. Adobe's sample code makes the same point: every resolver you open must be closed.

Common mistakes

  • One all-powerful service user for everything: it recreates the admin session problem with extra steps.
  • Mapping the wrong bundle symbolic name: no user is found for the sub-service and getServiceResourceResolver throws a LoginException.
  • Forgetting write permissions on /var or /conf for code that writes there.
  • Leaving resolvers open in long-running jobs: open per unit of work, close it, repeat.

Our refactoring factory rewrites the Java call automatically and leaves a TODO naming the sub-service; a person approves each service user's permissions, because that is a security decision, not a code change.

Sources