I have been stuck at a point for so long. I am trying to authenticate a user from an LDAP directory using LDAP via JNDI, but the code attached below returns nothing for "results" i.e. the search method (marked with **) returns null. I tried to debug the code, but did not understand what exactly is going on behind the "search" function of "DirContext" class. Can anybody help please?
public class Authenticate {
#SuppressWarnings({ "unchecked", "rawtypes", "unused" })
public static void main(String[] args) throws NamingException {
final String ldapAdServer = "ldap://iauth.tum.de:389/cn=someuser,ou=users,ou=data,ou=prod,ou=iauth,dc=tum,dc=de";
final String ldapUsername = "cn=someuser,ou=users,ou=data,ou=prod,ou=iauth,dc=tum,dc=de";
final String ldapPassword = "somepassword";
Hashtable env = new Hashtable();
env.put(Context.SECURITY_AUTHENTICATION, "none");
if(ldapUsername != null) {
env.put(Context.SECURITY_PRINCIPAL, ldapUsername);
}
if(ldapPassword != null) {
env.put(Context.SECURITY_CREDENTIALS, ldapPassword);
}
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapAdServer);
DirContext ctx = new InitialDirContext(env);
NamingEnumeration<SearchResult> results = null;
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
**results = ctx.search("", "(objectClass=Person)", controls);**
while (results.hasMoreElements())
{
SearchResult searchResult = (SearchResult) results.next();
Attributes attributes = searchResult.getAttributes();
Attribute attr = attributes.get("cn");
}
}
}
Since the search method of "DirContext" (marked in **), returns nothing, I can never enter the while loop. Can anybody tell where exactly I am being wrong.
You appear to be searching an Active Directory instance. If that's the case (and even if it's not the issue may be the same), then the problem is not to do with your search code but with the initial setup. In particular, this line is at fault:
env.put(Context.SECURITY_AUTHENTICATION, "none");
Per the documentation, setting SECURITY_AUTHENTICATION to "none" performs an anonymous bind, meaning that to the LDAP directory your search request appears to be coming from an anonymous user. By default, Active Directory does not return an error when it gets an anonymous bind request, but the anonymous user does not normally have permissions to search the directory. To ensure that your context is created and bound to the appropriate identity, you should change the line in question to:
env.put(Context.SECURITY_AUTHENTICATION, "simple");
Or even not set the SECURITY_AUTHENTICATION parameter at all, since it defaults to "simple".
Change your filter to "(objectClass=*)" and see, otoh, you are using JNDI not Apache LDAP API like you mentioned in the question.
Related
I've been trying to get a simple snippet of code to run in java - authentication as binder to Foxpass (hosted LDAP service) and then trying to authenticate another user to it.
I basically created a binder in my Foxpass settings and created some users as well.
The code is:
Hashtable<String,String> env = new Hashtable <String,String>();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, bindUser);
env.put(Context.SECURITY_CREDENTIALS, bindPassword);
env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
env.put(Context.PROVIDER_URL, myFoxpassUrl);
try {
DirContext ldapContext = new InitialDirContext(env);
NamingEnumeration<SearchResult> results = null;
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setCountLimit(1);
controls.setTimeLimit(5000);
String searchString = "(&(objectCategory=user)(sAMAccountName=username))";
results = ldapContext.search("", searchString, controls);
System.out.println(results.hasMore());
}
catch (Exception e)
{
e.printStackTrace()
}
The binding (authentication with bindUsername and bindPassword works well). However, searching for the users return 0 values. I tried using:
sAMAccountName=myusername
sAMAccountName=myusername#mydomain.com
sAMAccountName=CN=myusername,DC=mydomain,DC=com
Or
username=myusername
...
Or
Email=myusername#mydomain.com
But nothing seems to work. Any help on finding the right format to do the search.
Thank you
It seems to me your searchString once lived in an Active Directory environment. With Foxbase try
String searchString = String.format("(uid=%s)", username);
I followed this tutorial to search in active directory.
Sample code :
class SearchSubtree {
public static void main(String[] args) {
Hashtable<String, Object> env = new Hashtable<String, Object>(11);
env
.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
try {
DirContext ctx = new InitialDirContext(env);
String[] attrIDs = { "sn", "telephonenumber", "golfhandicap", "mail" };
SearchControls ctls = new SearchControls();
ctls.setReturningAttributes(attrIDs);
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(sn=Geisel)(mail=*))";
NamingEnumeration answer = ctx.search("", filter, ctls);
// Print the answer
ctx.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
But NameNotFoundException is thrown at
NamingEnumeration answer = ctx.search("", filter, ctls);
But when I pass, "DC=extldap,DC=com" as first argument, code works fine.
Is there any issue with the tutorial? Can first argument not be empty string? Or is this a limitation with Active Directory?
Generally speaking, for LDAP servers you always need a root context to start your search from. Basically, you are doing the equivalent of trying to search a SQL database without specifying a database or table name.
Some server implementations may allow an empty context (I know iPlanet used to allow it in some cases) but these are exceptions to the rule.
The javadoc for DirContext.search() says:
Searches in the named context or object for entries that satisfy the
given search filter. Performs the search as specified by the search
controls.
See search(Name, String, SearchControls) for details.
Parameters:
name the name of the context or object to search
...
Usually, in Active Directory it is fine to start searching from the domain root, which is always DC=<your>,DC=<domain>.
That is why your second search works.
I'm pretty sure i know the problem, I just don't know how to solve it.
I have a java EE application that do searches in ldap. I initialize the context with getEnv :
(note* : code is a bit simplified for understanding purposes)
InitialDirContext ctx = new InitialDirContext( getEnv( CONFIG_MAP ); //CONFIG_MAP contains the host, mng_dn, mng_pw
public static Hashtable<String, String> getEnv( Map<String, String> configMap ) {
// Hashtable for environmental information
Hashtable<String, String> env = new Hashtable<String, String>();
// Specify which class to use for our JNDI Provider
env.put( Context.INITIAL_CONTEXT_FACTORY, INITCTX );
// Specify the host and port to use for directory service
env.put( Context.PROVIDER_URL, configMap.get( HOST ) );
// Security Information
env.put( Context.SECURITY_AUTHENTICATION, "simple" );
env.put( Context.SECURITY_PRINCIPAL, configMap.get( MGR_DN ) );
env.put( Context.SECURITY_CREDENTIALS, configMap.get( MGR_PW ) );
env.put( "java.naming.ldap.attributes.binary", "objectSID" );
return env;
}
I don't know if this was bad practice but to prevent the initialization from happening before every search I did an Init function that does :
if(Util.ctx == null ){
Util.init()
}
So the problem comes from here. My application will work roughly 30 mins (not sure of the time) and then the searches won't work anymore and I'll get the connection reset error in my console. My guess is that the connection is "closed" and it's not doing the initialization again since ctx is not null. I need help to figure out what to add to my if statement in order to prevent this error from happening. Maybe something like
if(Util.ctx == null || Util.ctx.isClosed() ){
Util.init();
}
I read on InitialDirContext and couldn't find what I need.
Don't try to keep reusing the same Context. Get a new one every time you need one. The server will close idle connections any time it feels like it, and isClosed() won't tell you when it has done so.
You can use the JNDI LDAP connection pooling feature to conserve connections.
What about set no timeout as follows:
// Set up environment for creating initial context
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
// Specify time-out to be infinite time . make it like never expired
env.put("com.sun.jndi.ldap.connect.timeout", "-1");
// Create initial context
DirContext ctx = new InitialDirContext(env);
And you can close it later when needed as follows:
finally {
if (obj instanceof Context) {
Context ctx = (Context) obj;
try {
ctx.close();
}
catch (Exception e) {
}
}
}
Check this out:
https://docs.oracle.com/javase/tutorial/jndi/newstuff/readtimeout.html
i am trying to search user in users directory(ou=users,ou=system), but i am not getting result plz help me out.
following is my code for search users directory
public void search(String uid) {
String searchBase = "ou=users,ou=system";
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, url);
env.put(Context.SECURITY_CREDENTIALS, rootpass);
DirContext ctx = null;
try {
![enter image description here][1] // Create the initial directory context
ctx = new InitialDirContext(env);
// Create the search controls
SearchControls searchCtls = new SearchControls();
// Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
searchCtls.setReturningAttributes(new String[] { "uid", "cn" });
String searchFilter =" (uid="+uid+") ";//"(objectclass=*)"; //" (uid="+uid+") ";
// initialize counter to total the results
int totalResults = 0;
// Search for objects using the filter
NamingEnumeration answer = ctx.search(searchBase, searchFilter,
searchCtls);
while (answer.hasMore()) {
SearchResult sr = (SearchResult) answer.next();
totalResults++;
System.out.println(">>>" + sr.getName());
System.out.println(">>>");
}
} catch (NamingException e) {
e.printStackTrace();
}
}
and here is my directory structure
You might want to look at this sample.
If "nothing is printed but when I search in any other directory it works fine", the possibilities boil down to:
you are failing to connect to the LDAP directory at all
your search base is wrong
your filter is wrong
the record you're searching for does not exist
the credentials you're using to bind don't have permissions to search in that location
Some of those will throw a NamingException, but others (like "record does not exist" or "no permission to search") will simply return no results.
As far as the User folder goes, the answer is in another post LDAP Directory Entry in .Net - not working with OU=Users
This may seem silly and stupid, but the default tree setup in Active Directory is not OU=Users,dc=domain,dc=com but rather CN=Users,dc=domain,dc=com (Note the CN= not the OU= for Users.
I have directory context for LDAP but i need to find out the BASE DN from that
directory context object.
I have following code to get Directory context object,
// Configure our directory context environment.
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://test.mycomp.com:389");
env.put(Context.SECURITY_AUTHENTICATION, "Simple");
env.put(Context.SECURITY_PRINCIPAL,"uid=test.gen,OU=Generics,O=test.mycomp.com");
env.put(Context.SECURITY_CREDENTIALS, "test123");
DirContext dirContext = new InitialDirContext(env);
System.out.println("loaded dirContext");
I have following code to get the Base DN,
I has been returning base DN name but i want to make my filter optimised rather than putting 2 loops to get base DN,
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.OBJECT_SCOPE);
NamingEnumeration results = dirContext.search("",
"(&(objectClass=organization)(objectClass=top))", constraints);
// Fail if no entries found
if (results == null || !results.hasMore()) {
System.out.println("No result found");
return;
}
while(results.hasMoreElements()){
Object res = results.next();
SearchResult serResult = (SearchResult) res;
Attributes atts = serResult.getAttributes();
System.out.println(atts.toString());
Attribute baseAttr = atts.get("namingContexts");
NamingEnumeration ids = baseAttr.getAll();
while(ids.hasMoreElements()){
Object obj = ids.next();
System.out.println(obj.toString());
}
}
Please help me out to optimize my filter.
You don't need the search. Just get the namingContexts attribute from the InitialContext.
Attributes atttrs = context.getAttributes("", new String[]{"namingContexts"});
LDAP-compliant directory servers should provide information about the namingContexts when the root DSE is queried. For more information about the root DSE, see "LDAP: The root DSE". The UnboundID LDAP SDK provides a class to encapsulate the root DSE and a convenience method to retrieve it.