LDAP — or lightweight directory access protocol — is a protocol for accessing directory information services where information such as users and groups is stored. Historically, there have been two communication mechanisms to interact with an LDAP server:
- LDAPS (LDAP over SSL) which uses port 636, configured to require SSL. This protocol is old, but is still in use by many organizations.
- LDAP over TLS (StartTLS) using port 389. This protocol is the recommended method for organizations today.
Anypoint Platform provides an LDAP connector that can communicate with servers such as OpenLDAP, Apache Directory, or Microsoft Active Directory. However, this connector supports only LDAP over TLS (StartTLS) so it cannot be used by companies that are still using LDAPS.
To avoid that limitation of the LDAP connector, I have just written a Mule flow that can connect to an LDAPS server by using a custom Java method.
How to configure an LDAPS server
To try the code, you will need an LDAP server listening on port 636 using SSL. Installing and configuring an OpenLDAP server is out of the scope of this article, but you may find a step-by-step guide here. Once you have an LDAP server up and running on port 389 you will need to change its configuration to listen on port 636 by following these steps. To configure SSL you will need a certificate, you can either use a certificate signed by a Certificate Authority (CA) or a self-signed certificate.
To check if your LDAP server is running properly, use LDAP Admin, which is a handy GUI that you can install on your laptop.
In this screenshot, I have configured an LDAP server that contains two users (Bob Jones and Mary Clinton) and that it is listening on port 636 using SSL:
Writing Java code to connect to LDAPS
To configure your LDAP server to use SSL you have used a certificate. In case it is a self-signed certificate, Mule will get an error during the handshaking phase of the SSL communication unless you do one of these two actions:
- Download the certificate and import it on the trust store of the JVM.
- Create a dummy trust manager and a custom SSL Socket Factory.
In this example, we are going to use the second choice. To do that, it is needed to create two Java classes in the default package:
The first Java class is a dummy trust manager; it just contains these three methods:
public class DummyTrustmanager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { // do nothing } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { // do nothing } public X509Certificate[] getAcceptedIssuers() { return new java.security.cert.X509Certificate[0]; } }
The second Java class is a custom SSL Socket Factory; this is an excerpt of the code of that class:
public class MySSLSocketFactory extends SSLSocketFactory { private SSLSocketFactory socketFactory; public MySSLSocketFactory() { try { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, new TrustManager[] { new DummyTrustmanager() }, new SecureRandom()); socketFactory = ctx.getSocketFactory(); } catch (Exception ex) { ex.printStackTrace(System.err); /* handle exception */ } } }
To interact with the LDAP server I have created the following Java static method that it is able to search for one user in the directory and retrieve its phone number:
public class LDAPSRead { public static String getUserInfo(String url, String principal, String credentials, String search, String name) { String[] attrIDs = { "sn", "cn", "telephoneNumber" }; String phone = "111"; SearchControls ctls = new SearchControls(); ctls.setReturningAttributes(attrIDs); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); String filter = "(cn="+name+")"; Hashtable env = new Hashtable(); env.put(DirContext.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(DirContext.PROVIDER_URL, url ); env.put(DirContext.SECURITY_AUTHENTICATION, "simple"); env.put(DirContext.SECURITY_PRINCIPAL, principal); env.put(DirContext.SECURITY_CREDENTIALS, credentials); env.put("java.naming.ldap.factory.socket", "MySSLSocketFactory"); DirContext dirContext = null; try { dirContext = new InitialDirContext(env); NamingEnumeration e = dirContext.search(search, filter,ctls); while (e.hasMore()) { SearchResult entry = (SearchResult) e.next(); System.out.println(entry.getName()); Attributes attrs = entry.getAttributes(); Attribute attr = attrs.get("telephoneNumber"); phone = (String) attr.get(); } } catch (Exception e) { System.err.println(e); } return phone; } }
These are the main sections of the method:
- It accepts as parameters the URL of the LDAP server, the principal user and its password, the branch where the users are stored, and the user name.
- It uses the standard Java package javax.naming.directory to connect with the LDAP server.
- It populates a class InitialDirContext with several properties, including the URL of the server, the principal user, its password, and the custom SSL Socket Factory created on previous steps.
- It searches in the LDAP server the user name and retrieves an attribute with the telephone number.
This method is read only; in case you need to modify the content of the directory, you can easily create a second method that — for example — uses the operation dirContext.modifyAttributes() to change an attribute.
Creating a Mule flow to use the custom Java method
To test the custom Java method, I have created this simple flow that listens on port 8081 and invokes the static Java method using the Java module of Mule:
This is the configuration of the Java Invoke static module. Note, that the parameters are passed using DataWeave:
The properties are stored on a file called settings.properties on the folder src/main/resources:
url=ldaps://ldaps.compute-1.amazonaws.com principal=cn=Manager,dc=company,dc=com credentials=password search=dc=company,dc=com
To test the flow, I have used Postman, passing as a query parameter the name of the user to be searched:
As you can see, the flow takes the value of the query param userinfo and passes it to the custom Java method which in turn, returns the telephone number of that user stored in the LDAPS server.
Key takeaways
If you’re using an LDAPS server and you cannot use the standard LDAP Connector of Mule, you can use this custom Java method to interact with your LDAPS server. If the server has been configured with a self-signed certificate, and you do not want to modify the trust store of the JVM, you can use the dummy trust manager and the custom SSL Socket Factory provided in this article.
Although Mule provides hundreds of connectors, sometimes it is useful to write your own Java method. As you can see in this example, it is easy to execute custom Java code by using the standard Java module of Mule.
If you want to try the code, you can download it from this repository. You can find more information about the Mule 4 LDAP connector here.
Jose Ramon Huerga is a MuleSoft Ambassador, you can find him on LinkedIn here.