This is an automated email from the ASF dual-hosted git repository.
pradeepagrawal8184 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 6847eb7b9 RANGER-5110: Prevent entity soft deletion when LDAP DN is
updated (#1162)
6847eb7b9 is described below
commit 6847eb7b977b1027cdf8de68557299bc21492690
Author: KRISHNA CHAITANYA MUTTEVI
<[email protected]>
AuthorDate: Tue Sep 29 14:58:03 2026 +0530
RANGER-5110: Prevent entity soft deletion when LDAP DN is updated (#1162)
* RANGER-5110: Prevent entity soft deletion when LDAP DN is updated with
proper syncing of U&G
* RANGER-5110: Added the support for inter-syncsource dn updates &
addressed reviewer comments
* RANGER-5110: Addressed the review comments by moving the skip condition
* RANGER-5110: Resolved the Merge Conflicts & Checkstyle error
* RANGER-5110: Gate name-map DN removal on allowSyncSourceOverwrite
---
.../unixusersync/config/UserGroupSyncConfig.java | 13 +
.../process/PolicyMgrUserGroupBuilder.java | 81 ++++--
.../config/TestUserGroupSyncConfig.java | 18 ++
.../process/TestPolicyMgrUserGroupBuilder.java | 272 +++++++++++++++++++++
ugsync/src/test/resources/ranger-ugsync-site.xml | 12 +-
5 files changed, 371 insertions(+), 25 deletions(-)
diff --git
a/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
b/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
index 991748264..3b190e3c8 100644
---
a/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
+++
b/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java
@@ -71,6 +71,7 @@ public class UserGroupSyncConfig {
public static final String UGSYNC_SERVER_HA_ENABLED_PARAM =
"ranger-ugsync.server.ha.enabled";
public static final String UGSYNC_NAME_VALIDATION_ENABLED =
"ranger.usersync.name.validation.enabled";
public static final String UGSYNC_SYNC_SOURCE_VALIDATION_ENABLED =
"ranger.usersync.syncsource.validation.enabled";
+ public static final String UGSYNC_DN_VALIDATION_ENABLED =
"ranger.usersync.dn.validation.enabled";
private static final Logger LOG =
LoggerFactory.getLogger(UserGroupSyncConfig.class);
private static final String CORE_SITE_CONFIG_FILE =
"core-site.xml";
private static final String UGSYNC_SOURCE_CLASS_PARAM =
"ranger.usersync.source.impl.class";
@@ -180,6 +181,7 @@ public class UserGroupSyncConfig {
private static final long
UGSYNC_INIT_SLEEP_TIME_IN_MILLIS_BETWEEN_CYCLE_MIN_VALUE_FOR_HA = 5_000L;
private static final boolean DEFAULT_UGSYNC_NAME_VALIDATION_ENABLED
= false;
private static final boolean DEFAULT_UGSYNC_SYNC_SOURCE_VALIDATION_ENABLED
= true;
+ private static final boolean DEFAULT_UGSYNC_DN_VALIDATION_ENABLED
= false;
private static final String SYNC_SOURCE =
"ranger.usersync.sync.source";
private static final String LGSYNC_PAGED_RESULTS_ENABLED =
"ranger.usersync.pagedresultsenabled";
@@ -1405,6 +1407,17 @@ public boolean isSyncSourceValidationEnabled() {
return isSyncSourceValidationEnabled;
}
+ public boolean isDnValidationEnabled() {
+ boolean isDnValidationEnabled = DEFAULT_UGSYNC_DN_VALIDATION_ENABLED;
+ String val =
prop.getProperty(UGSYNC_DN_VALIDATION_ENABLED);
+
+ if (StringUtils.isNotEmpty(val)) {
+ isDnValidationEnabled = Boolean.parseBoolean(val);
+ }
+
+ return isDnValidationEnabled;
+ }
+
public boolean isLargeGroupSyncEnabled() {
boolean largeGroupSyncEnabled;
String val = prop.getProperty(LGSYNC_LDAP_LARGEGROUPSYNC_ENABLED);
diff --git
a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
index 2eb21755f..248e43df7 100644
---
a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
+++
b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
@@ -130,6 +130,7 @@ public class PolicyMgrUserGroupBuilder extends
AbstractUserGroupSource implement
private boolean isRangerCookieEnabled;
private boolean isUserSyncNameValidationEnabled;
private boolean isSyncSourceValidationEnabled;
+ private boolean isDnValidationEnabled;
private MetricCacheUtil metricCacheUtil = MetricCacheUtil.getInstance();
private String recordsToPullPerCall = "10";
private String currentSyncSource;
@@ -179,6 +180,7 @@ public static void main(String[] args) throws Throwable {
public synchronized void init() throws Throwable {
isUserSyncNameValidationEnabled =
config.isUserSyncNameValidationEnabled();
isSyncSourceValidationEnabled =
config.isSyncSourceValidationEnabled();
+ isDnValidationEnabled = config.isDnValidationEnabled();
recordsToPullPerCall = config.getMaxRecordsPerAPICall();
policyMgrBaseUrl = config.getPolicyManagerBaseURL();
isMockRun = config.isMockRunEnabled();
@@ -754,18 +756,16 @@ private void computeGroupDelta(Map<String, Map<String,
String>> sourceGroups) {
deltaGroups.put(groupName, curGroup);
noOfModifiedGroups++;
groupNameMap.put(groupDN, groupName);
- } else {
- if (MapUtils.isNotEmpty(curGroupAttrs) &&
!StringUtils.equalsIgnoreCase(groupDN, curGroupDN)) { // skip update
- LOG.debug("[{}]: SyncSource update skipped, current
group DN = {} new user DN = {}", groupName, curGroupDN, groupDN);
-
- if (StringUtils.equalsIgnoreCase(curGroupAttrsStr,
newGroupAttrsStr)) {
- groupNameMap.put(groupDN, groupName);
- }
+ } else if (MapUtils.isNotEmpty(curGroupAttrs) &&
!StringUtils.equalsIgnoreCase(groupDN, curGroupDN) && !isDnValidationEnabled) {
+ LOG.debug("[{}]: SyncSource update skipped, current group
DN = {} new group DN = {}", groupName, curGroupDN, groupDN);
- continue;
+ if (StringUtils.equalsIgnoreCase(curGroupAttrsStr,
newGroupAttrsStr)) {
+ groupNameMap.put(groupDN, groupName);
}
+ } else {
+ boolean allowSyncSourceOverwrite =
!isSyncSourceValidationEnabled && isDnValidationEnabled;
- if (StringUtils.isEmpty(curSyncSource) ||
(!StringUtils.equalsIgnoreCase(curGroupAttrsStr, newGroupAttrsStr) &&
StringUtils.equalsIgnoreCase(curSyncSource, newSyncSource))) { // update
+ if (StringUtils.isEmpty(curSyncSource) ||
(!StringUtils.equalsIgnoreCase(curGroupAttrsStr, newGroupAttrsStr) &&
(StringUtils.equalsIgnoreCase(curSyncSource, newSyncSource) ||
allowSyncSourceOverwrite))) {
if (StringUtils.isEmpty(curSyncSource)) {
LOG.debug("[{}]: SyncSource updated to {},
previously empty", groupName, newSyncSource);
} else {
@@ -777,6 +777,10 @@ private void computeGroupDelta(Map<String, Map<String,
String>> sourceGroups) {
deltaGroups.put(groupName, curGroup);
noOfModifiedGroups++;
groupNameMap.put(groupDN, groupName);
+
+ if (allowSyncSourceOverwrite) {
+ groupNameMap.remove(curGroupDN);
+ }
} else {
if (!StringUtils.equalsIgnoreCase(curSyncSource,
newSyncSource)) {
LOG.debug("[{}]: Different sync source exists,
update skipped!", groupName);
@@ -838,26 +842,21 @@ private void computeUserDelta(Map<String, Map<String,
String>> sourceUsers) {
if (isStartupFlag && !isSyncSourceValidationEnabled &&
(!StringUtils.equalsIgnoreCase(curSyncSource, newSyncSource))) {
LOG.debug("[{}]: SyncSource updated to {}, previous value:
{}", userName, newSyncSource, curSyncSource);
-
curUser = setOtherAttributes(curUser, newSyncSource,
newUserAttrs, newUserAttrsStr);
curUser.setUserSource(SOURCE_EXTERNAL);
deltaUsers.put(userName, curUser);
- noOfModifiedGroups++;
+ noOfModifiedUsers++;
userNameMap.put(userDN, userName);
- } else {
- if (MapUtils.isNotEmpty(curUserAttrs) &&
!StringUtils.equalsIgnoreCase(userDN, curUserDN)) { // skip update
- // Same username with different DN already exists
- LOG.debug("[{}]: SyncSource update skipped, current
user DN = {} new user DN = {}", userName, curUserDN, userDN);
-
- if (StringUtils.equalsIgnoreCase(curUserAttrsStr,
newUserAttrsStr)) {
- userNameMap.put(userDN, userName);
- }
-
- continue;
+ } else if (MapUtils.isNotEmpty(curUserAttrs) &&
!StringUtils.equalsIgnoreCase(userDN, curUserDN) && !isDnValidationEnabled) {
+ LOG.debug("[{}]: SyncSource update skipped, current user
DN = {} new user DN = {}", userName, curUserDN, userDN);
+ if (StringUtils.equalsIgnoreCase(curUserAttrsStr,
newUserAttrsStr)) {
+ userNameMap.put(userDN, userName);
}
+ } else {
+ boolean allowSyncSourceOverwrite =
!isSyncSourceValidationEnabled && isDnValidationEnabled;
- if (StringUtils.isEmpty(curSyncSource) ||
(!StringUtils.equalsIgnoreCase(curUserAttrsStr, newUserAttrsStr) &&
StringUtils.equalsIgnoreCase(curSyncSource, newSyncSource))) { // update
+ if (StringUtils.isEmpty(curSyncSource) ||
(!StringUtils.equalsIgnoreCase(curUserAttrsStr, newUserAttrsStr) &&
(StringUtils.equalsIgnoreCase(curSyncSource, newSyncSource) ||
allowSyncSourceOverwrite))) { // update
if (StringUtils.isEmpty(curSyncSource)) {
LOG.debug("[{}]: SyncSource updated to {},
previously empty", userName, newSyncSource);
} else {
@@ -870,6 +869,10 @@ private void computeUserDelta(Map<String, Map<String,
String>> sourceUsers) {
deltaUsers.put(userName, curUser);
noOfModifiedUsers++;
userNameMap.put(userDN, userName);
+
+ if (allowSyncSourceOverwrite) {
+ userNameMap.remove(curUserDN);
+ }
} else {
if (!StringUtils.equalsIgnoreCase(curSyncSource,
newSyncSource)) {
LOG.debug("[{}]: Different sync source exists,
update skipped!", userName);
@@ -1774,14 +1777,29 @@ private void computeDeletedGroups(Map<String,
Map<String, String>> sourceGroups)
deletedGroups = new HashMap<>();
+ Set<String> sourceGroupNames = new HashSet<>();
+ for (Map<String, String> attrs : sourceGroups.values()) {
+ String originalName =
attrs.get(UgsyncCommonConstants.ORIGINAL_NAME);
+ if (StringUtils.isNotEmpty(originalName)) {
+ sourceGroupNames.add(groupNameTransform(originalName.trim()));
+ }
+ }
+
// Check if the group from cache exists in the sourceGroups. If not,
mark as deleted group.
- for (XGroupInfo groupInfo : groupCache.values()) {
+ for (Map.Entry<String, XGroupInfo> entry : groupCache.entrySet()) {
+ String groupName = entry.getKey();
+ XGroupInfo groupInfo = entry.getValue();
Map<String, String> groupOtherAttrs = groupInfo.getOtherAttrsMap();
String groupDN = groupOtherAttrs != null ?
groupOtherAttrs.get(UgsyncCommonConstants.FULL_NAME) : null;
if (StringUtils.isNotEmpty(groupDN) &&
!sourceGroups.containsKey(groupDN)
&&
StringUtils.equalsIgnoreCase(groupOtherAttrs.get(UgsyncCommonConstants.SYNC_SOURCE),
currentSyncSource) &&
StringUtils.equalsIgnoreCase(groupOtherAttrs.get(UgsyncCommonConstants.LDAP_URL),
ldapUrl)) {
+ if (sourceGroupNames.contains(groupName)) {
+ LOG.info("group {} with different DN exists. Skip and wait
for it to be updated.", groupName);
+ continue;
+ }
+
if (!ISHIDDEN.equals(groupInfo.getIsVisible())) {
groupInfo.setIsVisible(ISHIDDEN);
deletedGroups.put(groupInfo.getName(), groupInfo);
@@ -1893,14 +1911,29 @@ private void computeDeletedUsers(Map<String,
Map<String, String>> sourceUsers) {
deletedUsers = new HashMap<>();
+ Set<String> sourceUserNames = new HashSet<>();
+ for (Map<String, String> attrs : sourceUsers.values()) {
+ String originalName =
attrs.get(UgsyncCommonConstants.ORIGINAL_NAME);
+ if (StringUtils.isNotEmpty(originalName)) {
+ sourceUserNames.add(userNameTransform(originalName.trim()));
+ }
+ }
+
// Check if the group from cache exists in the sourceGroups. If not,
mark as deleted group.
- for (XUserInfo userInfo : userCache.values()) {
+ for (Map.Entry<String, XUserInfo> entry : userCache.entrySet()) {
+ String userName = entry.getKey();
+ XUserInfo userInfo = entry.getValue();
Map<String, String> userOtherAttrs = userInfo.getOtherAttrsMap();
String userDN = userOtherAttrs != null ?
userOtherAttrs.get(UgsyncCommonConstants.FULL_NAME) : null;
if (StringUtils.isNotEmpty(userDN) &&
!sourceUsers.containsKey(userDN)
&&
StringUtils.equalsIgnoreCase(userOtherAttrs.get(UgsyncCommonConstants.SYNC_SOURCE),
currentSyncSource)
&&
StringUtils.equalsIgnoreCase(userOtherAttrs.get(UgsyncCommonConstants.LDAP_URL),
ldapUrl)) {
+ if (sourceUserNames.contains(userName)) {
+ LOG.info("user {} with different DN exists. Skip and wait
for it to be updated.", userName);
+ continue;
+ }
+
if (!ISHIDDEN.equals(userInfo.getIsVisible())) {
userInfo.setIsVisible(ISHIDDEN);
deletedUsers.put(userInfo.getName(), userInfo);
diff --git
a/ugsync/src/test/java/org/apache/ranger/unixusersync/config/TestUserGroupSyncConfig.java
b/ugsync/src/test/java/org/apache/ranger/unixusersync/config/TestUserGroupSyncConfig.java
index 7a09ad431..7563d964a 100644
---
a/ugsync/src/test/java/org/apache/ranger/unixusersync/config/TestUserGroupSyncConfig.java
+++
b/ugsync/src/test/java/org/apache/ranger/unixusersync/config/TestUserGroupSyncConfig.java
@@ -30,6 +30,7 @@
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -129,4 +130,21 @@ public void
testE_getUserSyncMetricsFileName_prefersConfiguredOrLogdir_thenDefau
String p4 = cfg.getUserSyncMetricsFileName();
assertNotNull(p4);
}
+
+ @Test
+ public void testF_isDnValidationEnabled_defaultsAndParses() {
+ UserGroupSyncConfig cfg = UserGroupSyncConfig.getInstance();
+
+ cfg.setProperty(UserGroupSyncConfig.UGSYNC_DN_VALIDATION_ENABLED,
"true");
+ assertTrue(cfg.isDnValidationEnabled());
+
+ cfg.setProperty(UserGroupSyncConfig.UGSYNC_DN_VALIDATION_ENABLED,
"TRUE");
+ assertTrue(cfg.isDnValidationEnabled());
+
+ cfg.setProperty(UserGroupSyncConfig.UGSYNC_DN_VALIDATION_ENABLED,
"false");
+ assertFalse(cfg.isDnValidationEnabled());
+
+ cfg.setProperty(UserGroupSyncConfig.UGSYNC_DN_VALIDATION_ENABLED, "");
+ assertFalse(cfg.isDnValidationEnabled());
+ }
}
diff --git
a/ugsync/src/test/java/org/apache/ranger/unixusersync/process/TestPolicyMgrUserGroupBuilder.java
b/ugsync/src/test/java/org/apache/ranger/unixusersync/process/TestPolicyMgrUserGroupBuilder.java
index 32da0b916..66b5622f7 100644
---
a/ugsync/src/test/java/org/apache/ranger/unixusersync/process/TestPolicyMgrUserGroupBuilder.java
+++
b/ugsync/src/test/java/org/apache/ranger/unixusersync/process/TestPolicyMgrUserGroupBuilder.java
@@ -1128,4 +1128,276 @@ public Response post(String relativeUrl, Map<String,
String> params, Object obj,
return null;
}
}
+
+ @Test
+ public void testAK_computeUserDelta_dnValidationEnabled_ouMoveUpdates()
throws Exception {
+ PolicyMgrUserGroupBuilder builder = new PolicyMgrUserGroupBuilder();
+
+ String oldDn = "cn=john,ou=People,dc=example,dc=com";
+ String newDn = "cn=john,ou=Moved,dc=example,dc=com";
+
+ Map<String, XUserInfo> userCache = new HashMap<>();
+ XUserInfo existing = new XUserInfo();
+ existing.setName("john");
+ existing.setSyncSource("LDAP/AD");
+ Map<String, String> oldAttrs = new HashMap<>();
+ oldAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "john");
+ oldAttrs.put(UgsyncCommonConstants.FULL_NAME, oldDn);
+ oldAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+ existing.setOtherAttrsMap(oldAttrs);
+ existing.setOtherAttributes(JsonUtils.objectToJson(oldAttrs));
+ userCache.put("john", existing);
+
+ setPrivate(builder, "userCache", userCache);
+ setPrivate(builder, "userNameMap", new HashMap<String, String>());
+ setPrivate(builder, "policyMgrUserName", "rangerusersync");
+ setPrivate(builder, "isStartupFlag", false);
+ setPrivate(builder, "isDnValidationEnabled", true);
+ setPrivate(builder, "isSyncSourceValidationEnabled", true);
+
+ Map<String, String> newAttrs = new HashMap<>();
+ newAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "john");
+ newAttrs.put(UgsyncCommonConstants.FULL_NAME, newDn);
+ newAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+
+ Map<String, Map<String, String>> source = new HashMap<>();
+ source.put(newDn, newAttrs);
+
+ Method compute =
PolicyMgrUserGroupBuilder.class.getDeclaredMethod("computeUserDelta",
Map.class);
+ compute.setAccessible(true);
+ compute.invoke(builder, source);
+
+ Map<String, XUserInfo> deltaUsers = getPrivate(builder, "deltaUsers",
Map.class);
+ Map<String, String> userNameMap = getPrivate(builder,
"userNameMap", Map.class);
+
+ assertTrue(deltaUsers.containsKey("john"));
+ assertEquals("john", userNameMap.get(newDn));
+ assertFalse(userNameMap.containsKey(oldDn));
+ }
+
+ @Test
+ public void testAL_computeUserDelta_dnValidationDisabled_skipsOuMove()
throws Exception {
+ PolicyMgrUserGroupBuilder builder = new PolicyMgrUserGroupBuilder();
+
+ String oldDn = "cn=john,ou=People,dc=example,dc=com";
+ String newDn = "cn=john,ou=Moved,dc=example,dc=com";
+
+ Map<String, XUserInfo> userCache = new HashMap<>();
+ XUserInfo existing = new XUserInfo();
+ existing.setName("john");
+ existing.setSyncSource("LDAP/AD");
+ Map<String, String> oldAttrs = new HashMap<>();
+ oldAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "john");
+ oldAttrs.put(UgsyncCommonConstants.FULL_NAME, oldDn);
+ oldAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+ existing.setOtherAttrsMap(oldAttrs);
+ existing.setOtherAttributes(JsonUtils.objectToJson(oldAttrs));
+ userCache.put("john", existing);
+
+ setPrivate(builder, "userCache", userCache);
+ setPrivate(builder, "userNameMap", new HashMap<String, String>());
+ setPrivate(builder, "policyMgrUserName", "rangerusersync");
+ setPrivate(builder, "isStartupFlag", false);
+ setPrivate(builder, "isDnValidationEnabled", false);
+ setPrivate(builder, "isSyncSourceValidationEnabled", true);
+
+ Map<String, String> newAttrs = new HashMap<>();
+ newAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "john");
+ newAttrs.put(UgsyncCommonConstants.FULL_NAME, oldDn);
+ newAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+
+ Map<String, Map<String, String>> source = new HashMap<>();
+ source.put(newDn, newAttrs);
+
+ Method compute =
PolicyMgrUserGroupBuilder.class.getDeclaredMethod("computeUserDelta",
Map.class);
+ compute.setAccessible(true);
+ compute.invoke(builder, source);
+
+ Map<String, XUserInfo> deltaUsers = getPrivate(builder, "deltaUsers",
Map.class);
+ assertFalse(deltaUsers.containsKey("john"));
+ }
+
+ @Test
+ public void
testAM_computeUserDelta_startupSyncSourceChange_dnValidationDisabled_updates()
throws Exception {
+ PolicyMgrUserGroupBuilder builder = new PolicyMgrUserGroupBuilder();
+
+ String oldDn = "cn=john,ou=People,dc=example,dc=com";
+ String newDn = "cn=john,ou=Moved,dc=example,dc=com";
+
+ Map<String, XUserInfo> userCache = new HashMap<>();
+ XUserInfo existing = new XUserInfo();
+ existing.setName("john");
+ existing.setSyncSource("Unix");
+ Map<String, String> oldAttrs = new HashMap<>();
+ oldAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "john");
+ oldAttrs.put(UgsyncCommonConstants.FULL_NAME, oldDn);
+ oldAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "Unix");
+ existing.setOtherAttrsMap(oldAttrs);
+ existing.setOtherAttributes(JsonUtils.objectToJson(oldAttrs));
+ userCache.put("john", existing);
+
+ setPrivate(builder, "userCache", userCache);
+ setPrivate(builder, "userNameMap", new HashMap<String, String>());
+ setPrivate(builder, "policyMgrUserName", "rangerusersync");
+ setPrivate(builder, "isStartupFlag", true);
+ setPrivate(builder, "isSyncSourceValidationEnabled", false);
+ setPrivate(builder, "isDnValidationEnabled", false);
+
+ Map<String, String> newAttrs = new HashMap<>();
+ newAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "john");
+ newAttrs.put(UgsyncCommonConstants.FULL_NAME, newDn);
+ newAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+
+ Map<String, Map<String, String>> source = new HashMap<>();
+ source.put(newDn, newAttrs);
+
+ Method compute =
PolicyMgrUserGroupBuilder.class.getDeclaredMethod("computeUserDelta",
Map.class);
+ compute.setAccessible(true);
+ compute.invoke(builder, source);
+
+ Map<String, XUserInfo> deltaUsers = getPrivate(builder, "deltaUsers",
Map.class);
+ Map<String, String> userNameMap = getPrivate(builder,
"userNameMap", Map.class);
+
+ assertTrue(deltaUsers.containsKey("john"));
+ assertEquals("john", userNameMap.get(newDn));
+ }
+
+ @Test
+ public void testAN_computeGroupDelta_dnValidationEnabled_ouMoveUpdates()
throws Exception {
+ UserGroupSyncConfig cfg = UserGroupSyncConfig.getInstance();
+ cfg.setProperty(UserGroupSyncConfig.UGSYNC_NAME_VALIDATION_ENABLED,
"false");
+
+ PolicyMgrUserGroupBuilder builder = new PolicyMgrUserGroupBuilder();
+
+ String oldDn = "cn=engineering,ou=Groups,dc=example,dc=com";
+ String newDn = "cn=engineering,ou=Moved,dc=example,dc=com";
+
+ Map<String, XGroupInfo> groupCache = new HashMap<>();
+ XGroupInfo existing = new XGroupInfo();
+ existing.setName("engineering");
+ existing.setSyncSource("LDAP/AD");
+ Map<String, String> oldAttrs = new HashMap<>();
+ oldAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "engineering");
+ oldAttrs.put(UgsyncCommonConstants.FULL_NAME, oldDn);
+ oldAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+ existing.setOtherAttrsMap(oldAttrs);
+ existing.setOtherAttributes(JsonUtils.objectToJson(oldAttrs));
+ groupCache.put("engineering", existing);
+
+ setPrivate(builder, "groupCache", groupCache);
+ setPrivate(builder, "groupNameMap", new HashMap<String, String>());
+ setPrivate(builder, "isStartupFlag", false);
+ setPrivate(builder, "isDnValidationEnabled", true);
+ setPrivate(builder, "isSyncSourceValidationEnabled", true);
+
+ Map<String, String> newAttrs = new HashMap<>();
+ newAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "engineering");
+ newAttrs.put(UgsyncCommonConstants.FULL_NAME, newDn);
+ newAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+
+ Map<String, Map<String, String>> source = new HashMap<>();
+ source.put(newDn, newAttrs);
+
+ Method compute =
PolicyMgrUserGroupBuilder.class.getDeclaredMethod("computeGroupDelta",
Map.class);
+ compute.setAccessible(true);
+ compute.invoke(builder, source);
+
+ Map<String, XGroupInfo> deltaGroups = getPrivate(builder,
"deltaGroups", Map.class);
+ Map<String, String> groupNameMap = getPrivate(builder,
"groupNameMap", Map.class);
+
+ assertTrue(deltaGroups.containsKey("engineering"));
+ assertEquals("engineering", groupNameMap.get(newDn));
+ assertFalse(groupNameMap.containsKey(oldDn));
+ }
+
+ @Test
+ public void testAO_computeGroupDelta_dnValidationDisabled_skipsOuMove()
throws Exception {
+ UserGroupSyncConfig cfg = UserGroupSyncConfig.getInstance();
+ cfg.setProperty(UserGroupSyncConfig.UGSYNC_NAME_VALIDATION_ENABLED,
"false");
+
+ PolicyMgrUserGroupBuilder builder = new PolicyMgrUserGroupBuilder();
+
+ String oldDn = "cn=engineering,ou=Groups,dc=example,dc=com";
+ String newDn = "cn=engineering,ou=Moved,dc=example,dc=com";
+
+ Map<String, XGroupInfo> groupCache = new HashMap<>();
+ XGroupInfo existing = new XGroupInfo();
+ existing.setName("engineering");
+ existing.setSyncSource("LDAP/AD");
+ Map<String, String> oldAttrs = new HashMap<>();
+ oldAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "engineering");
+ oldAttrs.put(UgsyncCommonConstants.FULL_NAME, oldDn);
+ oldAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+ existing.setOtherAttrsMap(oldAttrs);
+ existing.setOtherAttributes(JsonUtils.objectToJson(oldAttrs));
+ groupCache.put("engineering", existing);
+
+ setPrivate(builder, "groupCache", groupCache);
+ setPrivate(builder, "groupNameMap", new HashMap<String, String>());
+ setPrivate(builder, "isStartupFlag", false);
+ setPrivate(builder, "isDnValidationEnabled", false);
+ setPrivate(builder, "isSyncSourceValidationEnabled", true);
+
+ Map<String, String> newAttrs = new HashMap<>();
+ newAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "engineering");
+ newAttrs.put(UgsyncCommonConstants.FULL_NAME, oldDn);
+ newAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+
+ Map<String, Map<String, String>> source = new HashMap<>();
+ source.put(newDn, newAttrs);
+
+ Method compute =
PolicyMgrUserGroupBuilder.class.getDeclaredMethod("computeGroupDelta",
Map.class);
+ compute.setAccessible(true);
+ compute.invoke(builder, source);
+
+ Map<String, XGroupInfo> deltaGroups = getPrivate(builder,
"deltaGroups", Map.class);
+ assertFalse(deltaGroups.containsKey("engineering"));
+ }
+
+ @Test
+ public void
testAP_computeGroupDelta_startupSyncSourceChange_dnValidationDisabled_skips()
throws Exception {
+ UserGroupSyncConfig cfg = UserGroupSyncConfig.getInstance();
+ cfg.setProperty(UserGroupSyncConfig.UGSYNC_NAME_VALIDATION_ENABLED,
"false");
+
+ PolicyMgrUserGroupBuilder builder = new PolicyMgrUserGroupBuilder();
+
+ String oldDn = "cn=engineering,ou=Groups,dc=example,dc=com";
+ String newDn = "cn=engineering,ou=Moved,dc=example,dc=com";
+
+ Map<String, XGroupInfo> groupCache = new HashMap<>();
+ XGroupInfo existing = new XGroupInfo();
+ existing.setName("engineering");
+ existing.setSyncSource("Unix");
+ Map<String, String> oldAttrs = new HashMap<>();
+ oldAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "engineering");
+ oldAttrs.put(UgsyncCommonConstants.FULL_NAME, oldDn);
+ oldAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "Unix");
+ existing.setOtherAttrsMap(oldAttrs);
+ existing.setOtherAttributes(JsonUtils.objectToJson(oldAttrs));
+ groupCache.put("engineering", existing);
+
+ setPrivate(builder, "groupCache", groupCache);
+ setPrivate(builder, "groupNameMap", new HashMap<String, String>());
+ setPrivate(builder, "isStartupFlag", true);
+ setPrivate(builder, "isSyncSourceValidationEnabled", false);
+ setPrivate(builder, "isDnValidationEnabled", false);
+
+ Map<String, String> newAttrs = new HashMap<>();
+ newAttrs.put(UgsyncCommonConstants.ORIGINAL_NAME, "engineering");
+ newAttrs.put(UgsyncCommonConstants.FULL_NAME, newDn);
+ newAttrs.put(UgsyncCommonConstants.SYNC_SOURCE, "LDAP/AD");
+
+ Map<String, Map<String, String>> source = new HashMap<>();
+ source.put(newDn, newAttrs);
+
+ Method compute =
PolicyMgrUserGroupBuilder.class.getDeclaredMethod("computeGroupDelta",
Map.class);
+ compute.setAccessible(true);
+ compute.invoke(builder, source);
+
+ Map<String, XGroupInfo> deltaGroups = getPrivate(builder,
"deltaGroups", Map.class);
+ Map<String, String> groupNameMap = getPrivate(builder,
"groupNameMap", Map.class);
+
+ assertTrue(deltaGroups.containsKey("engineering"));
+ assertEquals("engineering", groupNameMap.get(newDn));
+ }
}
diff --git a/ugsync/src/test/resources/ranger-ugsync-site.xml
b/ugsync/src/test/resources/ranger-ugsync-site.xml
index 98a80ecf6..f7ec125e2 100644
--- a/ugsync/src/test/resources/ranger-ugsync-site.xml
+++ b/ugsync/src/test/resources/ranger-ugsync-site.xml
@@ -188,5 +188,15 @@
- "" : Uses both 'uSNChanged' and 'modifyTimestamp' attributes
(default behavior).
</description>
</property>
-
+ <property>
+ <name>ranger.usersync.dn.validation.enabled</name>
+ <value></value>
+ <description>
+ Controls whether LDAP DN changes (e.g. OU moves) are synced to
Ranger.
+ Accepted values:
+ - true : DN changes are allowed and synced
+ - false : DN mismatch causes update to be skipped
+ - empty : treated as false (default); DN changes are skipped
+ </description>
+ </property>
</configuration>