You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ve...@apache.org on 2017/01/25 06:36:53 UTC

[2/2] incubator-ranger git commit: RANGER-1211: Support incremental/Delta Sync with AD/LDAP for Ranger Usersync

RANGER-1211: Support incremental/Delta Sync with AD/LDAP for Ranger Usersync

Signed-off-by: Velmurugan Periasamy <ve...@apache.org>


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

Branch: refs/heads/master
Commit: 2261a9dca231b4b3009fc96e362ac64f9f09ece3
Parents: 7a1c722
Author: Sailaja Polavarapu <sp...@hortonworks.com>
Authored: Tue Jan 24 13:58:52 2017 -0800
Committer: Velmurugan Periasamy <ve...@apache.org>
Committed: Wed Jan 25 01:36:35 2017 -0500

----------------------------------------------------------------------
 .../java/org/apache/ranger/biz/XUserMgr.java    |  66 ++
 .../java/org/apache/ranger/rest/XUserREST.java  |  24 +
 .../ranger/security/context/RangerAPIList.java  |   1 +
 .../org/apache/ranger/view/VXGroupUserInfo.java |  66 ++
 .../process/LdapDeltaUserGroupBuilder.java      | 819 +++++++++++++++++++
 .../process/LdapPolicyMgrUserGroupBuilder.java  | 809 ++++++++++++++++++
 .../config/UserGroupSyncConfig.java             |  69 +-
 .../unixusersync/model/GroupUserInfo.java       |  40 +
 .../process/PolicyMgrUserGroupBuilder.java      |  14 +
 .../ranger/usergroupsync/UserGroupSink.java     |   4 +
 .../LdapPolicyMgrUserGroupBuilderTest.java      |  89 ++
 .../ranger/usergroupsync/TestLdapUserGroup.java | 107 ++-
 ugsync/src/test/resources/ADSchema.ldif         | 267 +++---
 .../src/test/resources/ranger-ugsync-site.xml   |  15 +
 14 files changed, 2259 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
index 189a254..dd6e6ca 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
@@ -88,6 +88,7 @@ import org.apache.ranger.view.VXGroup;
 import org.apache.ranger.view.VXGroupGroup;
 import org.apache.ranger.view.VXGroupList;
 import org.apache.ranger.view.VXGroupUser;
+import org.apache.ranger.view.VXGroupUserInfo;
 import org.apache.ranger.view.VXGroupUserList;
 import org.apache.ranger.view.VXLong;
 import org.apache.ranger.view.VXPermMap;
@@ -541,6 +542,71 @@ public class XUserMgr extends XUserMgrBase {
 
 		return vxUGInfo;
 	}
+	
+	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
+	public VXGroupUserInfo createXGroupUserFromMap(
+			VXGroupUserInfo vXGroupUserInfo) {
+		checkAdminAccess();
+		VXGroupUserInfo vxGUInfo = new VXGroupUserInfo();
+
+		VXGroup vXGroup = vXGroupUserInfo.getXgroupInfo();
+		// Add the group user mappings for a given group to x_group_user table
+		/*XXGroup xGroup = daoManager.getXXGroup().findByGroupName(vXGroup.getName());
+		if (xGroup == null) {
+			return vxGUInfo;
+		}*/
+
+		List<VXUser> vxu = new ArrayList<VXUser>();
+
+		for (VXUser vXUser : vXGroupUserInfo.getXuserInfo()) {
+			XXUser xUser = daoManager.getXXUser().findByUserName(vXUser.getName());
+			if (xUser != null) {
+				// Add or update group user mapping only if the user already exists in x_user table.
+				vXGroup = xGroupService.createXGroupWithOutLogin(vXGroup);
+				vxGUInfo.setXgroupInfo(vXGroup);
+				vxu.add(vXUser);
+				VXGroupUser vXGroupUser = new VXGroupUser();
+				vXGroupUser.setUserId(xUser.getId());
+				vXGroupUser.setName(vXGroup.getName());
+				vXGroupUser = xGroupUserService
+						.createXGroupUserWithOutLogin(vXGroupUser);
+			}
+		}
+
+		vxGUInfo.setXuserInfo(vxu);
+
+		return vxGUInfo;
+	}
+	
+	public VXGroupUserInfo getXGroupUserFromMap(
+			String groupName) {
+		checkAdminAccess();
+		VXGroupUserInfo vxGUInfo = new VXGroupUserInfo();
+
+		XXGroup xGroup = daoManager.getXXGroup().findByGroupName(groupName);
+		if (xGroup == null) {
+			return vxGUInfo;
+		}
+		SearchCriteria searchCriteria = new SearchCriteria();
+		searchCriteria.addParam("xGroupId", xGroup.getId());
+
+		VXGroupUserList vxGroupUserList = searchXGroupUsers(searchCriteria);
+		List<VXUser> vxu = new ArrayList<VXUser>();
+		logger.debug("removing all the group user mapping for : " + xGroup.getName());
+		for (VXGroupUser groupUser : vxGroupUserList.getList()) {
+			XXUser xUser = daoManager.getXXUser().getById(groupUser.getUserId());
+			if (xUser != null) {
+				VXUser vxUser = new VXUser();
+				vxUser.setName(xUser.getName());
+				vxu.add(vxUser);
+			}
+			
+		}
+		vxGUInfo.setXuserInfo(vxu);
+
+		return vxGUInfo;
+	}
+
 
 	public VXUser createXUserWithOutLogin(VXUser vXUser) {
 		checkAdminAccess();

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/security-admin/src/main/java/org/apache/ranger/rest/XUserREST.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/rest/XUserREST.java b/security-admin/src/main/java/org/apache/ranger/rest/XUserREST.java
index 646ade6..75ecec9 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/XUserREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/XUserREST.java
@@ -67,6 +67,7 @@ import org.apache.ranger.view.VXGroupList;
 import org.apache.ranger.view.VXGroupPermission;
 import org.apache.ranger.view.VXGroupPermissionList;
 import org.apache.ranger.view.VXGroupUser;
+import org.apache.ranger.view.VXGroupUserInfo;
 import org.apache.ranger.view.VXGroupUserList;
 import org.apache.ranger.view.VXLong;
 import org.apache.ranger.view.VXModuleDef;
@@ -173,6 +174,14 @@ public class XUserREST {
 	}
 
 	@POST
+	@Path("/groups/groupinfo")
+	@Produces({ "application/xml", "application/json" })
+	@PreAuthorize("hasRole('ROLE_SYS_ADMIN')")
+	public VXGroupUserInfo createXGroupUserFromMap(VXGroupUserInfo vXGroupUserInfo) {
+		return  xUserMgr.createXGroupUserFromMap(vXGroupUserInfo);
+	}
+	
+	@POST
 	@Path("/secure/groups")
 	@Produces({ "application/xml", "application/json" })
 	@PreAuthorize("hasRole('ROLE_SYS_ADMIN')")
@@ -413,6 +422,21 @@ public class XUserREST {
 				request, xGroupUserService.sortFields);
 		return xUserMgr.searchXGroupUsers(searchCriteria);
 	}
