eu.uqasar.util.ldap.LdapManager.java Source code

Java tutorial

Introduction

Here is the source code for eu.uqasar.util.ldap.LdapManager.java

Source

/*
 */
package eu.uqasar.util.ldap;

/*
 * #%L
 * U-QASAR
 * %%
 * Copyright (C) 2012 - 2015 U-QASAR Consortium
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import eu.uqasar.model.settings.ldap.LdapSettings;
import static eu.uqasar.model.settings.ldap.LdapSettings.getDefault;
import eu.uqasar.service.settings.LdapSettingsService;
import eu.uqasar.util.ldap.LdapUser.LdapUserComparator;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import static javax.naming.directory.SearchControls.SUBTREE_SCOPE;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.LdapReferralException;

import lombok.AccessLevel;
import lombok.Getter;
import org.apache.commons.lang.Validate;
import org.jboss.solder.logging.Logger;

/**
 *
 *
 */
@Getter(AccessLevel.PRIVATE)
public class LdapManager implements Serializable {

    private static final Logger logger = Logger.getLogger(LdapManager.class);
    private static final String EMPTY_FILTER = "(objectClass=*)";

    private final LdapSettings settings;
    private transient LdapContext context;

    private static final transient Comparator<LdapUser> userComparator = new LdapUser.LdapUserComparator();
    private static final transient Comparator<LdapGroup> groupComparator = new LdapGroup.LdapGroupComparator();

    private LdapManager(LdapSettings settings) throws NamingException {
        this.settings = settings;
        context = getConnection();
    }

    public static LdapManager getInstance(LdapSettings settings) throws NamingException {
        return new LdapManager(settings);
    }

    public static LdapManager getInstance(LdapSettingsService service) throws NamingException {
        LdapSettings settings = getDefault();
        settings = service.get(settings);
        return new LdapManager(settings);
    }

    public void closeConnection() throws NamingException {
        context.close();
    }

    public boolean authenticateBySAMAccountName(final String userName, final String password)
            throws NamingException {
        LdapUser user = getUserBySAMAccountName(userName);
        if (user == null) {
            return false;
        }
        return getConnection(settings, user.getDN(), password) != null;
    }

    public List<LdapGroup> getGroupsList(int maximum) throws NamingException {
        return filterGroupsList(getLdapEntities(maximum, settings.getGroupFilterBaseDN(), settings.getGroupFilter(),
                LdapGroup.class, groupComparator));
    }

    public List<LdapUser> getUsersList(int maximum) throws NamingException {
        return getLdapEntities(maximum, settings.getUserFilterBaseDN(), settings.getUserFilter(), LdapUser.class,
                userComparator);
    }

    public int getUsersCount(int maximum) throws NamingException {
        return countLdapEntities(maximum, settings.getUserFilterBaseDN(), settings.getUserFilter());
    }

    public List<LdapUser> getUsersFromGroup(int maximum, LdapGroup group) throws NamingException {
        List<LdapUser> users = new ArrayList<>();
        final String mapping = settings.getGroupMemberMapping();
        javax.naming.directory.Attribute members = group.getMappedAttribute(mapping);
        if (members == null) {
            return users;
        }
        NamingEnumeration<?> results = members.getAll();
        while (results.hasMoreElements() && users.size() < maximum) {
            try {
                final String userDN = (String) results.next();
                LdapUser user = getUserByDNAndFilter(userDN, settings.getUserFilter());
                if (user != null) {
                    users.add(user);
                }
            } catch (LdapReferralException ex) {
                logger.warn(ex.getMessage(), ex);
            }
        }
        Collections.sort(users, new LdapUserComparator());
        return users;
    }

    private List<LdapGroup> filterGroupsList(List<LdapGroup> groups) throws NamingException {
        Iterator<LdapGroup> iterator = groups.iterator();
        while (iterator.hasNext()) {
            LdapGroup group = iterator.next();
            if (!groupHasUserMembers(group)) {
                iterator.remove();
            }
        }
        return groups;
    }

    private boolean groupHasUserMembers(LdapGroup group) throws NamingException {
        return !group.getMembers().isEmpty();
    }

    private NamingEnumeration<SearchResult> searchLDAP(final String baseDN, final String preferredFilter)
            throws NamingException {
        final String filter = preferredFilter == null || preferredFilter.isEmpty() ? EMPTY_FILTER : preferredFilter;
        return getContext().search(baseDN, filter, getDefaultSearchControls());
    }

    private SearchControls getDefaultSearchControls() {
        SearchControls controls = new SearchControls();
        controls.setSearchScope(SUBTREE_SCOPE);
        controls.setReturningAttributes(null);
        controls.setReturningObjFlag(true);
        return controls;
    }

    private LdapUser getUserByDN(final String userDN) throws NamingException {
        NamingEnumeration<SearchResult> answer = getContext().search(settings.getUserFilterBaseDN(),
                "(distinguishedName=" + userDN + ")", getDefaultSearchControls());
        while (answer.hasMoreElements()) {
            Attributes attr = answer.next().getAttributes();
            if (hasRequiredUserAttributesFilled(attr, settings)) {
                return new LdapUser(attr, settings);
            }
        }
        return null;
    }

