Package org.libreplan.web.users.services

Source Code of org.libreplan.web.users.services.LDAPCustomAuthenticationProvider

/*
* This file is part of LibrePlan
*
* Copyright (C) 2011 ComtecSF S.L.
*
* 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 org.libreplan.web.users.services;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.libreplan.business.common.IAdHocTransactionService;
import org.libreplan.business.common.IOnTransaction;
import org.libreplan.business.common.daos.IConfigurationDAO;
import org.libreplan.business.common.entities.ConfigurationRolesLDAP;
import org.libreplan.business.common.entities.LDAPConfiguration;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.users.daos.IUserDAO;
import org.libreplan.business.users.entities.User;
import org.libreplan.business.users.entities.UserRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.transaction.annotation.Transactional;

/**
* An extending from AbstractUserDetailsAuthenticationProvider class which is
* used to implement the authentication against LDAP.
*
* This provider implements the process explained in <https
* ://wiki.libreplan.org/twiki/bin/view/LibrePlan/AnA04S06LdapAuthentication>
*
* At this time it authenticates user against LDAP and then searches it in BD to
* use the BD user in application.
*
* @author Ignacio Diaz Teijido <ignacio.diaz@comtecsf.es>
* @author Cristina Alvarino Perez <cristina.alvarino@comtecsf.es>
*
*/
public class LDAPCustomAuthenticationProvider extends
        AbstractUserDetailsAuthenticationProvider implements
        AuthenticationProvider {

    @Autowired
    private IAdHocTransactionService transactionService;

    @Autowired
    private IConfigurationDAO configurationDAO;

    @Autowired
    private IUserDAO userDAO;

    private LDAPConfiguration configuration;

    // Template to search in LDAP
    private LdapTemplate ldapTemplate;

    private UserDetailsService userDetailsService;

    private DBPasswordEncoderService passwordEncoderService;

    private static final String COLON = ":";

    private static final String USER_ID_SUBSTITUTION = "[USER_ID]";

    private static final Log LOG = LogFactory
            .getLog(LDAPCustomAuthenticationProvider.class);

    /**
     * LDAP role matching could be configured using an asterix (*) to specify
     * all users or groups
     */
    private static final String WILDCHAR_ALL = "*";

    @Override
    protected void additionalAuthenticationChecks(UserDetails arg0,
            UsernamePasswordAuthenticationToken arg1)
            throws AuthenticationException {
        // No needed at this time
    }

    @Transactional(readOnly = true)
    @Override
    public UserDetails retrieveUser(String username,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {

        String clearPassword = authentication.getCredentials().toString();

        if (StringUtils.isBlank(username) || StringUtils.isBlank(clearPassword)) {
            throw new BadCredentialsException(
                    "Username and password can not be empty");
        }

        String encodedPassword = passwordEncoderService.encodePassword(
                clearPassword, username);
        User user = getUserFromDB(username);

        // If user != null then exists in LibrePlan
        if (null != user && user.isLibrePlanUser()) {
            // is a LibrePlan user, then we must authenticate against DB
            return authenticateInDatabase(username, user, encodedPassword);
        }

        // If it's a LDAP or null user, then we must authenticate against LDAP
        // Load LDAPConfiguration properties
        configuration = loadLDAPConfiguration();

        if (configuration.getLdapAuthEnabled()) {
            // Sets the new context to ldapTemplate
            ldapTemplate.setContextSource(loadLDAPContext());

            try {
                // Test authentication for user against LDAP
                if (authenticateAgainstLDAP(username, clearPassword)) {
                    // Authentication against LDAP was ok
                    if (null == user) {
                        // User does not exist in LibrePlan must be imported
                        user = createLDAPUserWithRoles(username, encodedPassword);
                    } else {
                        // Update password
                        if (configuration.isLdapSavePasswordsDB()) {
                            user.setPassword(encodedPassword);
                        }
                        // Update roles from LDAP
                        setRoles(user);
                    }
                    saveUserOnTransaction(user);
                    return loadUserDetails(username);
                } else {
                    throw new BadCredentialsException("User is not in LDAP.");
                }
            } catch (Exception e) {
                // This exception captures when LDAP authentication is not
                // possible
                LOG.info(
                        "LDAP not reachable. Trying to authenticate against database.",
                        e);
            }
        }

        // LDAP is not enabled we must check if the LDAP user is in DB
        return authenticateInDatabase(username, user, encodedPassword);
    }

    private UserDetails loadUserDetails(String username) {
        return getUserDetailsService().loadUserByUsername(username);
    }

    private void setRoles(User user) {
        if (configuration.getLdapSaveRolesDB()) {
            user.clearRoles();
            List<String> roles = getMatchedRoles(configuration, ldapTemplate,
                    user.getLoginName());
            for (String role : roles) {
                user.addRole(UserRole.valueOf(UserRole.class, role));
            }
        }
    }

    private User createLDAPUserWithRoles(String username, String encodedPassword) {
        User user = User.create();
        user.setLoginName(username);
        // we must check if it is needed to save LDAP
        // passwords in DB
        if (!configuration.isLdapSavePasswordsDB()) {
            encodedPassword = null;
        }
        user.setPassword(encodedPassword);
        user.setLibrePlanUser(false);
        user.setDisabled(false);
        setRoles(user);
        return user;
    }

    private LDAPConfiguration loadLDAPConfiguration() {
        return transactionService
                .runOnReadOnlyTransaction(new IOnTransaction<LDAPConfiguration>() {

                    @Override
                    public LDAPConfiguration execute() {
                        return configurationDAO.getConfiguration()
                                .getLdapConfiguration();
                    }
                });
    }

    private User getUserFromDB(String username) {
        final String usernameInserted = username;
        return transactionService
                .runOnReadOnlyTransaction(new IOnTransaction<User>() {

                    @Override
                    public User execute() {
                        try {
                            return userDAO.findByLoginName(usernameInserted);
                        } catch (InstanceNotFoundException e) {
                            LOG.info("User " + usernameInserted
                                    + " not found in database.");
                            return null;
                        }
                    }
                });

    }

    private LDAPCustomContextSource loadLDAPContext() {
        // Establishes the context for LDAP connection.
        LDAPCustomContextSource context = (LDAPCustomContextSource) ldapTemplate
                .getContextSource();
        context.setUrl(configuration.getLdapHost() + COLON
                + configuration.getLdapPort());
        context.setBase(configuration.getLdapBase());
        context.setUserDn(configuration.getLdapUserDn());
        context.setPassword(configuration.getLdapPassword());
        try {
            context.afterPropertiesSet();
        } catch (Exception e) {
            // This exception will be never reached if the LDAP
            // properties are well-formed.
            LOG.error("There is a problem in LDAP connection: ", e);
        }
        return context;
    }

    private boolean authenticateAgainstLDAP(String username,
            String clearPassword) {
        return ldapTemplate.authenticate(DistinguishedName.EMPTY_PATH,
                new EqualsFilter(configuration.getLdapUserId(), username)
                        .toString(), clearPassword);
    }

    private void saveUserOnTransaction(User user) {
        final User userLibrePlan = user;
        transactionService.runOnTransaction(new IOnTransaction<Void>() {
            @Override
            public Void execute() {
                userDAO.save(userLibrePlan);
                return null;
            }
        });
    }

    private UserDetails authenticateInDatabase(String username, User user,
            String encodedPassword) {
        if (null != user && null != user.getPassword()
                && encodedPassword.equals(user.getPassword())) {
            return loadUserDetails(username);
        } else {
            throw new BadCredentialsException(
                    "Credentials are not the same as in database.");
        }
    }

    @SuppressWarnings("unchecked")
    private List<String> getRolesUsingNodeStrategy(
            Set<ConfigurationRolesLDAP> rolesLdap, String queryRoles,
            final LDAPConfiguration configuration) {

        String roleProperty = configuration.getLdapRoleProperty();

        List<String> rolesReturn = new ArrayList<String>();
        for (ConfigurationRolesLDAP roleLDAP : rolesLdap) {
            if (roleLDAP.getRoleLdap().equals(WILDCHAR_ALL)) {
                rolesReturn.add(roleLDAP.getRoleLibreplan());
                continue;
            }

            // We must make a search for each role-matching in nodes
            List<Attribute> resultsSearch = new ArrayList<Attribute>();
            resultsSearch.addAll(ldapTemplate.search(
                    DistinguishedName.EMPTY_PATH, new EqualsFilter(
                            roleProperty, roleLDAP.getRoleLdap()).toString(),
                    new AttributesMapper() {
                        @Override
                        public Object mapFromAttributes(Attributes attributes)
                                throws NamingException {
                            return attributes.get(configuration.getLdapUserId());
                        }
                    }));
            for (Attribute atrib : resultsSearch) {
                if (atrib.contains(queryRoles)) {
                    rolesReturn.add(roleLDAP.getRoleLibreplan());
                }
            }
        }
        return rolesReturn;
    }

    private List<String> getRolesUsingBranchStrategy(
            Set<ConfigurationRolesLDAP> rolesLdap, String queryRoles,
            LDAPConfiguration configuration) {

        String roleProperty = configuration.getLdapRoleProperty();
        String groupsPath = configuration.getLdapGroupPath();

        List<String> rolesReturn = new ArrayList<String>();
        for (ConfigurationRolesLDAP roleLdap : rolesLdap) {
            if (roleLdap.getRoleLdap().equals(WILDCHAR_ALL)) {
                rolesReturn.add(roleLdap.getRoleLibreplan());
                continue;
            }

            // We must make a search for each role matching
            DirContextAdapter adapter = null;
            try {
                adapter = (DirContextAdapter) ldapTemplate
                        .lookup(roleLdap.getRoleLdap() + "," + groupsPath);
            } catch (org.springframework.ldap.NamingException ne) {
                LOG.error(ne.getMessage());
            }
            if (adapter != null && adapter.attributeExists(roleProperty)) {
                Attributes atrs = adapter.getAttributes();

                if (atrs.get(roleProperty).contains(queryRoles)) {
                    rolesReturn.add(roleLdap.getRoleLibreplan());
                }
            }
        }
        return rolesReturn;
    }

    private List<String> getMatchedRoles(LDAPConfiguration configuration,
            LdapTemplate ldapTemplate, String username) {

        String queryRoles = configuration.getLdapSearchQuery().replace(
                USER_ID_SUBSTITUTION, username);

        Set<ConfigurationRolesLDAP> rolesLdap = configuration
                .getConfigurationRolesLdap();

        try {

            if (!configuration.getLdapGroupStrategy()) {
                // The LDAP has a node strategy for groups,
                // we must check the roleProperty in user node.
                return getRolesUsingNodeStrategy(rolesLdap, queryRoles,
                        configuration);
            } else {
                // The LDAP has a branch strategy for groups
                // we must check if the user is in one of the groups.
                return getRolesUsingBranchStrategy(rolesLdap, queryRoles,
                        configuration);
            }
        } catch (Exception e) {
            LOG.error(
                    "Configuration of LDAP role-matching is wrong. Please check it.",
                    e);
            return Collections.emptyList();
        }
    }

    public DBPasswordEncoderService getPasswordEncoderService() {
        return passwordEncoderService;
    }

    public void setPasswordEncoderService(
            DBPasswordEncoderService passwordEncoderService) {
        this.passwordEncoderService = passwordEncoderService;
    }

    // Getters and setters
    public LdapTemplate getLdapTemplate() {
        return ldapTemplate;
    }

    public void setLdapTemplate(LdapTemplate ldapTemplate) {
        this.ldapTemplate = ldapTemplate;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

}
TOP

Related Classes of org.libreplan.web.users.services.LDAPCustomAuthenticationProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.