announcement - icon

It’s just plain hard to get true, real-time visibility into a running auth flow.

Parts of the process can be completely hidden from us; if the complete authorization process requires a redirect from a remote OAuth production server, then every debugging effort must go through the production server.

It’s practically unfeasible to debug this locally. There’s no way to reproduce the exact state and no way to inspect what is actually happening under the hood. Not ideal.

Knowing these types of challenges, we built Lightrun - a real-time production debugging tool - to allow you to understand complicated flows with code-level information. Add logs, take snapshots (virtual breakpoints), and instrument metrics without a remote debugger, without stopping the running service, and, most importantly - in real-time and without side effects.

Learn more with this 5-minute tutorial focused on debugging these kinds of scenarios using Lightrun:

>> Debugging Authentication and Authorization Using Lightrun

1. Overview

In this tutorial, we're going to create a CLI application to test connections to any LDAP Authentication server. We won't use LDAP to secure our application, since this can be done better using Spring Security LDAP, for example.

Having a tool to quickly check the validity of LDAP connections is useful even before developing applications that use them. It's also useful when developing some kind of integration between applications, especially in the setup phase. And we'll do it using core Java classes. So no additional dependencies are required.

2. LDAP Java Client

Let's start by creating our only class, LdapConnectionTool. We'll start with the main method. To keep things simple, all our logic will go here:

public class LdapConnectionTool {
    public static void main(String[] args) {
        // ...
    }
}

First, we'll pass our parameters as system properties. We'll be using default values for the factory (LdapCtxFactory) and authType (simple) variables. LdapCtxFactory is the core Java class responsible for the whole process of connecting to a server and populating user attributes. And a simple authentication type means our password will be sent as clear text. Similarly, we'll default our query variable to the user, so we can specify either one or both. We'll see usage details later on:

String factory = System.getProperty("factory", "com.sun.jndi.ldap.LdapCtxFactory");
String authType = System.getProperty("authType", "simple");
String url = System.getProperty("url");
String user = System.getProperty("user");
String password = System.getProperty("password");
String query = System.getProperty("query", user);

Next, we're going to create our environment map, which holds all properties necessary for a connection using InitialDirContext:

Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, factory);
env.put(Context.SECURITY_AUTHENTICATION, authType);
env.put(Context.PROVIDER_URL, url);

We don't want to require a user and password because some servers allow anonymous access:

if (user != null) {
    env.put(Context.SECURITY_PRINCIPAL, user);
    env.put(Context.SECURITY_CREDENTIALS, password);
}

When testing connections, it's common that we pass in an incorrect URL or that the server is simply unresponsive. Since the default client behavior blocks indefinitely until a response is received, we'll define timeout parameters. The wait time is defined in milliseconds:

env.put("com.sun.jndi.ldap.read.timeout", "5000");
env.put("com.sun.jndi.ldap.connect.timeout", "5000");

After that, we try to establish a connection with a new instance of InitialDirContext, along with basic exception handling. This is essential since we'll use it to diagnose common problems. Likewise, since we're developing a CLI application, we print our messages to the standard output:

DirContext context = null;
try {
    context = new InitialDirContext(env);
    System.out.println("success");
    // ...
} catch (NamingException e) {
    System.out.println(e.getMessage());
} finally {
    context.close();
}

Finally, we use our context variable to query for all attributes resulting from our optional query:

if (query != null) {
    Attributes attributes = context.getAttributes(query);
    NamingEnumeration<? extends Attribute> all = attributes.getAll();
    while (all.hasMoreElements()) {
        Attribute next = all.next();

        String key = next.getID();
        Object value = next.get();

        System.out.println(key + "=" + value);
    }
}

3. Common Mistakes

In this section, we'll go over some common mistakes and error messages encountered when trying to connect to a server:

  • Wrong Base DN: We'll get “error code 49 – Invalid Credentials” if we don't set the Base DN properly. Since every server has its own structure, we should always check this first, as this message can be misleading.
  • No anonymous connections: We'll get the error “ERR_229 Cannot authenticate user” if we don't configure our server to allow anonymous access.

4. Usage

Now that we're all set up, we can use our application. Firstly, let's build it as a jar, rename it to ldap-connection-tool.jar, then try one of the following examples. Note that these values are completely dependent on our server configuration.

Connecting with a user and password:

java -cp ldap-connection-tool.jar \
-Durl=ldap://localhost:389 \
-Duser=uid=gauss,dc=baeldung,dc=com \
-Dpassword=password \
com.baeldung.jndi.ldap.connectionTool.LdapConnectionTool

Specifying only the server URL for a quick connection test:

java -cp ldap-connection-tool.jar \
-Durl=ldap://localhost:389 \
com.baeldung.jndi.ldap.connectionTool.LdapConnectionTool

Also, specifying a query along with a user and password, we can connect with a specific user but query for another. This is useful if we need to connect as an administrator, for example, before performing a query. Similarly, if we connect with a user with enough privileges, we can see protected attributes, such as passwords.

Finally, passing system properties as input is fine when dealing with simple parameters. But, there are more elegant ways of developing CLI applications, like Spring Shell. We should use something like that for anything more complex.

5. Conclusion

In this article, we created a CLI application that can connect to an LDAP server and run connection tests. Also, there are more application usage examples in the unit tests. And as always, the source code is available over on GitHub.

Security bottom

I just announced the new Learn Spring Security course, including the full material focused on the new OAuth2 stack in Spring Security 5:

>> CHECK OUT THE COURSE
Security footer banner
Comments are closed on this article!