    private LdapUser getUserBySAMAccountName(final String sAMAccountName) throws NamingException {
        NamingEnumeration<SearchResult> answer = getContext().search(settings.getUserFilterBaseDN(),
                "(sAMAccountName=" + sAMAccountName + ")", getDefaultSearchControls());
        while (answer.hasMoreElements()) {
            Attributes attr = answer.next().getAttributes();
            if (hasRequiredUserAttributesFilled(attr, settings)) {
                return new LdapUser(attr, settings);
            }
        }
        return null;
    }

    private LdapUser getUserByDNAndFilter(final String userDN, final String filter) throws NamingException {
        final String dnFilter = "(distinguishedName=" + userDN + ")";
        boolean conjunction = filter.startsWith("(&(") && filter.endsWith("))");
        String endFilter;
        if (conjunction) {
            endFilter = filter.substring(0, filter.length() - 1) + dnFilter + ")";
        } else {
            endFilter = dnFilter;
        }
        NamingEnumeration<SearchResult> answer = getContext().search(settings.getUserFilterBaseDN(), endFilter,
                getDefaultSearchControls());
        while (answer.hasMoreElements()) {
            Attributes attr = answer.next().getAttributes();
            if (hasRequiredUserAttributesFilled(attr, settings)) {
                return new LdapUser(attr, settings);
            }
        }
        return null;
    }

    private boolean hasRequiredUserAttributesFilled(Attributes attrs, LdapSettings settings) {
        return LdapUser.hasValidMailValue(attrs, settings) && LdapUser.hasValidUserNameValue(attrs, settings);
    }

    private int countLdapEntities(int maximum, final String baseDN, final String preferredFilter)
            throws NamingException {
        if (maximum <= 0) {
            return 0;
        }
        int count = 0;
        NamingEnumeration<SearchResult> results = searchLDAP(baseDN, preferredFilter);
        while (results.hasMoreElements() && count < maximum) {
            try {
                results.next();
                count++;
            } catch (LdapReferralException ex) {
                logger.warn(ex.getMessage(), ex);
            }
        }
        return count;
    }

    private <T extends LdapEntity> List<T> getLdapEntities(int maximum, final String baseDN,
            final String preferredFilter, Class<T> clazz, Comparator<T> comparator) throws NamingException {
        if (maximum <= 0) {
            return Collections.emptyList();
        }
        List<T> entities = new ArrayList<>();
        NamingEnumeration<SearchResult> results = searchLDAP(baseDN, preferredFilter);
        while (results.hasMoreElements() && entities.size() < maximum) {
            try {
                SearchResult group = results.next();
                Constructor<T> constructor = clazz.getConstructor(Attributes.class, LdapSettings.class);
                T entity = constructor.newInstance(group.getAttributes(), settings);
                entities.add(entity);
            } catch (LdapReferralException ex) {
                logger.warn(ex.getMessage(), ex);
            } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
                    | IllegalArgumentException | InvocationTargetException ex) {
                logger.error(ex.getMessage(), ex);
            }
        }
        Collections.sort(entities, comparator);
        return entities;
    }

    private LdapContext getConnection(LdapSettings settings) throws NamingException {
        return getConnection(settings, null, null);
    }

    private LdapContext getConnection(LdapSettings settings, final String userName, final String password)
            throws CommunicationException, NamingException {
        Validate.notEmpty(settings.getAuthUserDN());

        // bind by using the specified username/password
        Properties props = new Properties();
        props.put(Context.SECURITY_PRINCIPAL, userName == null ? settings.getAuthUserDN() : userName);
        if (settings.getAuthUserPassword() != null || password != null) {
            props.put(Context.SECURITY_CREDENTIALS, password == null ? settings.getAuthUserPassword() : password);
        }

        // ensures that objectSID attribute values
        // will be returned as a byte[] instead of a String
        props.put("java.naming.ldap.attributes.binary", "objectSID");

        // the following is helpful in debugging errors
        // props.put("com.sun.jndi.ldap.trace.ber", System.err);
        String ldapURL = String.format("ldap://%s:%s", settings.getHost(), settings.getPort());
        props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        props.put(Context.PROVIDER_URL, ldapURL);
        props.put(Context.REFERRAL, "follow");
        try {
            return new InitialLdapContext(props, null);
        } catch (CommunicationException e) {
            logger.warn(String.format("Failed to connect to %s:%s", settings.getHost(), settings.getPort()), e);
            throw e;
        } catch (NamingException e) {
            logger.warn(String.format("Failed to authenticate %s:%s", settings.getHost(), settings.getPort()), e);
            throw e;
        }
    }

    private LdapContext getConnection() throws CommunicationException, NamingException {
        return getConnection(this.settings);
    }
}