Expand my Community achievements bar.

How to Sync user permissions (groups that user belong to) from one publisher to another publisher

Avatar

Level 3

Hi all,

 

I have implemented Sling Content Distribution (of type Sync Distribution) to sync nodes from one publisher to another publisher. I am using a custom trigger based on ResourceChangeListener, only looking for added events under the path /home/users/IDCS. Use case being i am only trying to sync new created users as i have enabled encapsulation token support and want to ensure newly created users are synced across publishers.

 

I am able to sync the top level user nodes ( like /home/users/IDCS/ymk6sYtFij-x_-UkAyF_) across the publishers, How can i sync the group this user is part of in Publisher A to Publisher B, what nodes do i have to send to another publisher so the permissions are also synced?Below is the code to trigger an event to the sync Agent that i have configured called  "simple-agent".

 

package com.abc.test.core.services.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChangeListener;

import org.apache.sling.distribution.DistributionRequestType;
import org.apache.sling.distribution.Distributor;
import org.apache.sling.distribution.SimpleDistributionRequest;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(
    service = ResourceChangeListener.class,
    property = {
        ResourceChangeListener.PATHS + "=/home/users",
        ResourceChangeListener.CHANGES + "=ADDED"
    }
)
public class UsersAddedDistributor implements ResourceChangeListener {

    private static final Logger LOG = LoggerFactory.getLogger(UsersAddedDistributor.class);

    @Reference
    private ResourceResolverFactory rrf;

    @Reference
    private Distributor distributor;

    @Override
    public void onChange(List<ResourceChange> changes) {
        for (ResourceChange ch : changes) {
            String path = ch.getPath();
            LOG.info("Detected ADD event at path: {}", path);

            Map<String, Object> auth = new HashMap<>();
            auth.put(ResourceResolverFactory.SUBSERVICE, "simple_svc");

            try (ResourceResolver resolver = rrf.getServiceResourceResolver(auth)) {
                Resource res = resolver.getResource(path);
                if (res == null) {
                    LOG.warn("Resource not found at {}", path);
                    continue;
                }

                ValueMap vm = res.getValueMap();
                String primaryType = vm.get("jcr:primaryType", String.class);

                if ("rep:User".equals(primaryType)) {
                    LOG.info("rep:User detected at {} — triggering distribution", path);
                    distributor.distribute(
                        "simple-agent",
                        resolver,
                        new SimpleDistributionRequest(DistributionRequestType.ADD, path)
                    );
                } else {
                    LOG.debug("Skipping non-user node at {}", path);
                }

            } catch (LoginException e) {
                LOG.error("Error obtaining service resolver for {}", path, e);
            } catch (Exception e) {
                LOG.error("Error distributing path {}", path, e);
            }
        }
    }
}
 
Regards,
 
 
 
Topics

Topics help categorize Community content and increase your ability to discover relevant content.

4 Replies

Avatar

Level 3

@kautuk_sahni Can you please help with this question?

Avatar

Level 4

Hi @UserSD ,

 

 

To synchronize user permissions between AEM publishers using Sling Content Distribution, you must distribute not only the user node but also the group nodes that define the user’s permissions. AEM does not store group membership directly inside the user node under /home/users; instead, permissions are assigned through group membership, and those group relationships are stored under /home/groups in the rep:members property of each group node. Therefore, distributing only the user node will create the user on the target publisher but without any permissions, because their group membership is missing.

To solve this, after detecting a new user creation event, your distribution logic should also identify all groups the user belongs to and trigger a distribution request for each of those group nodes. Below is the required logic added to your existing listener class:

if ("rep:User".equals(primaryType)) {
    // Distribute the user node
    distributor.distribute("simple-agent", resolver,
        new SimpleDistributionRequest(DistributionRequestType.ADD, path));

    // Fetch user group memberships
    UserManager userManager = resolver.adaptTo(UserManager.class);
    Authorizable authUser = userManager.getAuthorizableByPath(path);

    if (authUser != null) {
        Iterator<Group> groups = authUser.memberOf();
        while (groups.hasNext()) {
            Group group = groups.next();
            String groupPath = group.getPath();

            // Avoid system/internal groups
            if (!groupPath.contains("/home/groups/system")) {
                // Distribute the group node so permissions sync
                distributor.distribute("simple-agent", resolver,
                    new SimpleDistributionRequest(DistributionRequestType.ADD, groupPath));
            }
        }
    }
}

 

This ensures that when a user is distributed from Publisher A to Publisher B, AEM also receives the relevant group membership information, and as a result the user retains their permissions on the target instance. Make sure your service user has read permissions on /home/groups and avoid distributing unnecessary system groups.

 
Thanks & Regards
Vishal

Avatar

Community Advisor

Hi @UserSD,

Have you tried to use OOTB User Synchronization mechanism?

Here is a link to documentation that describes, how it works, what are the limitations, and how it can be configured.

Avatar

Community Advisor

@UserSD are you expecting groups also getting created automatically? Usually in cug world, users will be created based on login but expects groups present in all publishers. So groups are kind of pre-configured and installed in all publishers before hand.