info.jtrac.acegi.JtracLdapAuthenticationProvider.java Source code

Java tutorial

Introduction

Here is the source code for info.jtrac.acegi.JtracLdapAuthenticationProvider.java

Source

/*
 * Copyright 2002-2005 the original author or authors.
 *
 * 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.
 */

package info.jtrac.acegi;

import info.jtrac.Jtrac;
import java.util.ArrayList;
import java.util.HashMap;

import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.providers.AuthenticationProvider;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.userdetails.UserDetails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

/**
 * custom simple LDAP integration approach, where only authentication
 * is expected from LDAP, Space allocations have to be performed within JTrac only
 *
 * we are not using Acegi LDAP support because
 * a) it does not appear to support binding _as_ the user signing in 
 *    as opposed to a "hardcoded" user and password which is not very nice
 * b) easier to configure, customize and extend in the future
 */
public class JtracLdapAuthenticationProvider implements AuthenticationProvider, InitializingBean {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private Jtrac jtrac;
    private String ldapUrl;
    private String activeDirectoryDomain;
    private String searchBase;
    private String searchKey;
    private String displayNameKey = "cn";
    private String mailKey = "mail";
    private String[] otherReturningAttributes;
    private String[] returningAttributes;

    // please refer http://forum.java.sun.com/thread.jspa?threadID=726601&tstart=0
    // for the Active Directory LDAP Fast Bind Control approach used here
    private Control control = new Control() {
        public byte[] getEncodedValue() {
            return null;
        }

        public String getID() {
            return "1.2.840.113556.1.4.1781";
        }

        public boolean isCritical() {
            return true;
        }
    };

    public void setJtrac(Jtrac jtrac) {
        this.jtrac = jtrac;
    }

    public void setLdapUrl(String ldapUrl) {
        this.ldapUrl = ldapUrl;
    }

    public void setActiveDirectoryDomain(String activeDirectoryDomain) {
        this.activeDirectoryDomain = activeDirectoryDomain;
    }

    public void setSearchBase(String searchBase) {
        this.searchBase = searchBase;
    }

    public void setSearchKey(String searchKey) {
        this.searchKey = searchKey;
    }

    public void setDisplayNameKey(String displayNameKey) {
        this.displayNameKey = displayNameKey;
    }

    public void setMailKey(String mailKey) {
        this.mailKey = mailKey;
    }

    public void setOtherReturningAttributes(String[] otherReturningAttributes) {
        this.otherReturningAttributes = otherReturningAttributes;
    }

    public boolean supports(Class clazz) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(clazz);
    }

    public Authentication authenticate(Authentication authentication) {
        if (!supports(authentication.getClass())) {
            return null;
        }
        logger.debug("attempting authentication via LDAP");
        Map<String, String> attributes = null;
        try {
            attributes = bind(authentication.getName(), authentication.getCredentials().toString());
        } catch (Exception e) {
            logger.debug("bind failed: " + e);
            logger.debug("returning null from ldap authentication provider");
            return null;
        }
        logger.debug("user details retrieved from LDAP, now checking local database");
        UserDetails userDetails = null;
        try {
            userDetails = jtrac.loadUserByUsername(authentication.getName());
        } catch (AuthenticationException ae) { // catch just to log, then re-throw as-is
            logger.debug("ldap user not allocated to any Spaces within JTrac");
            throw ae;
        }
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    /**
     * displayName and mail are returned always, the map allows us to support
     * getting arbitrary properties in the future, hopefully
     */
    public Map<String, String> bind(String loginName, String password) throws Exception {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, ldapUrl);
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        LdapContext ctx = null;
        if (activeDirectoryDomain != null) { // we are using Active Directory            
            Control[] controls = new Control[] { control };
            ctx = new InitialLdapContext(env, controls);
            logger.debug("Active Directory LDAP context initialized");
            ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, activeDirectoryDomain + "\\" + loginName);
            ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
            // javax.naming.AuthenticationException
            ctx.reconnect(controls);
            logger.debug("Active Directory LDAP bind successful");
        } else { // standard LDAP            
            env.put(Context.SECURITY_PRINCIPAL, searchKey + "=" + loginName + "," + searchBase);
            env.put(Context.SECURITY_CREDENTIALS, password);
            ctx = new InitialLdapContext(env, null);
            logger.debug("Standard LDAP bind successful");
        }
        SearchControls sc = new SearchControls();
        sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
        sc.setReturningAttributes(returningAttributes);
        NamingEnumeration results = ctx.search(searchBase, searchKey + "=" + loginName, sc);
        while (results.hasMoreElements()) {
            SearchResult sr = (SearchResult) results.next();
            Attributes attrs = sr.getAttributes();
            logger.debug("attributes: " + attrs);
            Map<String, String> map = new HashMap<String, String>(returningAttributes.length);
            for (String key : returningAttributes) {
                Attribute attr = attrs.get(key);
                if (attr != null) {
                    map.put(key, (String) attr.get());
                }
            }
            return map; // there should be only one anyway            
        }
        // if we reached here, there was no search result
        throw new Exception("no results returned from ldap");
    }

    // one-time init routine normally called by Spring as InitializingBean
    // but when we use a custom FactoryBean, we have to call this manually
    public void afterPropertiesSet() {
        if (otherReturningAttributes != null) {
            List<String> keys = new ArrayList<String>();
            keys.add(mailKey);
            keys.add(displayNameKey);
            for (String s : otherReturningAttributes) {
                keys.add(s);
            }
            returningAttributes = keys.toArray(new String[keys.size()]);
        } else {
            returningAttributes = new String[] { mailKey, displayNameKey };
        }
        if (searchKey == null) {
            if (activeDirectoryDomain != null && activeDirectoryDomain.trim().length() > 0) {
                searchKey = "sAMAccountName";
            } else {
                activeDirectoryDomain = null;
                searchKey = "uid";
            }
        }
        logger.info("ldap authenthication provider initialized searchKey = '" + searchKey + "'" + ", searchBase = '"
                + searchBase + "', activeDirectoryDomain = '" + activeDirectoryDomain + "'" + ", ldapUrl = '"
                + ldapUrl + "'");
    }

}