Create System | Service Users in AEM 6.5 with Code Configurations

In this article we will walk through how to configure your AEM project to have the AEM environment self configure System Users in AEM with code, so that you would not need to do it the manual way.

With the leverage of the org.apache.sling.jcr.api v2.4.0 bundle provided by AEM 6.5.4+ or AEM as a Cloud Service, we are able to utilize the Repository Initialization (repoinit) feature. At the start of this article, we will quickly provide a review of what a system user is, next we will explain what actually is the Repository Initialization (repoinit) feature and it’s dependencies, and finally, we will be providing some code examples on how to setup this feature on your own AEM instance with a code follow through.

AEM System Users are also referred as Service Users.


1. AEM System User Quick Review

Before we begin, let’s reiterate what a System User in AEM is. A system user is a JCR user with no password set and a minimal set of privileges that are necessary to perform a specific task. Having no password set means that it will not be possible to login with a system user. A system user is typically utilized by the AEM backend code which have privileges to create, read, update, and delete nodes in the JCR; the backend code will authenticate as a system user as a session when needed, and is required to session logout right when the task or tasks have been completed. Not logging out of a system user from the AEM backend will cause issues. If you do not logout of a session that you have opened, your AEM will be flooded by opened sessions, your AEM will start complaining with *INFO* [Apache Sling Resource Resolver Finalizer Thread] org.apache.sling.resourceresolver.impl.CommonResourceResolverFactoryImpl Unclosed ResourceResolver, and eventually your AEM environment will run out of memory and crash; to learn more about this topic, take a look at this blog article, ResourceResolvers and Sessions — “you open it, you close it”, by Jörg Hoh. The rule of thumb for creating System Users is to keep the privileges very specific; ensure you have a plan for this system user.


2. What Is Repository Initialization (repoinit)?

Sling RepoInit is a mechanism that allows configuration code to run before the SlingRepository service is registered. Configuration code is registered under the org.apache.sling.jcr.repoinit.RepositoryInitializer factory PID, and written in a mini-language.

The org.apache.sling.repoinit.parser implements a mini-language meant to create paths, system users and manage access control in a content repository, as well as registering JCR namespaces, node types and privileges. Defining access control content consists of setting and deleting policies of type access control lists (ACL) for which individual access control entries (ACE) can be added and removed.

Full documentation found here.


3. AEM Dependencies for the Repository Initialization Feature(Sling RepoInit)

To be able to use this feature, you must have AEM 6.5.4+ or AEM as a Cloud Service.

From out of the box AEM 6.5, it already includes a version of Sling RepoInit, but this is likely an older version without the service pack. If you are not able to replicate the code follow through mentioned on this article, consider upgrading your AEM service pack to the supported version AEM 6.5.4+. Take a look at my bundle versions found in /system/console/bundles (as these installed versions are working as expected on my machine):

  • org.apache.sling.jcr.api @ 2.4.0
  • org.apache.sling.jcr.repoinit @ 1.1.28
  • org.apache.sling.repoinit.parser @ 1.6.2

4. Code Follow Through

In the code follow through, you will create a system user with code configurations. Next, with the system user, you will write code for a simple AEM Servlet which will be a GET request, which will write a new property into an existing node in AEM.


A. Configure org.apache.sling.jcr.repoinit.RepositoryInitializer PID

With the org.apache.sling.jcr.repoinit.RepositoryInitializer factory PID, we will be creating a new configuration file.

With this configuration, we are creating a system user named sourcedCodeSystemUser, and will be placed into the folder structure of /home/users/system/sourcedCode. The system user, sourcedCodeSystemUser, will have ACL (Access Control List) privileges configured to:

  • allow read,write,replicate for all JCR resources which exists under /content/sourcedcode
  • allow read,write,replicate for all JCR resources which exists under /conf/sourcedcode
  • allow ALL-CRUD-ACTIONS for all JCR resources which exists under /content/we-retail

filename: /apps/sourcedcode/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~sourcedcodeSystemUser.config