+	
+	/**
+	 * Implements the traditional search functionalities for XGroupUsers by Group name
+	 *
+	 * @param request
+	 * @return
+	 */
+	@GET
+	@Path("/groupusers/groupName/{groupName}")
+	@Produces({ "application/xml", "application/json" })
+	@PreAuthorize("@rangerPreAuthSecurityHandler.isAPIAccessible(\"" + RangerAPIList.GET_X_GROUP_USERS_BY_GROUP_NAME + "\")")
+	public VXGroupUserInfo getXGroupUsersByGroupName(@Context HttpServletRequest request,
+			@PathParam("groupName") String groupName) {
+		return xUserMgr.getXGroupUserFromMap(groupName);
+	}
 
 	@GET
 	@Path("/groupusers/count")

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIList.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIList.java b/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIList.java
index dd74e8f..460c7fd 100644
--- a/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIList.java
+++ b/security-admin/src/main/java/org/apache/ranger/security/context/RangerAPIList.java
@@ -154,6 +154,7 @@ public class RangerAPIList {
 	public static final String UPDATE_X_GROUP_USER = "XUserREST.updateXGroupUser";
 	public static final String DELETE_X_GROUP_USER = "XUserREST.deleteXGroupUser";
 	public static final String SEARCH_X_GROUP_USERS = "XUserREST.searchXGroupUsers";
+	public static final String GET_X_GROUP_USERS_BY_GROUP_NAME = "XUserREST.getXGroupUsersByGroupName";
 	public static final String COUNT_X_GROUP_USERS = "XUserREST.countXGroupUsers";
 	public static final String GET_X_GROUP_GROUP = "XUserREST.getXGroupGroup";
 	public static final String CREATE_X_GROUP_GROUP = "XUserREST.createXGroupGroup";

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/security-admin/src/main/java/org/apache/ranger/view/VXGroupUserInfo.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/view/VXGroupUserInfo.java b/security-admin/src/main/java/org/apache/ranger/view/VXGroupUserInfo.java
new file mode 100644
index 0000000..9501a21
--- /dev/null
+++ b/security-admin/src/main/java/org/apache/ranger/view/VXGroupUserInfo.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+ package org.apache.ranger.view;
+
+/**
+ * UserGroupInfo
+ *
+ */
+
+import java.util.List;
+
+import javax.xml.bind.annotation.*;
+
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+@JsonAutoDetect(getterVisibility=Visibility.NONE, setterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL )
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+public class VXGroupUserInfo extends VXDataObject implements java.io.Serializable  {
+	
+	private static final long serialVersionUID = 1L;
+	
+	VXGroup xgroupInfo;
+	List<VXUser> xuserInfo;
+	
+	public VXGroupUserInfo ( ) {
+	}
+
+	public VXGroup getXgroupInfo() {
+		return xgroupInfo;
+	}
+
+	public void setXgroupInfo(VXGroup xgroupInfo) {
+		this.xgroupInfo = xgroupInfo;
+	}
+
+	public List<VXUser> getXuserInfo() {
+		return xuserInfo;
+	}
+
+	public void setXuserInfo(List<VXUser> xuserInfo) {
+		this.xuserInfo = xuserInfo;
+	}
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/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
new file mode 100644
index 0000000..0779918
--- /dev/null
+++ b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapDeltaUserGroupBuilder.java
@@ -0,0 +1,819 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+ package org.apache.ranger.ldapusersync.process;
+
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.naming.Context;
+import javax.naming.InvalidNameException;
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.naming.ldap.PagedResultsControl;
+import javax.naming.ldap.PagedResultsResponseControl;
+import javax.naming.ldap.StartTlsRequest;
+import javax.naming.ldap.StartTlsResponse;
+
+import org.apache.log4j.Logger;
+import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
+import org.apache.ranger.usergroupsync.AbstractUserGroupSource;
+import org.apache.ranger.usergroupsync.UserGroupSink;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+
+public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource {
+	
+	private static final Logger LOG = Logger.getLogger(LdapDeltaUserGroupBuilder.class);
+	
+	private static final int PAGE_SIZE = 500;
+	private static long deltaSyncUserTime = 0; // Used for AD uSNChanged 
+	private static long deltaSyncGroupTime = 0; // Used for AD uSNChanged
+	private String deltaSyncUserTimeStamp; // Used for OpenLdap modifyTimestamp
+	private String deltaSyncGroupTimeStamp; // Used for OpenLdap modifyTimestamp
+
+  private String ldapUrl;
+  private String ldapBindDn;
+  private String ldapBindPassword;
+  private String ldapAuthenticationMechanism;
+  private String ldapReferral;
+  private String searchBase;
+
+  private String[] userSearchBase;
+	private String userNameAttribute;
+  private int    userSearchScope;
+  private String userObjectClass;
+  private String userSearchFilter;
+  private String extendedUserSearchFilter;
+  private SearchControls userSearchControls;
+  private Set<String> userGroupNameAttributeSet;
+
+  private boolean pagedResultsEnabled = true;
+  private int pagedResultsSize = 500;
+
+  private boolean groupSearchFirstEnabled = false;
+  private boolean userSearchEnabled = false;
+  private boolean groupSearchEnabled = true;
+  private String[] groupSearchBase;
+  private int    groupSearchScope;
+  private String groupObjectClass;
+  private String groupSearchFilter;
+  private String extendedGroupSearchFilter;
+  private String extendedAllGroupsSearchFilter;
+  private SearchControls groupSearchControls;
+  private String groupMemberAttributeName;
+  private String groupNameAttribute;
+
+	private LdapContext ldapContext;
+	StartTlsResponse tls;
+
+	private boolean userNameCaseConversionFlag = false;
+	private boolean groupNameCaseConversionFlag = false;
+	private boolean userNameLowerCaseFlag = false;
+	private boolean groupNameLowerCaseFlag = false;
+
+  private boolean  groupUserMapSyncEnabled = false;
+
+  //private Map<String, UserInfo> userGroupMap;
+  
+  private Table<String, String, String> groupUserTable; 
+
+	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;
+		}
+		else {
+		    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;
+		}
+		else {
+		    groupNameCaseConversionFlag = true;
+		    groupNameLowerCaseFlag = UserGroupSyncConfig.UGSYNC_LOWER_CASE_CONVERSION_VALUE.equalsIgnoreCase(groupNameCaseConversion);
+		}
+	}
+
+	@Override
+	public void init() throws Throwable{		
+		deltaSyncUserTime = 0;
+		deltaSyncGroupTime = 0;
+		DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss");
+		deltaSyncUserTimeStamp = dateFormat.format(new Date(0));
+		deltaSyncGroupTimeStamp = dateFormat.format(new Date(0));
+		setConfig();
+	}
+	
+	private void createLdapContext() throws Throwable {
+		Properties env = new Properties();
+		env.put(Context.INITIAL_CONTEXT_FACTORY,
+				"com.sun.jndi.ldap.LdapCtxFactory");
+		env.put(Context.PROVIDER_URL, ldapUrl);
+		if (ldapUrl.startsWith("ldaps") && (config.getSSLTrustStorePath() != null && !config.getSSLTrustStorePath().trim().isEmpty())) {
+			env.put("java.naming.ldap.factory.socket", "org.apache.ranger.ldapusersync.process.CustomSSLSocketFactory");
+		}
+
+		ldapContext = new InitialLdapContext(env, null);
+		if (!ldapUrl.startsWith("ldaps")) {
+			if (config.isStartTlsEnabled()) {
+				tls = (StartTlsResponse) ldapContext.extendedOperation(new StartTlsRequest());
+				if (config.getSSLTrustStorePath() != null && !config.getSSLTrustStorePath().trim().isEmpty()) {
+					tls.negotiate(CustomSSLSocketFactory.getDefault());
+				} else {
+					tls.negotiate();
+				}
+				LOG.info("Starting TLS session...");
+			}
+		}
+
+		ldapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, ldapBindDn);
+		ldapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, ldapBindPassword);
+		ldapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, ldapAuthenticationMechanism);
+		ldapContext.addToEnvironment(Context.REFERRAL, ldapReferral);
+	}
+	
+	private void setConfig() throws Throwable {
+		LOG.info("LdapDeltaUserGroupBuilder initialization started");
+
+		groupSearchFirstEnabled =   config.isGroupSearchFirstEnabled();
+		userSearchEnabled =   config.isUserSearchEnabled();
+		groupSearchEnabled =   config.isGroupSearchEnabled();
+    ldapUrl = config.getLdapUrl();
+    ldapBindDn = config.getLdapBindDn();
+    ldapBindPassword = config.getLdapBindPassword();
+    //ldapBindPassword = "admin-password";
+    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
+		if (!groupSearchFirstEnabled && !groupSearchEnabled) {
+			userGroupNameAttributeSet = config.getUserGroupNameAttributeSet();
+			for (String useGroupNameAttribute : userGroupNameAttributeSet) {
+				userSearchAttributes.add(useGroupNameAttribute);
+			}
+		}
+		userSearchAttributes.add("uSNChanged");
+		userSearchAttributes.add("modifytimestamp");
+		userSearchControls = new SearchControls();
+		userSearchControls.setSearchScope(userSearchScope);
+		userSearchControls.setReturningAttributes(userSearchAttributes.toArray(
+				new String[userSearchAttributes.size()]));
+
+    pagedResultsEnabled =   config.isPagedResultsEnabled();
+    pagedResultsSize =   config.getPagedResultsSize();
+
+    groupSearchBase = config.getGroupSearchBase().split(";");
+    groupSearchScope = config.getGroupSearchScope();
+    groupObjectClass = config.getGroupObjectClass();
+    groupSearchFilter = config.getGroupSearchFilter();
+    groupMemberAttributeName =  config.getUserGroupMemberAttributeName();
+    groupNameAttribute = config.getGroupNameAttribute();
+
+    extendedGroupSearchFilter =  "(&"  + extendedGroupSearchFilter + "(|(" + groupMemberAttributeName + "={0})(" + groupMemberAttributeName + "={1})))";
+    groupUserMapSyncEnabled = config.isGroupUserMapSyncEnabled();
+
+    groupSearchControls = new SearchControls();
+    groupSearchControls.setSearchScope(groupSearchScope);
+
+    Set<String> groupSearchAttributes = new HashSet<String>();
+    groupSearchAttributes.add(groupNameAttribute);
+    groupSearchAttributes.add(groupMemberAttributeName);
+    groupSearchAttributes.add("uSNChanged");
+    groupSearchAttributes.add("modifytimestamp");
+    groupSearchControls.setReturningAttributes(groupSearchAttributes.toArray(
+			new String[groupSearchAttributes.size()]));
+
+		if (LOG.isInfoEnabled()) {
+			LOG.info("LdapDeltaUserGroupBuilder initialization completed with --  "
+					+ "ldapUrl: " + ldapUrl
+					+ ",  ldapBindDn: " + ldapBindDn
+					+ ",  ldapBindPassword: ***** "
+					+ ",  ldapAuthenticationMechanism: " + ldapAuthenticationMechanism
+          + ",  searchBase: " + searchBase
+          + ",  userSearchBase: " + Arrays.toString(userSearchBase)
+          + ",  userSearchScope: " + userSearchScope
+					+ ",  userObjectClass: " + userObjectClass
+					+ ",  userSearchFilter: " + userSearchFilter
+					+ ",  extendedUserSearchFilter: " + extendedUserSearchFilter
+					+ ",  userNameAttribute: " + userNameAttribute
+					+ ",  userSearchAttributes: " + userSearchAttributes
+          + ",  userGroupNameAttributeSet: " + userGroupNameAttributeSet
+          + ",  pagedResultsEnabled: " + pagedResultsEnabled
+          + ",  pagedResultsSize: " + pagedResultsSize
+          + ",  groupSearchEnabled: " + groupSearchEnabled
+          + ",  groupSearchBase: " + Arrays.toString(groupSearchBase)
+          + ",  groupSearchScope: " + groupSearchScope
+          + ",  groupObjectClass: " + groupObjectClass
+          + ",  groupSearchFilter: " + groupSearchFilter
+          + ",  extendedGroupSearchFilter: " + extendedGroupSearchFilter
+          + ",  extendedAllGroupsSearchFilter: " + extendedAllGroupsSearchFilter
+          + ",  groupMemberAttributeName: " + groupMemberAttributeName
+          + ",  groupNameAttribute: " + groupNameAttribute
+          + ", groupSearchAttributes: " + groupSearchAttributes
+          + ",  groupUserMapSyncEnabled: " + groupUserMapSyncEnabled
+          + ", groupSearchFirstEnabled: " + groupSearchFirstEnabled
+          + ", userSearchEnabled: " + userSearchEnabled
+          + ",  ldapReferral: " + ldapReferral
+      );
+		}
+
+	}
+	
+	private void closeLdapContext() throws Throwable {
+		if (tls != null) {
+			tls.close();
+		}
+		if (ldapContext != null) {
+			ldapContext.close();
+		}
+	}
+	
+	@Override
+	public boolean isChanged() {
+		// we do not want to get the full ldap dit and check whether anything has changed
+		return true;
+	}
+
+	@Override
+	public void updateSink(UserGroupSink sink) throws Throwable {
+		LOG.info("LdapDeltaUserGroupBuilder updateSink started");
+		//userGroupMap = new HashMap<String, UserInfo>();
+		groupUserTable = HashBasedTable.create(); 
+		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;
+		}
+		Iterator<String> groupUserTableIterator = groupUserTable.rowKeySet().iterator();
+		while (groupUserTableIterator.hasNext()) {
+			String groupName = groupUserTableIterator.next();
+			//System.out.println("Group name from the groupUserTable: " + groupName);
+			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()); 
+		         userSet.add(transformUserName);
+		    }
+			List<String> userList = new ArrayList<>(userSet);
+			String transformGroupName = groupNameTransform(groupName);
+			try { 
+				sink.addOrUpdateGroup(transformGroupName, userList);
+			} catch (Throwable t) {
+				LOG.error("sink.addOrUpdateGroup failed with exception: " + t.getMessage()
+				+ ", for group: " + transformGroupName
+				+ ", users: " + userList);
+			}
+		}
+	}
+	
+	private void getUsers(UserGroupSink sink) throws Throwable {
+		NamingEnumeration<SearchResult> userSearchResultEnum = null;
+		NamingEnumeration<SearchResult> groupSearchResultEnum = null;
+		try {
+			createLdapContext();
+			int total;
+			// Activate paged results
+			if (pagedResultsEnabled)   {
+				ldapContext.setRequestControls(new Control[]{
+						new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) });
+			}
+			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("(")) {
+					customFilter = "(" + customFilter + ")";
+				}
+
+				extendedUserSearchFilter = "(&" + extendedUserSearchFilter + customFilter + ")";
+			} else {
+				extendedUserSearchFilter = "(&" + extendedUserSearchFilter + ")";
+			}
+			LOG.info("extendedUserSearchFilter = " + extendedUserSearchFilter);
+
+			long highestdeltaSyncUserTime = deltaSyncUserTime;
+
+			// When multiple OUs are configured, go through each OU as the user search base to search for users.
+			for (int ou=0; ou<userSearchBase.length; ou++) {
+				byte[] cookie = null;
+				int counter = 0;
+				try {
+				do {
+					userSearchResultEnum = ldapContext
+							.search(userSearchBase[ou], extendedUserSearchFilter,
+									userSearchControls);
+					
+					while (userSearchResultEnum.hasMore()) {
+						// searchResults contains all the user entries
+						final SearchResult userEntry = userSearchResultEnum.next();
+
+						if (userEntry == null)  {
+							if (LOG.isInfoEnabled())  {
+								LOG.info("userEntry null, skipping sync for the entry");
+							}
+							continue;
+						}
+
+						Attributes attributes =   userEntry.getAttributes();
+						if (attributes == null)  {
+							if (LOG.isInfoEnabled())  {
+								LOG.info("attributes  missing for entry " + userEntry.getNameInNamespace() +
+										", skipping sync");
+							}
+							continue;
+						}
+
+						Attribute userNameAttr  = attributes.get(userNameAttribute);
+						if (userNameAttr == null)  {
+							if (LOG.isInfoEnabled())  {
+								LOG.info(userNameAttribute + " missing for entry " + userEntry.getNameInNamespace() +
+										", skipping sync");
+							}
+							continue;
+						}
+
+						String userName = (String) userNameAttr.get();
+
+						if (userName == null || userName.trim().isEmpty())  {
+							if (LOG.isInfoEnabled())  {
+								LOG.info(userNameAttribute + " empty for entry " + userEntry.getNameInNamespace() +
+										", skipping sync");
+							}
+							continue;
+						}
+						
+						Attribute timeStampAttr  = attributes.get("uSNChanged");
+						if (timeStampAttr != null) {
+							String uSNChangedVal = (String) timeStampAttr.get();
+							long currentDeltaSyncTime = Long.parseLong(uSNChangedVal);
+							LOG.info("uSNChangedVal = " + uSNChangedVal + "and currentDeltaSyncTime = " + currentDeltaSyncTime);
+							if (currentDeltaSyncTime > highestdeltaSyncUserTime) {
+								highestdeltaSyncUserTime = currentDeltaSyncTime;
+							}
+						} else {
+							timeStampAttr = attributes.get("modifytimestamp");
+							if (timeStampAttr != null) {
+								String timeStampVal = (String) timeStampAttr.get();
+								Date parseDate = dateFormat.parse(timeStampVal);						
+								long currentDeltaSyncTime = parseDate.getTime();
+								LOG.info("timeStampVal = " + timeStampVal + "and currentDeltaSyncTime = " + currentDeltaSyncTime);
+								if (currentDeltaSyncTime > highestdeltaSyncUserTime) {
+									highestdeltaSyncUserTime = currentDeltaSyncTime;
+									deltaSyncUserTimeStamp = timeStampVal;
+								}
+							}
+						}
+
+						if (!groupSearchFirstEnabled) {
+							String transformUserName = userNameTransform(userName);
+							try {
+								sink.addOrUpdateUser(transformUserName);
+							} catch (Throwable t) {
+								LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage()
+								+ ", for user: " + transformUserName);
+							}
+							Set<String> groups = new HashSet<String>();
+
+							// Get all the groups from the group name attribute of the user only when group search is not enabled.
+							if (!groupSearchEnabled) {
+								for (String useGroupNameAttribute : userGroupNameAttributeSet) {
+									Attribute userGroupfAttribute = userEntry.getAttributes().get(useGroupNameAttribute);
+									if (userGroupfAttribute != null) {
+										NamingEnumeration<?> groupEnum = userGroupfAttribute.getAll();
+										while (groupEnum.hasMore()) {
+											String gName = getShortGroupName((String) groupEnum
+													.next());
+											String transformGroupName = groupNameTransform(gName);
+											groups.add(transformGroupName);
+										}
+									}
+								}
+							}
+
+							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);
+							}
+							counter++;
+							if (counter <= 2000) {
+								if (LOG.isInfoEnabled()) {
+									LOG.info("Updating user count: " + counter
+											+ ", userName: " + userName + ", groupList: "
+											+ groupList);
+								}
+								if ( counter == 2000 ) {
+									LOG.info("===> 2000 user records have been synchronized so far. From now on, only a summary progress log will be written for every 100 users. To continue to see detailed log for every user, please enable Trace level logging. <===");
+								}
+							} else {
+								if (LOG.isTraceEnabled()) {
+									LOG.trace("Updating user count: " + counter
+											+ ", userName: " + userName + ", groupList: "
+											+ groupList);
+								} else  {
+									if ( counter % 100 == 0) {
+										LOG.info("Synced " + counter + " users till now");
+									}
+								}
+							}
+						} else {
+							// If the user from the search result is present in the group user table,
+							// then addorupdate user to ranger admin.
+							String userFullName = (userEntry.getNameInNamespace()).toLowerCase();
+							LOG.debug("Chekcing if the user " + userFullName + " is part of the retrieved groups");
+							if (groupUserTable.containsColumn(userFullName) || groupUserTable.containsColumn(userName)) {
+								String transformUserName = userNameTransform(userName);
+								try {
+									sink.addOrUpdateUser(transformUserName);
+								} catch (Throwable t) {
+									LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage()
+									+ ", for user: " + transformUserName);
+								}
+							}
+						}
+
+					}
+
+					// 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("LdapDeltaUserGroupBuilder.getUsers() completed with user count: "
+						+ counter);
+				} catch (Throwable t) {
+					LOG.error("LdapDeltaUserGroupBuilder.getUsers() failed with exception: " + t);
+					LOG.info("LdapDeltaUserGroupBuilder.getUsers() user count: "
+							+ counter);
+				}
+			}
+			if (deltaSyncUserTime < highestdeltaSyncUserTime) {
+				// Incrementing highestdeltaSyncUserTime (for AD) in order to avoid search record repetition for next sync cycle.
+				deltaSyncUserTime = highestdeltaSyncUserTime+1; 
+				// Incrementing the highest timestamp value (for Openldap) with 1min in order to avoid search record repetition for next sync cycle.
+				deltaSyncUserTimeStamp = dateFormat.format(new Date(highestdeltaSyncUserTime + 60000l));
+			}
+			
+		} finally {
+			if (userSearchResultEnum != null) {
+				userSearchResultEnum.close();
+			}
+			if (groupSearchResultEnum != null) {
+				groupSearchResultEnum.close();
+			}
+			closeLdapContext();
+		}
+	}
+	
+	private void getGroups(UserGroupSink sink) throws Throwable {
+		NamingEnumeration<SearchResult> groupSearchResultEnum = null;
+		try {
+			createLdapContext();
+			int total;
+			// Activate paged results
+			if (pagedResultsEnabled)   {
+				ldapContext.setRequestControls(new Control[]{
+						new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) });
+			}
+			extendedGroupSearchFilter = "(objectclass=" + groupObjectClass + ")";
+			if (groupSearchFilter != null && !groupSearchFilter.trim().isEmpty()) {
+				String customFilter = groupSearchFilter.trim();
+				if (!customFilter.startsWith("(")) {
+					customFilter = "(" + customFilter + ")";
+				}
+				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;
+				try {
+					do {
+						groupSearchResultEnum = ldapContext
+								.search(groupSearchBase[ou], extendedAllGroupsSearchFilter,
+										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;
+							}
+							String gName = (String) groupNameAttr.get();
+							String transformGroupName = groupNameTransform(gName);
+							// If group based search is enabled, then
+							// update the group name to ranger admin
+							// check for group members and populate userInfo object with user's full name and group mapping
+							if (groupSearchFirstEnabled) {
+								LOG.debug("Update Ranger admin with " + transformGroupName);
+								sink.addOrUpdateGroup(transformGroupName);
+							}
+							Attribute timeStampAttr  = groupEntry.getAttributes().get("uSNChanged");
+							if (timeStampAttr != null) {
+								String uSNChangedVal = (String) timeStampAttr.get();
+								long currentDeltaSyncTime = Long.parseLong(uSNChangedVal);
+								if (currentDeltaSyncTime > highestdeltaSyncGroupTime) {
+									highestdeltaSyncGroupTime = currentDeltaSyncTime;
+								}
+							} else {
+								timeStampAttr = groupEntry.getAttributes().get("modifytimestamp");
+								if (timeStampAttr != null) {
+									String timeStampVal = (String) timeStampAttr.get();
+									Date parseDate = dateFormat.parse(timeStampVal);						
+									long currentDeltaSyncTime = parseDate.getTime();
+									LOG.info("timeStampVal = " + timeStampVal + "and currentDeltaSyncTime = " + currentDeltaSyncTime);
+									if (currentDeltaSyncTime > highestdeltaSyncGroupTime) {
+										highestdeltaSyncGroupTime = currentDeltaSyncTime;
+										deltaSyncGroupTimeStamp = timeStampVal;
+									}
+								}
+							}
+							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++;
+								String userName = getShortUserName(originalUserFullName);
+								if (groupSearchFirstEnabled && !userSearchEnabled) {
+									String transformUserName = userNameTransform(userName);
+									try {
+										sink.addOrUpdateUser(transformUserName);
+									} catch (Throwable t) {
+										LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage()
+										+ ", for user: " + transformUserName);
+									}
+								}
+								groupUserTable.put(gName, userName, userName);
+							}
+							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(PAGE_SIZE, cookie, Control.CRITICAL) });
+						}
+					} while (cookie != null);
+					LOG.info("LdapDeltaUserGroupBuilder.getGroups() completed with group count: "
+							+ counter);
+				} catch (Throwable t) {
+					LOG.error("LdapDeltaUserGroupBuilder.getGroups() failed with exception: " + t.getStackTrace());
+					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) {
+				groupSearchResultEnum.close();
+			}
+			closeLdapContext();
+		}
+	}
+
+	
+	private static String getShortGroupName(String longGroupName) throws InvalidNameException {
+		if (longGroupName == null) {
+			return null;
+		}
+		StringTokenizer stc = new StringTokenizer(longGroupName, ",");
+		String firstToken = stc.nextToken();
+		StringTokenizer ste = new StringTokenizer(firstToken, "=");
+		String groupName =  ste.nextToken();
+		if (ste.hasMoreTokens()) {
+			groupName = ste.nextToken();
+		}
+		groupName = groupName.trim();
+		LOG.info("longGroupName: " + longGroupName + ", groupName: " + groupName);
+		return groupName;
+	}
+	
+	private static String getShortUserName(String longUserName) throws InvalidNameException {
+		if (longUserName == null) {
+			return null;
+		}
+		StringTokenizer stc = new StringTokenizer(longUserName, ",");
+		String firstToken = stc.nextToken();
+		StringTokenizer ste = new StringTokenizer(firstToken, "=");
+		String userName =  ste.nextToken();
+		if (ste.hasMoreTokens()) {
+			userName = ste.nextToken();
+		}
+		userName = userName.trim();
+		LOG.info("longUserName: " + longUserName + ", userName: " + userName);
+		return userName;
+	}
+	
+	private String userNameTransform(String userName) {
+		//String userNameTransform = userName;
+		if (userNameCaseConversionFlag) {
+			if (userNameLowerCaseFlag) {
+				userName = userName.toLowerCase();
+			}
+			else {
+				userName = userName.toUpperCase();
+			}
+		}
+
+		if (userNameRegExInst != null) {
+			userName = userNameRegExInst.transform(userName);
+		}
+
+		return userName;
+	}
+	
+	private String groupNameTransform(String groupName) {
+		//String userNameTransform = userName;
+		if (groupNameCaseConversionFlag) {
+			if (groupNameLowerCaseFlag) {
+				groupName = groupName.toLowerCase();
+			}
+			else {
+				groupName = groupName.toUpperCase();
+			}
+		}
+
+		if (groupNameRegExInst != null) {
+			groupName = groupNameRegExInst.transform(groupName);
+		}
+
+		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);
+	}
+	public List<String> getGroups() {
+		return (new ArrayList<String>(groupList));
+	}
+}*/

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapPolicyMgrUserGroupBuilder.java
----------------------------------------------------------------------
diff --git a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapPolicyMgrUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapPolicyMgrUserGroupBuilder.java
new file mode 100644
index 0000000..0b909d1
--- /dev/null
+++ b/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/LdapPolicyMgrUserGroupBuilder.java
@@ -0,0 +1,809 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.ldapusersync.process;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.UnknownHostException;
+import java.security.KeyStore;
+import java.security.PrivilegedAction;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.security.auth.Subject;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.hadoop.security.SecureClientLogin;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
+import org.apache.ranger.unixusersync.model.GroupUserInfo;
+import org.apache.ranger.unixusersync.model.MUserInfo;
+import org.apache.ranger.unixusersync.model.UserGroupInfo;
+import org.apache.ranger.unixusersync.model.XGroupInfo;
+import org.apache.ranger.unixusersync.model.XUserGroupInfo;
+import org.apache.ranger.unixusersync.model.XUserInfo;
+import org.apache.ranger.usergroupsync.UserGroupSink;
+import org.apache.ranger.usersync.util.UserSyncUtil;
+
+import com.google.common.collect.Table;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.sun.jersey.api.client.Client;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
+import com.sun.jersey.client.urlconnection.HTTPSProperties;
+
+public class LdapPolicyMgrUserGroupBuilder implements UserGroupSink {
+
+private static final Logger LOG = Logger.getLogger(LdapPolicyMgrUserGroupBuilder.class);
+	
+	private static final String AUTHENTICATION_TYPE = "hadoop.security.authentication";	
+	private String AUTH_KERBEROS = "kerberos";
+	private static final String PRINCIPAL = "ranger.usersync.kerberos.principal";
+	private static final String KEYTAB = "ranger.usersync.kerberos.keytab";
+	private static final String NAME_RULE = "hadoop.security.auth_to_local";
+	
+	public static final String PM_USER_LIST_URI  = "/service/xusers/users/";				// GET
+	private static final String PM_ADD_USER_GROUP_INFO_URI = "/service/xusers/users/userinfo";	// POST
+	
+	private static final String PM_ADD_GROUP_USER_INFO_URI = "/service/xusers/groups/groupinfo";	// POST
+	
+	public static final String PM_GROUP_LIST_URI = "/service/xusers/groups/";				// GET
+	private static final String PM_ADD_GROUP_URI = "/service/xusers/groups/";				// POST
+	
+	private static final String PM_DEL_USER_GROUP_LINK_URI = "/service/xusers/group/${groupName}/user/${userName}"; // DELETE
+	
+	public static final String PM_USER_GROUP_MAP_LIST_URI = "/service/xusers/groupusers/";		// GET
+	
+	public static final String PM_GET_GROUP_USER_MAP_LIST_URI = "/service/xusers/groupusers/groupName/${groupName}";		// GET
+	
+	private static final String PM_ADD_LOGIN_USER_URI = "/service/users/default";			// POST
+	private static final String GROUP_SOURCE_EXTERNAL ="1";
+	private static String LOCAL_HOSTNAME = "unknown";
+	private boolean isMockRun = false;
+	private String policyMgrBaseUrl;
+	
+	private UserGroupSyncConfig  config = UserGroupSyncConfig.getInstance();
+
+	private UserGroupInfo				usergroupInfo = new UserGroupInfo();
+	private GroupUserInfo				groupuserInfo = new GroupUserInfo();
+	
+	Table<String, String, String> groupsUsersTable;
+	
+	private String keyStoreFile =  null;
+	private String keyStoreFilepwd = null;
+	private String trustStoreFile = null;
+	private String trustStoreFilepwd = null;
+	private String keyStoreType = null;
+	private String trustStoreType = null;
+	private HostnameVerifier hv =  null;
+
+	private SSLContext sslContext = null;
+	private String authenticationType = null;
+	String principal;
+	String keytab;
+	String nameRules;
+	
+	static {
+		try {
+			LOCAL_HOSTNAME = java.net.InetAddress.getLocalHost().getCanonicalHostName();
+		} catch (UnknownHostException e) {
+			LOCAL_HOSTNAME = "unknown";
+		}
+	}
+	
+	@Override
+	public void init() throws Throwable {
+		policyMgrBaseUrl = config.getPolicyManagerBaseURL();
+		isMockRun = config.isMockRunEnabled();
+		
+		if (isMockRun) {
+			LOG.setLevel(Level.DEBUG);
+		}
+		
+		keyStoreFile =  config.getSSLKeyStorePath();
+		keyStoreFilepwd = config.getSSLKeyStorePathPassword();
+		trustStoreFile = config.getSSLTrustStorePath();
+		trustStoreFilepwd = config.getSSLTrustStorePathPassword();
+		keyStoreType = KeyStore.getDefaultType();
+		trustStoreType = KeyStore.getDefaultType();
+		authenticationType = config.getProperty(AUTHENTICATION_TYPE,"simple");
+		try {
+			principal = SecureClientLogin.getPrincipal(config.getProperty(PRINCIPAL,""), LOCAL_HOSTNAME);
+		} catch (IOException ignored) {
+			 // do nothing
+		}
+		keytab = config.getProperty(KEYTAB,"");
+		nameRules = config.getProperty(NAME_RULE,"DEFAULT");
+
+	}
+
+	@Override
+	public void addOrUpdateUser(String userName, List<String> groups) throws Throwable {
+		//* Add user to groups mapping in the x_user table. 
+		//* Here the assumption is that the user already exists in x_portal_user table.
+		if ( ! isMockRun ) {
+			// If the rest call to ranger admin fails, 
+			// propagate the failure to the caller for retry in next sync cycle.
+			if (addUserGroupInfo(userName, groups) == null ) {
+				String msg = "Failed to add addorUpdate user group info";
+				LOG.error(msg);
+				throw new Exception(msg);
+			}
+		}
+
+	}
+
+	@Override
+	public void addOrUpdateGroup(String groupName) throws Throwable {
+		//* Build the group info object and do the rest call
+			if ( ! isMockRun ) {
+				if ( addGroupInfo(groupName) == null) {
+					String msg = "Failed to add addorUpdate group info";
+					LOG.error(msg);
+					throw new Exception(msg);
+				}
+			}
+
+	}
+	
+	private XGroupInfo addGroupInfo(final String groupName){
+		XGroupInfo ret = null;
+		XGroupInfo group = null;
+		
+		LOG.debug("INFO: addPMXAGroup(" + groupName + ")" );
+		if (! isMockRun) {
+			group = addXGroupInfo(groupName);
+		}
+		if (authenticationType != null && AUTH_KERBEROS.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal,keytab)) {
+			try {
+				LOG.info("Using principal = " + principal + " and keytab = " + keytab);
+				Subject sub = SecureClientLogin.loginUserFromKeytab(principal, keytab, nameRules);
+				final XGroupInfo groupInfo = group;
+				ret = Subject.doAs(sub, new PrivilegedAction<XGroupInfo>() {
+					@Override
+					public XGroupInfo run() {
+						try {
+							return getAddedGroupInfo(groupInfo);
+						} catch (Exception e) {
+							LOG.error("Failed to build Group List : ", e);
+						}
+						return null;
+					}
+				});
+				return ret;
+			} catch (Exception e) {
+				LOG.error("Failed to Authenticate Using given Principal and Keytab : ", e);
+			}
+			return null;
+		} else {
+			return getAddedGroupInfo(group);
+		}	
+	}
+	
+	private XGroupInfo addXGroupInfo(String aGroupName) {
+		
+		XGroupInfo addGroup = new XGroupInfo();
+		
+		addGroup.setName(aGroupName);
+		
+		addGroup.setDescription(aGroupName + " - add from Unix box");
+		
+		addGroup.setGroupType("1");
+
+		addGroup.setGroupSource(GROUP_SOURCE_EXTERNAL);
+		groupuserInfo.setXgroupInfo(addGroup);
+
+		return addGroup;
+	}
+
+	private XGroupInfo getAddedGroupInfo(XGroupInfo group){	
+		XGroupInfo ret = null;
+		
+		Client c = getClient();
+		
+		WebResource r = c.resource(getURL(PM_ADD_GROUP_URI));
+		
+		Gson gson = new GsonBuilder().create();
+		
+		String jsonString = gson.toJson(group);
+		
+		LOG.debug("Group" + jsonString);
+		
+		String response = r.accept(MediaType.APPLICATION_JSON_TYPE).type(MediaType.APPLICATION_JSON_TYPE).post(String.class, jsonString);
+		
+		LOG.debug("RESPONSE: [" + response + "]");
+		
+		ret = gson.fromJson(response, XGroupInfo.class);
+		
+		return ret;	
+	}
+
+	public static void main(String[] args) throws Throwable {
+		LdapPolicyMgrUserGroupBuilder  ugbuilder = new LdapPolicyMgrUserGroupBuilder();
+		ugbuilder.init();
+
+	}
+
+	@Override
+	public void addOrUpdateUser(String userName) throws Throwable {
+		// First add to x_portal_user
+		LOG.debug("INFO: addPMAccount(" + userName + ")" );
+		if (! isMockRun) {
+			if (addMUser(userName) == null) {
+		        String msg = "Failed to add portal user";
+		        LOG.error(msg);
+		        throw new Exception(msg);
+			}
+		}
+		List<String> groups = new ArrayList<String>();
+		//* Build the user group info object with empty groups and do the rest call
+		if ( ! isMockRun ) {
+			// If the rest call to ranger admin fails, 
+			// propagate the failure to the caller for retry in next sync cycle.
+			if (addUserGroupInfo(userName, groups) == null ) {
+				String msg = "Failed to add addorUpdate user group info";
+				LOG.error(msg);
+				throw new Exception(msg);
+			}
+		}
+		
+	}
+	
+	private UserGroupInfo addUserGroupInfo(String userName, List<String> groups){
+		if(LOG.isDebugEnabled()) {
+	 		LOG.debug("==> LdapPolicyMgrUserGroupBuilder.addUserGroupInfo " + userName + " and groups");
+	 	}
+		UserGroupInfo ret = null;
+		XUserInfo user = null;
+		LOG.debug("INFO: addPMXAUser(" + userName + ")" );
+		
+		if (! isMockRun) {
+			user = addXUserInfo(userName);
+		}
+		for(String g : groups) {
+				LOG.debug("INFO: addPMXAGroupToUser(" + userName + "," + g + ")" );
+		}
+		if (! isMockRun ) {
+			addXUserGroupInfo(user, groups);
+		}
+		if (authenticationType != null && AUTH_KERBEROS.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab)){
+			try {
+				Subject sub = SecureClientLogin.loginUserFromKeytab(principal, keytab, nameRules);
+				final UserGroupInfo result = ret;
+				ret = Subject.doAs(sub, new PrivilegedAction<UserGroupInfo>() {
+					@Override
+					public UserGroupInfo run() {
+						try {
+							return getUsergroupInfo(result);
+						} catch (Exception e) {
+							LOG.error("Failed to add User Group Info : ", e);
+						}
+						return null;
+					}
+				});
+				return ret;
+			} catch (Exception e) {
+				LOG.error("Failed to Authenticate Using given Principal and Keytab : " , e);
+			}
+			return null;
+		}else{
+			return getUsergroupInfo(ret);
+		}
+	}
+	
+	private XUserInfo addXUserInfo(String aUserName) {
+		
+		XUserInfo xuserInfo = new XUserInfo();
+
+		xuserInfo.setName(aUserName);
+		
+		xuserInfo.setDescription(aUserName + " - add from Unix box");
+	   	
+		usergroupInfo.setXuserInfo(xuserInfo);
+		
+		return xuserInfo;
+	}
+
+	private void addXUserGroupInfo(XUserInfo aUserInfo, List<String> aGroupList) {
+		
+		List<XGroupInfo> xGroupInfoList = new ArrayList<XGroupInfo>();
+		
+		for(String groupName : aGroupList) {
+			XGroupInfo group = addXGroupInfo(groupName);
+			xGroupInfoList.add(group);
+			addXUserGroupInfo(aUserInfo, group);
+		}
+		
+		usergroupInfo.setXgroupInfo(xGroupInfoList);
+	}
+	
+	private XUserGroupInfo addXUserGroupInfo(XUserInfo aUserInfo, XGroupInfo aGroupInfo) {
+		
+		
+	    XUserGroupInfo ugInfo = new XUserGroupInfo();
+		
+		ugInfo.setUserId(aUserInfo.getId());
+		
+		ugInfo.setGroupName(aGroupInfo.getName());
+		
+		// ugInfo.setParentGroupId("1");
+		
+        return ugInfo;
+	}
+
+	private UserGroupInfo getUsergroupInfo(UserGroupInfo ret) {
+		Client c = getClient();
+		
+		WebResource r = c.resource(getURL(PM_ADD_USER_GROUP_INFO_URI));
+		
+		Gson gson = new GsonBuilder().create();
+		
+		String jsonString = gson.toJson(usergroupInfo);
+		
+		LOG.debug("USER GROUP MAPPING" + jsonString);
+		
+		String response = r.accept(MediaType.APPLICATION_JSON_TYPE).type(MediaType.APPLICATION_JSON_TYPE).post(String.class, jsonString);
+		
+		LOG.debug("RESPONSE: [" + response + "]");
+		
+		ret = gson.fromJson(response, UserGroupInfo.class);
+		
+		return ret;
+	}
+
+	@Override
+	public void addOrUpdateGroup(String groupName, List<String> users) throws Throwable {
+		// First get the existing group user mappings from Ranger admin.
+		// Then compute the delta and send the updated group user mappings to ranger admin.
+		GroupUserInfo groupUserInfo = new GroupUserInfo();
+		if (authenticationType != null && AUTH_KERBEROS.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal,keytab)) {
+			try {
+				LOG.info("Using principal = " + principal + " and keytab = " + keytab);
+				Subject sub = SecureClientLogin.loginUserFromKeytab(principal, keytab, nameRules);
+				final GroupUserInfo groupInfo = groupUserInfo;
+				final String gName = groupName;
+				Subject.doAs(sub, new PrivilegedAction<Void>() {
+					@Override
+					public Void run() {
+						try {
+							getGroupUserInfo(gName, groupInfo);
+						} catch (Exception e) {
+							LOG.error("Failed to build Group List : ", e);
+						}
+						return null;
+					}
+				});
+				groupUserInfo = groupInfo;
+			} catch (Exception e) {
+				LOG.error("Failed to Authenticate Using given Principal and Keytab : ", e);
+			}
+		} else {
+			getGroupUserInfo(groupName, groupUserInfo);
+		}	
+		
+		//GroupUserInfo groupUserInfo = getGroupUserInfo(groupName);
+		LOG.debug("Returned users for group " + groupUserInfo.getXgroupInfo() + " are: " + groupUserInfo.getXuserInfo());
+		List<String> oldUsers = new ArrayList<String>();
+		if (groupUserInfo.getXuserInfo() != null) {
+			for (XUserInfo xUserInfo : groupUserInfo.getXuserInfo()) {
+				oldUsers.add(xUserInfo.getName());
+			}
+		}
+		List<String> addUsers = new ArrayList<String>();
+		List<String> delUsers = new ArrayList<String>();
+		
+		for (String user : oldUsers) {
+			if (!users.contains(user)) {
+				delUsers.add(user);
+			}
+		}
+		if (oldUsers.isEmpty()) {
+			addUsers = users;
+		} else {
+			for (String user : users) {
+				if (!oldUsers.contains(user)) {
+					addUsers.add(user);
+				}
+			}
+		}
+		
+		LOG.debug("addUsers = " + addUsers);
+		delXGroupUserInfo(groupName, delUsers);
+		
+		//* Add user to group mapping in the x_group_user table. 
+		//* Here the assumption is that the user already exists in x_portal_user table.
+		if ( ! isMockRun ) {
+			// If the rest call to ranger admin fails, 
+			// propagate the failure to the caller for retry in next sync cycle.
+			if (addGroupUserInfo(groupName, addUsers) == null ) {
+				String msg = "Failed to add addorUpdate group user info";
+				LOG.error(msg);
+				throw new Exception(msg);
+			}
+		}
+	}
+	
+	private void delXGroupUserInfo(final String groupName, List<String> userList) {
+		if(LOG.isDebugEnabled()) {
+	 		LOG.debug("==> LdapPolicyMgrUserGroupBuilder.delXGroupUserInfo " + groupName + " and " + userList);
+	 	}
+		for(final String userName : userList) {
+			if (authenticationType != null && AUTH_KERBEROS.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab)) {
+				try {
+					LOG.info("Using principal = " + principal + " and keytab = " + keytab);
+					Subject sub = SecureClientLogin.loginUserFromKeytab(principal, keytab, nameRules);
+					Subject.doAs(sub, new PrivilegedAction<Void>() {
+						@Override
+						public Void run() {
+							try {
+								delXGroupUserInfo(groupName, userName);
+							} catch (Exception e) {
+								LOG.error("Failed to build Group List : ", e);
+							}
+							return null;
+						}
+					});
+				} catch (Exception e) {
+					LOG.error("Failed to Authenticate Using given Principal and Keytab : ",e);
+				}
+			} else {
+				delXGroupUserInfo(groupName, userName);
+			}
+		}
+	}
+
+	private void delXGroupUserInfo(String groupName, String userName) {
+	
+		try {
+
+			Client c = getClient();
+
+			String uri = PM_DEL_USER_GROUP_LINK_URI.replaceAll(Pattern.quote("${groupName}"),
+					   UserSyncUtil.encodeURIParam(groupName)).replaceAll(Pattern.quote("${userName}"), UserSyncUtil.encodeURIParam(userName));
+
+			WebResource r = c.resource(getURL(uri));
+
+		    ClientResponse response = r.delete(ClientResponse.class);
+
+		    if ( LOG.isDebugEnabled() ) {
+		    	LOG.debug("RESPONSE: [" + response.toString() + "]");
+		    }
+
+		} catch (Exception e) {
+
+			LOG.warn( "ERROR: Unable to delete GROUP: " + groupName  + " from USER:" + userName , e);
+		}
+
+	}
+	
+	private GroupUserInfo addGroupUserInfo(String groupName, List<String> users){
+		if(LOG.isDebugEnabled()) {
+	 		LOG.debug("==> LdapPolicyMgrUserGroupBuilder.addGroupUserInfo " + groupName + " and " + users);
+	 	}
+		GroupUserInfo ret = null;
+		XGroupInfo group = null;
+		
+		LOG.debug("INFO: addPMXAGroup(" + groupName + ")" );
+		if (! isMockRun) {
+			group = addXGroupInfo(groupName);
+		}
+		for(String u : users) {
+				LOG.debug("INFO: addPMXAGroupToUser(" + groupName + "," + u + ")" );
+		}
+		if (! isMockRun ) {
+			addXGroupUserInfo(group, users);
+		}
+		if (authenticationType != null && AUTH_KERBEROS.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab)){
+			try {
+				Subject sub = SecureClientLogin.loginUserFromKeytab(principal, keytab, nameRules);
+				final GroupUserInfo result = ret;
+				ret = Subject.doAs(sub, new PrivilegedAction<GroupUserInfo>() {
+					@Override
+					public GroupUserInfo run() {
+						try {
+							return getGroupUserInfo(result);
+						} catch (Exception e) {
+							LOG.error("Failed to add User Group Info : ", e);
+						}
+						return null;
+					}
+				});
+				return ret;
+			} catch (Exception e) {
+				LOG.error("Failed to Authenticate Using given Principal and Keytab : " , e);
+			}
+			return null;
+		}else{
+			return getGroupUserInfo(ret);
+		}
+	}
+	
+	private void addXGroupUserInfo(XGroupInfo aGroupInfo, List<String> aUserList) {
+
+		List<XUserInfo> xUserInfoList = new ArrayList<XUserInfo>();
+
+		for(String userName : aUserList) {
+			XUserInfo user = addXUserInfo(userName);
+			xUserInfoList.add(user);
+			addXUserGroupInfo(user, aGroupInfo);
+		}
+
+		groupuserInfo.setXuserInfo(xUserInfoList);
+	}
+
+	private GroupUserInfo getGroupUserInfo(GroupUserInfo ret) {
+		Client c = getClient();
+		
+		WebResource r = c.resource(getURL(PM_ADD_GROUP_USER_INFO_URI));
+		
+		Gson gson = new GsonBuilder().create();
+		
+		String jsonString = gson.toJson(groupuserInfo);
+		
+		LOG.debug("GROUP USER MAPPING" + jsonString);
+		
+		String response = r.accept(MediaType.APPLICATION_JSON_TYPE).type(MediaType.APPLICATION_JSON_TYPE).post(String.class, jsonString);
+		
+		LOG.debug("RESPONSE: [" + response + "]");
+		
+		ret = gson.fromJson(response, GroupUserInfo.class);
+		
+		return ret;
+	}
+
+	
+	private MUserInfo addMUser(String aUserName) {
+		MUserInfo ret = null;
+		MUserInfo userInfo = new MUserInfo();
+
+		userInfo.setLoginId(aUserName);
+		userInfo.setFirstName(aUserName);
+		userInfo.setLastName(aUserName);
+
+		if (authenticationType != null && AUTH_KERBEROS.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab)) {
+			try {
+				Subject sub = SecureClientLogin.loginUserFromKeytab(principal, keytab, nameRules);
+				final MUserInfo result = ret;
+				final MUserInfo userInfoFinal = userInfo;
+				ret = Subject.doAs(sub, new PrivilegedAction<MUserInfo>() {
+					@Override
+					public MUserInfo run() {
+						try {
+							return getMUser(userInfoFinal, result);
+						} catch (Exception e) {
+							LOG.error("Failed to add User : ", e);
+						}
+						return null;
+					}
+				});
+				return ret;
+			} catch (Exception e) {
+				LOG.error("Failed to Authenticate Using given Principal and Keytab : " , e);
+			}
+			return null;
+		} else {
+			return getMUser(userInfo, ret);
+		}
+	}
+
+
+	private MUserInfo getMUser(MUserInfo userInfo, MUserInfo ret) {		
+		Client c = getClient();
+	
+	    WebResource r = c.resource(getURL(PM_ADD_LOGIN_USER_URI));
+	
+	    Gson gson = new GsonBuilder().create();
+
+	    String jsonString = gson.toJson(userInfo);
+	
+	    String response = r.accept(MediaType.APPLICATION_JSON_TYPE).type(MediaType.APPLICATION_JSON_TYPE).post(String.class, jsonString);
+	
+	    LOG.debug("RESPONSE[" + response + "]");
+	
+	    ret = gson.fromJson(response, MUserInfo.class);
+	
+	    LOG.debug("MUser Creation successful " + ret);
+		
+		return ret;
+	}
+	
+	public void getGroupUserInfo(String groupName, GroupUserInfo ret) {
+		//GroupUserInfo ret = null;
+		try {
+
+			Client c = getClient();
+
+			String uri = PM_GET_GROUP_USER_MAP_LIST_URI.replaceAll(Pattern.quote("${groupName}"),
+					   UserSyncUtil.encodeURIParam(groupName));
+
+			WebResource r = c.resource(getURL(uri));
+
+			String response = r.accept(MediaType.APPLICATION_JSON_TYPE).get(String.class);
+			
+		    Gson gson = new GsonBuilder().create();
+	
+		    LOG.debug("RESPONSE: [" + response + "]");
+	
+		    ret = gson.fromJson(response, GroupUserInfo.class);
+		    LOG.debug("return value = " + ret);
+
+		} catch (Exception e) {
+
+			LOG.warn( "ERROR: Unable to get group user mappings for: " + groupName, e);
+		}
+	}
+	
+	private String getURL(String uri) {
+		String ret = null;
+		ret = policyMgrBaseUrl + (uri.startsWith("/") ? uri : ("/" + uri));
+		return ret;
+	}
+
+	private synchronized Client getClient() {
+		
+		Client ret = null;
+		
+		if (policyMgrBaseUrl.startsWith("https://")) {
+			
+			ClientConfig config = new DefaultClientConfig();
+			
+			if (sslContext == null) {
+				
+				try {
+
+				KeyManager[] kmList = null;
+				TrustManager[] tmList = null;
+	
+				if (keyStoreFile != null && keyStoreFilepwd != null) {
+	
+					KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+					InputStream in = null;
+					try {
+						in = getFileInputStream(keyStoreFile);
+						if (in == null) {
+							LOG.error("Unable to obtain keystore from file [" + keyStoreFile + "]");
+							return ret;
+						}
+						keyStore.load(in, keyStoreFilepwd.toCharArray());
+						KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+						keyManagerFactory.init(keyStore, keyStoreFilepwd.toCharArray());
+						kmList = keyManagerFactory.getKeyManagers();
+					}
+					finally {
+						if (in != null) {
+							in.close();
+						}
+					}
+					
+				}
+	
+				if (trustStoreFile != null && trustStoreFilepwd != null) {
+	
+					KeyStore trustStore = KeyStore.getInstance(trustStoreType);
+					InputStream in = null;
+					try {
+						in = getFileInputStream(trustStoreFile);
+						if (in == null) {
+							LOG.error("Unable to obtain keystore from file [" + trustStoreFile + "]");
+							return ret;
+						}
+						trustStore.load(in, trustStoreFilepwd.toCharArray());
+						TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+						trustManagerFactory.init(trustStore);
+						tmList = trustManagerFactory.getTrustManagers();
+					}
+					finally {
+						if (in != null) {
+							in.close();
+						}
+					}
+				}
+
+				sslContext = SSLContext.getInstance("SSL");
+	
+				sslContext.init(kmList, tmList, new SecureRandom());
+
+				hv = new HostnameVerifier() {
+					public boolean verify(String urlHostName, SSLSession session) {
+						return session.getPeerHost().equals(urlHostName);
+					}
+				};
+				}
+				catch(Throwable t) {
+					throw new RuntimeException("Unable to create SSLConext for communication to policy manager", t);
+				}
+
+			}
+
+			config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(hv, sslContext));
+
+			ret = Client.create(config);
+
+			
+		}
+		else {
+			ClientConfig cc = new DefaultClientConfig();
+		    cc.getProperties().put(ClientConfig.PROPERTY_FOLLOW_REDIRECTS, true);
+		    ret = Client.create(cc);	
+		}
+		if(!(authenticationType != null && AUTH_KERBEROS.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab))){
+			if(ret!=null){
+				 String username = config.getPolicyMgrUserName();
+				 String password = config.getPolicyMgrPassword();
+				 if(username==null||password==null||username.trim().isEmpty()||password.trim().isEmpty()){
+					 username=config.getDefaultPolicyMgrUserName();
+					 password=config.getDefaultPolicyMgrPassword();
+				 }
+				 if(username!=null && password!=null){
+					 ret.addFilter(new HTTPBasicAuthFilter(username, password));
+				 }
+			}
+		}
+		return ret;
+	}
+	
+	private InputStream getFileInputStream(String path) throws FileNotFoundException {
+
+		InputStream ret = null;
+
+		File f = new File(path);
+
+		if (f.exists()) {
+			ret = new FileInputStream(f);
+		} else {
+			ret = LdapPolicyMgrUserGroupBuilder.class.getResourceAsStream(path);
+			
+			if (ret == null) {
+				if (! path.startsWith("/")) {
+					ret = getClass().getResourceAsStream("/" + path);
+				}
+			}
+			
+			if (ret == null) {
+				ret = ClassLoader.getSystemClassLoader().getResourceAsStream(path);
+				if (ret == null) {
+					if (! path.startsWith("/")) {
+						ret = ClassLoader.getSystemResourceAsStream("/" + path);
+					}
+				}
+			}
+		}
+
+		return ret;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/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 734dc58..25c0824 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
@@ -68,6 +68,8 @@ public class UserGroupSyncConfig  {
 
 	public static final String  UGSYNC_MOCK_RUN_PROP  = 	"ranger.usersync.policymanager.mockrun";
 
+	public static final String  UGSYNC_TEST_RUN_PROP  = 	"ranger.usersync.policymanager.testrun";
+	
 	public static final String UGSYNC_SOURCE_FILE_PROC =	"ranger.usersync.filesource.file";
 
 	public static final String UGSYNC_SOURCE_FILE_DELIMITER = "ranger.usersync.filesource.text.delimiter";
@@ -97,9 +99,16 @@ public class UserGroupSyncConfig  {
 
 	private static final String UGSYNC_SINK_CLASS = "org.apache.ranger.unixusersync.process.PolicyMgrUserGroupBuilder";
 
+	private static final String LGSYNC_DELTASYNC_SINK_CLASS = "org.apache.ranger.ldapusersync.process.LdapPolicyMgrUserGroupBuilder";
+	
 	private static final String LGSYNC_SOURCE_CLASS = "org.apache.ranger.ldapusersync.process.LdapUserGroupBuilder";
 
+	private static final String LGSYNC_DELTASYNC_SOURCE_CLASS = "org.apache.ranger.ldapusersync.process.LdapDeltaUserGroupBuilder";
+
 	private static final String LGSYNC_LDAP_URL = "ranger.usersync.ldap.url";
+	
+	private static final String LGSYNC_LDAP_DELTASYNC_ENABLED = "ranger.usersync.ldap.deltasync";
+	private static final boolean DEFAULT_LGSYNC_LDAP_DELTASYNC_ENABLED = false;
 
 	private static final String LGSYNC_LDAP_STARTTLS_ENABLED = "ranger.usersync.ldap.starttls";
 	private static final boolean DEFAULT_LGSYNC_LDAP_STARTTLS_ENABLED = false;
@@ -382,6 +391,11 @@ public class UserGroupSyncConfig  {
 		String val = prop.getProperty(UGSYNC_MOCK_RUN_PROP);
 		return (val != null && val.trim().equalsIgnoreCase("true"));
 	}
+	
+	public boolean isTestRunEnabled() {
+		String val = prop.getProperty(UGSYNC_TEST_RUN_PROP);
+		return (val != null && val.trim().equalsIgnoreCase("true"));
+	}
 
 	public String getPolicyManagerBaseURL() {
 		return prop.getProperty(UGSYNC_PM_URL_PROP);
@@ -456,7 +470,7 @@ public class UserGroupSyncConfig  {
 			} else {
 				min_interval = UGSYNC_SLEEP_TIME_IN_MILLIS_BETWEEN_CYCLE_MIN_VALUE;
 			}
-			if(ret < min_interval)
+			if((!isTestRunEnabled()) && (ret < min_interval))
 			{
 				LOG.info("Sleep Time Between Cycle can not be lower than [" + min_interval  + "] millisec. resetting to min value.");
 				ret = min_interval;
@@ -465,27 +479,40 @@ public class UserGroupSyncConfig  {
 		}
 	}
 
-
-	public UserGroupSource getUserGroupSource() throws Throwable {
-
+	private String getUserGroupSourceClassName() {
 		String val =  prop.getProperty(UGSYNC_SOURCE_CLASS_PARAM);
-
+		String className = null;
+		
 		String syncSource = null;
 
 		if(val == null || val.trim().isEmpty()) {
 			syncSource=getSyncSource();
 		}
 		else {
+			if (val.equalsIgnoreCase(LGSYNC_SOURCE_CLASS) && isDeltaSyncEnabled()) {
+				val = LGSYNC_DELTASYNC_SOURCE_CLASS;
+			}
 			syncSource = val;
 		}
 
-		String className = val;
+		className = val;
 
 		if(syncSource!=null && syncSource.equalsIgnoreCase("UNIX")){
 			className = UGSYNC_SOURCE_CLASS;
 		}else if(syncSource!=null && syncSource.equalsIgnoreCase("LDAP")){
-			className = LGSYNC_SOURCE_CLASS;
-		}
+			if (!isDeltaSyncEnabled()) {
+				className = LGSYNC_SOURCE_CLASS;
+			} else {
+				className = LGSYNC_DELTASYNC_SOURCE_CLASS;
+			}
+		} 
+
+		return className;
+	}
+	
+	public UserGroupSource getUserGroupSource() throws Throwable {
+
+		String className = getUserGroupSourceClassName();
 
 		Class<UserGroupSource> ugSourceClass = (Class<UserGroupSource>)Class.forName(className);
 
@@ -494,12 +521,16 @@ public class UserGroupSyncConfig  {
 		return ret;
 	}
 
-
 	public UserGroupSink getUserGroupSink() throws Throwable {
 		String val =  prop.getProperty(UGSYNC_SINK_CLASS_PARAM);
+		String className = getUserGroupSourceClassName();
 
-		if(val == null || val.trim().isEmpty()) {
-			val = UGSYNC_SINK_CLASS;
+		if (className.equals(LGSYNC_DELTASYNC_SOURCE_CLASS)) {
+			val = LGSYNC_DELTASYNC_SINK_CLASS;
+		} else {
+			if(val == null || val.trim().isEmpty()) {
+				val = UGSYNC_SINK_CLASS;
+			}
 		}
 
 		Class<UserGroupSink> ugSinkClass = (Class<UserGroupSink>)Class.forName(val);
@@ -892,6 +923,17 @@ public class UserGroupSyncConfig  {
 		}
 		return starttlsEnabled;
 	}
+	
+	public boolean isDeltaSyncEnabled() {
+		boolean deltaSyncEnabled;
+		String val = prop.getProperty(LGSYNC_LDAP_DELTASYNC_ENABLED);
+		if(val == null || val.trim().isEmpty()) {
+			deltaSyncEnabled = DEFAULT_LGSYNC_LDAP_DELTASYNC_ENABLED;
+		} else {
+			deltaSyncEnabled  = Boolean.valueOf(val);
+		}
+		return deltaSyncEnabled;
+	}
 
 	/* Used only for unit testing */
 	public void setUserSearchFilter(String filter) {
@@ -952,4 +994,9 @@ public class UserGroupSyncConfig  {
 	public void setGroupObjectClass(String groupObjectClass) {
 		prop.setProperty(LGSYNC_GROUP_OBJECT_CLASS, groupObjectClass);
 	}
+	
+	/* Used only for unit testing */
+    public void setDeltaSync(boolean deltaSyncEnabled) {
+        prop.setProperty(LGSYNC_LDAP_DELTASYNC_ENABLED, String.valueOf(deltaSyncEnabled));
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/ugsync/src/main/java/org/apache/ranger/unixusersync/model/GroupUserInfo.java
----------------------------------------------------------------------
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/GroupUserInfo.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/GroupUserInfo.java
new file mode 100644
index 0000000..a2cfa7b
--- /dev/null
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/model/GroupUserInfo.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.unixusersync.model;
+
+import java.util.List;
+
+public class GroupUserInfo {
+	XGroupInfo xgroupInfo;
+	List<XUserInfo> xuserInfo;
+
+	public XGroupInfo getXgroupInfo() {
+		return xgroupInfo;
+	}
+	public void setXgroupInfo(XGroupInfo xgroupInfo) {
+		this.xgroupInfo = xgroupInfo;
+	}
+	public List<XUserInfo> getXuserInfo() {
+		return xuserInfo;
+	}
+	public void setXuserInfo(List<XUserInfo> xuserInfo) {
+		this.xuserInfo = xuserInfo;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
----------------------------------------------------------------------
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
index 7e4d41a..c5e8721 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/PolicyMgrUserGroupBuilder.java
@@ -1063,5 +1063,19 @@ public class PolicyMgrUserGroupBuilder implements UserGroupSink {
 		return ret;	
 	}
 
+
+	@Override
+	public void addOrUpdateUser(String user) throws Throwable {
+		// TODO Auto-generated method stub
+		
+	}
+
+
+	@Override
+	public void addOrUpdateGroup(String group, List<String> users) throws Throwable {
+		// TODO Auto-generated method stub
+		
+	}
+
 	
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
----------------------------------------------------------------------
diff --git a/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java b/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
index bb3ef82..494efc2 100644
--- a/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
+++ b/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
@@ -26,5 +26,9 @@ public interface UserGroupSink {
 
 	void addOrUpdateUser(String user, List<String> groups) throws Throwable;
 	
+	void addOrUpdateUser(String user) throws Throwable;
+	
 	void addOrUpdateGroup(String group) throws Throwable;
+	
+	void addOrUpdateGroup(String group, List<String> users) throws Throwable;
 }

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/ugsync/src/test/java/org/apache/ranger/usergroupsync/LdapPolicyMgrUserGroupBuilderTest.java
----------------------------------------------------------------------
diff --git a/ugsync/src/test/java/org/apache/ranger/usergroupsync/LdapPolicyMgrUserGroupBuilderTest.java b/ugsync/src/test/java/org/apache/ranger/usergroupsync/LdapPolicyMgrUserGroupBuilderTest.java
new file mode 100644
index 0000000..99bc2b4
--- /dev/null
+++ b/ugsync/src/test/java/org/apache/ranger/usergroupsync/LdapPolicyMgrUserGroupBuilderTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.usergroupsync;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ranger.ldapusersync.process.LdapPolicyMgrUserGroupBuilder;
+
+public class LdapPolicyMgrUserGroupBuilderTest extends LdapPolicyMgrUserGroupBuilder {
+        private Set<String> allGroups;
+        private Set<String> allUsers;
+
+        @Override
+        public void init() throws Throwable {
+                allGroups = new HashSet<>();
+                allUsers = new HashSet<>();
+        }
+
+        @Override
+        public void addOrUpdateUser(String user, List<String> groups) {
+                allGroups.addAll(groups);
+                //allUsers.add(user);
+                //System.out.println("Username: " + user + " and associated groups: " + groups);
+        }
+
+        @Override
+        public void addOrUpdateGroup(String group) {
+                allGroups.add(group);
+                //System.out.println("Groupname: " + group);
+        }
+        
+        @Override
+        public void addOrUpdateUser(String user) {
+                allUsers.add(user);
+                //System.out.println("Username: " + user);
+        }
+
+        @Override
+        public void addOrUpdateGroup(String group, List<String> users) {
+        	boolean addGroup = false;
+        		for (String user : users) {
+        			if (allUsers.contains(user)) {
+        				addGroup = true;
+        				break;
+        			}
+        		}
+        		if (addGroup) {
+        			allGroups.add(group);
+        		}
+                //allUsers.addAll(users);
+                //System.out.println("Groupname: " + group + " and associated users: " + users);
+        }
+
+        public int getTotalUsers() {
+                return allUsers.size();
+        }
+
+        public int getTotalGroups() {
+                //System.out.println("Groups = " + allGroups);
+                return allGroups.size();
+        }
+
+        public Set<String> getAllGroups() {
+                return allGroups;
+        }
+
+        public Set<String> getAllUsers() {
+                return allUsers;
+        }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/2261a9dc/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 81d952e..b4c9065 100644
--- a/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestLdapUserGroup.java
+++ b/ugsync/src/test/java/org/apache/ranger/usergroupsync/TestLdapUserGroup.java
@@ -73,7 +73,7 @@ partitions =
 		)
 public class TestLdapUserGroup extends AbstractLdapTestUnit{
 	private UserGroupSyncConfig config;
-	private LdapUserGroupBuilder ldapBuilder;
+	private UserGroupSource ldapBuilder;
 
 	@Before
 	public void setup() throws Exception {
@@ -553,6 +553,111 @@ public class TestLdapUserGroup extends AbstractLdapTestUnit{
 		assertEquals(110, sink.getTotalUsers());
 		assertEquals(0, sink.getTotalGroups());
 	}
+	
+	@Test
+	public void testDeltaUpdateSinkTotalGroups() throws Throwable {
+		config.setUserSearchBase("cn=users,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+		config.setUserSearchFilter("");
+		config.setGroupSearchBase("OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+		config.setGroupSearchFilter("");
+		config.setUserGroupMemberAttributeName("member");
+		config.setUserObjectClass("organizationalPerson");
+		config.setGroupObjectClass("groupOfNames");
+		config.setGroupSearchEnabled(true);
+		config.setGroupSearchFirstEnabled(false);
+		config.setDeltaSync(true);
+		//UserGroupSource ldapDeltaBuilder = config.getUserGroupSource();
+		ldapBuilder = config.getUserGroupSource();
+		ldapBuilder.init();
+		LdapPolicyMgrUserGroupBuilderTest sink = new LdapPolicyMgrUserGroupBuilderTest();
+		sink.init();
+		ldapBuilder.updateSink(sink);
+		assertEquals(10, sink.getTotalGroups());
+	}
+	
+	@Test
+	public void testDeltaUpdateSinkMultipleOUGroups() throws Throwable {
+		config.setUserSearchBase("cn=users,DC=ranger,DC=qe,DC=hortonworks,DC=com;ou=HadoopUsers,DC=ranger,DC=qe,DC=hortonworks,DC=com;ou=BusinessUsers,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+		config.setUserSearchFilter("cn=*");
+		config.setGroupSearchBase("OU=HdpGroups,OU=HadoopUsers,DC=ranger,DC=qe,DC=hortonworks,DC=com;OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+		config.setGroupSearchFilter("cn=*Group10");
+		config.setUserGroupMemberAttributeName("member");
+		config.setUserObjectClass("organizationalPerson");
+		config.setGroupObjectClass("groupOfNames");
+		config.setGroupSearchEnabled(true);
+		config.setGroupSearchFirstEnabled(false);
+		config.setDeltaSync(true);
+		ldapBuilder = config.getUserGroupSource();
+		ldapBuilder.init();
+		LdapPolicyMgrUserGroupBuilderTest sink = new LdapPolicyMgrUserGroupBuilderTest();
+		sink.init();
+		ldapBuilder.updateSink(sink);
+		assertEquals(111, sink.getTotalUsers());
+		assertEquals(2, sink.getTotalGroups());
+	}
+	
+	@Test
+	public void testDeltaGroupBasedWithUserFilter() throws Throwable {
+		config.setUserSearchBase("DC=ranger,DC=qe,DC=hortonworks,DC=com;");
+		config.setUserSearchFilter("cn=User*");
+		config.setGroupSearchBase("OU=HdpGroups,OU=HadoopUsers,DC=ranger,DC=qe,DC=hortonworks,DC=com;OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+		config.setGroupSearchFilter("cn=*Group10");
+		config.setUserGroupMemberAttributeName("member");
+		config.setUserObjectClass("organizationalPerson");
+		config.setGroupObjectClass("groupOfNames");
+		config.setGroupSearchFirstEnabled(true);
+		config.setUserSearchEnabled(true);
+		config.setDeltaSync(true);
+		ldapBuilder = config.getUserGroupSource();
+		ldapBuilder.init();
+		LdapPolicyMgrUserGroupBuilderTest sink = new LdapPolicyMgrUserGroupBuilderTest();
+		sink.init();
+		ldapBuilder.updateSink(sink);
+		assertEquals(1, sink.getTotalUsers());
+		assertEquals(2, sink.getTotalGroups());
+	}
+
+	@Test
+	public void testDeltaGroupBasedWithNoUsers() throws Throwable {
+		config.setUserSearchBase("DC=ranger,DC=qe,DC=hortonworks,DC=com;");
+		config.setUserSearchFilter("cn=*");
+		config.setGroupSearchBase("OU=HdpGroups,OU=HadoopUsers,DC=ranger,DC=qe,DC=hortonworks,DC=com;OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+		config.setGroupSearchFilter("cn=Group2*");
+		config.setUserGroupMemberAttributeName("member");
+		config.setUserObjectClass("organizationalPerson");
+		config.setGroupObjectClass("groupOfNames");
+		config.setGroupSearchFirstEnabled(true);
+		config.setUserSearchEnabled(true);
+		config.setDeltaSync(true);
+		ldapBuilder = config.getUserGroupSource();
+		ldapBuilder.init();
+		LdapPolicyMgrUserGroupBuilderTest sink = new LdapPolicyMgrUserGroupBuilderTest();
+		sink.init();
+		ldapBuilder.updateSink(sink);
+		assertEquals(0, sink.getTotalUsers());
+		assertEquals(2, sink.getTotalGroups());
+	}
+	
+	@Test
+	public void testDeltaGBWithUserSearchDisabled() throws Throwable {
+		config.setUserSearchBase("DC=ranger,DC=qe,DC=hortonworks,DC=com;");
+		config.setUserSearchFilter("cn=User*");
+		config.setGroupSearchBase("OU=HdpGroups,OU=HadoopUsers,DC=ranger,DC=qe,DC=hortonworks,DC=com;OU=Groups,DC=ranger,DC=qe,DC=hortonworks,DC=com");
+		config.setGroupSearchFilter("cn=*Group10");
+		config.setUserGroupMemberAttributeName("member");
+		config.setUserObjectClass("organizationalPerson");
+		config.setGroupObjectClass("groupOfNames");
+		config.setGroupSearchFirstEnabled(true);
+		config.setUserSearchEnabled(false);
+		config.setDeltaSync(true);
+		ldapBuilder = config.getUserGroupSource();
+		ldapBuilder.init();
+		LdapPolicyMgrUserGroupBuilderTest sink = new LdapPolicyMgrUserGroupBuilderTest();
+		sink.init();
+		ldapBuilder.updateSink(sink);
+		assertEquals(2, sink.getTotalUsers());
+		assertEquals(2, sink.getTotalGroups());
+	}
 
 	@After
 	public void shutdown() throws Exception {