ldap search is very slow - java

I am using JNDI to connect to the LDAP active directory, and I want to search for users where the name contains the search string, so my search method is as follows:
public static List<LDAPUser> searchContactsByName(
ExtendedDirContext extendedDirContext, String name) {
try {
LdapContext ldapContext = extendedDirContext.getLdapContext();
String searchBaseStr = extendedDirContext.getSearchBase();
String sortKey = LDAPAttributes.NAME;
ldapContext.setRequestControls(new Control[] { new SortControl(
sortKey, Control.CRITICAL) });
SearchControls searchCtls = new SearchControls();
searchCtls.setTimeLimit(1000 * 10);
String returnedAtts[] = { LDAPAttributes.USER_NAME,
LDAPAttributes.NAME };
searchCtls.setReturningAttributes(returnedAtts);
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String searchFilter = "(&(ObjectCategory=person)(cn=*" + name
+ "*))";
NamingEnumeration<SearchResult> results = ldapContext.search(
searchBaseStr, searchFilter, searchCtls);
List<LDAPUser> users = new ArrayList<LDAPUser>(0);
while (results.hasMoreElements()) {
SearchResult sr = (SearchResult) results.next();
Attributes attrs = sr.getAttributes();
LDAPUser user = new LDAPUser();
user.setName(attrs.get(LDAPAttributes.NAME).toString()
.replace("cn: ", ""));
user.setUserName(attrs.get(LDAPAttributes.USER_NAME).toString()
.replace("sAMAccountName: ", ""));
users.add(user);
}
return users;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
and here is how I am making the connection to LDAP:
public static ExtendedDirContext connectToLdap(MessageSource messageSource) {
try {
log.debug("connectToLdap");
String providerUrl = messageSource.getMessage("provider.url", null,
null);
String securityPrincipal = messageSource.getMessage(
"security.principal", null, null);
String securityCredentials = messageSource.getMessage(
"security.credentials", null, null);
String searchBase = messageSource.getMessage("search.base", null,
null);
boolean ssl = Boolean.parseBoolean(messageSource.getMessage("ssl",
null, null));
LdapContext ldapContext;
Hashtable<String, String> ldapEnv = new Hashtable<String, String>(
11);
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, providerUrl);
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, securityPrincipal);
ldapEnv.put(Context.SECURITY_CREDENTIALS, securityCredentials);
if (ssl)
ldapEnv.put(Context.SECURITY_PROTOCOL, "ssl");
// To get rid of the PartialResultException when using Active
// Directory
ldapEnv.put(Context.REFERRAL, "follow");
ldapContext = new InitialLdapContext(ldapEnv, null);
ExtendedDirContext extendedDirContext = new ExtendedDirContext();
extendedDirContext.setLdapContext(ldapContext);
extendedDirContext.setSearchBase(searchBase);
log.debug("success connection to ldap");
return extendedDirContext;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
The LDAP credentials are as follows:
provider.url=ldap://dc.fabrikam.com:389
security.principal=CN=administrator,CN=Users,DC=fabrikam,DC=com
security.credentials=password
search.base=dc=fabrikam,dc=com
Why does the search take so much time to retrieve the data? Is there any change that I can do to make the search faster, since I have only 285 contacts in the AD?

Solution was to change ldapEnv.put(Context.REFERRAL, "follow"); to ldapEnv.put(Context.REFERRAL, "ignore");

Your filter:
"(&(ObjectCategory=person)(cn=*" + name + "*))"
May be an issue.
I would recommend that you download a known LDAP utility (Apache Directory Studio Browser as an example) and try different search filters until you find one that works.
To Start, try
"(&(ObjectCategory=person)(cn= + name ))"

You're right,
ldapEnv.put(Context.REFERRAL, "ignore")
didn't get exception about connection timed out. But when I first try I get a partialexception. After I changed my LDAP configuration port from 389 to 3268 I didn't get any exception, build successfully. 3268 port about global catalog of LDAP. For example Outlook clients query the global catalog to locate Address Book information. You can try global catalog if you get an exception referral type setting.

Related

LDAPContext.search() returns empty result

Using LDAPContext class I search for a specific user and try to get whether it exists. But search() method returns an empty response.
private int checkUserOnLDAP() {
String strLDAPServer = "ldap://ldap.forumsys.com:389";
String strLDAPPricipal = "cn=read-only-admin,dc=example,dc=com";
String strPassword = "password";
String strSearchBase = "ou=mathematicians,dc=example,dc=com";
String strUserToSearch = "riemann";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, strLDAPServer);
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.SECURITY_PRINCIPAL, strLDAPPricipal);
environment.put(Context.SECURITY_CREDENTIALS, strPassword);
LdapContext ctxGC = null;
try {
ctxGC = new InitialLdapContext(environment, null);
ctxGC.getAttributes("");
} catch (NamingException e) {
System.err.print("SEARCHER BLOCKED USER");
e.printStackTrace();
} catch (Exception e) {
System.err.print("SEARCHER WRONG PASSWORD");
e.printStackTrace();
}
System.out.println("SEARCHER LOGIN SUCCESSFUL");
System.out.println("NOW TRYING TO SEARCH");
try {
String searchFilter = "(&(objectClass=user)(sAMAccountName=" + strUserToSearch + "))";
String returnedAtts[] = new String[0];
SearchControls searchCtls = new SearchControls();
searchCtls.setReturningAttributes(returnedAtts);
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<?> answer = ctxGC.search(strSearchBase, searchFilter, searchCtls);
if (answer.hasMoreElements()) {
Object a = answer.nextElement();
System.out.println("SUCCESFULLY, FOUND USER");
return 0;
} else {
System.out.println("ANSWER HAS NO ELEMENTS");
}
} catch (Exception e) {
System.out.println("SEARCH FAILED");
e.printStackTrace();
}
return 0;
}
While testing, I use an online ldap service: http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
Considering this online test service how can I check whether user exists?
Your search filter uses the sAMAccountName attribute, but that attribute is not available in the test server. Use uid instead.

