blob: c8fe3fcf94adefdd839f778016dace2350e6a62c [file] [log] [blame]
/*
* 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.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.HashMap;
import java.util.UUID;
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.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;
import org.apache.ranger.usergroupsync.UserGroupSink;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import org.apache.ranger.usergroupsync.UserGroupSource;
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;
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 String userCloudIdAttribute;
private int userSearchScope;
private String userObjectClass;
private String userSearchFilter;
private String extendedUserSearchFilter;
private SearchControls userSearchControls;
private Set<String> otherUserAttributes;
private boolean pagedResultsEnabled = true;
private int pagedResultsSize = PAGE_SIZE;
private boolean groupSearchFirstEnabled = true;
private boolean userSearchEnabled = true;
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 String groupCloudIdAttribute;
private Set<String> otherGroupAttributes;
private int groupHierarchyLevels;
private int deleteCycles;
private String currentSyncSource;
private LdapContext ldapContext;
StartTlsResponse tls;
private Table<String, String, String> groupUserTable;
UgsyncAuditInfo ugsyncAuditInfo;
LdapSyncSourceInfo ldapSyncSourceInfo;
private Map<String, Map<String, String>> sourceUsers; // key is user DN and value is map of user attributes containing original name, DN, etc...
private Map<String, Map<String, String>> sourceGroups; // key is group DN and value is map of group attributes containing original name, DN, etc...
private Map<String, Set<String>> sourceGroupUsers; // key is group DN and value is set of user DNs (members)
public static void main(String[] args) throws Throwable {
LdapUserGroupBuilder ugBuilder = new LdapUserGroupBuilder();
ugBuilder.init();
}
@Override
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));
setConfig();
ugsyncAuditInfo = new UgsyncAuditInfo();
ldapSyncSourceInfo = new LdapSyncSourceInfo();
ldapSyncSourceInfo.setLdapUrl(ldapUrl);
ldapSyncSourceInfo.setIncrementalSycn("True");
ldapSyncSourceInfo.setUserSearchEnabled(Boolean.toString(userSearchEnabled));
ldapSyncSourceInfo.setGroupSearchEnabled(Boolean.toString(groupSearchEnabled));
ldapSyncSourceInfo.setGroupSearchFirstEnabled(Boolean.toString(groupSearchFirstEnabled));
ldapSyncSourceInfo.setGroupHierarchyLevel(Integer.toString(groupHierarchyLevels));
ugsyncAuditInfo.setSyncSource(currentSyncSource);
ugsyncAuditInfo.setLdapSyncSourceInfo(ldapSyncSourceInfo);
}
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");
}
if (StringUtils.isNotEmpty(userCloudIdAttribute)) {
if (config.getUserCloudIdAttributeDataType().equals(DATA_TYPE_BYTEARRAY)) {
env.put("java.naming.ldap.attributes.binary", userCloudIdAttribute);
}
}
if (StringUtils.isNotEmpty(groupCloudIdAttribute)) {
if (config.getGroupCloudIdAttributeDataType().equals(DATA_TYPE_BYTEARRAY)) {
env.put("java.naming.ldap.attributes.binary", groupCloudIdAttribute);
}
}
for (String otherUserAttribute : otherUserAttributes) {
String attrType = config.getOtherUserAttributeDataType(otherUserAttribute);
if (attrType.equals(DATA_TYPE_BYTEARRAY)) {
env.put("java.naming.ldap.attributes.binary", otherUserAttribute);
}
}
for (String otherGroupAttribute : otherGroupAttributes) {
String attrType = config.getOtherGroupAttributeDataType(otherGroupAttribute);
if (attrType.equals(DATA_TYPE_BYTEARRAY)) {
env.put("java.naming.ldap.attributes.binary", otherGroupAttribute);
}
}
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("LdapUserGroupBuilder initialization started");
currentSyncSource = config.getCurrentSyncSource();
groupSearchFirstEnabled = true;
userSearchEnabled = true;
groupSearchEnabled = true;
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();
userCloudIdAttribute = config.getUserCloudIdAttribute();
Set<String> userSearchAttributes = new HashSet<String>();
userSearchAttributes.add(userNameAttribute);
userSearchAttributes.add(userCloudIdAttribute);
otherUserAttributes = config.getOtherUserAttributes();
for (String otherUserAttribute : otherUserAttributes) {
userSearchAttributes.add(otherUserAttribute);
}
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();
groupCloudIdAttribute = config.getGroupCloudIdAttribute();
groupHierarchyLevels = config.getGroupHierarchyLevels();
extendedGroupSearchFilter = "(&" + extendedGroupSearchFilter + "(|(" + groupMemberAttributeName + "={0})(" + groupMemberAttributeName + "={1})))";
groupSearchControls = new SearchControls();
groupSearchControls.setSearchScope(groupSearchScope);
Set<String> groupSearchAttributes = new HashSet<String>();
groupSearchAttributes.add(groupNameAttribute);
groupSearchAttributes.add(groupCloudIdAttribute);
groupSearchAttributes.add(groupMemberAttributeName);
groupSearchAttributes.add("uSNChanged");
groupSearchAttributes.add("modifytimestamp");
otherGroupAttributes = config.getOtherGroupAttributes();
for (String otherGroupAttribute : otherGroupAttributes) {
groupSearchAttributes.add(otherGroupAttribute);
}
groupSearchControls.setReturningAttributes(groupSearchAttributes.toArray(
new String[groupSearchAttributes.size()]));
if (LOG.isInfoEnabled()) {
LOG.info("LdapUserGroupBuilder 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
+ ", otherUserAttributes: " + otherUserAttributes
+ ", 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
+ ", 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("LdapUserGroupBuilder updateSink started");
boolean computeDeletes = false;
groupUserTable = HashBasedTable.create();
sourceGroups = new HashMap<>();
sourceUsers = new HashMap<>();
sourceGroupUsers = new HashMap<>();
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");
Set<String> groupFullNames = sourceGroups.keySet();
for(String group : groupFullNames) {
Set<String> nextLevelGroups = groupUserTable.column(group).keySet();
goUpGroupHierarchy(nextLevelGroups, groupHierarchyLevels-1, group);
}
LOG.info("Completed group hierarchy computation");
}
Iterator<String> groupUserTableIterator = groupUserTable.rowKeySet().iterator();
while (groupUserTableIterator.hasNext()) {
String groupName = groupUserTableIterator.next();
Map<String,String> groupUsersMap = groupUserTable.row(groupName);
Set<String> userSet = new HashSet<String>();
for(Map.Entry<String, String> entry : groupUsersMap.entrySet()){
if (sourceUsers.containsKey(entry.getValue())) {
userSet.add(entry.getValue());
}
}
sourceGroupUsers.put(groupName, userSet);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Users = " + sourceUsers.keySet());
LOG.debug("Groups = " + sourceGroups.keySet());
LOG.debug("GroupUsers = " + sourceGroupUsers.keySet());
}
try {
sink.addOrUpdateUsersGroups(sourceGroups, sourceUsers, sourceGroupUsers, computeDeletes);
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
LOG.info("deltaSyncUserTime = " + deltaSyncUserTime + " and highestdeltaSyncUserTime = " + highestdeltaSyncUserTime);
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));
}
LOG.info("deltaSyncGroupTime = " + deltaSyncGroupTime + " and highestdeltaSyncGroupTime = " + highestdeltaSyncGroupTime);
// Update deltaSyncUserTime/deltaSyncUserTimeStamp here so that in case of failures, we get updates in next cycle
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 + 60l));
}
} catch (Throwable t) {
LOG.error("Failed to update ranger admin. Will retry in next sync cycle!!", t);
}
ldapSyncSourceInfo.setUserSearchFilter(extendedUserSearchFilter);
ldapSyncSourceInfo.setGroupSearchFilter(extendedAllGroupsSearchFilter);
try {
sink.postUserGroupAuditInfo(ugsyncAuditInfo);
} catch (Throwable t) {
LOG.error("sink.postUserGroupAuditInfo failed with exception: " + t.getMessage());
}
}
private long getUsers(boolean computeDeletes) throws Throwable {
NamingEnumeration<SearchResult> userSearchResultEnum = null;
NamingEnumeration<SearchResult> groupSearchResultEnum = null;
long highestdeltaSyncUserTime;
try {
createLdapContext();
int total;
// Activate paged results
if (pagedResultsEnabled) {
ldapContext.setRequestControls(new Control[]{
new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) });
}
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
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));
}
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);
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;
}
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());
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;
}
}
}
Map<String, String> userAttrMap = new HashMap<>();
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());
}
for (String otherUserAttribute : otherUserAttributes) {
if (attributes.get(otherUserAttribute) != null) {
String attrType = config.getOtherUserAttributeDataType(otherUserAttribute);
addToAttrMap(userAttrMap, otherUserAttribute, attributes.get(otherUserAttribute), attrType);
}
}
sourceUsers.put(userFullName, userAttrMap);
counter++;
if (counter <= 2000) {
if (LOG.isInfoEnabled()) {
LOG.info("Updating user count: " + counter
+ ", userName: " + userName);
}
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);
} else {
if ( counter % 100 == 0) {
LOG.info("Synced " + counter + " users till now");
}
}
}
}
// 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) {
if (LOG.isDebugEnabled()) {
LOG.debug("END-OF-PAGE total : " + total);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("END-OF-PAGE total : unknown");
}
}
cookie = prrc.getCookie();
}
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No controls were sent from the server");
}
}
// Re-activate paged results
if (pagedResultsEnabled) {
if (LOG.isDebugEnabled()) {
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("LdapUserGroupBuilder.getUsers() completed with user count: "
+ counter);
} catch (Exception t) {
LOG.error("LdapUserGroupBuilder.getUsers() failed with exception: ", t);
LOG.info("LdapUserGroupBuilder.getUsers() user count: "
+ counter);
}
}
} finally {
if (userSearchResultEnum != null) {
userSearchResultEnum.close();
}
if (groupSearchResultEnum != null) {
groupSearchResultEnum.close();
}
closeLdapContext();
}
if (LOG.isDebugEnabled()) {
LOG.debug("highestdeltaSyncUserTime = " + highestdeltaSyncUserTime);
}
return highestdeltaSyncUserTime;
}
private long getGroups(boolean computeDeletes) throws Throwable {
NamingEnumeration<SearchResult> groupSearchResultEnum = null;
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
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;
}
if (!config.isDeltaSyncEnabled() || (computeDeletes)) {
// Perform full sync when incremental sync is not enabled
deltaSyncGroupTime = 0;
deltaSyncGroupTimeStamp = dateFormat.format(new Date(0));
}
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++;
Attributes attributes = groupEntry.getAttributes();
Attribute groupNameAttr = attributes.get(groupNameAttribute);
if (groupNameAttr == null) {
if (LOG.isInfoEnabled()) {
LOG.info(groupNameAttribute + " empty for entry " + groupEntry.getNameInNamespace() +
", skipping sync");
}
continue;
}
String groupFullName = (groupEntry.getNameInNamespace());
String gName = (String) groupNameAttr.get();
Map<String, String> groupAttrMap = new HashMap<>();
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());
}
for (String otherGroupAttribute : otherGroupAttributes) {
if (attributes.get(otherGroupAttribute) != null) {
String attrType = config.getOtherGroupAttributeDataType(otherGroupAttribute);
addToAttrMap(groupAttrMap, otherGroupAttribute, attributes.get(otherGroupAttribute), attrType);
}
}
sourceGroups.put(groupFullName, groupAttrMap);
Attribute timeStampAttr = attributes.get("uSNChanged");
if (timeStampAttr != null) {
String uSNChangedVal = (String) timeStampAttr.get();
long currentDeltaSyncTime = Long.parseLong(uSNChangedVal);
if (currentDeltaSyncTime > highestdeltaSyncGroupTime) {
highestdeltaSyncGroupTime = 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 > highestdeltaSyncGroupTime) {
highestdeltaSyncGroupTime = currentDeltaSyncTime;
deltaSyncGroupTimeStamp = timeStampVal;
}
}
}
Attribute groupMemberAttr = attributes.get(groupMemberAttributeName);
int userCount = 0;
if (groupMemberAttr == null || groupMemberAttr.size() <= 0) {
LOG.info("No members available for " + gName);
sourceGroupUsers.put(groupFullName, new HashSet<>());
continue;
}
NamingEnumeration<?> userEnum = groupMemberAttr.getAll();
while (userEnum.hasMore()) {
String originalUserFullName = (String) userEnum.next();
if (originalUserFullName == null || originalUserFullName.trim().isEmpty()) {
sourceGroupUsers.put(groupFullName, new HashSet<>());
continue;
}
userCount++;
groupUserTable.put(groupFullName, originalUserFullName, originalUserFullName);
}
LOG.info("No. of members in the group " + gName + " = " + userCount);
}
// Examine the paged results control response
Control[] controls = ldapContext.getResponseControls();
if (controls != null) {
for (int i = 0; i < controls.length; i++) {
if (controls[i] instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl)controls[i];
total = prrc.getResultSize();
if (total != 0) {
if (LOG.isDebugEnabled()) {
LOG.debug("END-OF-PAGE total : " + total);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("END-OF-PAGE total : unknown");
}
}
cookie = prrc.getCookie();
}
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No controls were sent from the server");
}
}
// Re-activate paged results
if (pagedResultsEnabled) {
if (LOG.isDebugEnabled()) {
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("LdapUserGroupBuilder.getGroups() completed with group count: "
+ counter);
} catch (Exception t) {
LOG.error("LdapUserGroupBuilder.getGroups() failed with exception: " + t);
LOG.info("LdapUserGroupBuilder.getGroups() group count: "
+ counter);
}
}
} finally {
if (groupSearchResultEnum != null) {
groupSearchResultEnum.close();
}
closeLdapContext();
}
if (groupHierarchyLevels > 0) {
if (LOG.isDebugEnabled()) {
LOG.debug("deltaSyncGroupTime = " + deltaSyncGroupTime);
}
if (deltaSyncGroupTime > 0) {
LOG.info("LdapUserGroupBuilder.getGroups(): Going through group hierarchy for nested group evaluation for deltasync");
goUpGroupHierarchyLdap(sourceGroups.keySet(), groupHierarchyLevels-1);
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("highestdeltaSyncGroupTime = " + highestdeltaSyncGroupTime);
}
return highestdeltaSyncGroupTime;
}
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) {
// 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();
LOG.info("members of " + groupSName + " = " + allMembers);
for(String member : allMembers) {
if (!groupUserTable.containsRow(member)) { //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(group).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;
}
String groupFullName = (groupEntry.getNameInNamespace());
nextLevelGroups.add(groupFullName);
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;
}
Map<String, String> groupAttrMap = new HashMap<>();
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) {
groupAttrMap.put(otherGroupAttribute, (String) otherGroupAttr.get());
}
}
sourceGroups.put(gName, groupAttrMap);
NamingEnumeration<?> userEnum = groupMemberAttr.getAll();
while (userEnum.hasMore()) {
String originalUserFullName = (String) userEnum.next();
if (originalUserFullName == null || originalUserFullName.trim().isEmpty()) {
continue;
}
userCount++;
groupUserTable.put(groupFullName, originalUserFullName, originalUserFullName);
}
LOG.info("No. of members in the group " + gName + " = " + userCount);
}
// Examine the paged results control response
Control[] controls = ldapContext.getResponseControls();
if (controls != null) {
for (int i = 0; i < controls.length; i++) {
if (controls[i] instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl)controls[i];
total = prrc.getResultSize();
if (total != 0) {
if (LOG.isDebugEnabled()) {
LOG.debug("END-OF-PAGE total : " + total);
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("END-OF-PAGE total : unknown");
}
}
cookie = prrc.getCookie();
}
}
} else {
if (LOG.isDebugEnabled()) {
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("LdapUserGroupBuilder.goUpGroupHierarchyLdap() completed with group count: "
+ counter);
} catch (RuntimeException re) {
LOG.error("LdapUserGroupBuilder.goUpGroupHierarchyLdap() failed with runtime exception: ", re);
throw re;
} catch (Exception t) {
LOG.error("LdapUserGroupBuilder.goUpGroupHierarchyLdap() failed with exception: ", t);
LOG.info("LdapUserGroupBuilder.goUpGroupHierarchyLdap() group count: "
+ counter);
}
}
} catch (RuntimeException re) {
LOG.error("LdapUserGroupBuilder.goUpGroupHierarchyLdap() failed with exception: ", re);
throw re;
} finally {
if (groupSearchResultEnum != null) {
groupSearchResultEnum.close();
}
closeLdapContext();
}
goUpGroupHierarchyLdap(nextLevelGroups, groupHierarchyLevels-1);
}
private void addToAttrMap(Map<String, String> userAttrMap, String attrName, Attribute attr, String attrType) throws Throwable{
if (attrType.equals(DATA_TYPE_BYTEARRAY)) {
try {
byte[] otherUserAttrBytes = (byte[]) attr.get();
//Convert objectGUID into string and add to userAttrMap
String attrVal = UUID.nameUUIDFromBytes(otherUserAttrBytes).toString();
userAttrMap.put(attrName, attrVal);
} catch (ClassCastException e) {
LOG.error(attrName + " type is not set properly " + e.getMessage());
}
} else if (attrType.equals("String")) {
userAttrMap.put(attrName, (String) attr.get());
} else {
// This should not be reached.
LOG.warn("Attribute Type " + attrType + " not supported for " + attrName);
}
}
}