net.identio.server.service.authentication.ldap.LdapConnectionFactory.java Source code

Java tutorial

Introduction

Here is the source code for net.identio.server.service.authentication.ldap.LdapConnectionFactory.java

Source

/*
 * This file is part of Ident.io.
 *
 * Ident.io - A flexible authentication server
 * Copyright (c) 2017 Loeiz TANGUY
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package net.identio.server.service.authentication.ldap;

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.InitialLdapContext;
import java.util.Hashtable;

public class LdapConnectionFactory extends BasePooledObjectFactory<InitialLdapContext> {

    private static final Logger LOG = LoggerFactory.getLogger(LdapConnectionFactory.class);

    private LdapAuthMethod ldapAuthMethod;

    private int currentUrlIndex;

    public LdapConnectionFactory(LdapAuthMethod ldapAuthMethod) {
        this.ldapAuthMethod = ldapAuthMethod;

        currentUrlIndex = 0;
    }

    @Override
    public InitialLdapContext create() throws NamingException {

        InitialLdapContext ctx = createContext(ldapAuthMethod, ldapAuthMethod.getProxyUser(),
                ldapAuthMethod.getProxyPassword());

        LOG.debug("Created LDAP connection to: {}", ldapAuthMethod.getName());

        return ctx;
    }

    public boolean authenticate(String name, String dn, String password) {

        InitialLdapContext ctx = null;

        try {
            ctx = createContext(ldapAuthMethod, dn, password);

            return true;
        } catch (NamingException e) {

            return false;
        } finally {
            if (ctx != null) {
                try {
                    ctx.close();
                } catch (NamingException e) {
                    LOG.error("Error when closing connection to LDAP {}", ldapAuthMethod.getName());
                }
            }
        }
    }

    private InitialLdapContext createContext(LdapAuthMethod ldapAuthMethod, String userDn, String password)
            throws NamingException {

        LOG.debug("Begin creation of an LDAP connection to: {}", ldapAuthMethod.getName());

        int currentUrlIndexTs = currentUrlIndex;

        String currentUrl = ldapAuthMethod.getLdapUrl().get(currentUrlIndexTs);

        Hashtable<String, String> env = new Hashtable<>();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, currentUrl);

        if (currentUrl.startsWith("ldaps://")) {

            // Add a custom SSL Socket factory to validate server CA
            env.put("java.naming.ldap.factory.socket",
                    "net.identio.server.service.authentication.ldap.LdapSslSocketFactory");
        }

        if (userDn != null) {
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL, userDn);
            env.put(Context.SECURITY_CREDENTIALS, password);
        }

        InitialLdapContext ctx;

        try {
            ctx = new InitialLdapContext(env, null);
        } catch (CommunicationException e) {

            LOG.error("Error when contacting LDAP server {}", ldapAuthMethod.getLdapUrl().get(currentUrlIndexTs));

            if (ldapAuthMethod.getLdapUrl().size() > 1) {
                int newCurrentUrlIndex = currentUrlIndexTs < ldapAuthMethod.getLdapUrl().size() - 1
                        ? currentUrlIndexTs + 1
                        : 0;

                LOG.error("Switching to LDAP server {}", ldapAuthMethod.getLdapUrl().get(newCurrentUrlIndex));

                currentUrlIndex = newCurrentUrlIndex;

                env.put(Context.PROVIDER_URL, ldapAuthMethod.getLdapUrl().get(newCurrentUrlIndex));

                ctx = new InitialLdapContext(env, null);
            } else {
                throw e;
            }
        }

        return ctx;
    }

    @Override
    public PooledObject<InitialLdapContext> wrap(InitialLdapContext ctx) {
        return new DefaultPooledObject<>(ctx);
    }

    @Override
    public void destroyObject(PooledObject<InitialLdapContext> p) {

        LOG.debug("Begin destruction of an LDAP connection to: {}", ldapAuthMethod.getName());

        try {
            p.getObject().close();

            LOG.debug("Destroyed LDAP connection to: {}", ldapAuthMethod.getName());

        } catch (NamingException e) {
            LOG.error("Error when closing connection to LDAP server {}", ldapAuthMethod.getName());
        }
    }

    @Override
    public boolean validateObject(PooledObject<InitialLdapContext> p) {

        LOG.debug("Validating connection to LDAP directory {}", ldapAuthMethod.getName());

        SearchControls controls = new SearchControls();
        controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        controls.setCountLimit(1);
        controls.setTimeLimit(500);

        try {
            p.getObject().search("", ldapAuthMethod.getPoolConfig().getTestRequestFilter(), controls);
        } catch (NamingException e) {
            LOG.error("Validation of connection to LDAP directory {} failed", ldapAuthMethod.getName());
            return false;
        }

        return true;
    }

}