Not able to delete LDAP entry

I want to delete a LDAP entry with its DN in java. I have the code below for that:
private void deleteUserAssociations(String customer) throws Exception {
DirContext ctx = null;
#SuppressWarnings("rawtypes")
NamingEnumeration results = null;
if (customer != null) {
ctx = new InitialDirContext(env);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
results = ctx.search("", "(customer=" + customer+ ")", controls);
while (results.hasMore()) {
SearchResult searchResult = (SearchResult) results.next();
String NameSpaceToDelete = searchResult.getNameInNamespace();
LdapContext ctxTemp = new InitialLdapContext(env, null);
Control[] tdCtls = new Control[]{new TreeDeleteControl()};
ctxTemp.setRequestControls(tdCtls);
ctxTemp.destroySubcontext(NameSpaceToDelete);
ctxTemp.close();
}
}
}
The method takes a customer ID to delete, searches the entry in the LDAP and tries to delete that with Tree Delete Control. The Tree Delete Control class is as below:
class TreeDeleteControl implements Control
{
public byte[] getEncodedValue() {
return new byte[] {};
}
public String getID() {
return "1.2.840.113556.1.4.805";
}
public boolean isCritical() {
return true;
}
}
Problem: The code runs without any error/exception but it does not delete any of the entry. After the execution of the code, I could see all the customers from a LDAP browser (JXplorer).
Please help.

How can I get subdomain users that are members of group in main domain

My goal is recursive getting all group members from domain and subdomains using only connection to main dc.
I have Active Directory forest containing domain.com, sub.domain.com.
My 'MainGroup' located in domain.com and contains members/groups from domain.com and sub.domain.com.
I'm getting 'member' field strings of MainGroup using ldapContext connection with dc.domain.com. Here is it:
cn=userA,ou=Users,dc=domain,dc=com
cn=userB,ou=Users,dc=sub,dc=domain,dc=com
cn=groupB,ou=Users,dc=sub,dc=domain,dc=com
Can I get subdomain's user/group data (I need ObjectClass to recursive iterate) using current ldapConext?
private LdapContext createLdapContext() {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapHost);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "user#" + domain);
env.put(Context.SECURITY_CREDENTIALS, "******");
try {
ldapContext = new InitialLdapContext(env, null);
} catch (NamingException e) {
e.printStackTrace();
}
return ldapContext;
}
public List getGroupMembers(String groupName) {
List resultList = new ArrayList<String>();
int Start = 0;
int Finish = 1499;
int Step = 1500;
boolean Finished = false;
String Range;
try {
while (!Finished) {
Range = Start + "-" + Finish;
String[] returningAttrs = {"member;range=" + Range};
String searchFilter = "(&(objectClass=group)(sAMAccountName=" + groupName + "))";
SearchControls searchControls = new SearchControls();
searchControls.setReturningAttributes(returningAttrs);
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchControls);
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
Attributes attrs = sr.getAttributes();
if (attrs != null) {
for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements(); ) {
Attribute attr = (Attribute) ae.next();
if (attr.getID().endsWith("*")) {
Finished = true;
}
for (NamingEnumeration e = attr.getAll(); e.hasMoreElements(); ) {
resultList.add(e.next().toString());
}
}
}
}
Start = Start + Step;
Finish = Finish + Step;
}
ldapContext.close();
} catch (NamingException e) {
e.printStackTrace();
} finally {
return resultList;
}
}
Edited. Found another solution:
Anyway we should query every domain.
We can make 'recursive one shoot query' using filter (memberof:1.2.840.113556.1.4.1941:=cn=Group1,OU=groupsOU,DC=x)
Related c# question
msdn Search Filter Syntax

