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 2021/01/08 17:55:17 UTC

[ranger] branch ranger-2.2 updated: RANGER-980: Support to update users/groups that are deleted at the sync source

This is an automated email from the ASF dual-hosted git repository.

spolavarapu pushed a commit to branch ranger-2.2
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/ranger-2.2 by this push:
     new 09911ad  RANGER-980: Support to update users/groups that are deleted at the sync source
09911ad is described below

commit 09911ad82c0a136db10d4430b4a5bb3c82fb62cc
Author: Sailaja Polavarapu <sp...@cloudera.com>
AuthorDate: Fri Jan 8 09:47:49 2021 -0800

    RANGER-980: Support to update users/groups that are deleted at the sync source
---
 .../main/java/org/apache/ranger/biz/XUserMgr.java  |  25 +
 .../java/org/apache/ranger/rest/XUserREST.java     |  16 +
 .../apache/ranger/ugsyncutil/model/XGroupInfo.java |  21 +
 .../apache/ranger/ugsyncutil/model/XUserInfo.java  |  19 +
 .../ugsyncutil/util/UgsyncCommonConstants.java     |  19 +-
 .../ldapusersync/process/LdapUserGroupBuilder.java |  52 +-
 .../unixusersync/config/UserGroupSyncConfig.java   |  74 ++-
 .../process/FileSourceUserGroupBuilder.java        |  38 +-
 .../process/PolicyMgrUserGroupBuilder.java         | 560 ++++++++++++++++-----
 .../unixusersync/process/UnixUserGroupBuilder.java |  80 ++-
 .../apache/ranger/usergroupsync/UserGroupSink.java |   3 +-
 .../PolicyMgrUserGroupBuilderTest.java             |   3 +-
 12 files changed, 703 insertions(+), 207 deletions(-)

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 b0d8569..010f320 100755
--- a/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/XUserMgr.java
@@ -2972,6 +2972,7 @@ public class XUserMgr extends XUserMgrBase {
 			logger.warn("Could not find corresponding xUser for username: [" + vXPortalUser.getLoginId() + "], So not updating this user");
 			return vXUser;
 		}
+		logger.info("xUser.getName() = " + xUser.getName() + " vXUser.getName() = " + vXUser.getName());
 		vXUser.setId(xUser.getId());
 		vXUser = xUserService.updateResource(vXUser);
 		vXUser.setUserRoleList(roleList);
@@ -2995,4 +2996,28 @@ public class XUserMgr extends XUserMgrBase {
 
 		return vXUser;
 	}
+
+	public int updateDeletedUsers(Set<String> deletedUsers) {
+		for (String deletedUser : deletedUsers) {
+			XXUser xUser = daoManager.getXXUser().findByUserName(deletedUser);
+			if (xUser != null) {
+				VXUser vObj = xUserService.populateViewBean(xUser);
+				vObj.setIsVisible(RangerCommonEnums.IS_HIDDEN);
+				xUserService.updateResource(vObj);
+			}
+		}
+		return deletedUsers.size();
+	}
+
+	public int updateDeletedGroups(Set<String> deletedGroups) {
+		for (String deletedGroup : deletedGroups) {
+			XXGroup xGroup = daoManager.getXXGroup().findByGroupName(deletedGroup);
+			if (xGroup !=  null) {
+				VXGroup vObj = xGroupService.populateViewBean(xGroup);
+				vObj.setIsVisible(RangerCommonEnums.IS_HIDDEN);
+				xGroupService.updateResource(vObj);
+			}
+		}
+		return deletedGroups.size();
+	}
 }
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 03ccfbe..8fae634 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
@@ -1408,4 +1408,20 @@ public class XUserREST {
 	public List<String> setXUserRolesByName(UsersGroupRoleAssignments ugRoleAssignments) {
 		return xUserMgr.updateUserRoleAssignments(ugRoleAssignments);
 	}
+
+	@POST
+	@Path("/ugsync/groups/visibility")
+	@Produces({ "application/xml", "application/json" })
+	@PreAuthorize("hasRole('ROLE_SYS_ADMIN')")
+	public int updateDeletedGroups(Set<String> deletedGroups){
+		return xUserMgr.updateDeletedGroups(deletedGroups);
+	}
+
+	@POST
+	@Path("/ugsync/users/visibility")
+	@Produces({ "application/xml", "application/json" })
+	@PreAuthorize("hasRole('ROLE_SYS_ADMIN')")
+	public int updateDeletedUsers(Set<String> deletedUsers){
+		return xUserMgr.updateDeletedUsers(deletedUsers);
+	}
 }
diff --git a/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XGroupInfo.java b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XGroupInfo.java
index df04266..5f5c9aa 100644
--- a/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XGroupInfo.java
+++ b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XGroupInfo.java
@@ -19,14 +19,18 @@
 
  package org.apache.ranger.ugsyncutil.model;
 
