From a01afbca8d3437b1143b1ca33f9649ae8fd6fd56 Mon Sep 17 00:00:00 2001
From: "p.zindyaev"
Date: Tue, 19 May 2026 00:07:50 +0300
Subject: [PATCH] fix(ADPS-828): added mapping for user's first and last name
for ldap sync
---
.../ranger/ugsyncutil/model/XUserInfo.java | 8 ++-
.../util/UgsyncCommonConstants.java | 2 +
.../process/LdapUserGroupBuilder.java | 28 +++++++++
.../config/UserGroupSyncConfig.java | 32 ++++++++++
.../process/PolicyMgrUserGroupBuilder.java | 17 ++++-
.../PolicyMgrUserGroupBuilderTest.java | 10 ++-
.../usergroupsync/TestLdapUserGroup.java | 62 +++++++++++++++++++
7 files changed, 156 insertions(+), 3 deletions(-)
diff --git a/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XUserInfo.java b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XUserInfo.java
index 8012f15d978..c4a2490a399 100644
--- a/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XUserInfo.java
+++ b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XUserInfo.java
@@ -28,6 +28,7 @@ public class XUserInfo {
private String id;
private String name;
private String firstName;
+ private String lastName;
private String description;
private String otherAttributes;
private String syncSource;
@@ -58,6 +59,10 @@ public void setName(String name) {
public void setFirstName(String firstName) { this.firstName = firstName; }
+ public String getLastName() { return lastName; }
+
+ public void setLastName(String lastName) { this.lastName = lastName; }
+
public String getDescription() {
return description;
}
@@ -140,7 +145,8 @@ public void setOtherAttributes(String otherAttributes) {
@Override
public String toString() {
- return "XUserInfo [id=" + id + ", name=" + name + ", firstName=" + firstName + ", description="
+ return "XUserInfo [id=" + id + ", name=" + name + ", firstName=" + firstName
+ + ", lastName=" + lastName + ", description="
+ description + ", groupNameList=" + groupNameList
+ ", userRoleList=" + userRoleList + "]";
}
diff --git a/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/util/UgsyncCommonConstants.java b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/util/UgsyncCommonConstants.java
index f20bf919675..0e6ec282a00 100644
--- a/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/util/UgsyncCommonConstants.java
+++ b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/util/UgsyncCommonConstants.java
@@ -25,5 +25,7 @@ public class UgsyncCommonConstants {
public static final String FULL_NAME = "full_name";
public static final String SYNC_SOURCE = "sync_source";
public static final String LDAP_URL = "ldap_url";
+ public static final String FIRST_NAME = "first_name";
+ public static final String LAST_NAME = "last_name";
}
diff --git a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapUserGroupBuilder.java
index 09bf3c1ff65..862fb6c4211 100644
--- a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapUserGroupBuilder.java
@@ -91,6 +91,8 @@ public class LdapUserGroupBuilder implements UserGroupSource {
private String[] userSearchBase;
private String userNameAttribute;
+ private String userFirstNameAttribute;
+ private String userLastNameAttribute;
private String userCloudIdAttribute;
private int userSearchScope;
private String userObjectClass;
@@ -232,10 +234,14 @@ private void setConfig() throws Throwable {
userObjectClass = config.getUserObjectClass();
userSearchFilter = config.getUserSearchFilter();
userNameAttribute = config.getUserNameAttribute();
+ userFirstNameAttribute = config.getUserFirstNameAttribute();
+ userLastNameAttribute = config.getUserLastNameAttribute();
userCloudIdAttribute = config.getUserCloudIdAttribute();
Set userSearchAttributes = new HashSet();
userSearchAttributes.add(userNameAttribute);
+ userSearchAttributes.add(userFirstNameAttribute);
+ userSearchAttributes.add(userLastNameAttribute);
userGroupNameAttributeSet = config.getUserGroupNameAttributeSet();
for (String useGroupNameAttribute : userGroupNameAttributeSet) {
userSearchAttributes.add(useGroupNameAttribute);
@@ -319,6 +325,8 @@ private void setConfig() throws Throwable {
+ ", userSearchFilter: " + userSearchFilter
+ ", extendedUserSearchFilter: " + extendedUserSearchFilter
+ ", userNameAttribute: " + userNameAttribute
+ + ", userFirstNameAttribute: " + userFirstNameAttribute
+ + ", userLastNameAttribute: " + userLastNameAttribute
+ ", userSearchAttributes: " + userSearchAttributes
+ ", userGroupNameAttributeSet: " + userGroupNameAttributeSet
+ ", otherUserAttributes: " + otherUserAttributes
@@ -578,6 +586,8 @@ private long getUsers(boolean computeDeletes) throws Throwable {
userAttrMap.put(UgsyncCommonConstants.FULL_NAME, userFullName);
userAttrMap.put(UgsyncCommonConstants.SYNC_SOURCE, currentSyncSource);
userAttrMap.put(UgsyncCommonConstants.LDAP_URL, config.getLdapUrl());
+ addNameAttrToMap(attributes, userFirstNameAttribute, UgsyncCommonConstants.FIRST_NAME, userAttrMap);
+ addNameAttrToMap(attributes, userLastNameAttribute, UgsyncCommonConstants.LAST_NAME, userAttrMap);
Attribute userCloudIdAttr = attributes.get(userCloudIdAttribute);
if (userCloudIdAttr != null) {
addToAttrMap(userAttrMap, "cloud_id", userCloudIdAttr, config.getUserCloudIdAttributeDataType());
@@ -1056,6 +1066,24 @@ private void goUpGroupHierarchyLdap(Set groupDNs, int groupHierarchyLeve
goUpGroupHierarchyLdap(nextLevelGroups, groupHierarchyLevels-1);
}
+ private void addNameAttrToMap(Attributes attributes, String ldapAttrName, String mapKey, Map userAttrMap) throws NamingException {
+ if (StringUtils.isEmpty(ldapAttrName) || attributes == null) {
+ return;
+ }
+ Attribute attr = attributes.get(ldapAttrName);
+ if (attr == null) {
+ return;
+ }
+ Object val = attr.get();
+ if (val == null) {
+ return;
+ }
+ String strVal = val.toString().trim();
+ if (!strVal.isEmpty()) {
+ userAttrMap.put(mapKey, strVal);
+ }
+ }
+
private void addToAttrMap(Map userAttrMap, String attrName, Attribute attr, String attrType) throws Throwable{
if (attrType.equals(DATA_TYPE_BYTEARRAY)) {
try {
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 8703a52c645..51fa9118247 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
@@ -150,6 +150,12 @@ public class UserGroupSyncConfig {
private static final String LGSYNC_USER_NAME_ATTRIBUTE = "ranger.usersync.ldap.user.nameattribute";
private static final String DEFAULT_USER_NAME_ATTRIBUTE = "cn";
+ private static final String LGSYNC_USER_FIRST_NAME_ATTRIBUTE = "ranger.usersync.ldap.user.firstnameattribute";
+ private static final String DEFAULT_USER_FIRST_NAME_ATTRIBUTE = "givenName";
+
+ private static final String LGSYNC_USER_LAST_NAME_ATTRIBUTE = "ranger.usersync.ldap.user.lastnameattribute";
+ private static final String DEFAULT_USER_LAST_NAME_ATTRIBUTE = "sn";
+
private static final String LGSYNC_USER_GROUP_NAME_ATTRIBUTE = "ranger.usersync.ldap.user.groupnameattribute";
private static final String DEFAULT_USER_GROUP_NAME_ATTRIBUTE = "memberof,ismemberof";
@@ -707,6 +713,22 @@ public String getUserNameAttribute() {
return val;
}
+ public String getUserFirstNameAttribute() {
+ String val = prop.getProperty(LGSYNC_USER_FIRST_NAME_ATTRIBUTE);
+ if (val == null || val.trim().isEmpty()) {
+ return DEFAULT_USER_FIRST_NAME_ATTRIBUTE;
+ }
+ return val.trim();
+ }
+
+ public String getUserLastNameAttribute() {
+ String val = prop.getProperty(LGSYNC_USER_LAST_NAME_ATTRIBUTE);
+ if (val == null || val.trim().isEmpty()) {
+ return DEFAULT_USER_LAST_NAME_ATTRIBUTE;
+ }
+ return val.trim();
+ }
+
public String getUserGroupNameAttribute() {
String val = prop.getProperty(LGSYNC_USER_GROUP_NAME_ATTRIBUTE);
if(val == null || val.trim().isEmpty()) {
@@ -1248,6 +1270,16 @@ public void setUserNameAttribute(String userNameAttr) {
prop.setProperty(LGSYNC_USER_NAME_ATTRIBUTE, userNameAttr);
}
+ /* Used only for unit testing */
+ public void setUserFirstNameAttribute(String userFirstNameAttr) {
+ prop.setProperty(LGSYNC_USER_FIRST_NAME_ATTRIBUTE, userFirstNameAttr);
+ }
+
+ /* Used only for unit testing */
+ public void setUserLastNameAttribute(String userLastNameAttr) {
+ prop.setProperty(LGSYNC_USER_LAST_NAME_ATTRIBUTE, userLastNameAttr);
+ }
+
/* Used only for unit testing */
public void setGroupHierarchyLevel(int groupHierarchyLevel) {
prop.setProperty(LGSYNC_GROUP_HIERARCHY_LEVELS, String.valueOf(groupHierarchyLevel));
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 8c33baf80da..d2a6ea255f9 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
@@ -639,6 +639,16 @@ private T setOtherAttributes(T uginfo, String syncSource, Map computeGroupUsersDelta(Map> sour
private XUserInfo addXUserInfo(String aUserName, Map otherAttrsMap, String otherAttributes) {
XUserInfo xuserInfo = new XUserInfo();
xuserInfo.setName(aUserName);
- xuserInfo.setFirstName(aUserName);
+ String firstName = otherAttrsMap != null ? otherAttrsMap.get(UgsyncCommonConstants.FIRST_NAME) : null;
+ String lastName = otherAttrsMap != null ? otherAttrsMap.get(UgsyncCommonConstants.LAST_NAME) : null;
+ xuserInfo.setFirstName(StringUtils.isNotBlank(firstName) ? firstName : aUserName);
+ if (StringUtils.isNotBlank(lastName)) {
+ xuserInfo.setLastName(lastName);
+ }
xuserInfo.setDescription(aUserName + " - add from Unix box");
xuserInfo.setUserSource(SOURCE_EXTERNAL);
xuserInfo.setStatus(STATUS_ENABLED);
diff --git a/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java b/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java
index fd8181be93e..7014081c025 100644
--- a/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java
+++ b/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java
@@ -32,6 +32,7 @@ public class PolicyMgrUserGroupBuilderTest extends PolicyMgrUserGroupBuilder {
private Map> groupUsers;
private Set invalidGroups;
private Set invalidUsers;
+ private Map> userAttrsByName;
public PolicyMgrUserGroupBuilderTest() {
super();
@@ -44,6 +45,11 @@ public void init() throws Throwable {
groupUsers = new HashMap<>();
invalidGroups = new HashSet<>();
invalidUsers = new HashSet<>();
+ userAttrsByName = new HashMap<>();
+ }
+
+ public Map getUserAttrs(String username) {
+ return userAttrsByName.get(username);
}
public int getTotalUsers() {
@@ -97,8 +103,10 @@ public void addOrUpdateUsersGroups(Map> sourceGroups
for (String userdn : sourceUsers.keySet()) {
//System.out.println("Username: " + sourceUsers.get(userdn).get("original_name"));
- String username = userNameTransform(sourceUsers.get(userdn).get("original_name"));
+ Map userAttrs = sourceUsers.get(userdn);
+ String username = userNameTransform(userAttrs.get("original_name"));
allUsers.add(username);
+ userAttrsByName.put(username, userAttrs);
if (!isValidString(username)) {
invalidUsers.add(username);
}
diff --git a/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestLdapUserGroup.java b/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestLdapUserGroup.java
index 2011b5b75be..4f235428b20 100644
--- a/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestLdapUserGroup.java
+++ b/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestLdapUserGroup.java
@@ -21,6 +21,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Map;
+
+import org.apache.ranger.ugsyncutil.util.UgsyncCommonConstants;
import org.apache.directory.server.annotations.CreateLdapConnectionPool;
import org.apache.directory.server.core.annotations.ApplyLdifFiles;
@@ -466,6 +472,62 @@ public void testGroupsWithNoUsers() throws Throwable {
assertEquals(2, sink.getGroupsWithNoUsers());
}
+ @Test
+ public void testUserFirstAndLastNameMapping() throws Throwable {
+ config.setUserNameAttribute("sAMAccountName");
+ config.setUserFirstNameAttribute("cn");
+ config.setUserLastNameAttribute("sn");
+ config.setUserSearchBase("cn=users,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+ config.setUserSearchFilter("");
+ config.setGroupSearchBase("OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+ config.setUserGroupMemberAttributeName("member");
+ config.setUserObjectClass("organizationalPerson");
+ config.setGroupObjectClass("groupOfNames");
+ config.setGroupSearchEnabled(false);
+ config.setPagedResultsEnabled(true);
+ config.setGroupSearchFirstEnabled(false);
+ config.setUserSearchEnabled(true);
+ // clear any group-name filter leftover from prior tests in this suite
+ config.setGroupnames("");
+ ldapBuilder.init();
+ sink.init();
+ ldapBuilder.updateSink(sink);
+
+ // sAMAccountName for cn=User1000 entry is "U1000"; cn/sn provide first/last name
+ Map u1000Attrs = sink.getUserAttrs("U1000");
+ assertNotNull("U1000 should have been synced", u1000Attrs);
+ assertEquals("User1000", u1000Attrs.get(UgsyncCommonConstants.FIRST_NAME));
+ assertEquals("User1000", u1000Attrs.get(UgsyncCommonConstants.LAST_NAME));
+ }
+
+ @Test
+ public void testUserNameMappingWithoutFirstNameAttribute() throws Throwable {
+ config.setUserNameAttribute("sAMAccountName");
+ // firstName attribute defaults to givenName which is not present in the test LDIF
+ config.setUserFirstNameAttribute("givenName");
+ config.setUserLastNameAttribute("sn");
+ config.setUserSearchBase("cn=users,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+ config.setUserSearchFilter("");
+ config.setGroupSearchBase("OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+ config.setUserGroupMemberAttributeName("member");
+ config.setUserObjectClass("organizationalPerson");
+ config.setGroupObjectClass("groupOfNames");
+ config.setGroupSearchEnabled(false);
+ config.setPagedResultsEnabled(true);
+ config.setGroupSearchFirstEnabled(false);
+ config.setUserSearchEnabled(true);
+ // clear any group-name filter leftover from prior tests in this suite
+ config.setGroupnames("");
+ ldapBuilder.init();
+ sink.init();
+ ldapBuilder.updateSink(sink);
+
+ Map u1000Attrs = sink.getUserAttrs("U1000");
+ assertNotNull("U1000 should have been synced", u1000Attrs);
+ assertNull("givenName is absent in the test LDIF", u1000Attrs.get(UgsyncCommonConstants.FIRST_NAME));
+ assertEquals("User1000", u1000Attrs.get(UgsyncCommonConstants.LAST_NAME));
+ }
+
@After
public void shutdown() throws Exception {
if (getService().isStarted()) {