This is an automated email from the ASF dual-hosted git repository. spolavarapu pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push: new cb621c3 RANGER-980: Support to update users/groups that are deleted at the sync source cb621c3 is described below commit cb621c3a07e7f9ec3013fb4b830e356e58051b34 Author: Sailaja Polavarapu <spolavar...@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"));