+import java.util.Map;
+
 public class XGroupInfo {
 	
 	private String id;
 	private String name;
 	private String description;
 	private String groupType;
+	private String isVisible;
 	private String groupSource;
 	private String otherAttributes;
+	private Map<String, String> otherAttrsMap;
 	public String getId() {
 		return id;
 	}
@@ -52,6 +56,15 @@ public class XGroupInfo {
 	public void setGroupType(String groupType) {
 		this.groupType = groupType;
 	}
+
+	public String getIsVisible() {
+		return isVisible;
+	}
+
+	public void setIsVisible(String isVisible) {
+		this.isVisible = isVisible;
+	}
+
 	public String getGroupSource() {
 		return groupSource;
 	}
@@ -59,6 +72,14 @@ public class XGroupInfo {
 		this.groupSource = groupSource;
 	}
 
+	public Map<String, String> getOtherAttrsMap() {
+		return otherAttrsMap;
+	}
+
+	public void setOtherAttrsMap(Map<String, String> otherAttrsMap) {
+		this.otherAttrsMap = otherAttrsMap;
+	}
+
 	public String getOtherAttributes() {
 		return otherAttributes;
 	}
diff --git a/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XUserInfo.java b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XUserInfo.java
index 9405a76..058b984 100644
--- a/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XUserInfo.java
+++ b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/model/XUserInfo.java
@@ -21,14 +21,17 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 public class XUserInfo {
 	private String id;
 	private String name;
 	private String 	description;
 	private String otherAttributes;
+	private Map<String, String> otherAttrsMap;
 	private String userSource;
 	private String status;
+	private String isVisible;
     private List<String> groupNameList = new ArrayList<String>();
     private List<String> userRoleList = new ArrayList<String>();
 	
@@ -67,6 +70,14 @@ public class XUserInfo {
 		this.status = status;
 	}
 
+	public String getIsVisible() {
+		return isVisible;
+	}
+
+	public void setIsVisible(String isVisible) {
+		this.isVisible = isVisible;
+	}
+
 	public void setGroupNameList(List<String> groupNameList) {
 		this.groupNameList = groupNameList;
 	}
@@ -93,6 +104,14 @@ public class XUserInfo {
         this.userRoleList = userRoleList;
     }
 
+	public Map<String, String> getOtherAttrsMap() {
+		return otherAttrsMap;
+	}
+
+	public void setOtherAttrsMap(Map<String, String> otherAttrsMap) {
+		this.otherAttrsMap = otherAttrsMap;
+	}
+
 	public String getOtherAttributes() {
 		return otherAttributes;
 	}
diff --git a/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/util/UgsyncCommonConstants.java
similarity index 62%
copy from ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
copy to ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/util/UgsyncCommonConstants.java
index 794bc81..f20bf91 100644
--- a/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
+++ b/ugsync-util/src/main/java/org/apache/ranger/ugsyncutil/util/UgsyncCommonConstants.java
@@ -17,20 +17,13 @@
  * under the License.
  */
 
- package org.apache.ranger.usergroupsync;
+package org.apache.ranger.ugsyncutil.util;
 
-import org.apache.ranger.ugsyncutil.model.UgsyncAuditInfo;
+public class UgsyncCommonConstants {
 
-import java.util.Map;
-import java.util.Set;
-
-public interface UserGroupSink {
-	void init() throws Throwable;
-
-	void postUserGroupAuditInfo(UgsyncAuditInfo ugsyncAuditInfo) throws Throwable;
-
-	void addOrUpdateUsersGroups(Map<String, Map<String, String>> sourceGroups,
-								Map<String, Map<String, String>> sourceUsers,
-								Map<String, Set<String>> sourceGroupUsers) throws Throwable;
+    public static final String ORIGINAL_NAME = "original_name";
+    public static final String FULL_NAME = "full_name";
+    public static final String SYNC_SOURCE = "sync_source";
+    public static final String LDAP_URL = "ldap_url";
 
 }
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 7e5e70a..c8fe3fc 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
@@ -48,6 +48,7 @@ import javax.naming.ldap.StartTlsRequest;
 import javax.naming.ldap.StartTlsResponse;
 import org.apache.commons.lang.StringUtils;
 import org.apache.log4j.Logger;
+import org.apache.ranger.ugsyncutil.util.UgsyncCommonConstants;
 import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
 import org.apache.ranger.ugsyncutil.model.LdapSyncSourceInfo;
 import org.apache.ranger.ugsyncutil.model.UgsyncAuditInfo;
@@ -62,7 +63,7 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 	private static final Logger LOG = Logger.getLogger(LdapUserGroupBuilder.class);
 
 	private UserGroupSyncConfig config = UserGroupSyncConfig.getInstance();
-	
+
 	private static final String DATA_TYPE_BYTEARRAY = "byte[]";
 	private static final String DATE_FORMAT = "yyyyMMddHHmmss";
 	private static final int PAGE_SIZE = 500;
@@ -106,6 +107,8 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 	private String groupCloudIdAttribute;
 	private Set<String> otherGroupAttributes;
 	private int groupHierarchyLevels;
+	private int deleteCycles;
+	private String currentSyncSource;
 
 	private LdapContext ldapContext;
 	StartTlsResponse tls;
@@ -127,6 +130,7 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 	public void init() throws Throwable{
 		deltaSyncUserTime = 0;
 		deltaSyncGroupTime = 0;
+		deleteCycles = 1;
 		DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
 		deltaSyncUserTimeStamp = dateFormat.format(new Date(0));
 		deltaSyncGroupTimeStamp = dateFormat.format(new Date(0));
@@ -139,7 +143,7 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 		ldapSyncSourceInfo.setGroupSearchEnabled(Boolean.toString(groupSearchEnabled));
 		ldapSyncSourceInfo.setGroupSearchFirstEnabled(Boolean.toString(groupSearchFirstEnabled));
 		ldapSyncSourceInfo.setGroupHierarchyLevel(Integer.toString(groupHierarchyLevels));
-		ugsyncAuditInfo.setSyncSource("LDAP/AD");
+		ugsyncAuditInfo.setSyncSource(currentSyncSource);
 		ugsyncAuditInfo.setLdapSyncSourceInfo(ldapSyncSourceInfo);
 	}
 
@@ -200,6 +204,7 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 	private void setConfig() throws Throwable {
 		LOG.info("LdapUserGroupBuilder initialization started");
 
+		currentSyncSource = config.getCurrentSyncSource();
 		groupSearchFirstEnabled =   true;
 		userSearchEnabled =   true;
 		groupSearchEnabled =   true;
@@ -316,13 +321,24 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 	@Override
 	public void updateSink(UserGroupSink sink) throws Throwable {
 		LOG.info("LdapUserGroupBuilder updateSink started");
+		boolean computeDeletes = false;
 		groupUserTable = HashBasedTable.create();
 		sourceGroups = new HashMap<>();
 		sourceUsers = new HashMap<>();
 		sourceGroupUsers = new HashMap<>();
 
-        long highestdeltaSyncGroupTime = getGroups();
-        long highestdeltaSyncUserTime = getUsers();
+		if (config.isUserSyncDeletesEnabled() && deleteCycles >= config.getUserSyncDeletesFrequency()) {
+			deleteCycles = 1;
+			computeDeletes = true;
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Compute deleted users/groups is enabled for this sync cycle");
+			}
+		}
+		if (config.isUserSyncDeletesEnabled()) {
+			deleteCycles++;
+		}
+        long highestdeltaSyncGroupTime = getGroups(computeDeletes);
+        long highestdeltaSyncUserTime = getUsers(computeDeletes);
 
 		if (groupHierarchyLevels > 0) {
 			LOG.info("Going through group hierarchy for nested group evaluation");
@@ -354,7 +370,7 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 		}
 
 		try {
-			sink.addOrUpdateUsersGroups(sourceGroups, sourceUsers, sourceGroupUsers);
+			sink.addOrUpdateUsersGroups(sourceGroups, sourceUsers, sourceGroupUsers, computeDeletes);
 			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
 			LOG.info("deltaSyncUserTime = " + deltaSyncUserTime + " and highestdeltaSyncUserTime = " + highestdeltaSyncUserTime);
 			if (deltaSyncUserTime < highestdeltaSyncUserTime) {
@@ -386,7 +402,7 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 		}
 	}
 
-	private long getUsers() throws Throwable {
+	private long getUsers(boolean computeDeletes) throws Throwable {
 		NamingEnumeration<SearchResult> userSearchResultEnum = null;
 		NamingEnumeration<SearchResult> groupSearchResultEnum = null;
 		long highestdeltaSyncUserTime;
@@ -399,7 +415,7 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 						new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) });
 			}
 			DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
-			if (groupUserTable.rowKeySet().size() != 0 || !config.isDeltaSyncEnabled()) {
+			if (groupUserTable.rowKeySet().size() != 0 || !config.isDeltaSyncEnabled() || (computeDeletes)) {
 				// Fix RANGER-1957: Perform full sync when there are updates to the groups or when incremental sync is not enabled
 				deltaSyncUserTime = 0;
 				deltaSyncUserTimeStamp = dateFormat.format(new Date(0));
@@ -495,8 +511,10 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 						}
 
 						Map<String, String> userAttrMap = new HashMap<>();
-						userAttrMap.put("original_name", userName);
-						userAttrMap.put("full_name", userFullName);
+						userAttrMap.put(UgsyncCommonConstants.ORIGINAL_NAME, userName);
+						userAttrMap.put(UgsyncCommonConstants.FULL_NAME, userFullName);
+						userAttrMap.put(UgsyncCommonConstants.SYNC_SOURCE, currentSyncSource);
+						userAttrMap.put(UgsyncCommonConstants.LDAP_URL, config.getLdapUrl());
 						Attribute userCloudIdAttr = attributes.get(userCloudIdAttribute);
 						if (userCloudIdAttr != null) {
 							addToAttrMap(userAttrMap, "cloud_id", userCloudIdAttr, config.getUserCloudIdAttributeDataType());
@@ -589,7 +607,7 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 		return highestdeltaSyncUserTime;
 	}
 
-	private long getGroups() throws Throwable {
+	private long getGroups(boolean computeDeletes) throws Throwable {
 		NamingEnumeration<SearchResult> groupSearchResultEnum = null;
         DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
         long highestdeltaSyncGroupTime = deltaSyncGroupTime;
@@ -610,7 +628,7 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 				extendedGroupSearchFilter = extendedGroupSearchFilter + customFilter;
 			}
 
-			if (!config.isDeltaSyncEnabled()) {
+			if (!config.isDeltaSyncEnabled() || (computeDeletes)) {
 				// Perform full sync when incremental sync is not enabled
 				deltaSyncGroupTime = 0;
 				deltaSyncGroupTimeStamp = dateFormat.format(new Date(0));
@@ -649,8 +667,10 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 							String groupFullName = (groupEntry.getNameInNamespace());
 							String gName = (String) groupNameAttr.get();
 							Map<String, String> groupAttrMap = new HashMap<>();
-							groupAttrMap.put("original_name", gName);
-							groupAttrMap.put("full_name", groupFullName);
+							groupAttrMap.put(UgsyncCommonConstants.ORIGINAL_NAME, gName);
+							groupAttrMap.put(UgsyncCommonConstants.FULL_NAME, groupFullName);
+							groupAttrMap.put(UgsyncCommonConstants.SYNC_SOURCE, currentSyncSource);
+							groupAttrMap.put(UgsyncCommonConstants.LDAP_URL, config.getLdapUrl());
 							Attribute groupCloudIdAttr = attributes.get(groupCloudIdAttribute);
 							if (groupCloudIdAttr != null) {
 								addToAttrMap(groupAttrMap, "cloud_id", groupCloudIdAttr, config.getGroupCloudIdAttributeDataType());
@@ -870,8 +890,10 @@ public class LdapUserGroupBuilder implements UserGroupSource {
 
 
 							Map<String, String> groupAttrMap = new HashMap<>();
-							groupAttrMap.put("original_name", gName);
-							groupAttrMap.put("full_name", groupFullName);
+							groupAttrMap.put(UgsyncCommonConstants.ORIGINAL_NAME, gName);
+							groupAttrMap.put(UgsyncCommonConstants.FULL_NAME, groupFullName);
+							groupAttrMap.put(UgsyncCommonConstants.SYNC_SOURCE, currentSyncSource);
+							groupAttrMap.put(UgsyncCommonConstants.LDAP_URL, config.getLdapUrl());
 							for (String otherGroupAttribute : otherGroupAttributes) {
 								Attribute otherGroupAttr = groupEntry.getAttributes().get(otherGroupAttribute);
 								if (otherGroupAttr != null) {
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 5d5ad58..31e15a1 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
@@ -61,7 +61,7 @@ public class UserGroupSyncConfig  {
 	
 	public static final String UGSYNC_UNIX_GROUP_FILE = "ranger.usersync.unix.group.file";
 	public static final String  DEFAULT_UGSYNC_UNIX_GROUP_FILE =   "/etc/group";
-	
+
 	public static final String  UGSYNC_MIN_USERID_PROP  = 	"ranger.usersync.unix.minUserId";
 
 	public static final String  UGSYNC_MIN_GROUPID_PROP =   "ranger.usersync.unix.minGroupId";
@@ -72,7 +72,7 @@ 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";
@@ -113,7 +113,7 @@ public class UserGroupSyncConfig  {
 	private static final String LGSYNC_SOURCE_CLASS = "org.apache.ranger.ldapusersync.process.LdapUserGroupBuilder";
 
 	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;
 
@@ -269,6 +269,11 @@ public class UserGroupSyncConfig  {
     private static final long    DEFAULT_UGSYNC_METRICS_FREQUENCY_TIME_IN_MILLIS = 10000L;
     public static final String   UGSYNC_METRICS_ENABLED_PROP = "ranger.usersync.metrics.enabled";
 
+	private static final String UGSYNC_DELETES_ENABLED = "ranger.usersync.deletes.enabled";
+	private static final boolean DEFAULT_UGSYNC_DELETES_ENABLED = false;
+	private static final String  UGSYNC_DELETES_FREQUENCY = "ranger.usersync.deletes.frequency";
+	private static final long    DEFAULT_UGSYNC_DELETES_FREQUENCY = 10L; // After every 10 sync cycles
+
     private Properties prop = new Properties();
 
 	private static volatile UserGroupSyncConfig me = null;
@@ -322,7 +327,7 @@ public class UserGroupSyncConfig  {
 		}
 		return val;
 	}
-	
+
 	public String getUnixPasswordFile() {
 		String val = prop.getProperty(UGSYNC_UNIX_PASSWORD_FILE);
 		if ( val == null ) {
@@ -331,7 +336,7 @@ public class UserGroupSyncConfig  {
 
 		return val;
 	}
-	
+
 	public String getUnixGroupFile() {
 		String val = prop.getProperty(UGSYNC_UNIX_GROUP_FILE);
 		if ( val == null ) {
@@ -368,7 +373,7 @@ 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"));
@@ -505,7 +510,7 @@ public class UserGroupSyncConfig  {
 	private String getUserGroupSourceClassName() {
 		String val =  prop.getProperty(UGSYNC_SOURCE_CLASS_PARAM);
 		String className = UGSYNC_SOURCE_CLASS;
-		
+
 		String syncSource = null;
 
 		if(val == null || val.trim().isEmpty()) {
@@ -524,11 +529,11 @@ public class UserGroupSyncConfig  {
 			className = UGSYNC_SOURCE_CLASS;
 		}else if(syncSource!=null && syncSource.equalsIgnoreCase("LDAP")){
 			className = LGSYNC_SOURCE_CLASS;
-		} 
+		}
 
 		return className;
 	}
-	
+
 	public UserGroupSource getUserGroupSource() throws Throwable {
 
 		String className = getUserGroupSourceClassName();
@@ -1078,7 +1083,7 @@ public class UserGroupSyncConfig  {
 		}
 		return starttlsEnabled;
 	}
-	
+
 	public boolean isDeltaSyncEnabled() {
 		boolean deltaSyncEnabled;
 		String val = prop.getProperty(LGSYNC_LDAP_DELTASYNC_ENABLED);
@@ -1149,11 +1154,11 @@ 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));
-    	}	
+    	}
 
 	/* Used only for unit testing */
     	public void setUserNameAttribute(String userNameAttr) {
@@ -1217,4 +1222,49 @@ public class UserGroupSyncConfig  {
 		String val = prop.getProperty(UGSYNC_METRICS_ENABLED_PROP);
 		return "true".equalsIgnoreCase(StringUtils.trimToEmpty(val));
 	}
+
+
+	public boolean isUserSyncDeletesEnabled() {
+		boolean isUserSyncDeletesEnabled;
+		String val = prop.getProperty(UGSYNC_DELETES_ENABLED);
+		if(StringUtils.isEmpty(val)) {
+			isUserSyncDeletesEnabled = DEFAULT_UGSYNC_DELETES_ENABLED;
+		} else {
+			isUserSyncDeletesEnabled  = Boolean.valueOf(val);
+		}
+		return isUserSyncDeletesEnabled;
+	}
+
+	/*
+	 * This is the frequency of computing deleted users/groups from the sync source.
+	 * Default and minimum value is 8hrs
+	 * If the delete frequency interval value is less than sync interval and greater than 8hrs,
+	 * then deleted objects are computed at every sync cycle.
+	 */
+	public long getUserSyncDeletesFrequency() throws Throwable {
+		long ret = 1;
+
+		String val = prop.getProperty(UGSYNC_DELETES_FREQUENCY);
+		if (StringUtils.isNotBlank(val)) {
+			ret = Long.valueOf(val);
+			if (!isTestRunEnabled() && ret < DEFAULT_UGSYNC_DELETES_FREQUENCY) {
+				LOG.info("Frequency of computing deletes cannot be set below " + DEFAULT_UGSYNC_DELETES_FREQUENCY);
+				ret = DEFAULT_UGSYNC_DELETES_FREQUENCY;
+			}
+		}
+		return ret;
+	}
+
+	public String getCurrentSyncSource() throws Throwable{
+		String currentSyncSource;
+		String className = getUserGroupSource().getClass().getName();
+		if (LGSYNC_SOURCE_CLASS.equals(className)) {
+			currentSyncSource = "LDAP/AD";
+		} else if (UGSYNC_SOURCE_CLASS.equalsIgnoreCase(className)){
+			currentSyncSource = "Unix";
+		} else {
+			currentSyncSource = "File";
+		}
+		return currentSyncSource;
+	}
 }
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/FileSourceUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/FileSourceUserGroupBuilder.java
index 5f3523e..0a4de66 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/FileSourceUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/FileSourceUserGroupBuilder.java
@@ -37,6 +37,7 @@ import org.apache.commons.csv.CSVFormat;
 import org.apache.commons.csv.CSVParser;
 import org.apache.commons.csv.CSVRecord;
 import org.apache.log4j.Logger;
+import org.apache.ranger.ugsyncutil.util.UgsyncCommonConstants;
 import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
 import org.apache.ranger.ugsyncutil.model.FileSyncSourceInfo;
 import org.apache.ranger.ugsyncutil.model.UgsyncAuditInfo;
@@ -62,6 +63,9 @@ public class FileSourceUserGroupBuilder extends AbstractUserGroupSource  impleme
 	private boolean isStartupFlag = false;
 
 	private boolean isUpdateSinkSucc = true;
+	private int deleteCycles;
+	private boolean computeDeletes = false;
+	private String currentSyncSource;
 
 	public static void main(String[] args) throws Throwable {
 		FileSourceUserGroupBuilder filesourceUGBuilder = new FileSourceUserGroupBuilder();
@@ -90,12 +94,14 @@ public class FileSourceUserGroupBuilder extends AbstractUserGroupSource  impleme
 	@Override
 	public void init() throws Throwable {
 		isStartupFlag = true;
+		deleteCycles = 1;
+		currentSyncSource = config.getCurrentSyncSource();
 		if(userGroupFilename == null) {
 			userGroupFilename = config.getUserSyncFileSource();
 		}
 		ugsyncAuditInfo = new UgsyncAuditInfo();
 		fileSyncSourceInfo = new FileSyncSourceInfo();
-		ugsyncAuditInfo.setSyncSource("File");
+		ugsyncAuditInfo.setSyncSource(currentSyncSource);
 		ugsyncAuditInfo.setFileSyncSourceInfo(fileSyncSourceInfo);
 		fileSyncSourceInfo.setFileName(userGroupFilename);
 		buildUserGroupInfo();
@@ -103,13 +109,29 @@ public class FileSourceUserGroupBuilder extends AbstractUserGroupSource  impleme
 	
 	@Override
 	public boolean isChanged() {
+		computeDeletes = false;
 		// If previous update to Ranger admin fails, 
 		// we want to retry the sync process even if there are no changes to the sync files
 		if (!isUpdateSinkSucc) {
 			LOG.info("Previous updateSink failed and hence retry!!");
 			return true;
 		}
-		
+		try {
+			if (config.isUserSyncDeletesEnabled() && deleteCycles >= config.getUserSyncDeletesFrequency()) {
+				deleteCycles = 1;
+				computeDeletes = true;
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("Compute deleted users/groups is enabled for this sync cycle");
+				}
+				return true;
+			}
+		} catch (Throwable t) {
+			LOG.error("Failed to get information about usersync delete frequency", t);
+		}
+		if (config.isUserSyncDeletesEnabled()) {
+			deleteCycles++;
+		}
+
 		long TempUserGroupFileModifedAt = new File(userGroupFilename).lastModified();
 		if (usergroupFileModified != TempUserGroupFileModifedAt) {
 			return true;
@@ -132,15 +154,17 @@ public class FileSourceUserGroupBuilder extends AbstractUserGroupSource  impleme
 			for (Map.Entry<String, List<String>> entry : user2GroupListMap.entrySet()) {
 				String userName = entry.getKey();
 				Map<String, String> userAttrMap = new HashMap<>();
-				userAttrMap.put("original_name", userName);
-				userAttrMap.put("full_name", userName);
+				userAttrMap.put(UgsyncCommonConstants.ORIGINAL_NAME, userName);
+				userAttrMap.put(UgsyncCommonConstants.FULL_NAME, userName);
+				userAttrMap.put(UgsyncCommonConstants.SYNC_SOURCE, currentSyncSource);
 				sourceUsers.put(userName, userAttrMap);
 				List<String> groups = entry.getValue();
 				if (groups != null) {
 					for(String groupName : groups) {
 						Map<String, String> groupAttrMap = new HashMap<>();
-						groupAttrMap.put("original_name", groupName);
-						groupAttrMap.put("full_name", groupName);
+						groupAttrMap.put(UgsyncCommonConstants.ORIGINAL_NAME, groupName);
+						groupAttrMap.put(UgsyncCommonConstants.FULL_NAME, groupName);
+						groupAttrMap.put(UgsyncCommonConstants.SYNC_SOURCE, currentSyncSource);
 						sourceGroups.put(groupName, groupAttrMap);
 						Set<String> groupUsers = sourceGroupUsers.get(groupName);
 						if (CollectionUtils.isNotEmpty(groupUsers)) {
@@ -161,7 +185,7 @@ public class FileSourceUserGroupBuilder extends AbstractUserGroupSource  impleme
 			}
 
 			try {
-				sink.addOrUpdateUsersGroups(sourceGroups, sourceUsers, sourceGroupUsers);
+				sink.addOrUpdateUsersGroups(sourceGroups, sourceUsers, sourceGroupUsers, computeDeletes);
 			} catch (Throwable t) {
 				LOG.error("Failed to update ranger admin. Will retry in next sync cycle!!", t);
 				isUpdateSinkSucc = false;
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 556d976..15a7a38 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
@@ -44,6 +44,7 @@ import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.security.SecureClientLogin;
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
+import org.apache.ranger.ugsyncutil.util.UgsyncCommonConstants;
 import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
 import org.apache.ranger.unixusersync.model.GetXGroupListResponse;
 import org.apache.ranger.unixusersync.model.GetXUserListResponse;
@@ -57,7 +58,7 @@ import org.apache.ranger.usergroupsync.UserGroupSink;
 
 public class PolicyMgrUserGroupBuilder extends AbstractUserGroupSource implements UserGroupSink {
 
-private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.class);
+	private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.class);
 
 	private static final String AUTHENTICATION_TYPE = "hadoop.security.authentication";
 	private String AUTH_KERBEROS = "kerberos";
@@ -80,8 +81,14 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 
 	public static final String PM_UPDATE_USERS_ROLES_URI  = "/service/xusers/users/roleassignments";	// PUT
 
+	private static final String PM_UPDATE_DELETED_GROUPS_URI = "/service/xusers/ugsync/groups/visibility";	// POST
+
+	private static final String PM_UPDATE_DELETED_USERS_URI = "/service/xusers/ugsync/users/visibility";	// POST
+
 	private static final String SOURCE_EXTERNAL ="1";
 	private static final String STATUS_ENABLED = "1";
+	private static final String ISVISIBLE = "1";
+	private static final String ISHIDDEN = "0";
 
 	private static String LOCAL_HOSTNAME = "unknown";
 	private String recordsToPullPerCall = "10";
@@ -107,6 +114,9 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 	private Map<String, Set<String>> deltaGroupUsers;
 	private Set<String> computeRolesForUsers;
 
+	private Map<String, XGroupInfo> deletedGroups;
+	private Map<String, XUserInfo> deletedUsers;
+
 	private int noOfNewUsers;
 	private int noOfNewGroups;
 	private int noOfModifiedUsers;
@@ -116,16 +126,18 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 	private boolean groupNameCaseConversionFlag;
 	private boolean userNameLowerCaseFlag = false;
 	private boolean groupNameLowerCaseFlag = false;
+	private String currentSyncSource;
+	private String ldapUrl;
 
 	private String authenticationType = null;
 	String principal;
 	String keytab;
 	String nameRules;
-    Map<String, String> userMap = new LinkedHashMap<String, String>();
-    Map<String, String> groupMap = new LinkedHashMap<>();
+	Map<String, String> userMap = new LinkedHashMap<String, String>();
+	Map<String, String> groupMap = new LinkedHashMap<>();
 
-    private boolean isRangerCookieEnabled;
-    private String rangerCookieName;
+	private boolean isRangerCookieEnabled;
+	private String rangerCookieName;
 	static {
 		try {
 			LOCAL_HOSTNAME = java.net.InetAddress.getLocalHost().getCanonicalHostName();
@@ -176,6 +188,11 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		groupCache = new HashMap<>();
 		groupUsersCache = new HashMap<>();
 		isStartupFlag = true;
+		ldapUrl = null;
+		currentSyncSource = config.getCurrentSyncSource();
+		if (StringUtils.equalsIgnoreCase(currentSyncSource, "LDAP/AD")) {
+			ldapUrl = config.getLdapUrl();
+		}
 
 		if (isMockRun) {
 			LOG.setLevel(Level.DEBUG);
@@ -191,7 +208,7 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		try {
 			principal = SecureClientLogin.getPrincipal(config.getProperty(PRINCIPAL,""), LOCAL_HOSTNAME);
 		} catch (IOException ignored) {
-			 // do nothing
+			// do nothing
 		}
 		keytab = config.getProperty(KEYTAB,"");
 		nameRules = config.getProperty(NAME_RULE,"DEFAULT");
@@ -199,17 +216,17 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 				trustStoreFile, trustStoreFilepwd, trustStoreType, authenticationType, principal, keytab,
 				config.getPolicyMgrUserName(), config.getPolicyMgrPassword());
 
-        String userGroupRoles = config.getGroupRoleRules();
-        if (userGroupRoles != null && !userGroupRoles.isEmpty()) {
-            getRoleForUserGroups(userGroupRoles);
-        }
+		String userGroupRoles = config.getGroupRoleRules();
+		if (userGroupRoles != null && !userGroupRoles.isEmpty()) {
+			getRoleForUserGroups(userGroupRoles);
+		}
 		buildUserGroupInfo();
 
-        if (LOG.isDebugEnabled()) {
+		if (LOG.isDebugEnabled()) {
 			LOG.debug("PolicyMgrUserGroupBuilderOld.init()==> PolMgrBaseUrl : "+policyMgrBaseUrl+" KeyStore File : "+keyStoreFile+" TrustStore File : "+trustStoreFile+ "Authentication Type : "+authenticationType);
 		}
 
-    }
+	}
 
 	@Override
 	public void postUserGroupAuditInfo(UgsyncAuditInfo ugsyncAuditInfo) throws Throwable {
@@ -245,7 +262,8 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 	@Override
 	public void addOrUpdateUsersGroups(Map<String, Map<String, String>> sourceGroups,
 									   Map<String, Map<String, String>> sourceUsers,
-									   Map<String, Set<String>> sourceGroupUsers) throws Throwable {
+									   Map<String, Set<String>> sourceGroupUsers,
+									   boolean computeDeletes) throws Throwable {
 
 		noOfNewUsers = 0;
 		noOfNewGroups = 0;
@@ -253,6 +271,27 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		noOfModifiedGroups = 0;
 		computeRolesForUsers = new HashSet<>();
 
+		if (!isStartupFlag && computeDeletes) {
+			LOG.info("Computing deleted users/groups");
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Computing deleted users/groups");
+			}
+			if (MapUtils.isNotEmpty(sourceGroups)) {
+				updateDeletedGroups(sourceGroups);
+			}
+			if (MapUtils.isNotEmpty(sourceUsers)) {
+				updateDeletedUsers(sourceUsers);
+			}
+
+			if (MapUtils.isNotEmpty(deletedGroups)) {
+				groupCache.putAll(deletedGroups);
+			}
+
+			if (MapUtils.isNotEmpty(deletedUsers)) {
+				userCache.putAll(deletedUsers);
+			}
+		}
+
 		if (MapUtils.isNotEmpty(sourceGroups)) {
 			addOrUpdateGroups(sourceGroups);
 		}
@@ -387,6 +426,7 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 						LOG.debug("GROUP:  Id:" + g.getId() + ", Name: " + g.getName() + ", Description: "
 								+ g.getDescription());
 					}
+					g.setOtherAttrsMap(gson.fromJson(g.getOtherAttributes(), Map.class));
 					groupCache.put(g.getName(), g);
 				}
 				retrievedCount = groupCache.size();
@@ -440,6 +480,7 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 						LOG.debug("USER: Id:" + u.getId() + ", Name: " + u.getName() + ", Description: "
 								+ u.getDescription());
 					}
+					u.setOtherAttrsMap(gson.fromJson(u.getOtherAttributes(), Map.class));
 					userCache.put(u.getName(), u);
 				}
 				retrievedCount = userCache.size();
@@ -458,34 +499,34 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		String relativeUrl = PM_GET_ALL_GROUP_USER_MAP_LIST_URI;
 
 		String response = null;
-			ClientResponse clientResp = null;
+		ClientResponse clientResp = null;
 
-			Gson gson = new GsonBuilder().create();
-			if (isRangerCookieEnabled) {
-				response = cookieBasedGetEntity(relativeUrl, 0);
-			} else {
-				try {
-					clientResp = ldapUgSyncClient.get(relativeUrl, null);
-					if (clientResp != null) {
-						response = clientResp.getEntity(String.class);
-					}
-				} catch (Exception e) {
-					LOG.error("Failed to get response, group user mappings from Ranger admin. Error is : " + e.getMessage());
-					throw e;
+		Gson gson = new GsonBuilder().create();
+		if (isRangerCookieEnabled) {
+			response = cookieBasedGetEntity(relativeUrl, 0);
+		} else {
+			try {
+				clientResp = ldapUgSyncClient.get(relativeUrl, null);
+				if (clientResp != null) {
+					response = clientResp.getEntity(String.class);
 				}
+			} catch (Exception e) {
+				LOG.error("Failed to get response, group user mappings from Ranger admin. Error is : " + e.getMessage());
+				throw e;
 			}
-			if (LOG.isDebugEnabled()) {
-				LOG.debug("RESPONSE: [" + response + "]");
-			}
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("RESPONSE: [" + response + "]");
+		}
 
-			groupUsersCache = gson.fromJson(response, Map.class);
-			if (MapUtils.isEmpty(groupUsersCache)) {
-				groupUsersCache = new HashMap<>();
-			}
+		groupUsersCache = gson.fromJson(response, Map.class);
+		if (MapUtils.isEmpty(groupUsersCache)) {
+			groupUsersCache = new HashMap<>();
+		}
 
-			if (LOG.isDebugEnabled()) {
-				LOG.debug("Group User List : " + groupUsersCache.values());
-			}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("Group User List : " + groupUsersCache.values());
+		}
 		if (LOG.isDebugEnabled()) {
 			LOG.debug("<== PolicyMgrUserGroupBuilder.buildGroupUserLinkList()");
 		}
@@ -548,27 +589,42 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		deltaGroups = new HashMap<>();
 		// Check if the group exists in cache. If not, mark as new group.
 		// else check if other attributes are updated and mark as updated group
-
+		Gson gson = new Gson();
 		for (String groupDN : sourceGroups.keySet()) {
 			Map<String, String> newGroupAttrs = sourceGroups.get(groupDN);
-			Gson gson = new Gson();
 			String newGroupAttrsStr = gson.toJson(newGroupAttrs);
 			String groupName = groupNameMap.get(groupDN);
 			if (StringUtils.isEmpty(groupName)) {
-				groupName = groupNameTransform(newGroupAttrs.get("original_name"));
-				groupNameMap.put(groupDN, groupName);
+				groupName = groupNameTransform(newGroupAttrs.get(UgsyncCommonConstants.ORIGINAL_NAME));
+				if (StringUtils.isNotEmpty(groupName) && !groupNameMap.containsValue(groupName)) {
+					// This is to avoid updating same groupName with different DN that already exists
+					groupNameMap.put(groupDN, groupName);
+				}
 			}
 			if (!groupCache.containsKey(groupName)) {
-				XGroupInfo newGroup = addXGroupInfo(groupName, newGroupAttrsStr);
+				XGroupInfo newGroup = addXGroupInfo(groupName, newGroupAttrs, newGroupAttrsStr);
 				deltaGroups.put(groupName, newGroup);
 				noOfNewGroups++;
 			} else {
 				XGroupInfo oldGroup = groupCache.get(groupName);
-				String oldGroupAttrs = oldGroup.getOtherAttributes();
-				if (!StringUtils.equalsIgnoreCase(oldGroupAttrs, newGroupAttrsStr)) {
-					oldGroup.setOtherAttributes(newGroupAttrsStr);
-					deltaGroups.put(groupName, oldGroup);
-					noOfModifiedGroups++;
+				Map<String, String> oldGroupAttrs = oldGroup.getOtherAttrsMap();
+				String oldGroupDN = oldGroupAttrs.get(UgsyncCommonConstants.FULL_NAME);
+				//LOG.info("DN from source = " + groupDN + " saved DN = " + oldGroupDN);
+				//LOG.info("OldGroupAttr = " + oldGroupAttrs + " newGroupAttrs = " + newGroupAttrs);
+				if (StringUtils.equalsIgnoreCase(groupDN, oldGroupDN)
+						&& StringUtils.equalsIgnoreCase(oldGroupAttrs.get(UgsyncCommonConstants.SYNC_SOURCE), newGroupAttrs.get(UgsyncCommonConstants.SYNC_SOURCE))
+						&& StringUtils.equalsIgnoreCase(oldGroupAttrs.get(UgsyncCommonConstants.LDAP_URL), newGroupAttrs.get(UgsyncCommonConstants.LDAP_URL))) {
+					String oldGroupAttrsStr = gson.toJson(oldGroupAttrs);
+					if(!StringUtils.equalsIgnoreCase(oldGroupAttrsStr, newGroupAttrsStr)) {
+						oldGroup.setOtherAttributes(newGroupAttrsStr);
+						oldGroup.setOtherAttrsMap(newGroupAttrs);
+						deltaGroups.put(groupName, oldGroup);
+						noOfModifiedGroups++;
+					}
+				} else {
+					if (LOG.isDebugEnabled()) {
+						LOG.debug("Skipping to update " + groupName + " as same group name with different DN already exists");
+					}
 				}
 			}
 		}
@@ -584,31 +640,42 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		deltaUsers = new HashMap<>();
 		// Check if the user exists in cache. If not, mark as new user.
 		// else check if other attributes are updated and mark as updated user
-
+		Gson gson = new Gson();
 		for (String userDN : sourceUsers.keySet()) {
 			Map<String, String> newUserAttrs = sourceUsers.get(userDN);
-			Gson gson = new Gson();
 			String newUserAttrsStr = gson.toJson(newUserAttrs);
-
 			String userName = userNameMap.get(userDN);
 			if (StringUtils.isEmpty(userName)) {
-				userName = userNameTransform(newUserAttrs.get("original_name"));
-				userNameMap.put(userDN, userName);
+				userName = userNameTransform(newUserAttrs.get(UgsyncCommonConstants.ORIGINAL_NAME));
+				if (StringUtils.isNotEmpty(userName) && !userNameMap.containsValue(userName)) {
+					// This is to avoid updating same username with different DN that already exists
+					userNameMap.put(userDN, userName);
+				}
 			}
 
 			if (!userCache.containsKey(userName)) {
 
-				XUserInfo newUser = addXUserInfo(userName, newUserAttrsStr);
+				XUserInfo newUser = addXUserInfo(userName, newUserAttrs, newUserAttrsStr);
 				deltaUsers.put(userName, newUser);
 				noOfNewUsers++;
 			} else {
-				// Update other attributes if changed
 				XUserInfo oldUser = userCache.get(userName);
-				String oldUserAttrs = oldUser.getOtherAttributes();
-				if (!StringUtils.equalsIgnoreCase(oldUserAttrs, newUserAttrsStr)) {
-					oldUser.setOtherAttributes(newUserAttrsStr);
-					deltaUsers.put(userName, oldUser);
-					noOfModifiedUsers++;
+				Map<String, String> oldUserAttrs = oldUser.getOtherAttrsMap();
+				String oldUserDN = oldUserAttrs.get(UgsyncCommonConstants.FULL_NAME);
+				if (StringUtils.equalsIgnoreCase(userDN, oldUserDN)
+						&& StringUtils.equalsIgnoreCase(oldUserAttrs.get(UgsyncCommonConstants.SYNC_SOURCE), newUserAttrs.get(UgsyncCommonConstants.SYNC_SOURCE))
+						&& StringUtils.equalsIgnoreCase(oldUserAttrs.get(UgsyncCommonConstants.LDAP_URL), newUserAttrs.get(UgsyncCommonConstants.LDAP_URL))) {
+					String oldUserAttrsStr = gson.toJson(oldUserAttrs);
+					if( !StringUtils.equalsIgnoreCase(oldUserAttrsStr, newUserAttrsStr)) {
+						oldUser.setOtherAttributes(newUserAttrsStr);
+						oldUser.setOtherAttrsMap(newUserAttrs);
+						deltaUsers.put(userName, oldUser);
+						noOfModifiedUsers++;
+					}
+				} else {
+					if (LOG.isDebugEnabled()) {
+						LOG.debug("Skipping to update " + userName + " as same username with different DN already exists");
+					}
 				}
 			}
 		}
@@ -691,12 +758,13 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		return deltaGroupUserInfoList;
 	}
 
-	private XUserInfo addXUserInfo(String aUserName, String otherAttributes) {
+	private XUserInfo addXUserInfo(String aUserName, Map<String, String> otherAttrsMap, String otherAttributes) {
 		XUserInfo xuserInfo = new XUserInfo();
 		xuserInfo.setName(aUserName);
 		xuserInfo.setDescription(aUserName + " - add from Unix box");
 		xuserInfo.setUserSource(SOURCE_EXTERNAL);
 		xuserInfo.setStatus(STATUS_ENABLED);
+		xuserInfo.setIsVisible(ISVISIBLE);
 		List<String> roleList = new ArrayList<String>();
 		if (userMap.containsKey(aUserName)) {
 			roleList.add(userMap.get(aUserName));
@@ -705,12 +773,13 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		}
 		xuserInfo.setUserRoleList(roleList);
 		xuserInfo.setOtherAttributes(otherAttributes);
+		xuserInfo.setOtherAttrsMap(otherAttrsMap);
 
 		return xuserInfo;
 	}
 
 
-	private XGroupInfo addXGroupInfo(String aGroupName, String otherAttributes) {
+	private XGroupInfo addXGroupInfo(String aGroupName, Map<String, String> otherAttrsMap, String otherAttributes) {
 
 		XGroupInfo addGroup = new XGroupInfo();
 
@@ -719,9 +788,10 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		addGroup.setDescription(aGroupName + " - add from Unix box");
 
 		addGroup.setGroupType("1");
-
+		addGroup.setIsVisible(ISVISIBLE);
 		addGroup.setGroupSource(SOURCE_EXTERNAL);
 		addGroup.setOtherAttributes(otherAttributes);
+		addGroup.setOtherAttrsMap(otherAttrsMap);
 
 		return addGroup;
 	}
@@ -817,7 +887,7 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 				}
 			} else {
 				LOG.error("Failed to addOrUpdateUsers " + uploadedCount );
-				throw new Exception("Failed to addOrUpdateUsers " + uploadedCount);
+				throw new Exception("Failed to addOrUpdateUsers" + uploadedCount);
 			}
 			LOG.info("ret = " + ret + " No. of users uploaded to ranger admin= " + (uploadedCount>totalCount?totalCount:uploadedCount));
 		}
@@ -1270,7 +1340,7 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		String jsonString = gson.toJson(obj);
 
 		if ( LOG.isDebugEnabled() ) {
-		   LOG.debug("USER GROUP MAPPING" + jsonString);
+			LOG.debug("USER GROUP MAPPING" + jsonString);
 		}
 		try{
 			clientResp = ldapUgSyncClient.post(apiURL, null, obj);
@@ -1408,74 +1478,74 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		return response;
 	}
 
-    private void getRoleForUserGroups(String userGroupRolesData) {
-        String roleDelimiter = config.getRoleDelimiter();
-        String userGroupDelimiter = config.getUserGroupDelimiter();
-        String userNameDelimiter = config.getUserGroupNameDelimiter();
-        if (roleDelimiter == null || roleDelimiter.isEmpty()) {
-            roleDelimiter = "&";
-        }
-        if (userGroupDelimiter == null || userGroupDelimiter.isEmpty()) {
-            userGroupDelimiter = ":";
-        }
-        if (userNameDelimiter == null || userNameDelimiter.isEmpty()) {
-            userNameDelimiter = ",";
-        }
-        StringTokenizer str = new StringTokenizer(userGroupRolesData,
-                roleDelimiter);
-        int flag = 0;
-        String userGroupCheck = null;
-        String roleName = null;
-        while (str.hasMoreTokens()) {
-            flag = 0;
-            String tokens = str.nextToken();
-            if (tokens != null && !tokens.isEmpty()) {
-                StringTokenizer userGroupRoles = new StringTokenizer(tokens,
-                        userGroupDelimiter);
-                if (userGroupRoles != null) {
-                    while (userGroupRoles.hasMoreElements()) {
-                        String userGroupRolesTokens = userGroupRoles
-                                .nextToken();
-                        if (userGroupRolesTokens != null
-                                && !userGroupRolesTokens.isEmpty()) {
-                            flag++;
-                            switch (flag) {
-                            case 1:
-                                roleName = userGroupRolesTokens;
-                                break;
-                            case 2:
-                                userGroupCheck = userGroupRolesTokens;
-                                break;
-                            case 3:
-                                StringTokenizer userGroupNames = new StringTokenizer(
-                                        userGroupRolesTokens, userNameDelimiter);
-                                if (userGroupNames != null) {
-                                    while (userGroupNames.hasMoreElements()) {
-                                        String userGroup = userGroupNames
-                                                .nextToken();
-                                        if (userGroup != null
-                                                && !userGroup.isEmpty()) {
-                                            if (userGroupCheck.trim().equalsIgnoreCase("u")) {
-                                                userMap.put(userGroup.trim(), roleName.trim());
-                                            } else if (userGroupCheck.trim().equalsIgnoreCase("g")) {
-                                                groupMap.put(userGroup.trim(),
-                                                        roleName.trim());
-                                            }
-                                        }
-                                    }
-                                }
-                                break;
-                            default:
-                                userMap.clear();
-                                groupMap.clear();
-                                break;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
+	private void getRoleForUserGroups(String userGroupRolesData) {
+		String roleDelimiter = config.getRoleDelimiter();
+		String userGroupDelimiter = config.getUserGroupDelimiter();
+		String userNameDelimiter = config.getUserGroupNameDelimiter();
+		if (roleDelimiter == null || roleDelimiter.isEmpty()) {
+			roleDelimiter = "&";
+		}
+		if (userGroupDelimiter == null || userGroupDelimiter.isEmpty()) {
+			userGroupDelimiter = ":";
+		}
+		if (userNameDelimiter == null || userNameDelimiter.isEmpty()) {
+			userNameDelimiter = ",";
+		}
+		StringTokenizer str = new StringTokenizer(userGroupRolesData,
+				roleDelimiter);
+		int flag = 0;
+		String userGroupCheck = null;
+		String roleName = null;
+		while (str.hasMoreTokens()) {
+			flag = 0;
+			String tokens = str.nextToken();
+			if (tokens != null && !tokens.isEmpty()) {
+				StringTokenizer userGroupRoles = new StringTokenizer(tokens,
+						userGroupDelimiter);
+				if (userGroupRoles != null) {
+					while (userGroupRoles.hasMoreElements()) {
+						String userGroupRolesTokens = userGroupRoles
+								.nextToken();
+						if (userGroupRolesTokens != null
+								&& !userGroupRolesTokens.isEmpty()) {
+							flag++;
+							switch (flag) {
+								case 1:
+									roleName = userGroupRolesTokens;
+									break;
+								case 2:
+									userGroupCheck = userGroupRolesTokens;
+									break;
+								case 3:
+									StringTokenizer userGroupNames = new StringTokenizer(
+											userGroupRolesTokens, userNameDelimiter);
+									if (userGroupNames != null) {
+										while (userGroupNames.hasMoreElements()) {
+											String userGroup = userGroupNames
+													.nextToken();
+											if (userGroup != null
+													&& !userGroup.isEmpty()) {
+												if (userGroupCheck.trim().equalsIgnoreCase("u")) {
+													userMap.put(userGroup.trim(), roleName.trim());
+												} else if (userGroupCheck.trim().equalsIgnoreCase("g")) {
+													groupMap.put(userGroup.trim(),
+															roleName.trim());
+												}
+											}
+										}
+									}
+									break;
+								default:
+									userMap.clear();
+									groupMap.clear();
+									break;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
 
 	protected String userNameTransform(String userName) {
 		if (userNameCaseConversionFlag) {
@@ -1511,4 +1581,232 @@ private static final Logger LOG = Logger.getLogger(PolicyMgrUserGroupBuilder.cla
 		return groupName;
 	}
 
+	private void updateDeletedGroups(Map<String, Map<String, String>> sourceGroups) throws Throwable {
+		computeDeletedGroups(sourceGroups);
+		if (MapUtils.isNotEmpty(deletedGroups)) {
+			if (updateDeletedGroups() == 0) {
+				String msg = "Failed to update deleted groups to ranger admin";
+				LOG.error(msg);
+				throw new Exception(msg);
+			}
+		}
+	}
+
+	private void computeDeletedGroups(Map<String, Map<String, String>> sourceGroups) {
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("PolicyMgrUserGroupBuilder.computeDeletedGroups(" + sourceGroups.keySet() + ")");
+		}
+		deletedGroups = new HashMap<>();
+		// Check if the group from cache exists in the sourceGroups. If not, mark as deleted group.
+		for (XGroupInfo groupInfo : groupCache.values()) {
+			Map<String, String> groupOtherAttrs = groupInfo.getOtherAttrsMap();
+			String groupDN = groupOtherAttrs != null ? groupOtherAttrs.get(UgsyncCommonConstants.FULL_NAME) : null;
+			if (StringUtils.isNotEmpty(groupDN) && !sourceGroups.containsKey(groupDN)
+					&& StringUtils.equalsIgnoreCase(groupOtherAttrs.get(UgsyncCommonConstants.SYNC_SOURCE), currentSyncSource)
+					&& StringUtils.equalsIgnoreCase(groupOtherAttrs.get(UgsyncCommonConstants.LDAP_URL), ldapUrl)) {
+				groupInfo.setIsVisible(ISHIDDEN);
+				deletedGroups.put(groupInfo.getName(), groupInfo);
+			}
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("<== PolicyMgrUserGroupBuilder.computeDeletedGroups(" + deletedGroups + ")");
+		}
+	}
+
+	private int updateDeletedGroups() throws Throwable{
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("==> PolicyMgrUserGroupBuilder.updateDeletedGroups(" + deletedGroups + ")");
+		}
+		int ret = 0;
+
+		if (authenticationType != null
+				&& AUTH_KERBEROS.equalsIgnoreCase(authenticationType)
+				&& SecureClientLogin.isKerberosCredentialExists(principal,
+				keytab)) {
+			try {
+				Subject sub = SecureClientLogin.loginUserFromKeytab(principal, keytab, nameRules);
+				ret = Subject.doAs(sub, new PrivilegedAction<Integer>() {
+					@Override
+					public Integer run() {
+						try {
+							return getDeletedGroups();
+						} catch (Throwable e) {
+							LOG.error("Failed to add or update deleted groups : ", e);
+						}
+						return 0;
+					}
+				});
+			} catch (Exception e) {
+				LOG.error("Failed to add or update deleted groups : " , e);
+				throw e;
+			}
+		} else {
+			ret = getDeletedGroups();
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("<== PolicyMgrUserGroupBuilder.updateDeletedGroups(" + deletedGroups + ")");
+		}
+		return ret;
+	}
+
+
+	private int getDeletedGroups() throws Throwable{
+		if(LOG.isDebugEnabled()){
+			LOG.debug("==> PolicyMgrUserGroupBuilder.getDeletedGroups()");
+		}
+		int ret = 0;
+		String response = null;
+		ClientResponse clientRes = null;
+		String relativeUrl = PM_UPDATE_DELETED_GROUPS_URI;
+
+		if(isRangerCookieEnabled){
+			response = cookieBasedUploadEntity(deletedGroups.keySet(), relativeUrl);
+		}
+		else {
+			try {
+				clientRes = ldapUgSyncClient.post(relativeUrl, null, deletedGroups.keySet());
+				if (clientRes != null) {
+					response = clientRes.getEntity(String.class);
+				}
+			}
+			catch(Throwable t){
+				LOG.error("Failed to get response, Error is : ", t);
+			}
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("RESPONSE[" + response + "]");
+		}
+		if (response != null) {
+			try {
+				ret = Integer.valueOf(response);
+			} catch (NumberFormatException e) {
+				LOG.error("Failed to update deleted groups", e );
+				throw e;
+			}
+		} else {
+			LOG.error("Failed to update deleted groups ");
+			throw new Exception("Failed to update deleted groups ");
+		}
+
+		if(LOG.isDebugEnabled()){
+			LOG.debug("<== PolicyMgrUserGroupBuilder.getDeletedGroups()" + ret);
+		}
+
+		return ret;
+	}
+
+
+	private void updateDeletedUsers(Map<String, Map<String, String>> sourceUsers) throws Throwable {
+		computeDeletedUsers(sourceUsers);
+		if (MapUtils.isNotEmpty(deletedUsers)) {
+			if (updateDeletedUsers() == 0) {
+				String msg = "Failed to update deleted users to ranger admin";
+				LOG.error(msg);
+				throw new Exception(msg);
+			}
+		}
+	}
+
+	private void computeDeletedUsers(Map<String, Map<String, String>> sourceUsers) {
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("PolicyMgrUserGroupBuilder.computeDeletedUsers(" + sourceUsers.keySet() + ")");
+		}
+		deletedUsers = new HashMap<>();
+		// Check if the group from cache exists in the sourceGroups. If not, mark as deleted group.
+		for (XUserInfo userInfo : userCache.values()) {
+			Map<String, String> userOtherAttrs = userInfo.getOtherAttrsMap();
+			String userDN = userOtherAttrs != null ? userOtherAttrs.get(UgsyncCommonConstants.FULL_NAME) : null;
+			if (StringUtils.isNotEmpty(userDN) && !sourceUsers.containsKey(userDN)
+					&& StringUtils.equalsIgnoreCase(userOtherAttrs.get(UgsyncCommonConstants.SYNC_SOURCE), currentSyncSource)
+					&& StringUtils.equalsIgnoreCase(userOtherAttrs.get(UgsyncCommonConstants.LDAP_URL), ldapUrl)) {
+				userInfo.setIsVisible(ISHIDDEN);
+				deletedUsers.put(userInfo.getName(), userInfo);
+			}
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("<== PolicyMgrUserGroupBuilder.computeDeletedUsers(" + deletedUsers + ")");
+		}
+	}
+
+	private int updateDeletedUsers() throws Throwable{
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("==> PolicyMgrUserGroupBuilder.updateDeletedUsers(" + deletedUsers + ")");
+		}
+		int ret = 0;
+
+		if (authenticationType != null
+				&& AUTH_KERBEROS.equalsIgnoreCase(authenticationType)
+				&& SecureClientLogin.isKerberosCredentialExists(principal,
+				keytab)) {
+			try {
+				Subject sub = SecureClientLogin.loginUserFromKeytab(principal, keytab, nameRules);
+				ret = Subject.doAs(sub, new PrivilegedAction<Integer>() {
+					@Override
+					public Integer run() {
+						try {
+							return getDeletedUsers();
+						} catch (Throwable e) {
+							LOG.error("Failed to add or update deleted users : ", e);
+						}
+						return 0;
+					}
+				});
+			} catch (Exception e) {
+				LOG.error("Failed to add or update deleted users : " , e);
+				throw e;
+			}
+		} else {
+			ret = getDeletedUsers();
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("<== PolicyMgrUserGroupBuilder.updateDeletedUsers(" + deletedUsers + ")");
+		}
+		return ret;
+	}
+
+
+	private int getDeletedUsers() throws Throwable{
+		if(LOG.isDebugEnabled()){
+			LOG.debug("==> PolicyMgrUserGroupBuilder.getDeletedUsers()");
+		}
+		int ret = 0;
+		String response = null;
+		ClientResponse clientRes = null;
+		String relativeUrl = PM_UPDATE_DELETED_USERS_URI;
+
+		if(isRangerCookieEnabled){
+			response = cookieBasedUploadEntity(deletedUsers.keySet(), relativeUrl);
+		}
+		else {
+			try {
+				clientRes = ldapUgSyncClient.post(relativeUrl, null, deletedUsers.keySet());
+				if (clientRes != null) {
+					response = clientRes.getEntity(String.class);
+				}
+			}
+			catch(Throwable t){
+				LOG.error("Failed to get response, Error is : ", t);
+			}
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("RESPONSE[" + response + "]");
+		}
+		if (response != null) {
+			try {
+				ret = Integer.valueOf(response);
+			} catch (NumberFormatException e) {
+				LOG.error("Failed to update deleted users", e );
+				throw e;
+			}
+		} else {
+			LOG.error("Failed to update deleted users ");
+			throw new Exception("Failed to update deleted users ");
+		}
+
+		if(LOG.isDebugEnabled()){
+			LOG.debug("<== PolicyMgrUserGroupBuilder.getDeletedUsers()" + ret);
+		}
+
+		return ret;
+	}
 }
diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/UnixUserGroupBuilder.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/UnixUserGroupBuilder.java
index 597cbf8..b33812e 100644
--- a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/UnixUserGroupBuilder.java
+++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/UnixUserGroupBuilder.java
@@ -40,12 +40,13 @@ import com.google.common.collect.Table;
 import org.apache.log4j.Logger;
 import org.apache.ranger.ugsyncutil.model.UgsyncAuditInfo;
 import org.apache.ranger.ugsyncutil.model.UnixSyncSourceInfo;
+import org.apache.ranger.ugsyncutil.util.UgsyncCommonConstants;
 import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
 import org.apache.ranger.usergroupsync.UserGroupSink;
 import org.apache.ranger.usergroupsync.UserGroupSource;
 
 public class UnixUserGroupBuilder implements UserGroupSource {
-	
+
 	private static final Logger LOG = Logger.getLogger(UnixUserGroupBuilder.class);
 	private final static String OS = System.getProperty("os.name");
 
@@ -96,6 +97,9 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 	private UgsyncAuditInfo ugsyncAuditInfo;
 	private UnixSyncSourceInfo unixSyncSourceInfo;
 	private boolean isStartupFlag = false;
+	private int deleteCycles;
+	private String currentSyncSource;
+	private boolean computeDeletes = false;
 	Set<String> allGroups = new HashSet<>();
 
 
@@ -104,20 +108,13 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 		ugbuilder.init();
 		ugbuilder.print();
 	}
-	
+
 	public UnixUserGroupBuilder() {
 		isStartupFlag = true;
 		minimumUserId = Integer.parseInt(config.getMinUserId());
 		minimumGroupId = Integer.parseInt(config.getMinGroupId());
 		unixPasswordFile = config.getUnixPasswordFile();
 		unixGroupFile = config.getUnixGroupFile();
-		ugsyncAuditInfo = new UgsyncAuditInfo();
-		unixSyncSourceInfo = new UnixSyncSourceInfo();
-		ugsyncAuditInfo.setSyncSource("Unix");
-		ugsyncAuditInfo.setUnixSyncSourceInfo(unixSyncSourceInfo);
-		unixSyncSourceInfo.setFileName(unixPasswordFile);
-		unixSyncSourceInfo.setMinUserId(config.getMinUserId());
-		unixSyncSourceInfo.setMinGroupId(config.getMinGroupId());
 
 		if (LOG.isDebugEnabled()) {
 			LOG.debug("Minimum UserId: " + minimumUserId + ", minimum GroupId: " + minimumGroupId);
@@ -126,6 +123,21 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 		timeout = config.getUpdateMillisMin();
 		enumerateGroupMembers = config.isGroupEnumerateEnabled();
 
+	}
+
+	@Override
+	public void init() throws Throwable {
+		deleteCycles = 1;
+
+		currentSyncSource = config.getCurrentSyncSource();
+
+		ugsyncAuditInfo = new UgsyncAuditInfo();
+		unixSyncSourceInfo = new UnixSyncSourceInfo();
+		ugsyncAuditInfo.setSyncSource(currentSyncSource);
+		ugsyncAuditInfo.setUnixSyncSourceInfo(unixSyncSourceInfo);
+		unixSyncSourceInfo.setFileName(unixPasswordFile);
+		unixSyncSourceInfo.setMinUserId(config.getMinUserId());
+		unixSyncSourceInfo.setMinGroupId(config.getMinGroupId());
 		if (!config.getUnixBackend().equalsIgnoreCase(BACKEND_PASSWD)) {
 			useNss = true;
 			unixSyncSourceInfo.setUnixBackend("nss");
@@ -134,23 +146,34 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 					"instead of standard system mechanisms.");
 			unixSyncSourceInfo.setUnixBackend(BACKEND_PASSWD);
 		}
-
-	}
-
-	@Override
-	public void init() throws Throwable {
 		buildUserGroupInfo();
 	}
 
 	@Override
 	public boolean isChanged() {
-		// If previous update to Ranger admin fails, 
+		computeDeletes = false;
+		// If previous update to Ranger admin fails,
 		// we want to retry the sync process even if there are no changes to the sync files
 		if (!isUpdateSinkSucc) {
 			LOG.info("Previous updateSink failed and hence retry!!");
 			return true;
 		}
-		
+		try {
+			if (config.isUserSyncDeletesEnabled() && deleteCycles >= config.getUserSyncDeletesFrequency()) {
+				deleteCycles = 1;
+				computeDeletes = true;
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("Compute deleted users/groups is enabled for this sync cycle");
+				}
+				return true;
+			}
+		} catch (Throwable t) {
+			LOG.error("Failed to get information about usersync delete frequency", t);
+		}
+		if (config.isUserSyncDeletesEnabled()) {
+			deleteCycles++;
+		}
+
 		if (useNss)
 			return System.currentTimeMillis() - lastUpdateTime > timeout;
 
@@ -185,7 +208,7 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 			}
 
 			try {
-				sink.addOrUpdateUsersGroups(sourceGroups, sourceUsers, sourceGroupUsers);
+				sink.addOrUpdateUsersGroups(sourceGroups, sourceUsers, sourceGroupUsers, computeDeletes);
 			} catch (Throwable t) {
 				LOG.error("Failed to update ranger admin. Will retry in next sync cycle!!", t);
 				isUpdateSinkSucc = false;
@@ -198,8 +221,8 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 		}
 		isStartupFlag = false;
 	}
-	
-	
+
+
 	private void buildUserGroupInfo() throws Throwable {
 		groupId2groupNameMap = new HashMap<String, String>();
 		sourceUsers = new HashMap<>();
@@ -239,7 +262,7 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 			print();
 		}
 	}
-	
+
 	private void print() {
 		for(String user : sourceUsers.keySet()) {
 			if (LOG.isDebugEnabled()) {
@@ -255,7 +278,7 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 			}
 		}
 	}
-	
+
 	private void buildUnixUserList(String command) throws Throwable {
 		BufferedReader reader = null;
 		Map<String, String> userName2uid = new HashMap<String, String>();
@@ -316,8 +339,9 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 					String groupName = groupId2groupNameMap.get(groupId);
 					if (groupName != null) {
 						Map<String, String> userAttrMap = new HashMap<>();
-						userAttrMap.put("original_name", userName);
-						userAttrMap.put("full_name", userName);
+						userAttrMap.put(UgsyncCommonConstants.ORIGINAL_NAME, userName);
+						userAttrMap.put(UgsyncCommonConstants.FULL_NAME, userName);
+						userAttrMap.put(UgsyncCommonConstants.SYNC_SOURCE, currentSyncSource);
 						sourceUsers.put(userName, userAttrMap);
 						groupUserTable.put(groupName, userName, groupId);
 					} else {
@@ -403,8 +427,9 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 					}
 				}
 				Map<String, String> userAttrMap = new HashMap<>();
-				userAttrMap.put("original_name", userName);
-				userAttrMap.put("full_name", userName);
+				userAttrMap.put(UgsyncCommonConstants.ORIGINAL_NAME, userName);
+				userAttrMap.put(UgsyncCommonConstants.FULL_NAME, userName);
+				userAttrMap.put(UgsyncCommonConstants.SYNC_SOURCE, currentSyncSource);
 				sourceUsers.put(userName, userAttrMap);
 			}
 			if (LOG.isDebugEnabled()) {
@@ -439,8 +464,9 @@ public class UnixUserGroupBuilder implements UserGroupSource {
 
 		groupId2groupNameMap.put(groupId, groupName);
 		Map<String, String> groupAttrMap = new HashMap<>();
-		groupAttrMap.put("original_name", groupName);
-		groupAttrMap.put("full_name", groupName);
+		groupAttrMap.put(UgsyncCommonConstants.ORIGINAL_NAME, groupName);
+		groupAttrMap.put(UgsyncCommonConstants.FULL_NAME, groupName);
+		groupAttrMap.put(UgsyncCommonConstants.SYNC_SOURCE, currentSyncSource);
 		sourceGroups.put(groupName, groupAttrMap);
 
 		if (groupMembers != null && !groupMembers.trim().isEmpty()) {
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 794bc81..e79fcf7 100644
--- a/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
+++ b/ugsync/src/main/java/org/apache/ranger/usergroupsync/UserGroupSink.java
@@ -31,6 +31,7 @@ public interface UserGroupSink {
 
 	void addOrUpdateUsersGroups(Map<String, Map<String, String>> sourceGroups,
 								Map<String, Map<String, String>> sourceUsers,
-								Map<String, Set<String>> sourceGroupUsers) throws Throwable;
+								Map<String, Set<String>> sourceGroupUsers,
+								boolean computeDeletes) throws Throwable;
 
 }
diff --git a/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java b/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java
index 5d2e62c..4fb6b6c 100644
--- a/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java
+++ b/ugsync/src/test/java/org/apache/ranger/usergroupsync/PolicyMgrUserGroupBuilderTest.java
@@ -72,7 +72,8 @@ public class PolicyMgrUserGroupBuilderTest extends PolicyMgrUserGroupBuilder {
         @Override
         public void addOrUpdateUsersGroups(Map<String, Map<String, String>> sourceGroups,
                                            Map<String, Map<String, String>> sourceUsers,
-                                           Map<String, Set<String>> sourceGroupUsers) throws Throwable {
+                                           Map<String, Set<String>> sourceGroupUsers,
+                                           boolean computeDeletes) throws Throwable {
 
                 for (String userdn : sourceUsers.keySet()) {
                         //System.out.println("Username: " + sourceUsers.get(userdn).get("original_name"));