How to set a LDAP client-requested size limit in Java?

An article about best practices in LDAP searches states:
Programmers should always provide a client-requested size limit.
How can this be done in Java? I can not find any appropriate option the in the documentation.
Can anybody give me a hint?
If the client is using the UnboundID LDAP SDK:
SearchRequest req = new SearchRequest(baseObject,scope,filter,requestedAttributes);
req.setSizeLimit(maxNumberOfEntriesToReturn);
If the client is using JNDI, use setCountLimit, and consider using the UnboundID LDAP SDK instead of JNDI for new code.
.setSizeLimit() does not help to enlange server-size limit.
and this code also
SearchControls ctls = new SearchControls();
ctls.setCountLimit(99000);
You should use paging mode.
public static void main(String[] args) {
try {
int count = 0;
LDAPConnection connection = new LDAPConnection("hostname", 389, "user#domain", "password");
final String path = "OU=Users,DC=org,DC=com";
String[] attributes = {"SamAccountName","name"};
SearchRequest searchRequest = new SearchRequest(path, SearchScope.SUB, Filter.createEqualityFilter("objectClass", "person"), attributes);
ASN1OctetString resumeCookie = null;
while (true)
{
searchRequest.setControls(
new SimplePagedResultsControl(100, resumeCookie));
SearchResult searchResult = connection.search(searchRequest);
for (SearchResultEntry e : searchResult.getSearchEntries())
{
if (e.hasAttribute("SamAccountName"))
System.out.print(count++ + ": " + e.getAttributeValue("SamAccountName"));
if (e.hasAttribute("name"))
System.out.println("->" + e.getAttributeValue("name"));
}
LDAPTestUtils.assertHasControl(searchResult,
SimplePagedResultsControl.PAGED_RESULTS_OID);
SimplePagedResultsControl responseControl =
SimplePagedResultsControl.get(searchResult);
if (responseControl.moreResultsToReturn())
{
resumeCookie = responseControl.getCookie();
}
else
{
break;
}
}
}
catch (Exception e)
{
System.out.println(e.toString());
}
}

Creating WebLogic users programmatically from a standalone Java client

