blob: c297f6806343819a9f26705d1a88ddebfa1a31d8 [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.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");
if (groupSearchFirstEnabled && groupUserTable.rowKeySet().size() != 0) {
// Fix RANGER-1957: Perform full sync when group search is enabled and when there are updates to the groups
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);
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++;
} 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))
&& !userNameMap.containsKey(userFullName)) {
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);
}
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) {
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 += config.escapeSearchFilter(filter.toString());
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);
}
}