1
2
3
4
5
6
7
8
scripts=["
    create service user sourcedCodeSystemUser with path /home/users/system/sourcedCode
    set ACL for sourcedCodeSystemUser
        allow jcr:read,rep:write,crx:replicate on /content/sourcedcode
        allow jcr:read,rep:write,crx:replicate on /conf/sourcedcode
        allow jcr:all on /content/we-retail
    end
"]

filename: /apps/sourcedcode/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~sourcedcodeSystemUser.cfg.json

1
2
3
4
5
6
7
8
scripts=["
    create service user sourcedCodeSystemUser with path /home/users/system/sourcedCode
    set ACL for sourcedCodeSystemUser
        allow jcr:read,rep:write,crx:replicate on /content/sourcedcode
        allow jcr:read,rep:write,crx:replicate on /conf/sourcedcode
        allow jcr:all on /content/we-retail
    end
"
]

Such configurations have two optional fields:
A multi-value references field with each value providing the URL (as a String) of raw repoinit statements.
A multi-value scripts field with each value providing repoinit statements as plain text in a String.


B. Configure org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended PID

To add a mapping from your service to the corresponding System Users you need to create a factory configuration for the [ServiceUserMapper](https://sling.apache.org/apidocs/sling7/org/apache/sling/serviceusermapping/ServiceUserMapper.html) service. To keep this modular such configurations can be provided using the Sling amend mechanism.

Below, you will see us mapping the sourcedcode.core backend to the sourcedCodeSystemUser.

filename: org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended-sourcedcodeSystemUser.config

1
2
3
user.mapping=[ \
  "sourcedcode.core:sourcedCodeSystemUser\=[sourcedCodeSystemUser]"
]

C. Register Basic Servlet (GET Request)

line:38, we are creating the Map authInfo class with ResourceResolverFactory.SUBSERVICE set to the system user that we have just created, “sourcedCodeSystemUser”.

line:41, we will utilize the Java 7 API, “try-wth-resource”, best practice, to ensure that when we open a new session, we are never forgetting to close the session after the use of it; Since Sling9 (roughly 2016) the ResourceResolver interface implements the AutoCloseable marker interface, so the try-with-resource idiom can be used. Here we are obtaining the resolver via ResourceResolverFactory.getServiceResourceResolver() method.

line:42, we are adapting the resolver into a javax.jcr.Session.class.

line:44-50, we are getting the AEM JCR node, writing new properties to /content/sourcedcode/jcr:content, and replicating the node. And on succeess, we will return the message of “Test is saved and replicated”.

filename: sourcedcode/core/src/main/java/com/sourcedcode/core/servlets/SimpleGetServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.sourcedcode.core.servlets;

import com.day.cq.replication.ReplicationActionType;
import com.day.cq.replication.Replicator;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.servlets.HttpConstants;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

import javax.jcr.Node;
import javax.jcr.Session;
import javax.servlet.Servlet;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;

@Component(
        service = Servlet.class,
        property = {
                "sling.servlet.paths=/bin/simple-get",
                "sling.servlet.methods=" + HttpConstants.METHOD_GET
        })
public class SimpleGetServlet extends SlingAllMethodsServlet {

    @Reference
    private ResourceResolverFactory factory;

    @Reference
    private Replicator replicator;

    @Override
    protected void doGet(final SlingHttpServletRequest req, final SlingHttpServletResponse resp) throws IOException {
        final Map<String, Object> authInfo = Collections.singletonMap(
                ResourceResolverFactory.SUBSERVICE,
                "sourcedCodeSystemUser");
        try (ResourceResolver resolver = factory.getServiceResourceResolver(authInfo)) {
            Session session = resolver.adaptTo(Session.class);
            Node contentNode = session.getNode("/content/sourcedcode/jcr:content");
            if (Objects.nonNull(contentNode)) {
                contentNode.setProperty("test", "works");
                session.save();
                replicator.replicate(session, ReplicationActionType.ACTIVATE, contentNode.getPath());
                resp.setStatus(200);
                resp.getWriter().write("Test is saved and replicated.");
            }
        } catch (Exception e) {
            e.printStackTrace();
            resp.setStatus(500);
            resp.getWriter().write("Failed to save.");
        }
    }
}

D. Test and Validate the Servlet Response

Now use your maven skills to build the project!

Next, after a successful build, you would want to make a call to your AEM servlet, via http://localhost:4502/bin/simple-get. With the service system successfully configured in your environment, you should be able to see a successful message.

Test is saved and replicated.

Revealing the changes in the JCR content, http://localhost:4502/crx/de/index.jsp#/content/sourcedcode/jcr%3Acontent, you should be able to see the changes.
Screenshot of the crx/de properties for the JCR node, /content/sourcedcode/jcr%3Acontent


E. ServiceUser in UserAdmin

If you like, take a look at http://localhost:4502/useradmin. Search for the sourcedcodeSystemUser and review the permissions for this system user. You should be able to see permissions reflected for this system user:

  • allow read,write,replicate for all JCR resources which exists under /content/sourcedcode
  • allow read,write,replicate for all JCR resources which exists under /conf/sourcedcode
  • allow ALL-CRUD-ACTIONS for all JCR resources which exists under /content/we-retail

In the AEM useradmin console, showcasing the sourcedcodeSystemUser with correct permissions.


This article covers questions like

  • How to create AEM System User Programmatically
  • How to create AEM System User Automatically
  • How to create AEM System User on AEMaaCS
  • How to create AEM System User with Code
  • How to create AEM System User that is not manual
  • How to create AEM System User the preferred way
  • How to create AEM Service User Programmatically
  • How to create AEM Service User Automatically
  • How to create AEM Service User on AEMaaCS
  • How to create AEM Service User with Code
  • How to create AEM Service User that is not manual
  • How to create AEM Service User the preferred way

Path not found by AEMaaCS

Sometimes you may find errors in the AEMaaCS pipelines due to the reason that when reference to path that you are allowing system user permissions to, path does not exist. And so, the node must declare creation of these path’s in the repo-init script. Although the path’s are already installed inside of AEM, it wouldn’t do anything anyways… this is to fix the repo-init system user pipeline errors.

Here’s an example of us creating a repo-init system user which allows permissions for the system user, and includes the creation of paths:

  • allow jcr:all on /content/sourcedcode
  • allow jcr:all on /conf/sourcedcode
  • allow jcr:all on /content/dam/sourcedcode
  • allow jcr:all on /content/cq:tags/sourcedcode

repoinit cfg json example for creating system user

filename: /apps/sourcedcode/config/org.apache.sling.jcr.repoinit.RepositoryInitializer~sourcedcodeSystemUser.cfg.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "scripts": [
    "create path (sling:OrderedFolder) /content/dam/sourcedcode",
    "create path (nt:unstructured) /content/dam/sourcedcode/jcr:content",
    "create path (cq:Page) /content/sourcedcode",
    "create path (cq:PageContent) /content/sourcedcode/jcr:content",
    "create path (sling:Folder) /conf/sourcedcode",
    "create path (cq:Tag) /content/cq:tags/sourcedcode",
    "set properties on /conf/sourcedcode\r\n  set jcr:title{String} to "Sourced Code"\r\nend",
    "set properties on /content/dam/sourcedcode/jcr:content\r\n  set cq:conf{String} to /conf/sourcedcode\r\n  set jcr:title{String} to "Sourced Code"\r\nend",
    "create service user sourcedcodeSystemUser with path /home/users/system/sourcedcode",
    "set ACL for sourcedcodeSystemUser \r\n  allow jcr:all on /content/sourcedcode\r\n  allow jcr:all on /conf/sourcedcode\r\n  allow jcr:all on /content/dam/sourcedcode\n  allow jcr:all on /content/cq:tags/sourcedcode\r\nend"
  ]
}

Hello, I am an enthusiastic Adobe Community Advisor and a seasoned Lead AEM Developer. I am currently serving as an AEM Technical Lead at MNPDigital.ca, bringing over a decade of extensive web engineering experience and more than eight years of practical AEM experience to the table. My goal is to give back to the AEM Full Stack Development community by sharing my wealth of knowledge with others. You can connect with me on LinkedIn.

Leave a Reply

Your email address will not be published. Required fields are marked *


Back To Top