I'm trying to create users in WebLogic (10.3.4) programmatically from a simple standalone Java client (one class --> two methods: createWeblogicUser() & main()).
public void createWeblogicUser() {
try {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.SECURITY_PRINCIPAL, "weblogic");
env.put(Context.SECURITY_CREDENTIALS, "weblogic");
env.put(Context.PROVIDER_URL, "t3://myserver:7001");
InitialContext ctx = new InitialContext(env);
MBeanServer wls = (MBeanServer) ctx.lookup("java:comp/env/jmx/runtime");
ObjectName userEditor = null;
ObjectName mBeanTypeService = new ObjectName( "com.bea:Name=MBeanTypeService, Type=weblogic.management.mbeanservers.MBeanTypeService");
ObjectName rs = new ObjectName("com.bea:Name=RuntimeService, Type=weblogic.management.mbeanservers.runtime.RuntimeServiceMBean");
ObjectName domainMBean = (ObjectName) wls.getAttribute(rs, "DomainConfiguration");
ObjectName securityConfig = (ObjectName) wls.getAttribute(domainMBean, "SecurityConfiguration");
ObjectName defaultRealm = (ObjectName) wls.getAttribute(securityConfig, "DefaultRealm");
ObjectName[] authProviders = (ObjectName[]) wls.getAttribute(defaultRealm, "AuthenticationProviders");
for(ObjectName providerName : authProviders) {
if(userEditor == null) {
ModelMBeanInfo info = (ModelMBeanInfo) wls.getMBeanInfo(providerName);
String className = (String) info.getMBeanDescriptor().getFieldValue("interfaceClassName");
if(className != null) {
String[] mba = (String[]) wls.invoke(mBeanTypeService
, "getSubtypes"
, new Object[] {"weblogic.management.security.authentication.UserEditorMBean"}
, new String[] {"java.lang.String"}
);
for(String mb : mba) {
if(className.equals(mb))
userEditor = providerName;
}
}
}
if(userEditor == null)
throw new RuntimeException("Could not retrieve user editor");
try {
wls.invoke(userEditor
, "createUser"
, new Object[] {"wls_user", "password123","User created programmatically."}
, new String[] {"java.lang.String", "java.lang.String","java.lang.String"}
);
}
catch(Exception e){
e.printStackTrace();
}
ctx.close();
}
}
catch(Exception ex) {
ex.printStackTrace();
}
}
Any ideas on what the context lookup I should be making? "java:comp" throws a javax.naming.NameNotFoundException; looks like I can use that only from w/in a container.
Got it to work.
private void createWeblogicUser(String username) {
try {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.SECURITY_PRINCIPAL, "weblogic");
env.put(Context.SECURITY_CREDENTIALS, "weblogic");
String hostname = "myserver";
int port = 7001;
String protocol = "rmi";
String url= new String("/jndi/iiop://myserver:7001/weblogic.management.mbeanservers.domainruntime");
JMXServiceURL serviceURL = new JMXServiceURL(protocol, hostname, port, url);
JMXConnector connector = JMXConnectorFactory.connect(serviceURL, env);
MBeanServerConnection connection = connector.getMBeanServerConnection();
ObjectName userEditor = null;
ObjectName mBeanTypeService = new ObjectName( "com.bea:Name=MBeanTypeService,Type=weblogic.management.mbeanservers.MBeanTypeService");
ObjectName rs = new ObjectName("com.bea:Name=DomainRuntimeService,Type=weblogic.management.mbeanservers.domainruntime.DomainRuntimeServiceMBean");
ObjectName domainMBean = (ObjectName) connection.getAttribute(rs, "DomainConfiguration");
ObjectName securityConfig = (ObjectName) connection.getAttribute(domainMBean, "SecurityConfiguration");
ObjectName defaultRealm = (ObjectName) connection.getAttribute(securityConfig, "DefaultRealm");
ObjectName[] authProviders = (ObjectName[]) connection.getAttribute(defaultRealm, "AuthenticationProviders");
for(ObjectName providerName : authProviders) {
System.out.println("Auth provider is: " + providerName) ;
if(userEditor == null) {
ModelMBeanInfo info = (ModelMBeanInfo) connection.getMBeanInfo(providerName);
String className = (String) info.getMBeanDescriptor().getFieldValue("interfaceClassName");
System.out.println("className is: " + className) ;
if(className != null) {
String[] mba = (String[]) connection.invoke(mBeanTypeService
, "getSubtypes"
, new Object[] {"weblogic.management.security.authentication.UserEditorMBean"}
, new String[] {"java.lang.String"}
);
for(String mb : mba) {
System.out.println("Model Bean is: " + mb) ;
if(className.equals(mb)) {
System.out.println("Found a macth for the model bean and class name!") ;
userEditor = providerName;
}
}
}
}
}
if(userEditor == null)
throw new RuntimeException("Could not retrieve user editor");
try {
connection.invoke(userEditor
, "createUser"
, new Object[] {username, "password123","User created programmatically."}
, new String[] {"java.lang.String", "java.lang.String","java.lang.String"}
);
System.out.println("User created successfully") ;
}
catch(Exception e){
e.printStackTrace();
}
connector.close();
}
catch(Exception ex) {
ex.printStacktrace();
}
}
You need only weblogic.jar and wljmxclient.jar in classpath. I ran this against JDK 1.6.0_29. I have to add that I ran this on a machine on which WebLogic was installed as well. So the classpath entries were fully qualified path names to the jar files.
One "gotcha" I came across:
While providing the "com.bea:Name=XXXX,Type=XXXX", DONOT give a space between anything - not the colon; not the comma; nothing - I spent sometime debugging this, before it finally hit it.

Categories

Resources