blob: 6b2648d9736672dbd31983c7dc308166b2779edf [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.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
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.log4j.Logger;
import org.apache.ranger.unixusersync.config.UserGroupSyncConfig;
import org.apache.ranger.usergroupsync.AbstractUserGroupSource;
import org.apache.ranger.usergroupsync.UserGroupSink;
public class LdapUserGroupBuilder extends AbstractUserGroupSource {
private static final Logger LOG = Logger.getLogger(LdapUserGroupBuilder.class);
private static final int PAGE_SIZE = 500;
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;
private boolean userSearchEnabled;
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;
private StartTlsResponse tls;
private boolean userNameCaseConversionFlag;
private boolean groupNameCaseConversionFlag;
private boolean userNameLowerCaseFlag;
private boolean groupNameLowerCaseFlag;
private boolean groupUserMapSyncEnabled;
private Map<String, UserInfo> userGroupMap;
//private Set<String> firstGroupDNs;
public static void main(String[] args) throws Throwable {
LdapUserGroupBuilder ugBuilder = new LdapUserGroupBuilder();
ugBuilder.init();
}
public LdapUserGroupBuilder() {
super();
LOG.info("LdapUserGroupBuilder 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{
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("LdapUserGroupBuilder initialization started");
groupSearchFirstEnabled = config.isGroupSearchFirstEnabled();
userSearchEnabled = config.isUserSearchEnabled();
groupSearchEnabled = config.isGroupSearchEnabled();
ldapUrl = config.getLdapUrl();
ldapBindDn = config.getLdapBindDn();
ldapBindPassword = config.getLdapBindPassword();
ldapAuthenticationMechanism = config.getLdapAuthenticationMechanism();
ldapReferral = config.getContextReferral();
searchBase = config.getSearchBase();
userSearchBase = config.getUserSearchBase().split(";");
userSearchScope = config.getUserSearchScope();
userObjectClass = config.getUserObjectClass();
userSearchFilter = config.getUserSearchFilter();
extendedUserSearchFilter = "(objectclass=" + userObjectClass + ")";
if (userSearchFilter != null && !userSearchFilter.trim().isEmpty()) {
String customFilter = userSearchFilter.trim();
if (!customFilter.startsWith("(")) {
customFilter = "(" + customFilter + ")";
}
extendedUserSearchFilter = "(&" + extendedUserSearchFilter + customFilter + ")";
}
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);
}
}
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 = "(objectclass=" + groupObjectClass + ")";
if (groupSearchFilter != null && !groupSearchFilter.trim().isEmpty()) {
String customFilter = groupSearchFilter.trim();
if (!customFilter.startsWith("(")) {
customFilter = "(" + customFilter + ")";
}
extendedGroupSearchFilter = extendedGroupSearchFilter + customFilter;
}
extendedAllGroupsSearchFilter = "(&" + extendedGroupSearchFilter + ")";
if (!groupSearchFirstEnabled) {
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);
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
+ ", 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("LDAPUserGroupBuilder updateSink started");
userGroupMap = new HashMap<String, UserInfo>();
if (!groupSearchFirstEnabled) {
LOG.info("Performing user search first");
getUsers(sink);
LOG.debug("Total No. of users saved = " + userGroupMap.size());
if (!groupSearchEnabled && groupHierarchyLevels > 0) {
getRootDN();
}
//Iterator<UserInfo> userInfoIterator = userGroupMap.
for (UserInfo userInfo : userGroupMap.values()) {
String userName = userInfo.getUserName();
if (groupSearchEnabled) {
// Perform group search
LOG.info("groupSearch is enabled, would search for groups and compute memberships");
//firstGroupDNs = new HashSet<String>();
getGroups(sink, userInfo);
}
if (groupHierarchyLevels > 0) {
LOG.debug("Going through group hierarchy for nested group evaluation");
goUpGroupHierarchyLdap(userInfo.getGroupDNs(), groupHierarchyLevels - 1, userInfo);
LOG.debug("Completed group hierarchy computation");
}
List<String> groupList = userInfo.getGroups();
LOG.debug("updateSink(): group list for " + userName + " = " + groupList);
if (userNameCaseConversionFlag) {
if (userNameLowerCaseFlag) {
userName = userName.toLowerCase();
}
else {
userName = userName.toUpperCase();
}
}
if (userNameRegExInst != null) {
userName = userNameRegExInst.transform(userName);
}
try {
sink.addOrUpdateUser(userName, groupList);
} catch (Throwable t) {
LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage()
+ ", for user: " + userName
+ ", groups: " + groupList);
}
}
} else {
LOG.info("Performing Group search first");
getGroups(sink, null);
// Go through the userInfo map and update ranger admin.
for (UserInfo userInfo : userGroupMap.values()) {
String userName = getShortUserName(userInfo.getUserFullName());
if (groupHierarchyLevels > 0) {
//System.out.println("Going through group hierarchy for nested group evaluation");
goUpGroupHierarchyLdap(userInfo.getGroupDNs(), groupHierarchyLevels - 1, userInfo);
//System.out.println("Completed group hierarchy computation");
}
if (userSearchEnabled) {
LOG.info("User search is enabled and hence computing user membership.");
getUsers(sink);
} else {
LOG.info("User search is disabled and hence using the group member attribute for username" + userName);
List<String> groupList = userInfo.getGroups();
if (userNameCaseConversionFlag) {
if (userNameLowerCaseFlag) {
userName = userName.toLowerCase();
} else {
userName = userName.toUpperCase();
}
}
if (userNameRegExInst != null) {
userName = userNameRegExInst.transform(userName);
}
try {
sink.addOrUpdateUser(userName, groupList);
} catch (Throwable t) {
LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage()
+ ", for user: " + userName
+ ", groups: " + groupList);
}
}
}
}
}
private void getUsers(UserGroupSink sink) throws Throwable {
UserInfo userInfo;
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) });
}
// When multiple OUs are configured, go through each OU as the user search base to search for users.
for (String ou : userSearchBase) {
byte[] cookie = null;
int counter = 0;
try {
int paged = 0;
do {
userSearchResultEnum = ldapContext
.search(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 userName = (String) userNameAttr.get();
if (userName == null || userName.trim().isEmpty()) {
if (LOG.isInfoEnabled()) {
LOG.info(userNameAttribute + " empty for entry " + userEntry.getNameInNamespace() +
", skipping sync");
}
continue;
}
if (!groupSearchFirstEnabled) {
userInfo = new UserInfo(userName, userEntry.getNameInNamespace());
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 groupDN = (String) groupEnum.next();
LOG.debug("Adding " + groupDN + " to " + userName);
userInfo.addGroupDN(groupDN);
String gName = getShortGroupName(groupDN);
if (groupNameCaseConversionFlag) {
if (groupNameLowerCaseFlag) {
gName = gName.toLowerCase();
} else {
gName = gName.toUpperCase();
}
}
if (groupNameRegExInst != null) {
gName = groupNameRegExInst.transform(gName);
}
groups.add(gName);
}
}
}
}
userInfo.addGroups(groups);
//populate the userGroupMap with username, userInfo.
//userInfo contains details of user that will be later used for
//group search to compute group membership as well as to call sink.addOrUpdateUser()
if (userGroupMap.containsKey(userName)) {
LOG.warn("user object with username " + userName + " already exists and is replaced with the latest user object." );
}
userGroupMap.put(userName, userInfo);
//List<String> groupList = new ArrayList<String>(groups);
List<String> groupList = userInfo.getGroups();
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 usersList,
// then update user name in the userInfo map with the value from the search result
// and update ranger admin.
String userFullName = (userEntry.getNameInNamespace()).toLowerCase();
LOG.debug("Checking if the user " + userFullName + " is part of the retrieved groups");
userInfo = userGroupMap.get(userFullName);
if (userInfo == null) {
userInfo = userGroupMap.get(userName.toLowerCase());
}
if (userInfo != null) {
counter++;
LOG.info("Updating username for " + userFullName + " with " + userName);
userInfo.updateUserName(userName);
List<String> groupList = userInfo.getGroups();
if (userNameCaseConversionFlag) {
if (userNameLowerCaseFlag) {
userName = userName.toLowerCase();
}
else {
userName = userName.toUpperCase();
}
}
if (userNameRegExInst != null) {
userName = userNameRegExInst.transform(userName);
}
try {
sink.addOrUpdateUser(userName, groupList);
} catch (Throwable t) {
LOG.error("sink.addOrUpdateUser failed with exception: " + t.getMessage()
+ ", for user: " + userName
+ ", groups: " + groupList);
}
}
}
}
// Examine the paged results control response
Control[] controls = ldapContext.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl)control;
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("LDAPUserGroupBuilder.getUsers() completed with user count: "
+ counter);
} catch (Throwable 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();
}
}
private void getGroups(UserGroupSink sink, UserInfo userInfo) throws Throwable {
//LOG.debug("getGroups(): for user " + userInfo.getUserName());
NamingEnumeration<SearchResult> groupSearchResultEnum = null;
try {
createLdapContext();
int total;
// Activate paged results
if (pagedResultsEnabled) {
ldapContext.setRequestControls(new Control[]{
new PagedResultsControl(pagedResultsSize, Control.NONCRITICAL) });
}
for (String ou : groupSearchBase) {
byte[] cookie = null;
int counter = 0;
try {
int paged = 0;
do {
if (!groupSearchFirstEnabled) {
if (userInfo == null) {
// Should never reach this.
LOG.error("No user information provided for group search!");
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("Searching for groups for user " + userInfo.getUserName() +
" using filter " + String.format(extendedGroupSearchFilter, userInfo.getUserFullName(),
userInfo.getUserName()));
}
groupSearchResultEnum = ldapContext
.search(ou, extendedGroupSearchFilter,
new Object[]{userInfo.getUserFullName(), userInfo.getUserName()},
groupSearchControls);
} else {
// If group based search is enabled, then first retrieve all the groups based on the group configuration.
groupSearchResultEnum = ldapContext
.search(ou, extendedAllGroupsSearchFilter,
groupSearchControls);
}
while (groupSearchResultEnum.hasMore()) {
final SearchResult groupEntry = groupSearchResultEnum.next();
if (groupEntry != null) {
counter++;
Attribute groupNameAttr = groupEntry.getAttributes().get(groupNameAttribute);
//System.out.println("getGroups(): Going through all groups");
if (groupNameAttr == null) {
if (LOG.isInfoEnabled()) {
LOG.info(groupNameAttribute + " empty for entry " + groupEntry.getNameInNamespace() +
", skipping sync");
}
continue;
}
String groupDN = groupEntry.getNameInNamespace();
//System.out.println("getGroups(): groupDN = " + groupDN);
String gName = (String) groupNameAttr.get();
if (groupNameCaseConversionFlag) {
if (groupNameLowerCaseFlag) {
gName = gName.toLowerCase();
} else {
gName = gName.toUpperCase();
}
}
if (groupNameRegExInst != null) {
gName = groupNameRegExInst.transform(gName);
}
if (!groupSearchFirstEnabled) {
//computedGroups.add(gName);
if (LOG.isInfoEnabled()) {
LOG.info("computed groups for user: " + userInfo.getUserName() + ", groups: " + gName);
}
userInfo.addGroupDN(groupDN);
userInfo.addGroup(gName);
} else {
// 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
Attribute groupMemberAttr = groupEntry.getAttributes().get(groupMemberAttributeName);
LOG.debug("Update Ranger admin with " + gName);
sink.addOrUpdateGroup(gName);
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;
}
String userFullName = originalUserFullName.toLowerCase();
userCount++;
if (!userGroupMap.containsKey(userFullName)) {
userInfo = new UserInfo(userFullName, originalUserFullName); // Preserving the original full name for later
userGroupMap.put(userFullName, userInfo);
} else {
userInfo = userGroupMap.get(userFullName);
}
LOG.info("Adding " + gName + " to user " + userInfo.getUserFullName());
userInfo.addGroup(gName);
userInfo.addGroupDN(groupDN);
}
LOG.info("No. of members in the group " + gName + " = " + userCount);
}
}
}
// Examine the paged results control response
Control[] controls = ldapContext.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl)control;
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("LDAPUserGroupBuilder.getGroups() completed with group count: "
+ counter);
} catch (Throwable t) {
LOG.error("LDAPUserGroupBuilder.getGroups() failed with exception: " + t);
LOG.info("LDAPUserGroupBuilder.getGroups() group count: "
+ counter);
}
}
} finally {
if (groupSearchResultEnum != null) {
groupSearchResultEnum.close();
}
closeLdapContext();
}
}
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 void goUpGroupHierarchyLdap(Set<String> groupDNs, int groupHierarchyLevels, UserInfo userInfo) throws Throwable {
LOG.debug("goUpGroupHierarchyLdap(): Incoming groups " + groupDNs);
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.debug("extendedAllGroupsSearchFilter = " + groupFilter);
for (String ou : groupSearchBase) {
byte[] cookie = null;
int counter = 0;
try {
do {
groupSearchResultEnum = ldapContext
.search(ou, groupFilter,
groupSearchControls);
//System.out.println("goUpGroupHierarchyLdap(): Going through the sub groups");
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 groupDN = groupEntry.getNameInNamespace();
//System.out.println("goUpGroupHierarchyLdap(): next Level Group DN = " + groupDN);
nextLevelGroups.add(groupDN);
String gName = (String) groupNameAttr.get();
if (groupNameCaseConversionFlag) {
if (groupNameLowerCaseFlag) {
gName = gName.toLowerCase();
} else {
gName = gName.toUpperCase();
}
}
if (groupNameRegExInst != null) {
gName = groupNameRegExInst.transform(gName);
}
userInfo.addGroup(gName);
}
// Examine the paged results control response
Control[] controls = ldapContext.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl)control;
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(PAGE_SIZE, 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, userInfo);
}
private void getRootDN() throws Throwable {
NamingEnumeration groupSearchResultEnum = null;
SearchControls sc1 = new SearchControls();
sc1.setSearchScope(SearchControls.OBJECT_SCOPE);
sc1.setReturningAttributes(new String[]{"namingContexts"});
try {
createLdapContext();
groupSearchResultEnum = ldapContext
.search("", "objectclass=*", sc1);
//System.out.println("goUpGroupHierarchyLdap(): Going through the sub groups");
while (groupSearchResultEnum.hasMore()) {
SearchResult result1 = (SearchResult) groupSearchResultEnum.next();
Attributes attrs = result1.getAttributes();
Attribute attr = attrs.get("namingContexts");
LOG.debug("namingContexts = " + attr);
groupSearchBase = new String[] {attr.get(0).toString()};
LOG.info("RootDN = " + Arrays.toString(groupSearchBase));
}
} catch (RuntimeException re) {
throw re;
} finally {
if (groupSearchResultEnum != null) {
groupSearchResultEnum.close();
}
closeLdapContext();
}
}
}
class UserInfo {
private String userName;
private String userFullName;
private Set<String> groupList;
private Set<String> groupDNList;
public UserInfo(String userName, String userFullName) {
this.userName = userName;
this.userFullName = userFullName;
this.groupList = new HashSet<String>();
this.groupDNList = new HashSet<String>();
}
public void updateUserName(String userName) {
this.userName = userName;
}
public String getUserName() {
return userName;
}
public String getUserFullName() {
return userFullName;
}
public void addGroups(Set<String> groups) {
groupList.addAll(groups);
}
public void addGroup(String group) {
groupList.add(group);
}
public List<String> getGroups() {
return (new ArrayList<String>(groupList));
}
public void addGroupDNs(Set<String> groupDNs) {
groupDNList.addAll(groupDNs);
}
public void addGroupDN(String groupDN) {
groupDNList.add(groupDN);
}
public Set<String> getGroupDNs() {
return (groupDNList);
}
}