You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by sp...@apache.org on 2017/09/07 22:06:48 UTC

ranger git commit: RANGER-1735: Support representing nested group memberships in Ranger Admin --ranger-0.7 branch

Repository: ranger
Updated Branches:
  refs/heads/ranger-0.7 ab0fa6f31 -> f780aba3e


RANGER-1735: Support representing nested group memberships in Ranger Admin --ranger-0.7 branch


Project: http://git-wip-us.apache.org/repos/asf/ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/ranger/commit/f780aba3
Tree: http://git-wip-us.apache.org/repos/asf/ranger/tree/f780aba3
Diff: http://git-wip-us.apache.org/repos/asf/ranger/diff/f780aba3

Branch: refs/heads/ranger-0.7
Commit: f780aba3e717b7276e0a69f7c23734052b281f30
Parents: ab0fa6f
Author: Sailaja Polavarapu <sp...@hortonworks.com>
Authored: Thu Sep 7 14:07:52 2017 -0700
Committer: Sailaja Polavarapu <sp...@hortonworks.com>
Committed: Thu Sep 7 14:07:52 2017 -0700

----------------------------------------------------------------------
 .../process/LdapDeltaUserGroupBuilder.java      | 309 +++++++++++++-----
 .../process/LdapUserGroupBuilder.java           | 322 +++++++++++++++----
 .../config/UserGroupSyncConfig.java             |  38 ++-
 .../ranger/usergroupsync/TestLdapUserGroup.java |  11 +-
 ugsync/src/test/resources/ADSchema.ldif         |   2 +
 .../src/test/resources/ranger-ugsync-site.xml   |   5 +
 6 files changed, 532 insertions(+), 155 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ranger/blob/f780aba3/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/f780aba3/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 4e9f87a..9999988 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) });
@@ -617,6 +637,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() +
@@ -624,7 +645,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();
@@ -638,9 +661,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
@@ -666,9 +690,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);
 								}
@@ -750,17 +775,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) {
@@ -782,4 +962,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/f780aba3/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 1564760..d150cd4 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
@@ -200,6 +200,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
 
@@ -864,6 +867,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);
 	}
@@ -1065,14 +1082,19 @@ public class UserGroupSyncConfig  {
 	}
 	
 	/* Used only for unit testing */
-    	public void setDeltaSync(boolean deltaSyncEnabled) {
-        	prop.setProperty(LGSYNC_LDAP_DELTASYNC_ENABLED, String.valueOf(deltaSyncEnabled));
-    	}
-
-    	/* Used only for unit testing */
-    	public void setUserNameAttribute(String userNameAttr) {
-		prop.setProperty(LGSYNC_USER_NAME_ATTRIBUTE, userNameAttr);
-    	}
+    public void setDeltaSync(boolean deltaSyncEnabled) {
+        prop.setProperty(LGSYNC_LDAP_DELTASYNC_ENABLED, String.valueOf(deltaSyncEnabled));
+    }
+
+    /* Used only for unit testing */
+    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));
+	}
 
     public String getGroupRoleRules() {
         if(prop != null && prop.containsKey(GROUP_BASED_ROLE_ASSIGNMENT_RULES)) {

http://git-wip-us.apache.org/repos/asf/ranger/blob/f780aba3/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/f780aba3/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/f780aba3/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>