Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.ranger.ldapusersync.process; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.HashMap; import javax.naming.Context; import javax.naming.InvalidNameException; import javax.naming.NamingEnumeration; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.Control; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapContext; import javax.naming.ldap.PagedResultsControl; import javax.naming.ldap.PagedResultsResponseControl; import javax.naming.ldap.StartTlsRequest; import javax.naming.ldap.StartTlsResponse; import org.apache.commons.collections.BidiMap; import org.apache.commons.collections.bidimap.DualHashBidiMap; import org.apache.log4j.Logger; import org.apache.ranger.unixusersync.config.UserGroupSyncConfig; import org.apache.ranger.usergroupsync.AbstractUserGroupSource; import org.apache.ranger.usergroupsync.UserGroupSink; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; public class LdapDeltaUserGroupBuilder extends AbstractUserGroupSource { private static final Logger LOG = Logger.getLogger(LdapDeltaUserGroupBuilder.class); private static final int PAGE_SIZE = 500; private static long deltaSyncUserTime = 0; // Used for AD uSNChanged private static long deltaSyncGroupTime = 0; // Used for AD uSNChanged private String deltaSyncUserTimeStamp; // Used for OpenLdap modifyTimestamp private String deltaSyncGroupTimeStamp; // Used for OpenLdap modifyTimestamp private String ldapUrl; private String ldapBindDn; private String ldapBindPassword; private String ldapAuthenticationMechanism; private String ldapReferral; private String searchBase; private String[] userSearchBase; private String userNameAttribute; private int userSearchScope; private String userObjectClass; private String userSearchFilter; private String extendedUserSearchFilter; private SearchControls userSearchControls; private Set<String> userGroupNameAttributeSet; private boolean pagedResultsEnabled = true; private int pagedResultsSize = PAGE_SIZE; private boolean groupSearchFirstEnabled = false; private boolean userSearchEnabled = false; private boolean groupSearchEnabled = true; private String[] groupSearchBase; private int groupSearchScope; private String groupObjectClass; private String groupSearchFilter; private String extendedGroupSearchFilter; private String extendedAllGroupsSearchFilter; private SearchControls groupSearchControls; private String groupMemberAttributeName; private String groupNameAttribute; private int groupHierarchyLevels; private LdapContext ldapContext; StartTlsResponse tls; private boolean userNameCaseConversionFlag = false; private boolean groupNameCaseConversionFlag = false; private boolean userNameLowerCaseFlag = false; private boolean groupNameLowerCaseFlag = false; private boolean groupUserMapSyncEnabled = false; //private Map<String, UserInfo> userGroupMap; private Table<String, String, String> groupUserTable; private Map<String, String> userNameMap; private BidiMap groupNameMap; public static void main(String[] args) throws Throwable { LdapDeltaUserGroupBuilder ugBuilder = new LdapDeltaUserGroupBuilder(); ugBuilder.init(); } public LdapDeltaUserGroupBuilder() { super(); LOG.info("LdapDeltaUserGroupBuilder created"); String userNameCaseConversion = config.getUserNameCaseConversion(); if (UserGroupSyncConfig.UGSYNC_NONE_CASE_CONVERSION_VALUE.equalsIgnoreCase(userNameCaseConversion)) { userNameCaseConversionFlag = false; } else { userNameCaseConversionFlag = true; userNameLowerCaseFlag = UserGroupSyncConfig.UGSYNC_LOWER_CASE_CONVERSION_VALUE .equalsIgnoreCase(userNameCaseConversion); } String groupNameCaseConversion = config.getGroupNameCaseConversion(); if (UserGroupSyncConfig.UGSYNC_NONE_CASE_CONVERSION_VALUE.equalsIgnoreCase(groupNameCaseConversion)) { groupNameCaseConversionFlag = false; } else { groupNameCaseConversionFlag = true; groupNameLowerCaseFlag = UserGroupSyncConfig.UGSYNC_LOWER_CASE_CONVERSION_VALUE .equalsIgnoreCase(groupNameCaseConversion); } } @Override public void init() throws Throwable { deltaSyncUserTime = 0; deltaSyncGroupTime = 0; DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); deltaSyncUserTimeStamp = dateFormat.format(new Date(0)); deltaSyncGroupTimeStamp = dateFormat.format(new Date(0)); userNameMap = new HashMap<String, String>(); setConfig(); } private void createLdapContext() throws Throwable { Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, ldapUrl); if (ldapUrl.startsWith("ldaps") && (config.getSSLTrustStorePath() != null && !config.getSSLTrustStorePath().trim().isEmpty())) { env.put("java.naming.ldap.factory.socket", "org.apache.ranger.ldapusersync.process.CustomSSLSocketFactory"); } ldapContext = new InitialLdapContext(env, null); if (!ldapUrl.startsWith("ldaps")) { if (config.isStartTlsEnabled()) { tls = (StartTlsResponse) ldapContext.extendedOperation(new StartTlsRequest()); if (config.getSSLTrustStorePath() != null && !config.getSSLTrustStorePath().trim().isEmpty()) { tls.negotiate(CustomSSLSocketFactory.getDefault()); } else { tls.negotiate(); } LOG.info("Starting TLS session..."); } } ldapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, ldapBindDn); ldapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, ldapBindPassword); ldapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, ldapAuthenticationMechanism); ldapContext.addToEnvironment(Context.REFERRAL, ldapReferral); } private void setConfig() throws Throwable { LOG.info("LdapDeltaUserGroupBuilder initialization started"); groupSearchFirstEnabled = config.isGroupSearchFirstEnabled(); userSearchEnabled = config.isUserSearchEnabled(); groupSearchEnabled = config.isGroupSearchEnabled(); ldapUrl = config.getLdapUrl(); ldapBindDn = config.getLdapBindDn(); ldapBindPassword = config.getLdapBindPassword(); //ldapBindPassword = "admin-password"; ldapAuthenticationMechanism = config.getLdapAuthenticationMechanism(); ldapReferral = config.getContextReferral(); searchBase = config.getSearchBase(); userSearchBase = config.getUserSearchBase().split(";"); userSearchScope = config.getUserSearchScope(); userObjectClass = config.getUserObjectClass(); userSearchFilter = config.getUserSearchFilter(); userNameAttribute = config.getUserNameAttribute(); Set<String> userSearchAttributes = new HashSet<String>(); userSearchAttributes.add(userNameAttribute); // For Group based search, user's group name attribute should not be added to the user search attributes if (!groupSearchFirstEnabled && !groupSearchEnabled) { userGroupNameAttributeSet = config.getUserGroupNameAttributeSet(); for (String useGroupNameAttribute : userGroupNameAttributeSet) { userSearchAttributes.add(useGroupNameAttribute); } } userSearchAttributes.add("uSNChanged"); userSearchAttributes.add("modifytimestamp"); userSearchControls = new SearchControls(); userSearchControls.setSearchScope(userSearchScope); userSearchControls .setReturningAttributes(userSearchAttributes.toArray(new String[userSearchAttributes.size()])); pagedResultsEnabled = config.isPagedResultsEnabled(); pagedResultsSize = config.getPagedResultsSize(); groupSearchBase = config.getGroupSearchBase().split(";"); groupSearchScope = config.getGroupSearchScope(); groupObjectClass = config.getGroupObjectClass(); groupSearchFilter = config.getGroupSearchFilter(); groupMemberAttributeName = config.getUserGroupMemberAttributeName(); groupNameAttribute = config.getGroupNameAttribute(); groupHierarchyLevels = config.getGroupHierarchyLevels(); extendedGroupSearchFilter = "(&" + extendedGroupSearchFilter + "(|(" + groupMemberAttributeName + "={0})(" + groupMemberAttributeName + "={1})))"; groupUserMapSyncEnabled = config.isGroupUserMapSyncEnabled(); groupSearchControls = new SearchControls(); groupSearchControls.setSearchScope(groupSearchScope); Set<String> groupSearchAttributes = new HashSet<String>(); groupSearchAttributes.add(groupNameAttribute); groupSearchAttributes.add(groupMemberAttributeName); groupSearchAttributes.add("uSNChanged"); groupSearchAttributes.add("modifytimestamp"); groupSearchControls .setReturningAttributes(groupSearchAttributes.toArray(new String[groupSearchAttributes.size()])); if (LOG.isInfoEnabled()) { LOG.info("LdapDeltaUserGroupBuilder initialization completed with -- " + "ldapUrl: " + ldapUrl + ", ldapBindDn: " + ldapBindDn + ", ldapBindPassword: ***** " + ", ldapAuthenticationMechanism: " + ldapAuthenticationMechanism + ", searchBase: " + searchBase + ", userSearchBase: " + Arrays.toString(userSearchBase) + ", userSearchScope: " + userSearchScope + ", userObjectClass: " + userObjectClass + ", userSearchFilter: " + userSearchFilter + ", extendedUserSearchFilter: " + extendedUserSearchFilter + ", userNameAttribute: " + userNameAttribute + ", userSearchAttributes: " + userSearchAttributes + ", userGroupNameAttributeSet: " + userGroupNameAttributeSet + ", pagedResultsEnabled: " + pagedResultsEnabled + ", pagedResultsSize: " + pagedResultsSize + ", groupSearchEnabled: " + groupSearchEnabled + ", groupSearchBase: " + Arrays.toString(groupSearchBase) + ", groupSearchScope: " + groupSearchScope + ", groupObjectClass: " + groupObjectClass + ", groupSearchFilter: " + groupSearchFilter + ", extendedGroupSearchFilter: " + extendedGroupSearchFilter + ", extendedAllGroupsSearchFilter: " + extendedAllGroupsSearchFilter + ", groupMemberAttributeName: " + groupMemberAttributeName + ", groupNameAttribute: " + groupNameAttribute + ", groupSearchAttributes: " + groupSearchAttributes + ", groupUserMapSyncEnabled: " + groupUserMapSyncEnabled + ", groupSearchFirstEnabled: " + groupSearchFirstEnabled + ", userSearchEnabled: " + userSearchEnabled + ", ldapReferral: " + ldapReferral); } } private void closeLdapContext() throws Throwable { if (tls != null) { tls.close(); } if (ldapContext != null) { ldapContext.close(); } } @Override public boolean isChanged() { // we do not want to get the full ldap dit and check whether anything has changed return true; } @Override public void updateSink(UserGroupSink sink) throws Throwable { LOG.info("LdapDeltaUserGroupBuilder updateSink started"); //userGroupMap = new HashMap<String, UserInfo>(); groupUserTable = HashBasedTable.create(); groupNameMap = new DualHashBidiMap(); if (!groupSearchFirstEnabled) { LOG.info("Performing user search first"); getUsers(sink); if (groupSearchEnabled) { getGroups(sink); } //LOG.debug("Total No. of users saved = " + groupUserTable.columnKeySet().size()); } else { LOG.info("Performing Group search first"); getGroups(sink); if (userSearchEnabled) { LOG.info("User search is enabled and hence computing user membership."); getUsers(sink); } } if (groupUserTable.isEmpty()) { //System.out.println("groupUserTable is empty!!"); return; } if (groupHierarchyLevels > 0) { LOG.info("Going through group hierarchy for nested group evaluation"); Set<String> groupFullNames = groupNameMap.keySet(); for (String group : groupFullNames) { Set<String> nextLevelGroups = groupUserTable.column(group).keySet(); goUpGroupHierarchy(nextLevelGroups, groupHierarchyLevels - 1, groupNameMap.get(group).toString()); } LOG.info("Completed group hierarchy computation"); } Iterator<String> groupUserTableIterator = groupUserTable.rowKeySet().iterator(); while (groupUserTableIterator.hasNext()) { String groupName = groupUserTableIterator.next(); //System.out.println("Group name from the groupUserTable: " + groupName); Map<String, String> groupUsersMap = groupUserTable.row(groupName); Set<String> userSet = new HashSet<String>(); for (Map.Entry<String, String> entry : groupUsersMap.entrySet()) { //String transformUserName = userNameTransform(entry.getKey()); userSet.add(entry.getValue()); } List<String> userList = new ArrayList<>(userSet); String transformGroupName = groupNameTransform(groupName); LOG.debug("addOrUpdateGroup(): group = " + groupName + " users = " + userList); try { sink.addOrUpdateGroup(transformGroupName, userList); } catch (Throwable t) { LOG.error("sink.addOrUpdateGroup failed with exception: " + t.getMessage() + ", for group: " + transformGroupName + ", users: " + userList); } } } private void getUsers(UserGroupSink sink) throws Throwable { NamingEnumeration<SearchResult> userSearchResultEnum = null; NamingEnumeration<SearchResult> groupSearchResultEnum = null; try { createLdapContext(); int total; // Activate paged results if (pagedResultsEnabled) { ldapContext.setRequestControls( new Control[] { new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) }); } DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); extendedUserSearchFilter = "(objectclass=" + userObjectClass + ")(|(uSNChanged>=" + deltaSyncUserTime + ")(modifyTimestamp>=" + deltaSyncUserTimeStamp + "Z))"; if (userSearchFilter != null && !userSearchFilter.trim().isEmpty()) { String customFilter = userSearchFilter.trim(); if (!customFilter.startsWith("(")) { customFilter = "(" + customFilter + ")"; } extendedUserSearchFilter = "(&" + extendedUserSearchFilter + customFilter + ")"; } else { extendedUserSearchFilter = "(&" + extendedUserSearchFilter + ")"; } LOG.info("extendedUserSearchFilter = " + extendedUserSearchFilter); long highestdeltaSyncUserTime = deltaSyncUserTime; // When multiple OUs are configured, go through each OU as the user search base to search for users. for (int ou = 0; ou < userSearchBase.length; ou++) { byte[] cookie = null; int counter = 0; try { int paged = 0; do { userSearchResultEnum = ldapContext.search(userSearchBase[ou], extendedUserSearchFilter, userSearchControls); while (userSearchResultEnum.hasMore()) { // searchResults contains all the user entries final SearchResult userEntry = userSearchResultEnum.next(); if (userEntry == null) { if (LOG.isInfoEnabled()) { LOG.info("userEntry null, skipping sync for the entry"); } continue; } //System.out.println("userEntry = " + userEntry); Attributes attributes = userEntry.getAttributes(); if (attributes == null) { if (LOG.isInfoEnabled()) { LOG.info("attributes missing for entry " + userEntry.getNameInNamespace() + ", skipping sync"); } continue; } Attribute userNameAttr = attributes.get(userNameAttribute); if (userNameAttr == null) { if (LOG.isInfoEnabled()) { LOG.info(userNameAttribute + " missing for entry " + userEntry.getNameInNamespace() + ", skipping sync"); } continue; } String userFullName = (userEntry.getNameInNamespace()).toLowerCase(); String userName = (String) userNameAttr.get(); if (userName == null || userName.trim().isEmpty()) { if (LOG.isInfoEnabled()) { LOG.info(userNameAttribute + " empty for entry " + userEntry.getNameInNamespace() + ", skipping sync"); } continue; } Attribute timeStampAttr = attributes.get("uSNChanged"); if (timeStampAttr != null) { String uSNChangedVal = (String) timeStampAttr.get(); long currentDeltaSyncTime = Long.parseLong(uSNChangedVal); LOG.info("uSNChangedVal = " + uSNChangedVal + "and currentDeltaSyncTime = " + currentDeltaSyncTime); if (currentDeltaSyncTime > highestdeltaSyncUserTime) { highestdeltaSyncUserTime = currentDeltaSyncTime; } } else { timeStampAttr = attributes.get("modifytimestamp"); if (timeStampAttr != null) { String timeStampVal = (String) timeStampAttr.get(); Date parseDate = dateFormat.parse(timeStampVal); long currentDeltaSyncTime = parseDate.getTime(); LOG.info("timeStampVal = " + timeStampVal + "and currentDeltaSyncTime = " + currentDeltaSyncTime); if (currentDeltaSyncTime > highestdeltaSyncUserTime) { highestdeltaSyncUserTime = currentDeltaSyncTime; deltaSyncUserTimeStamp = timeStampVal; } } } if (!groupSearchFirstEnabled) { String transformUserName = userNameTransform(userName); try { sink.addOrUpdateUser(transformUserName); } catch (Throwable t) { LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage() + ", for user: " + transformUserName); } //System.out.println("Adding user fullname = " + userFullName + " username = " + transformUserName); userNameMap.put(userFullName, transformUserName); Set<String> groups = new HashSet<String>(); // Get all the groups from the group name attribute of the user only when group search is not enabled. if (!groupSearchEnabled) { for (String useGroupNameAttribute : userGroupNameAttributeSet) { Attribute userGroupfAttribute = userEntry.getAttributes() .get(useGroupNameAttribute); if (userGroupfAttribute != null) { NamingEnumeration<?> groupEnum = userGroupfAttribute.getAll(); while (groupEnum.hasMore()) { String gName = getShortGroupName((String) groupEnum.next()); String transformGroupName = groupNameTransform(gName); groups.add(transformGroupName); } } } } List<String> groupList = new ArrayList<String>(groups); try { sink.addOrUpdateUser(transformUserName, groupList); } catch (Throwable t) { LOG.error("sink.addOrUpdateUserGroups failed with exception: " + t.getMessage() + ", for user: " + transformUserName + " and groups: " + groupList); } counter++; if (counter <= 2000) { if (LOG.isInfoEnabled()) { LOG.info("Updating user count: " + counter + ", userName: " + userName + ", groupList: " + groupList); } if (counter == 2000) { LOG.info( "===> 2000 user records have been synchronized so far. From now on, only a summary progress log will be written for every 100 users. To continue to see detailed log for every user, please enable Trace level logging. <==="); } } else { if (LOG.isTraceEnabled()) { LOG.trace("Updating user count: " + counter + ", userName: " + userName + ", groupList: " + groupList); } else { if (counter % 100 == 0) { LOG.info("Synced " + counter + " users till now"); } } } } else { // If the user from the search result is present in the group user table, // then addorupdate user to ranger admin. LOG.debug("Chekcing if the user " + userFullName + " is part of the retrieved groups"); if (groupUserTable.containsColumn(userFullName) || groupUserTable.containsColumn(userName)) { String transformUserName = userNameTransform(userName); try { sink.addOrUpdateUser(transformUserName); } catch (Throwable t) { LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage() + ", for user: " + transformUserName); } userNameMap.put(userFullName, transformUserName); //Also update the username in the groupUserTable with the one from username attribute. Map<String, String> userMap = groupUserTable.column(userFullName); for (Map.Entry<String, String> entry : userMap.entrySet()) { LOG.debug("Updating groupUserTable " + entry.getValue() + " with: " + transformUserName + " for " + entry.getKey()); groupUserTable.put(entry.getKey(), userFullName, transformUserName); } } } } // Examine the paged results control response Control[] controls = ldapContext.getResponseControls(); if (controls != null) { for (int i = 0; i < controls.length; i++) { if (controls[i] instanceof PagedResultsResponseControl) { PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i]; total = prrc.getResultSize(); if (total != 0) { LOG.debug("END-OF-PAGE total : " + total); } else { LOG.debug("END-OF-PAGE total : unknown"); } cookie = prrc.getCookie(); } } } else { LOG.debug("No controls were sent from the server"); } // Re-activate paged results if (pagedResultsEnabled) { LOG.debug(String.format("Fetched paged results round: %s", ++paged)); ldapContext.setRequestControls(new Control[] { new PagedResultsControl(pagedResultsSize, cookie, Control.CRITICAL) }); } } while (cookie != null); LOG.info("LdapDeltaUserGroupBuilder.getUsers() completed with user count: " + counter); } catch (Exception t) { LOG.error("LdapDeltaUserGroupBuilder.getUsers() failed with exception: " + t); LOG.info("LdapDeltaUserGroupBuilder.getUsers() user count: " + counter); } } if (deltaSyncUserTime < highestdeltaSyncUserTime) { // Incrementing highestdeltaSyncUserTime (for AD) in order to avoid search record repetition for next sync cycle. deltaSyncUserTime = highestdeltaSyncUserTime + 1; // Incrementing the highest timestamp value (for Openldap) with 1sec in order to avoid search record repetition for next sync cycle. deltaSyncUserTimeStamp = dateFormat.format(new Date(highestdeltaSyncUserTime + 60l)); } } finally { if (userSearchResultEnum != null) { userSearchResultEnum.close(); } if (groupSearchResultEnum != null) { groupSearchResultEnum.close(); } closeLdapContext(); } } private void getGroups(UserGroupSink sink) throws Throwable { NamingEnumeration<SearchResult> groupSearchResultEnum = null; DateFormat dateFormat = new SimpleDateFormat("yyyyMMddhhmmss"); long highestdeltaSyncGroupTime = deltaSyncGroupTime; try { createLdapContext(); int total; // Activate paged results if (pagedResultsEnabled) { ldapContext.setRequestControls( new Control[] { new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) }); } extendedGroupSearchFilter = "(objectclass=" + groupObjectClass + ")"; if (groupSearchFilter != null && !groupSearchFilter.trim().isEmpty()) { String customFilter = groupSearchFilter.trim(); if (!customFilter.startsWith("(")) { customFilter = "(" + customFilter + ")"; } extendedGroupSearchFilter = extendedGroupSearchFilter + customFilter; } extendedAllGroupsSearchFilter = "(&" + extendedGroupSearchFilter + "(|(uSNChanged>=" + deltaSyncGroupTime + ")(modifyTimestamp>=" + deltaSyncGroupTimeStamp + "Z)))"; LOG.info("extendedAllGroupsSearchFilter = " + extendedAllGroupsSearchFilter); for (int ou = 0; ou < groupSearchBase.length; ou++) { byte[] cookie = null; int counter = 0; try { int paged = 0; do { groupSearchResultEnum = ldapContext.search(groupSearchBase[ou], extendedAllGroupsSearchFilter, groupSearchControls); while (groupSearchResultEnum.hasMore()) { final SearchResult groupEntry = groupSearchResultEnum.next(); if (groupEntry == null) { if (LOG.isInfoEnabled()) { LOG.info("groupEntry null, skipping sync for the entry"); } continue; } counter++; Attribute groupNameAttr = groupEntry.getAttributes().get(groupNameAttribute); if (groupNameAttr == null) { if (LOG.isInfoEnabled()) { LOG.info(groupNameAttribute + " empty for entry " + groupEntry.getNameInNamespace() + ", skipping sync"); } continue; } String gName = (String) groupNameAttr.get(); String transformGroupName = groupNameTransform(gName); // If group based search is enabled, then // update the group name to ranger admin // check for group members and populate userInfo object with user's full name and group mapping if (groupSearchFirstEnabled) { LOG.debug("Update Ranger admin with " + transformGroupName); sink.addOrUpdateGroup(transformGroupName); } Attribute timeStampAttr = groupEntry.getAttributes().get("uSNChanged"); if (timeStampAttr != null) { String uSNChangedVal = (String) timeStampAttr.get(); long currentDeltaSyncTime = Long.parseLong(uSNChangedVal); if (currentDeltaSyncTime > highestdeltaSyncGroupTime) { highestdeltaSyncGroupTime = currentDeltaSyncTime; } } else { timeStampAttr = groupEntry.getAttributes().get("modifytimestamp"); if (timeStampAttr != null) { String timeStampVal = (String) timeStampAttr.get(); Date parseDate = dateFormat.parse(timeStampVal); long currentDeltaSyncTime = parseDate.getTime(); LOG.info("timeStampVal = " + timeStampVal + "and currentDeltaSyncTime = " + currentDeltaSyncTime); if (currentDeltaSyncTime > highestdeltaSyncGroupTime) { highestdeltaSyncGroupTime = currentDeltaSyncTime; deltaSyncGroupTimeStamp = timeStampVal; } } } Attribute groupMemberAttr = groupEntry.getAttributes().get(groupMemberAttributeName); int userCount = 0; if (groupMemberAttr == null || groupMemberAttr.size() <= 0) { LOG.info("No members available for " + gName); continue; } NamingEnumeration<?> userEnum = groupMemberAttr.getAll(); while (userEnum.hasMore()) { String originalUserFullName = (String) userEnum.next(); if (originalUserFullName == null || originalUserFullName.trim().isEmpty()) { continue; } userCount++; String userName = getShortUserName(originalUserFullName); originalUserFullName = originalUserFullName.toLowerCase(); if (groupSearchFirstEnabled && !userSearchEnabled) { String transformUserName = userNameTransform(userName); try { sink.addOrUpdateUser(transformUserName); } catch (Throwable t) { LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage() + ", for user: " + transformUserName); } userNameMap.put(originalUserFullName, transformUserName); } //System.out.println("Adding " + userNameMap.get(originalUserFullName) + " and fullname = " + originalUserFullName + " to " + gName); if (userNameMap.get(originalUserFullName) != null) { groupUserTable.put(gName, originalUserFullName, userNameMap.get(originalUserFullName)); } else { groupUserTable.put(gName, originalUserFullName, originalUserFullName); } groupNameMap.put(groupEntry.getNameInNamespace().toLowerCase(), gName); } LOG.info("No. of members in the group " + gName + " = " + userCount); } // Examine the paged results control response Control[] controls = ldapContext.getResponseControls(); if (controls != null) { for (int i = 0; i < controls.length; i++) { if (controls[i] instanceof PagedResultsResponseControl) { PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i]; total = prrc.getResultSize(); if (total != 0) { LOG.debug("END-OF-PAGE total : " + total); } else { LOG.debug("END-OF-PAGE total : unknown"); } cookie = prrc.getCookie(); } } } else { LOG.debug("No controls were sent from the server"); } // Re-activate paged results if (pagedResultsEnabled) { LOG.debug(String.format("Fetched paged results round: %s", ++paged)); ldapContext.setRequestControls(new Control[] { new PagedResultsControl(pagedResultsSize, cookie, Control.CRITICAL) }); } } while (cookie != null); LOG.info("LdapDeltaUserGroupBuilder.getGroups() completed with group count: " + counter); } catch (Exception t) { LOG.error("LdapDeltaUserGroupBuilder.getGroups() failed with exception: " + t); LOG.info("LdapDeltaUserGroupBuilder.getGroups() group count: " + counter); } } } finally { if (groupSearchResultEnum != null) { groupSearchResultEnum.close(); } closeLdapContext(); } if (groupHierarchyLevels > 0) { LOG.debug("deltaSyncGroupTime = " + deltaSyncGroupTime); if (deltaSyncGroupTime > 0) { LOG.info( "LdapDeltaUserGroupBuilder.getGroups(): Going through group hierarchy for nested group evaluation for deltasync"); goUpGroupHierarchyLdap(groupNameMap.keySet(), groupHierarchyLevels - 1); } } if (deltaSyncGroupTime < highestdeltaSyncGroupTime) { // Incrementing highestdeltaSyncGroupTime (for AD) in order to avoid search record repetition for next sync cycle. deltaSyncGroupTime = highestdeltaSyncGroupTime + 1; // Incrementing the highest timestamp value (for OpenLdap) with 1min in order to avoid search record repetition for next sync cycle. deltaSyncGroupTimeStamp = dateFormat.format(new Date(highestdeltaSyncGroupTime + 60000l)); } } private static String getShortGroupName(String longGroupName) throws InvalidNameException { if (longGroupName == null) { return null; } StringTokenizer stc = new StringTokenizer(longGroupName, ","); String firstToken = stc.nextToken(); StringTokenizer ste = new StringTokenizer(firstToken, "="); String groupName = ste.nextToken(); if (ste.hasMoreTokens()) { groupName = ste.nextToken(); } groupName = groupName.trim(); LOG.info("longGroupName: " + longGroupName + ", groupName: " + groupName); return groupName; } private static String getShortUserName(String longUserName) throws InvalidNameException { if (longUserName == null) { return null; } StringTokenizer stc = new StringTokenizer(longUserName, ","); String firstToken = stc.nextToken(); StringTokenizer ste = new StringTokenizer(firstToken, "="); String userName = ste.nextToken(); if (ste.hasMoreTokens()) { userName = ste.nextToken(); } userName = userName.trim(); LOG.info("longUserName: " + longUserName + ", userName: " + userName); return userName; } private String userNameTransform(String userName) { //String userNameTransform = userName; if (userNameCaseConversionFlag) { if (userNameLowerCaseFlag) { userName = userName.toLowerCase(); } else { userName = userName.toUpperCase(); } } if (userNameRegExInst != null) { userName = userNameRegExInst.transform(userName); } return userName; } private String groupNameTransform(String groupName) { //String userNameTransform = userName; if (groupNameCaseConversionFlag) { if (groupNameLowerCaseFlag) { groupName = groupName.toLowerCase(); } else { groupName = groupName.toUpperCase(); } } if (groupNameRegExInst != null) { groupName = groupNameRegExInst.transform(groupName); } return groupName; } private void goUpGroupHierarchy(Set<String> groups, int groupHierarchyLevels, String groupSName) throws InvalidNameException { if (groupHierarchyLevels <= 0 || groups.isEmpty()) { return; } LOG.info("nextLevelGroups = " + groups + " for group = " + groupSName); Set<String> nextLevelGroups; for (String group : groups) { String groupFullName = groupNameMap.getKey(group).toString(); // Add all members of sub group to the parent groups if the member is not a group in turn Set<String> allMembers = groupUserTable.row(groupSName).keySet(); for (String member : allMembers) { String memberName = getShortGroupName(member); if (!groupUserTable.containsRow(memberName)) { //Check if the member of a group is in turn a group LOG.info("Adding " + member + " to " + group); String userSName = groupUserTable.get(groupSName, member); LOG.info("Short name of " + member + " = " + userSName); if (userSName != null) { groupUserTable.put(group, member, userSName); //Add users from the nested group to parent group } } } nextLevelGroups = groupUserTable.column(groupFullName).keySet(); goUpGroupHierarchy(nextLevelGroups, groupHierarchyLevels - 1, group); } } private void goUpGroupHierarchyLdap(Set<String> groupDNs, int groupHierarchyLevels) throws Throwable { if (groupHierarchyLevels <= 0 || groupDNs.isEmpty()) { return; } Set<String> nextLevelGroups = new HashSet<String>(); NamingEnumeration<SearchResult> groupSearchResultEnum = null; try { createLdapContext(); int total; // Activate paged results if (pagedResultsEnabled) { ldapContext.setRequestControls( new Control[] { new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) }); } String groupFilter = "(&(objectclass=" + groupObjectClass + ")"; if (groupSearchFilter != null && !groupSearchFilter.trim().isEmpty()) { String customFilter = groupSearchFilter.trim(); if (!customFilter.startsWith("(")) { customFilter = "(" + customFilter + ")"; } groupFilter += customFilter + "(|"; } StringBuilder filter = new StringBuilder(); for (String groupDN : groupDNs) { filter.append("(").append(groupMemberAttributeName).append("=").append(groupDN).append(")"); } filter.append("))"); groupFilter += filter; LOG.info("extendedAllGroupsSearchFilter = " + groupFilter); for (int ou = 0; ou < groupSearchBase.length; ou++) { byte[] cookie = null; int counter = 0; try { do { groupSearchResultEnum = ldapContext.search(groupSearchBase[ou], groupFilter, groupSearchControls); while (groupSearchResultEnum.hasMore()) { final SearchResult groupEntry = groupSearchResultEnum.next(); if (groupEntry == null) { if (LOG.isInfoEnabled()) { LOG.info("groupEntry null, skipping sync for the entry"); } continue; } counter++; Attribute groupNameAttr = groupEntry.getAttributes().get(groupNameAttribute); if (groupNameAttr == null) { if (LOG.isInfoEnabled()) { LOG.info(groupNameAttribute + " empty for entry " + groupEntry.getNameInNamespace() + ", skipping sync"); } continue; } nextLevelGroups.add(groupEntry.getNameInNamespace()); String gName = (String) groupNameAttr.get(); Attribute groupMemberAttr = groupEntry.getAttributes().get(groupMemberAttributeName); int userCount = 0; if (groupMemberAttr == null || groupMemberAttr.size() <= 0) { LOG.info("No members available for " + gName); continue; } NamingEnumeration<?> userEnum = groupMemberAttr.getAll(); while (userEnum.hasMore()) { String originalUserFullName = (String) userEnum.next(); if (originalUserFullName == null || originalUserFullName.trim().isEmpty()) { continue; } userCount++; originalUserFullName = originalUserFullName.toLowerCase(); if (userNameMap.get(originalUserFullName) != null) { groupUserTable.put(gName, originalUserFullName, userNameMap.get(originalUserFullName)); } else { groupUserTable.put(gName, originalUserFullName, originalUserFullName); } groupNameMap.put(groupEntry.getNameInNamespace().toLowerCase(), gName); } LOG.info("No. of members in the group " + gName + " = " + userCount); } // Examine the paged results control response Control[] controls = ldapContext.getResponseControls(); if (controls != null) { for (int i = 0; i < controls.length; i++) { if (controls[i] instanceof PagedResultsResponseControl) { PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i]; total = prrc.getResultSize(); if (total != 0) { LOG.debug("END-OF-PAGE total : " + total); } else { LOG.debug("END-OF-PAGE total : unknown"); } cookie = prrc.getCookie(); } } } else { LOG.debug("No controls were sent from the server"); } // Re-activate paged results if (pagedResultsEnabled) { ldapContext.setRequestControls(new Control[] { new PagedResultsControl(pagedResultsSize, cookie, Control.CRITICAL) }); } } while (cookie != null); LOG.info("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() completed with group count: " + counter); } catch (RuntimeException re) { LOG.error("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() failed with runtime exception: ", re); throw re; } catch (Exception t) { LOG.error("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() failed with exception: ", t); LOG.info("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() group count: " + counter); } } } catch (RuntimeException re) { LOG.error("LdapDeltaUserGroupBuilder.goUpGroupHierarchyLdap() failed with exception: ", re); throw re; } finally { if (groupSearchResultEnum != null) { groupSearchResultEnum.close(); } closeLdapContext(); } goUpGroupHierarchyLdap(nextLevelGroups, groupHierarchyLevels - 1); } }