Repository: ranger Updated Branches: refs/heads/master 6890976ee -> f4019d11b
RANGER-1735: Support representing nested group memberships in Ranger Admin Project: http://git-wip-us.apache.org/repos/asf/ranger/repo Commit: http://git-wip-us.apache.org/repos/asf/ranger/commit/f4019d11 Tree: http://git-wip-us.apache.org/repos/asf/ranger/tree/f4019d11 Diff: http://git-wip-us.apache.org/repos/asf/ranger/diff/f4019d11 Branch: refs/heads/master Commit: f4019d11ba20fb6d61171876e4f14ea428028887 Parents: 6890976 Author: Sailaja Polavarapu <spolavar...@hortonworks.com> Authored: Thu Sep 7 14:21:27 2017 -0700 Committer: Sailaja Polavarapu <spolavar...@hortonworks.com> Committed: Thu Sep 7 14:21:27 2017 -0700 ---------------------------------------------------------------------- .../process/LdapDeltaUserGroupBuilder.java | 309 +++++++++++++----- .../process/LdapUserGroupBuilder.java | 322 +++++++++++++++---- .../config/UserGroupSyncConfig.java | 22 ++ .../ranger/usergroupsync/TestLdapUserGroup.java | 11 +- ugsync/src/test/resources/ADSchema.ldif | 2 + .../src/test/resources/ranger-ugsync-site.xml | 5 + 6 files changed, 524 insertions(+), 147 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ranger/blob/f4019d11/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java ---------------------------------------------------------------------- diff --git a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java index d27a217..394bde2 100644 --- a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java +++ b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java @@ -49,6 +49,8 @@ import javax.naming.ldap.PagedResultsResponseControl; import javax.naming.ldap.StartTlsRequest; import javax.naming.ldap.StartTlsResponse; +import org.apache.commons.collections.BidiMap; +import org.apache.commons.collections.bidimap.DualHashBidiMap; import org.apache.log4j.Logger; import org.apache.ranger.unixusersync.config.UserGroupSyncConfig; import org.apache.ranger.usergroupsync.AbstractUserGroupSource; @@ -98,6 +100,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { private SearchControls groupSearchControls; private String groupMemberAttributeName; private String groupNameAttribute; + private int groupHierarchyLevels; private LdapContext ldapContext; StartTlsResponse tls; @@ -110,21 +113,22 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { private boolean groupUserMapSyncEnabled = false; //private Map<String, UserInfo> userGroupMap; - - private Table<String, String, String> groupUserTable; + + private Table<String, String, String> groupUserTable; private Map<String, String> userNameMap; + private BidiMap groupNameMap; public static void main(String[] args) throws Throwable { LdapDeltaUserGroupBuilder ugBuilder = new LdapDeltaUserGroupBuilder(); ugBuilder.init(); } - + public LdapDeltaUserGroupBuilder() { super(); LOG.info("LdapDeltaUserGroupBuilder created"); - + String userNameCaseConversion = config.getUserNameCaseConversion(); - + if (UserGroupSyncConfig.UGSYNC_NONE_CASE_CONVERSION_VALUE.equalsIgnoreCase(userNameCaseConversion)) { userNameCaseConversionFlag = false; } @@ -132,9 +136,9 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { userNameCaseConversionFlag = true; userNameLowerCaseFlag = UserGroupSyncConfig.UGSYNC_LOWER_CASE_CONVERSION_VALUE.equalsIgnoreCase(userNameCaseConversion); } - + String groupNameCaseConversion = config.getGroupNameCaseConversion(); - + if (UserGroupSyncConfig.UGSYNC_NONE_CASE_CONVERSION_VALUE.equalsIgnoreCase(groupNameCaseConversion)) { groupNameCaseConversionFlag = false; } @@ -145,7 +149,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { } @Override - public void init() throws Throwable{ + public void init() throws Throwable{ deltaSyncUserTime = 0; deltaSyncGroupTime = 0; DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); @@ -154,7 +158,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { userNameMap = new HashMap<String, String>(); setConfig(); } - + private void createLdapContext() throws Throwable { Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, @@ -182,7 +186,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { ldapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, ldapAuthenticationMechanism); ldapContext.addToEnvironment(Context.REFERRAL, ldapReferral); } - + private void setConfig() throws Throwable { LOG.info("LdapDeltaUserGroupBuilder initialization started"); @@ -196,14 +200,14 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { ldapAuthenticationMechanism = config.getLdapAuthenticationMechanism(); ldapReferral = config.getContextReferral(); searchBase = config.getSearchBase(); - + userSearchBase = config.getUserSearchBase().split(";"); userSearchScope = config.getUserSearchScope(); userObjectClass = config.getUserObjectClass(); userSearchFilter = config.getUserSearchFilter(); - + userNameAttribute = config.getUserNameAttribute(); - + Set<String> userSearchAttributes = new HashSet<String>(); userSearchAttributes.add(userNameAttribute); // For Group based search, user's group name attribute should not be added to the user search attributes @@ -229,6 +233,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { groupSearchFilter = config.getGroupSearchFilter(); groupMemberAttributeName = config.getUserGroupMemberAttributeName(); groupNameAttribute = config.getGroupNameAttribute(); + groupHierarchyLevels = config.getGroupHierarchyLevels(); extendedGroupSearchFilter = "(&" + extendedGroupSearchFilter + "(|(" + groupMemberAttributeName + "={0})(" + groupMemberAttributeName + "={1})))"; groupUserMapSyncEnabled = config.isGroupUserMapSyncEnabled(); @@ -279,7 +284,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { } } - + private void closeLdapContext() throws Throwable { if (tls != null) { tls.close(); @@ -288,7 +293,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { ldapContext.close(); } } - + @Override public boolean isChanged() { // we do not want to get the full ldap dit and check whether anything has changed @@ -299,27 +304,39 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { public void updateSink(UserGroupSink sink) throws Throwable { LOG.info("LdapDeltaUserGroupBuilder updateSink started"); //userGroupMap = new HashMap<String, UserInfo>(); - groupUserTable = HashBasedTable.create(); - if (!groupSearchFirstEnabled) { + groupUserTable = HashBasedTable.create(); + groupNameMap = new DualHashBidiMap(); + if (!groupSearchFirstEnabled) { LOG.info("Performing user search first"); getUsers(sink); if (groupSearchEnabled) { getGroups(sink); } //LOG.debug("Total No. of users saved = " + groupUserTable.columnKeySet().size()); - + } else { LOG.info("Performing Group search first"); getGroups(sink); if (userSearchEnabled) { LOG.info("User search is enabled and hence computing user membership."); getUsers(sink); - } + } } if (groupUserTable.isEmpty()) { //System.out.println("groupUserTable is empty!!"); return; } + + if (groupHierarchyLevels > 0) { + LOG.info("Going through group hierarchy for nested group evaluation"); + Set<String> groupFullNames = groupNameMap.keySet(); + for(String group : groupFullNames) { + Set<String> nextLevelGroups = groupUserTable.column(group).keySet(); + goUpGroupHierarchy(nextLevelGroups, groupHierarchyLevels-1, groupNameMap.get(group).toString()); + } + LOG.info("Completed group hierarchy computation"); + } + Iterator<String> groupUserTableIterator = groupUserTable.rowKeySet().iterator(); while (groupUserTableIterator.hasNext()) { String groupName = groupUserTableIterator.next(); @@ -327,12 +344,12 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { Map<String,String> groupUsersMap = groupUserTable.row(groupName); Set<String> userSet = new HashSet<String>(); for(Map.Entry<String, String> entry : groupUsersMap.entrySet()){ - //String transformUserName = userNameTransform(entry.getKey()); + //String transformUserName = userNameTransform(entry.getKey()); userSet.add(entry.getValue()); } List<String> userList = new ArrayList<>(userSet); String transformGroupName = groupNameTransform(groupName); - try { + try { sink.addOrUpdateGroup(transformGroupName, userList); } catch (Throwable t) { LOG.error("sink.addOrUpdateGroup failed with exception: " + t.getMessage() @@ -341,7 +358,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { } } } - + private void getUsers(UserGroupSink sink) throws Throwable { NamingEnumeration<SearchResult> userSearchResultEnum = null; NamingEnumeration<SearchResult> groupSearchResultEnum = null; @@ -355,7 +372,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { } DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); extendedUserSearchFilter = "(objectclass=" + userObjectClass + ")(|(uSNChanged>=" + deltaSyncUserTime + ")(modifyTimestamp>=" + deltaSyncUserTimeStamp + "Z))"; - + if (userSearchFilter != null && !userSearchFilter.trim().isEmpty()) { String customFilter = userSearchFilter.trim(); if (!customFilter.startsWith("(")) { @@ -380,7 +397,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { userSearchResultEnum = ldapContext .search(userSearchBase[ou], extendedUserSearchFilter, userSearchControls); - + while (userSearchResultEnum.hasMore()) { // searchResults contains all the user entries final SearchResult userEntry = userSearchResultEnum.next(); @@ -410,7 +427,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { } continue; } - + String userFullName = (userEntry.getNameInNamespace()).toLowerCase(); String userName = (String) userNameAttr.get(); @@ -421,7 +438,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { } continue; } - + Attribute timeStampAttr = attributes.get("uSNChanged"); if (timeStampAttr != null) { String uSNChangedVal = (String) timeStampAttr.get(); @@ -434,7 +451,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { timeStampAttr = attributes.get("modifytimestamp"); if (timeStampAttr != null) { String timeStampVal = (String) timeStampAttr.get(); - Date parseDate = dateFormat.parse(timeStampVal); + Date parseDate = dateFormat.parse(timeStampVal); long currentDeltaSyncTime = parseDate.getTime(); LOG.info("timeStampVal = " + timeStampVal + "and currentDeltaSyncTime = " + currentDeltaSyncTime); if (currentDeltaSyncTime > highestdeltaSyncUserTime) { @@ -475,7 +492,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { List<String> groupList = new ArrayList<String>(groups); try { sink.addOrUpdateUser(transformUserName, groupList); - + } catch (Throwable t) { LOG.error("sink.addOrUpdateUserGroups failed with exception: " + t.getMessage() + ", for user: " + transformUserName + " and groups: " + groupList); @@ -575,9 +592,11 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { closeLdapContext(); } } - + private void getGroups(UserGroupSink sink) throws Throwable { NamingEnumeration<SearchResult> groupSearchResultEnum = null; + DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); + long highestdeltaSyncGroupTime = deltaSyncGroupTime; try { createLdapContext(); int total; @@ -594,12 +613,10 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { } extendedGroupSearchFilter = extendedGroupSearchFilter + customFilter; } - - DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); + extendedAllGroupsSearchFilter = "(&" + extendedGroupSearchFilter + "(|(uSNChanged>=" + deltaSyncGroupTime + ")(modifyTimestamp>=" + deltaSyncGroupTimeStamp + "Z)))"; - + LOG.info("extendedAllGroupsSearchFilter = " + extendedAllGroupsSearchFilter); - long highestdeltaSyncGroupTime = deltaSyncGroupTime; for (int ou=0; ou<groupSearchBase.length; ou++) { byte[] cookie = null; int counter = 0; @@ -646,7 +663,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { timeStampAttr = groupEntry.getAttributes().get("modifytimestamp"); if (timeStampAttr != null) { String timeStampVal = (String) timeStampAttr.get(); - Date parseDate = dateFormat.parse(timeStampVal); + Date parseDate = dateFormat.parse(timeStampVal); long currentDeltaSyncTime = parseDate.getTime(); LOG.info("timeStampVal = " + timeStampVal + "and currentDeltaSyncTime = " + currentDeltaSyncTime); if (currentDeltaSyncTime > highestdeltaSyncGroupTime) { @@ -661,7 +678,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { LOG.info("No members available for " + gName); continue; } - + NamingEnumeration<?> userEnum = groupMemberAttr.getAll(); while (userEnum.hasMore()) { String originalUserFullName = (String) userEnum.next(); @@ -687,6 +704,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { } else { groupUserTable.put(gName, originalUserFullName, originalUserFullName); } + groupNameMap.put(groupEntry.getNameInNamespace().toLowerCase(), gName); } LOG.info("No. of members in the group " + gName + " = " + userCount); } @@ -719,17 +737,11 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { LOG.info("LdapDeltaUserGroupBuilder.getGroups() completed with group count: " + counter); } catch (Exception t) { - LOG.error("LdapDeltaUserGroupBuilder.getGroups() failed with exception: " + t); + LOG.error("LdapDeltaUserGroupBuilder.getGroups() failed with exception: " + t); LOG.info("LdapDeltaUserGroupBuilder.getGroups() group count: " + counter); } } - if (deltaSyncGroupTime < highestdeltaSyncGroupTime) { - // Incrementing highestdeltaSyncGroupTime (for AD) in order to avoid search record repetition for next sync cycle. - deltaSyncGroupTime = highestdeltaSyncGroupTime+1; - // Incrementing the highest timestamp value (for OpenLdap) with 1min in order to avoid search record repetition for next sync cycle. - deltaSyncGroupTimeStamp = dateFormat.format(new Date(highestdeltaSyncGroupTime + 60000l)); - } } finally { if (groupSearchResultEnum != null) { @@ -737,9 +749,22 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { } closeLdapContext(); } + + if (groupHierarchyLevels > 0) { + if (deltaSyncGroupTime > 0) { + goUpGroupHierarchyLdap(groupNameMap.keySet(), groupHierarchyLevels-1); + } + } + + if (deltaSyncGroupTime < highestdeltaSyncGroupTime) { + // Incrementing highestdeltaSyncGroupTime (for AD) in order to avoid search record repetition for next sync cycle. + deltaSyncGroupTime = highestdeltaSyncGroupTime+1; + // Incrementing the highest timestamp value (for OpenLdap) with 1min in order to avoid search record repetition for next sync cycle. + deltaSyncGroupTimeStamp = dateFormat.format(new Date(highestdeltaSyncGroupTime + 60000l)); + } } - + private static String getShortGroupName(String longGroupName) throws InvalidNameException { if (longGroupName == null) { return null; @@ -755,7 +780,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { LOG.info("longGroupName: " + longGroupName + ", groupName: " + groupName); return groupName; } - + private static String getShortUserName(String longUserName) throws InvalidNameException { if (longUserName == null) { return null; @@ -771,7 +796,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { LOG.info("longUserName: " + longUserName + ", userName: " + userName); return userName; } - + private String userNameTransform(String userName) { //String userNameTransform = userName; if (userNameCaseConversionFlag) { @@ -789,7 +814,7 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { return userName; } - + private String groupNameTransform(String groupName) { //String userNameTransform = userName; if (groupNameCaseConversionFlag) { @@ -807,37 +832,167 @@ public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { return groupName; } - -} -/*class UserInfo { - private String userName; - private String userFullName; - private Set<String> groupList; - - public UserInfo(String userName, String userFullName) { - this.userName = userName; - this.userFullName = userFullName; - this.groupList = new HashSet<String>(); - } - - public void updateUserName(String userName) { - this.userName = userName; - } - - public String getUserName() { - return userName; - } - public String getUserFullName() { - return userFullName; - } - public void addGroups(Set<String> groups) { - groupList.addAll(groups); - } - public void addGroup(String group) { - groupList.add(group); + private void goUpGroupHierarchy(Set<String> groups, int groupHierarchyLevels, String groupSName) throws InvalidNameException { + if (groupHierarchyLevels <= 0 || groups.isEmpty()) { + return; + } + LOG.info("nextLevelGroups = " + groups + " for group = " + groupSName); + Set<String> nextLevelGroups; + + for (String group : groups) { + String groupFullName = groupNameMap.getKey(group).toString(); + + // Add all members of sub group to the parent groups if the member is not a group in turn + Set<String> allMembers = groupUserTable.row(groupSName).keySet(); + for(String member : allMembers) { + String memberName = getShortGroupName(member); + if (!groupUserTable.containsRow(memberName)) { //Check if the member of a group is in turn a group + LOG.info("Adding " + member + " to " + group); + String userSName = groupUserTable.get(groupSName, member); + LOG.info("Short name of " + member + " = " + userSName); + if (userSName != null) { + groupUserTable.put(group, member, userSName); //Add users from the nested group to parent group + } + } + } + nextLevelGroups = groupUserTable.column(groupFullName).keySet(); + goUpGroupHierarchy(nextLevelGroups, groupHierarchyLevels - 1, group); + } } - public List<String> getGroups() { - return (new ArrayList<String>(groupList)); + + private void goUpGroupHierarchyLdap(Set<String> groupDNs, int groupHierarchyLevels) throws Throwable { + if (groupHierarchyLevels <= 0 || groupDNs.isEmpty()) { + return; + } + Set<String> nextLevelGroups = new HashSet<String>(); + + NamingEnumeration<SearchResult> groupSearchResultEnum = null; + try { + createLdapContext(); + int total; + // Activate paged results + if (pagedResultsEnabled) { + ldapContext.setRequestControls(new Control[]{ + new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) }); + } + String groupFilter = "(&(objectclass=" + groupObjectClass + ")"; + if (groupSearchFilter != null && !groupSearchFilter.trim().isEmpty()) { + String customFilter = groupSearchFilter.trim(); + if (!customFilter.startsWith("(")) { + customFilter = "(" + customFilter + ")"; + } + groupFilter += customFilter + "(|"; + } + StringBuilder filter = new StringBuilder(); + + for (String groupDN : groupDNs) { + filter.append("(").append(groupMemberAttributeName).append("=") + .append(groupDN).append(")"); + } + filter.append("))"); + groupFilter += filter; + + LOG.info("extendedAllGroupsSearchFilter = " + groupFilter); + for (int ou=0; ou<groupSearchBase.length; ou++) { + byte[] cookie = null; + int counter = 0; + try { + do { + groupSearchResultEnum = ldapContext + .search(groupSearchBase[ou], groupFilter, + groupSearchControls); + while (groupSearchResultEnum.hasMore()) { + final SearchResult groupEntry = groupSearchResultEnum.next(); + if (groupEntry == null) { + if (LOG.isInfoEnabled()) { + LOG.info("groupEntry null, skipping sync for the entry"); + } + continue; + } + counter++; + Attribute groupNameAttr = groupEntry.getAttributes().get(groupNameAttribute); + if (groupNameAttr == null) { + if (LOG.isInfoEnabled()) { + LOG.info(groupNameAttribute + " empty for entry " + groupEntry.getNameInNamespace() + + ", skipping sync"); + } + continue; + } + nextLevelGroups.add(groupEntry.getNameInNamespace()); + String gName = (String) groupNameAttr.get(); + + Attribute groupMemberAttr = groupEntry.getAttributes().get(groupMemberAttributeName); + int userCount = 0; + if (groupMemberAttr == null || groupMemberAttr.size() <= 0) { + LOG.info("No members available for " + gName); + continue; + } + + NamingEnumeration<?> userEnum = groupMemberAttr.getAll(); + while (userEnum.hasMore()) { + String originalUserFullName = (String) userEnum.next(); + if (originalUserFullName == null || originalUserFullName.trim().isEmpty()) { + continue; + } + userCount++; + originalUserFullName = originalUserFullName.toLowerCase(); + if (userNameMap.get(originalUserFullName) != null) { + groupUserTable.put(gName, originalUserFullName, userNameMap.get(originalUserFullName)); + } else { + groupUserTable.put(gName, originalUserFullName, originalUserFullName); + } + + } + LOG.info("No. of members in the group " + gName + " = " + userCount); + } + // Examine the paged results control response + Control[] controls = ldapContext.getResponseControls(); + if (controls != null) { + for (int i = 0; i < controls.length; i++) { + if (controls[i] instanceof PagedResultsResponseControl) { + PagedResultsResponseControl prrc = + (PagedResultsResponseControl)controls[i]; + total = prrc.getResultSize(); + if (total != 0) { + LOG.debug("END-OF-PAGE total : " + total); + } else { + LOG.debug("END-OF-PAGE total : unknown"); + } + cookie = prrc.getCookie(); + } + } + } else { + LOG.debug("No controls were sent from the server"); + } + // Re-activate paged results + if (pagedResultsEnabled) { + ldapContext.setRequestControls(new Control[]{ + new PagedResultsControl(pagedResultsSize, cookie, Control.CRITICAL) }); + } + } while (cookie != null); + LOG.info("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() completed with group count: " + + counter); + } catch (RuntimeException re) { + LOG.error("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() failed with runtime exception: ", re); + throw re; + } catch (Exception t) { + LOG.error("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() failed with exception: ", t); + LOG.info("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() group count: " + + counter); + } + } + + } catch (RuntimeException re) { + LOG.error("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() failed with exception: ", re); + throw re; + } finally { + if (groupSearchResultEnum != null) { + groupSearchResultEnum.close(); + } + closeLdapContext(); + } + goUpGroupHierarchyLdap(nextLevelGroups, groupHierarchyLevels-1); } -}*/ +} + http://git-wip-us.apache.org/repos/asf/ranger/blob/f4019d11/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapUserGroupBuilder.java ---------------------------------------------------------------------- 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 e62da5b..8dc147c 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 @@ -87,6 +87,7 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { private SearchControls groupSearchControls; private String groupMemberAttributeName; private String groupNameAttribute; + private int groupHierarchyLevels; private LdapContext ldapContext; private StartTlsResponse tls; @@ -99,6 +100,7 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { private boolean groupUserMapSyncEnabled; private Map<String, UserInfo> userGroupMap; + //private Set<String> firstGroupDNs; public static void main(String[] args) throws Throwable { LdapUserGroupBuilder ugBuilder = new LdapUserGroupBuilder(); @@ -131,7 +133,7 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { } @Override - public void init() throws Throwable{ + public void init() throws Throwable{ setConfig(); } @@ -205,7 +207,7 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { userSearchControls = new SearchControls(); userSearchControls.setSearchScope(userSearchScope); userSearchControls.setReturningAttributes(userSearchAttributes.toArray( - new String[userSearchAttributes.size()])); + new String[userSearchAttributes.size()])); pagedResultsEnabled = config.isPagedResultsEnabled(); pagedResultsSize = config.getPagedResultsSize(); @@ -216,6 +218,7 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { groupSearchFilter = config.getGroupSearchFilter(); groupMemberAttributeName = config.getUserGroupMemberAttributeName(); groupNameAttribute = config.getGroupNameAttribute(); + groupHierarchyLevels = config.getGroupHierarchyLevels(); extendedGroupSearchFilter = "(objectclass=" + groupObjectClass + ")"; if (groupSearchFilter != null && !groupSearchFilter.trim().isEmpty()) { @@ -296,20 +299,30 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { public void updateSink(UserGroupSink sink) throws Throwable { LOG.info("LDAPUserGroupBuilder updateSink started"); userGroupMap = new HashMap<String, UserInfo>(); + if (!groupSearchFirstEnabled) { LOG.info("Performing user search first"); getUsers(sink); - LOG.debug("Total No. of users saved = " + userGroupMap.size()); - //Iterator<UserInfo> userInfoIterator = userGroupMap. + if (!groupSearchEnabled && groupHierarchyLevels > 0) { + getRootDN(); + } + //Iterator<UserInfo> userInfoIterator = userGroupMap. for (UserInfo userInfo : userGroupMap.values()) { String userName = userInfo.getUserName(); if (groupSearchEnabled) { // Perform group search LOG.info("groupSearch is enabled, would search for groups and compute memberships"); + //firstGroupDNs = new HashSet<String>(); getGroups(sink, userInfo); } + if (groupHierarchyLevels > 0) { + LOG.debug("Going through group hierarchy for nested group evaluation"); + goUpGroupHierarchyLdap(userInfo.getGroupDNs(), groupHierarchyLevels - 1, userInfo); + LOG.debug("Completed group hierarchy computation"); + } List<String> groupList = userInfo.getGroups(); + LOG.debug("updateSink(): group list for " + userName + " = " + groupList); if (userNameCaseConversionFlag) { if (userNameLowerCaseFlag) { userName = userName.toLowerCase(); @@ -334,37 +347,41 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { } else { LOG.info("Performing Group search first"); getGroups(sink, null); - if (userSearchEnabled) { - LOG.info("User search is enabled and hence computing user membership."); - getUsers(sink); - } else { - LOG.info("User search is disabled and hence using the group member attribute for username."); - // Go through the userInfo map and update ranger admin. - for (UserInfo userInfo : userGroupMap.values()) { - String userName = getShortUserName(userInfo.getUserFullName()); - List<String> groupList = userInfo.getGroups(); - if (userNameCaseConversionFlag) { - if (userNameLowerCaseFlag) { - userName = userName.toLowerCase(); - } - else { - userName = userName.toUpperCase(); - } - } - - if (userNameRegExInst != null) { - userName = userNameRegExInst.transform(userName); - } - - try { - sink.addOrUpdateUser(userName, groupList); - } catch (Throwable t) { - LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage() - + ", for user: " + userName - + ", groups: " + groupList); - } - } - } + // Go through the userInfo map and update ranger admin. + for (UserInfo userInfo : userGroupMap.values()) { + String userName = getShortUserName(userInfo.getUserFullName()); + if (groupHierarchyLevels > 0) { + //System.out.println("Going through group hierarchy for nested group evaluation"); + goUpGroupHierarchyLdap(userInfo.getGroupDNs(), groupHierarchyLevels - 1, userInfo); + //System.out.println("Completed group hierarchy computation"); + } + if (userSearchEnabled) { + LOG.info("User search is enabled and hence computing user membership."); + getUsers(sink); + } else { + LOG.info("User search is disabled and hence using the group member attribute for username" + userName); + List<String> groupList = userInfo.getGroups(); + if (userNameCaseConversionFlag) { + if (userNameLowerCaseFlag) { + userName = userName.toLowerCase(); + } else { + userName = userName.toUpperCase(); + } + } + + if (userNameRegExInst != null) { + userName = userNameRegExInst.transform(userName); + } + + try { + sink.addOrUpdateUser(userName, groupList); + } catch (Throwable t) { + LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage() + + ", for user: " + userName + + ", groups: " + groupList); + } + } + } } } @@ -442,8 +459,10 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { if (userGroupfAttribute != null) { NamingEnumeration<?> groupEnum = userGroupfAttribute.getAll(); while (groupEnum.hasMore()) { - String gName = getShortGroupName((String) groupEnum - .next()); + String groupDN = (String) groupEnum.next(); + LOG.debug("Adding " + groupDN + " to " + userName); + userInfo.addGroupDN(groupDN); + String gName = getShortGroupName(groupDN); if (groupNameCaseConversionFlag) { if (groupNameLowerCaseFlag) { gName = gName.toLowerCase(); @@ -498,7 +517,7 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { // then update user name in the userInfo map with the value from the search result // and update ranger admin. String userFullName = (userEntry.getNameInNamespace()).toLowerCase(); - LOG.debug("Chekcing if the user " + userFullName + " is part of the retrieved groups"); + LOG.debug("Checking if the user " + userFullName + " is part of the retrieved groups"); userInfo = userGroupMap.get(userFullName); if (userInfo == null) { @@ -508,27 +527,27 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { counter++; LOG.info("Updating username for " + userFullName + " with " + userName); userInfo.updateUserName(userName); - List<String> groupList = userInfo.getGroups(); - if (userNameCaseConversionFlag) { - if (userNameLowerCaseFlag) { - userName = userName.toLowerCase(); - } - else { - userName = userName.toUpperCase(); - } - } - - if (userNameRegExInst != null) { - userName = userNameRegExInst.transform(userName); - } - - try { - sink.addOrUpdateUser(userName, groupList); - } catch (Throwable t) { - LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage() - + ", for user: " + userName - + ", groups: " + groupList); - } + List<String> groupList = userInfo.getGroups(); + if (userNameCaseConversionFlag) { + if (userNameLowerCaseFlag) { + userName = userName.toLowerCase(); + } + else { + userName = userName.toUpperCase(); + } + } + + if (userNameRegExInst != null) { + userName = userNameRegExInst.transform(userName); + } + + try { + sink.addOrUpdateUser(userName, groupList); + } catch (Throwable t) { + LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage() + + ", for user: " + userName + + ", groups: " + groupList); + } } } @@ -581,11 +600,12 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { } private void getGroups(UserGroupSink sink, UserInfo userInfo) throws Throwable { + //LOG.debug("getGroups(): for user " + userInfo.getUserName()); NamingEnumeration<SearchResult> groupSearchResultEnum = null; try { createLdapContext(); int total; - // Activate paged results + // Activate paged results if (pagedResultsEnabled) { ldapContext.setRequestControls(new Control[]{ new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) }); @@ -622,6 +642,7 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { if (groupEntry != null) { counter++; Attribute groupNameAttr = groupEntry.getAttributes().get(groupNameAttribute); + //System.out.println("getGroups(): Going through all groups"); if (groupNameAttr == null) { if (LOG.isInfoEnabled()) { LOG.info(groupNameAttribute + " empty for entry " + groupEntry.getNameInNamespace() + @@ -629,7 +650,9 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { } continue; } - String gName = (String) groupNameAttr.get(); + String groupDN = groupEntry.getNameInNamespace(); + //System.out.println("getGroups(): groupDN = " + groupDN); + String gName = (String) groupNameAttr.get(); if (groupNameCaseConversionFlag) { if (groupNameLowerCaseFlag) { gName = gName.toLowerCase(); @@ -643,9 +666,10 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { if (!groupSearchFirstEnabled) { //computedGroups.add(gName); if (LOG.isInfoEnabled()) { - LOG.info("computed groups for user: " + userInfo.getUserName() +", groups: " + gName); + LOG.info("computed groups for user: " + userInfo.getUserName() + ", groups: " + gName); } - userInfo.addGroup(gName); + userInfo.addGroupDN(groupDN); + userInfo.addGroup(gName); } else { // If group based search is enabled, then // update the group name to ranger admin @@ -671,9 +695,10 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { userGroupMap.put(userFullName, userInfo); } else { userInfo = userGroupMap.get(userFullName); - } - LOG.info("Adding " + gName + " to user " + userInfo.getUserFullName()); - userInfo.addGroup(gName); + } + LOG.info("Adding " + gName + " to user " + userInfo.getUserFullName()); + userInfo.addGroup(gName); + userInfo.addGroupDN(groupDN); } LOG.info("No. of members in the group " + gName + " = " + userCount); } @@ -755,17 +780,172 @@ public class LdapUserGroupBuilder extends AbstractUserGroupSource { return userName; } + private void goUpGroupHierarchyLdap(Set<String> groupDNs, int groupHierarchyLevels, UserInfo userInfo) throws Throwable { + LOG.debug("goUpGroupHierarchyLdap(): Incoming groups " + groupDNs); + if (groupHierarchyLevels <= 0 || groupDNs.isEmpty()) { + return; + } + Set<String> nextLevelGroups = new HashSet<String>(); + + NamingEnumeration<SearchResult> groupSearchResultEnum = null; + try { + createLdapContext(); + int total; + // Activate paged results + if (pagedResultsEnabled) { + ldapContext.setRequestControls(new Control[]{ + new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) }); + } + String groupFilter = "(&(objectclass=" + groupObjectClass + ")"; + if (groupSearchFilter != null && !groupSearchFilter.trim().isEmpty()) { + String customFilter = groupSearchFilter.trim(); + if (!customFilter.startsWith("(")) { + customFilter = "(" + customFilter + ")"; + } + groupFilter += customFilter + "(|"; + } + StringBuilder filter = new StringBuilder(); + + for (String groupDN : groupDNs) { + filter.append("(").append(groupMemberAttributeName).append("=") + .append(groupDN).append(")"); + } + filter.append("))"); + groupFilter += filter; + + LOG.debug("extendedAllGroupsSearchFilter = " + groupFilter); + for (int ou=0; ou<groupSearchBase.length; ou++) { + byte[] cookie = null; + int counter = 0; + try { + do { + groupSearchResultEnum = ldapContext + .search(groupSearchBase[ou], groupFilter, + groupSearchControls); + //System.out.println("goUpGroupHierarchyLdap(): Going through the sub groups"); + while (groupSearchResultEnum.hasMore()) { + final SearchResult groupEntry = groupSearchResultEnum.next(); + if (groupEntry == null) { + if (LOG.isInfoEnabled()) { + LOG.info("groupEntry null, skipping sync for the entry"); + } + continue; + } + counter++; + Attribute groupNameAttr = groupEntry.getAttributes().get(groupNameAttribute); + if (groupNameAttr == null) { + if (LOG.isInfoEnabled()) { + LOG.info(groupNameAttribute + " empty for entry " + groupEntry.getNameInNamespace() + + ", skipping sync"); + } + continue; + } + String groupDN = groupEntry.getNameInNamespace(); + //System.out.println("goUpGroupHierarchyLdap(): next Level Group DN = " + groupDN); + nextLevelGroups.add(groupDN); + String gName = (String) groupNameAttr.get(); + if (groupNameCaseConversionFlag) { + if (groupNameLowerCaseFlag) { + gName = gName.toLowerCase(); + } else { + gName = gName.toUpperCase(); + } + } + if (groupNameRegExInst != null) { + gName = groupNameRegExInst.transform(gName); + } + userInfo.addGroup(gName); + } + // Examine the paged results control response + Control[] controls = ldapContext.getResponseControls(); + if (controls != null) { + for (int i = 0; i < controls.length; i++) { + if (controls[i] instanceof PagedResultsResponseControl) { + PagedResultsResponseControl prrc = + (PagedResultsResponseControl)controls[i]; + total = prrc.getResultSize(); + if (total != 0) { + LOG.debug("END-OF-PAGE total : " + total); + } else { + LOG.debug("END-OF-PAGE total : unknown"); + } + cookie = prrc.getCookie(); + } + } + } else { + LOG.debug("No controls were sent from the server"); + } + // Re-activate paged results + if (pagedResultsEnabled) { + ldapContext.setRequestControls(new Control[]{ + new PagedResultsControl(PAGE_SIZE, cookie, Control.CRITICAL) }); + } + } while (cookie != null); + LOG.info("LdapUserGroupBuilder.goUpGroupHierarchyLdap() completed with group count: " + + counter); + } catch (RuntimeException re) { + LOG.error("LdapUserGroupBuilder.goUpGroupHierarchyLdap() failed with runtime exception: ", re); + throw re; + } catch (Exception t) { + LOG.error("LdapUserGroupBuilder.goUpGroupHierarchyLdap() failed with exception: ", t); + LOG.info("LdapUserGroupBuilder.goUpGroupHierarchyLdap() group count: " + + counter); + } + } + + } catch (RuntimeException re) { + LOG.error("LdapUserGroupBuilder.goUpGroupHierarchyLdap() failed with exception: ", re); + throw re; + } finally { + if (groupSearchResultEnum != null) { + groupSearchResultEnum.close(); + } + closeLdapContext(); + } + goUpGroupHierarchyLdap(nextLevelGroups, groupHierarchyLevels - 1, userInfo); + } + + private void getRootDN() throws Throwable { + NamingEnumeration groupSearchResultEnum = null; + SearchControls sc1 = new SearchControls(); + sc1.setSearchScope(SearchControls.OBJECT_SCOPE); + sc1.setReturningAttributes(new String[]{"namingContexts"}); + try { + createLdapContext(); + groupSearchResultEnum = ldapContext + .search("", "objectclass=*", sc1); + //System.out.println("goUpGroupHierarchyLdap(): Going through the sub groups"); + while (groupSearchResultEnum.hasMore()) { + SearchResult result1 = (SearchResult) groupSearchResultEnum.next(); + + Attributes attrs = result1.getAttributes(); + Attribute attr = attrs.get("namingContexts"); + LOG.debug("namingContexts = " + attr); + groupSearchBase = new String[] {attr.get(0).toString()}; + LOG.info("RootDN = " + Arrays.toString(groupSearchBase)); + } + } catch (RuntimeException re) { + throw re; + } finally { + if (groupSearchResultEnum != null) { + groupSearchResultEnum.close(); + } + closeLdapContext(); + } + } } class UserInfo { private String userName; private String userFullName; private Set<String> groupList; + private Set<String> groupDNList; public UserInfo(String userName, String userFullName) { this.userName = userName; this.userFullName = userFullName; this.groupList = new HashSet<String>(); + this.groupDNList = new HashSet<String>(); } public void updateUserName(String userName) { @@ -787,4 +967,14 @@ class UserInfo { public List<String> getGroups() { return (new ArrayList<String>(groupList)); } + + public void addGroupDNs(Set<String> groupDNs) { + groupDNList.addAll(groupDNs); + } + public void addGroupDN(String groupDN) { + groupDNList.add(groupDN); + } + public Set<String> getGroupDNs() { + return (groupDNList); + } } http://git-wip-us.apache.org/repos/asf/ranger/blob/f4019d11/ugsync/src/main/java/org/apache/ranger/unixusersync/config/UserGroupSyncConfig.java ---------------------------------------------------------------------- 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 af27efa..45eeb1b 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 @@ -188,6 +188,9 @@ public class UserGroupSyncConfig { private static final String LGSYNC_GROUP_MEMBER_ATTRIBUTE_NAME = "ranger.usersync.group.memberattributename"; private static final String DEFAULT_LGSYNC_GROUP_MEMBER_ATTRIBUTE_NAME = "member"; + private static final String LGSYNC_GROUP_HIERARCHY_LEVELS = "ranger.usersync.ldap.grouphierarchylevels"; + private static final int DEFAULT_LGSYNC_GROUP_HIERARCHY_LEVELS = 0; + private static final String UGSYNC_UPDATE_MILLIS_MIN = "ranger.usersync.unix.updatemillismin"; private final static long DEFAULT_UGSYNC_UPDATE_MILLIS_MIN = 1 * 60 * 1000; // ms @@ -757,6 +760,20 @@ public class UserGroupSyncConfig { return val; } + public int getGroupHierarchyLevels() { + int groupHierarchyLevels; + String val = prop.getProperty(LGSYNC_GROUP_HIERARCHY_LEVELS); + if(val == null || val.trim().isEmpty()) { + groupHierarchyLevels = DEFAULT_LGSYNC_GROUP_HIERARCHY_LEVELS; + } else { + groupHierarchyLevels = Integer.parseInt(val); + } + if (groupHierarchyLevels < 0) { + groupHierarchyLevels = DEFAULT_LGSYNC_GROUP_HIERARCHY_LEVELS; + } + return groupHierarchyLevels; + } + public String getProperty(String aPropertyName) { return prop.getProperty(aPropertyName); } @@ -1012,4 +1029,9 @@ public class UserGroupSyncConfig { public void setUserNameAttribute(String userNameAttr) { prop.setProperty(LGSYNC_USER_NAME_ATTRIBUTE, userNameAttr); } + + /* Used only for unit testing */ + public void setGroupHierarchyLevel(int groupHierarchyLevel) { + prop.setProperty(LGSYNC_GROUP_HIERARCHY_LEVELS, String.valueOf(groupHierarchyLevel)); + } } http://git-wip-us.apache.org/repos/asf/ranger/blob/f4019d11/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestLdapUserGroup.java ---------------------------------------------------------------------- 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 6ebc311..6393b3d 100644 --- a/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestLdapUserGroup.java +++ b/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestLdapUserGroup.java @@ -104,6 +104,7 @@ public class TestLdapUserGroup extends AbstractLdapTestUnit{ config.setGroupSearchEnabled(false); config.setPagedResultsEnabled(true); config.setGroupSearchFirstEnabled(false); + //config.setGroupHierarchyLevel(0); ldapBuilder.init(); PolicyMgrUserGroupBuilderTest sink = new PolicyMgrUserGroupBuilderTest(); sink.init(); @@ -123,6 +124,7 @@ public class TestLdapUserGroup extends AbstractLdapTestUnit{ config.setGroupSearchEnabled(false); config.setPagedResultsEnabled(false); config.setGroupSearchFirstEnabled(false); + //config.setGroupHierarchyLevel(0); ldapBuilder.init(); PolicyMgrUserGroupBuilderTest sink = new PolicyMgrUserGroupBuilderTest(); sink.init(); @@ -318,11 +320,12 @@ public class TestLdapUserGroup extends AbstractLdapTestUnit{ config.setGroupObjectClass("groupOfNames"); config.setGroupSearchEnabled(true); config.setGroupSearchFirstEnabled(true); + config.setUserSearchEnabled(false); ldapBuilder.init(); PolicyMgrUserGroupBuilderTest sink = new PolicyMgrUserGroupBuilderTest(); sink.init(); ldapBuilder.updateSink(sink); - assertEquals(2, sink.getTotalUsers()); + assertEquals(3, sink.getTotalUsers()); assertEquals(2, sink.getTotalGroups()); } @@ -444,7 +447,7 @@ public class TestLdapUserGroup extends AbstractLdapTestUnit{ PolicyMgrUserGroupBuilderTest sink = new PolicyMgrUserGroupBuilderTest(); sink.init(); ldapBuilder.updateSink(sink); - assertEquals(2, sink.getTotalUsers()); + assertEquals(3, sink.getTotalUsers()); assertEquals(2, sink.getTotalGroups()); } @@ -561,7 +564,7 @@ public class TestLdapUserGroup extends AbstractLdapTestUnit{ PolicyMgrUserGroupBuilderTest sink = new PolicyMgrUserGroupBuilderTest(); sink.init(); ldapBuilder.updateSink(sink); - assertEquals(1, sink.getTotalUsers()); + assertEquals(2, sink.getTotalUsers()); assertEquals(1, sink.getTotalGroups()); } @@ -694,7 +697,7 @@ public class TestLdapUserGroup extends AbstractLdapTestUnit{ LdapPolicyMgrUserGroupBuilderTest sink = new LdapPolicyMgrUserGroupBuilderTest(); sink.init(); ldapBuilder.updateSink(sink); - assertEquals(2, sink.getTotalUsers()); + assertEquals(3, sink.getTotalUsers()); assertEquals(2, sink.getTotalGroups()); } http://git-wip-us.apache.org/repos/asf/ranger/blob/f4019d11/ugsync/src/test/resources/ADSchema.ldif ---------------------------------------------------------------------- diff --git a/ugsync/src/test/resources/ADSchema.ldif b/ugsync/src/test/resources/ADSchema.ldif index a45a2fb..1a343ed 100644 --- a/ugsync/src/test/resources/ADSchema.ldif +++ b/ugsync/src/test/resources/ADSchema.ldif @@ -2362,6 +2362,7 @@ objectClass: top objectClass: groupOfNames cn: Group10 member: CN=User1000,CN=Users,DC=ranger,DC=qe,DC=hortonworks,DC=com +member: CN=Group19,OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com distinguishedName: CN=Group10,OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com sAMAccountName: Group10 sn: Group10 @@ -2571,6 +2572,7 @@ member: CN=User1804,CN=Users,DC=ranger,DC=qe,DC=hortonworks,DC=com member: CN=User1803,CN=Users,DC=ranger,DC=qe,DC=hortonworks,DC=com member: CN=User1802,CN=Users,DC=ranger,DC=qe,DC=hortonworks,DC=com member: CN=User1801,CN=Users,DC=ranger,DC=qe,DC=hortonworks,DC=com +member: CN=Group18,OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com distinguishedName: CN=Group19,OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com sAMAccountName: Group19 sn: Group19 http://git-wip-us.apache.org/repos/asf/ranger/blob/f4019d11/ugsync/src/test/resources/ranger-ugsync-site.xml ---------------------------------------------------------------------- diff --git a/ugsync/src/test/resources/ranger-ugsync-site.xml b/ugsync/src/test/resources/ranger-ugsync-site.xml index 7d1c27c..0a1a86d 100644 --- a/ugsync/src/test/resources/ranger-ugsync-site.xml +++ b/ugsync/src/test/resources/ranger-ugsync-site.xml @@ -177,4 +177,9 @@ <name>ranger.usersync.ldap.deltasync</name> <value>false</value> </property> + + <property> + <name>ranger.usersync.group.hierarchylevels</name> + <value>2</value> + </property> </configuration>