LDAP

JVM since1.1.0 Native since3.0.0

Perform searches on LDAP servers.

What’s inside

Please refer to the above link for usage and configuration details.

Maven coordinates

Or add the coordinates to your existing project:

<dependency>
    <groupId>org.apache.camel.quarkus</groupId>
    <artifactId>camel-quarkus-ldap</artifactId>
</dependency>

Check the User guide for more information about writing Camel Quarkus applications.

Usage

DirContext

The URI, ldap:ldapserver, references a bean with the ID ldapserver. A CDI producer method may be used to instantiate a DirContext object as follows:

public class LdapServerProducer {

    @Produces
    @Dependent
    @Named("ldapserver")
    public DirContext createLdapServer() throws Exception {
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://localhost:10389");
        env.put(Context.SECURITY_AUTHENTICATION, "none");

        return new InitialDirContext(env);
    }
}

The preceding example creates a regular Sun based LDAP DirContext that connects anonymously to a locally hosted LDAP server. The use of the @Named annotation binds the DirContext into the Camel registry automatically.

Configuring SSL

When connecting to an LDAP server over SSL/TLS, you may encounter situations where the default trust manager used by the JVM is unable to verify the certificate. This can happen, for example, when the server uses a self-signed certificate or when the certificate is issued by a non-trusted CA. In such cases, you may need to provide a custom trust manager implementation that can verify the server’s certificate.

The following code shows an implementation of a custom socket factory that can be used to create SSL/TLS sockets. The class name of the custom SSL socket factory is then specified in the java.naming.ldap.factory.socket property of the environment hashtable used to create the LDAP context.

public class CustomSSLSocketFactory extends SSLSocketFactory {

    private SSLSocketFactory delegate;

    public CustomSSLSocketFactory() throws Exception {
        String trustStoreFilename = ConfigProvider.getConfig().getValue("ldap.trustStore", String.class);
        String trustStorePassword = ConfigProvider.getConfig().getValue("ldap.trustStorePassword", String.class);
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        try (InputStream in = new FileInputStream(trustStoreFilename)) {
            keyStore.load(in, trustStorePassword.toCharArray());
        }
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(keyStore);
        SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(null, tmf.getTrustManagers(), null);
        delegate = ctx.getSocketFactory();

    }

    public static SocketFactory getDefault() {
        try {
            return new CustomSSLSocketFactory();
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return delegate.createSocket(s, host, port, autoClose);
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return delegate.createSocket(host, port);
    }

    @Override
    public Socket createSocket(InetAddress address, int port) throws IOException {
        return delegate.createSocket(address, port);
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localAddress, int localPort)
            throws IOException, UnknownHostException {
        return delegate.createSocket(host, port, localAddress, localPort);
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
            throws IOException {
        return delegate.createSocket(address, port, localAddress, localPort);
    }
}

The constructor uses the ConfigProvider to read the ldap.trustStore and ldap.trustStorePassword configuration properties, which could be specified in the application.properties file as follows:

ldap.trustStore=/path/to/truststore.jks
ldap.trustStorePassword=secret

Finally, alter the LdapServerProducer.createLdapServer() method so that the PROVIDER_URL entry uses the ldaps protocol instead of ldap, and add the CustomSSLSocketFactory entry:

public class LdapServerProducer {

    @Produces
    @Dependent
    @Named("ldapserver")
    public DirContext createLdapServer() throws Exception {
        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldaps://" + InetAddress.getLocalHost().getCanonicalHostName() + ":10636");
        env.put(Context.SECURITY_AUTHENTICATION, "none");
        env.put("java.naming.ldap.factory.socket", CustomSSLSocketFactory.class.getName());

        return new InitialDirContext(env);
    }
}

Using SSL in Native Mode

When using a custom SSLSocketFactory in native mode, you need to register the class for reflection otherwise the class will not be made available on the classpath. Add the @RegisterForReflection annotation above the class definition, as follows:

@RegisterForReflection
public class CustomSSLSocketFactory extends SSLSocketFactory {
    // The class definition is the same as above.
}