org.infoscoop.account.ldap.LDAPAccountManager.java Source code

Java tutorial

Introduction

Here is the source code for org.infoscoop.account.ldap.LDAPAccountManager.java

Source

/* infoScoop OpenSource
 * Copyright (C) 2010 Beacon IT Inc.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.
 * 
 * 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0-standalone.html>.
 */

package org.infoscoop.account.ldap;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.security.auth.Subject;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.infoscoop.account.AuthenticationException;
import org.infoscoop.account.IAccount;
import org.infoscoop.account.IAccountManager;
import org.infoscoop.account.IGroup;
import org.infoscoop.account.PrincipalDef;
import org.infoscoop.acl.ISPrincipal;

/**
 * @author hr-endoh
 *
 * TODO:getNameInNamespace isn't possible if version of Java isn't 1.5 or more.
 * As for present, the way of making dn is slight.
 */
public class LDAPAccountManager implements IAccountManager {
    private static Log log = LogFactory.getLog(LDAPAccountManager.class);
    private static final String LDAP_GROUP_PRINCIPAL = "LDAPGroupPrincipal";

    private static String USER_SEARCH_BASE_KEY = "userSearchBase";
    private static String GROUP_SEARCH_BASE_KEY = "groupSearchBase";
    private static Collection principalDefs = new ArrayList();
    static {
        principalDefs.add(new PrincipalDef(LDAP_GROUP_PRINCIPAL, "LDAP Group"));
    }

    private String connectionURL;
    private String connectionName;
    private String connectionPassword;
    private String userBase = "ou=Users,dc=example,dc=com";
    private String groupBase = "cn=groups,dc=example,dc=com";

    private Map<String, String> propAttrMap = new HashMap<String, String>();
    private Map<String, String> propBaseMap = new HashMap<String, String>();

    /**
     * Account atrribute in User entry;
     */
    private String accountAttr = "uid";

    /**
     * Display name atrribute in User entry.
     */
    private String userNameAttr = null;

    /**
     * Group Name in User entry.
     */
    private String userGroupNameAttr = "departmentnumber";

    /**
     * Reference of group in User entry
     */
    private String userGroupAttr = "memberOf";

    /**
     * Display name attribute in Group entry.
     */
    private String groupNameAttr;//"departmentnumber";

    public LDAPAccountManager(String connectionURL, String userSearchBase, String groupSearchBase)
            throws NamingException {
        this(connectionURL, null, null, userSearchBase, groupSearchBase);
    }

    public LDAPAccountManager(String connectionURL, String connectionName, String connectionPassword,
            String userSearchBase, String groupSearchBase) throws NamingException {
        this.connectionURL = connectionURL;
        this.connectionName = connectionName;
        this.connectionPassword = connectionPassword;
        this.userBase = userSearchBase;
        this.groupBase = groupSearchBase;
    }

    private DirContext initContext() throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

        env.put(Context.PROVIDER_URL, this.connectionURL);
        env.put("java.naming.ldap.version", "3");
        if (this.connectionName != null) {
            env.put(Context.SECURITY_PRINCIPAL, this.connectionName);
            env.put(Context.SECURITY_CREDENTIALS, this.connectionPassword);
        }
        return new InitialDirContext(env);
    }

    public void setUserSearchAttr(Map propAttrMap) {
        this.propAttrMap.putAll(propAttrMap);

        for (Iterator it = propAttrMap.keySet().iterator(); it.hasNext();) {
            String propName = (String) it.next();
            this.propBaseMap.put(propName, USER_SEARCH_BASE_KEY);
        }

    }

    public void setGroupSearchAttr(Map propAttrMap) {
        this.propAttrMap.putAll(propAttrMap);

        for (Iterator it = propAttrMap.keySet().iterator(); it.hasNext();) {
            String propName = (String) it.next();
            this.propBaseMap.put(propName, GROUP_SEARCH_BASE_KEY);
        }
    }

    public void setUserNameAttr(String userNameAttr) {
        this.userNameAttr = userNameAttr;
    }

    public void setUserGroupNameAttr(String userGroupNameAttr) {
        this.userGroupNameAttr = userGroupNameAttr;
    }

    public void setGroupNameAttr(String groupNameAttr) {
        this.groupNameAttr = groupNameAttr;
    }

    /**
     * Return the filter of each SearchBase.
     * @param searchConditionMap
     * @return
     */
    private Map getConditionForSearchBase(Map searchConditionMap) {
        Map conditionForBase = new HashMap();
        for (Iterator it = searchConditionMap.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry) it.next();
            String propName = (String) entry.getKey();
            String value = (String) entry.getValue();
            if (value == null || "".equals(value))
                continue;
            String attrName = (String) this.propAttrMap.get(propName);
            String searchBase = (String) this.propBaseMap.get(propName);

            Map filterMap = (Map) conditionForBase.get(searchBase);
            if (filterMap == null)
                filterMap = new HashMap();
            filterMap.put(attrName, value);
            conditionForBase.put(searchBase, filterMap);
        }
        return conditionForBase;
    }

    public IAccount getUser(String uid) throws NamingException {

        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        NamingEnumeration searchResultEnum;
        Map filters = new HashMap();

        String uidAttrName = "uid";
        if (this.propAttrMap.containsKey("user_id")) {
            try {
                uidAttrName = (String) this.propAttrMap.get("user_id");
            } catch (Exception ex) {
                //ignore
            }
        }
        if (uid != null && !"".equals(uid))
            filters.put(uidAttrName, uid);

        DirContext context = null;
        try {
            context = this.initContext();
            searchResultEnum = context.search(userBase, buildFilterByUid(filters), searchControls);
            //roop of retrieval result

            while (searchResultEnum.hasMore()) {
                SearchResult searchResult = (SearchResult) searchResultEnum.next();

                String dn = searchResult.getName() + "," + userBase;
                LDAPAccount user = createLDAPUser(dn, searchResult.getAttributes());
                setGroup(context, user);

                return user;
            }

            return null;
        } finally {
            if (context != null)
                context.close();
        }
    }

    public List searchUser(Map searchConditionMap) throws Exception {
        Map confitionForBase = getConditionForSearchBase(searchConditionMap);

        Collection users = new TreeSet(new Comparator() {

            public int compare(Object o1, Object o2) {
                try {
                    LDAPAccount user1 = (LDAPAccount) o1;
                    LDAPAccount user2 = (LDAPAccount) o2;

                    return user1.getUid().compareTo(user2.getUid());
                } catch (Exception e) {
                    log.error("", e);
                    return 0;
                }
            }

        });

        DirContext context = null;
        try {
            context = this.initContext();

            Map groupFilterMap = (Map) confitionForBase.get(GROUP_SEARCH_BASE_KEY);
            Collection groupMembers = null;
            if (groupFilterMap != null) {
                groupMembers = searchGroupMember(context, groupFilterMap);
            }
            Map userFilterMap = (Map) confitionForBase.get(USER_SEARCH_BASE_KEY);

            if (userFilterMap != null) {
                users = searchFromUsers(context, userFilterMap);

                if (groupMembers != null) {
                    users.retainAll(groupMembers);
                }
            } else if (groupMembers != null) {
                users.addAll(groupMembers);
            }

            List result = new ArrayList();
            for (Iterator it = users.iterator(); it.hasNext();) {
                LDAPAccount user = (LDAPAccount) it.next();
                if (user.getGroupName() == null)
                    setGroup(context, user);
                result.add(user);
            }

            return result;
        } finally {
            context.close();
        }

    }

    public void login(String userid, String password) throws AuthenticationException {
        try {
            LDAPAccount user = (LDAPAccount) getUser(userid);
            if (user == null) {
                throw new AuthenticationException(userid + " is not found.");
            }
            Hashtable env = new Hashtable();
            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            env.put(Context.PROVIDER_URL, this.connectionURL);
            env.put("java.naming.ldap.version", "3");
            env.put(Context.SECURITY_PRINCIPAL, user.getDn());
            env.put(Context.SECURITY_CREDENTIALS, password);

            new InitialDirContext(env);

        } catch (NamingException e) {
            throw new AuthenticationException(e);
        }
    }

    public Subject getSubject(String userid) throws Exception {
        LDAPAccount user = (LDAPAccount) getUser(userid);
        if (user == null) {
            throw new AuthenticationException(userid + " is not found.");
        }
        Subject loginUser = new Subject();
        ISPrincipal p = new ISPrincipal(ISPrincipal.UID_PRINCIPAL, user.getUid());
        p.setDisplayName(user.getName());
        loginUser.getPrincipals().add(p);
        for (IGroup group : user.getGroups()) {
            p = new ISPrincipal(LDAP_GROUP_PRINCIPAL, group.getName());
            loginUser.getPrincipals().add(p);
        }
        return loginUser;
    }

    public boolean enableChangePassword() {
        return false;
    }

    public void changePassword(String userid, String password, String oldPassword) {
        throw new UnsupportedOperationException();
    }

    private void setGroup(DirContext context, LDAPAccount user) throws NamingException {

        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        //create the filter of group
        Map filters = new HashMap();
        String uniqueMemberAttrName = "uniquemember";
        if (this.propAttrMap.containsKey("org_member"))
            uniqueMemberAttrName = (String) this.propAttrMap.get("org_member");

        filters.put(uniqueMemberAttrName, user.getDn());
        String grpFilter = buildGroupFilterByDN(filters);

        NamingEnumeration grpRes = context.search(groupBase, grpFilter, searchControls);

        List grpList = new ArrayList();

        while (grpRes.hasMoreElements()) {
            SearchResult findGrpEntry = (SearchResult) grpRes.next();
            if (log.isDebugEnabled())
                log.debug("Found Groups: " + findGrpEntry.getAttributes().toString());
            String grpdn = findGrpEntry.getName() + "," + groupBase;

            grpList.add(createLDAPGroup(grpdn, findGrpEntry.getAttributes()));
        }

        IGroup[] igroup = new IGroup[grpList.size()];

        for (int i = 0; i < igroup.length; i++) {
            igroup[i] = (IGroup) grpList.get(i);
        }
        user.setGroups(igroup);

    }

    private List searchFromUsers(DirContext context, Map filters) throws NamingException {

        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        NamingEnumeration searchResultEnum;

        String filter = buildFilter(filters);
        if (log.isInfoEnabled())
            log.info("Search User from " + userBase + " by " + filter);
        searchResultEnum = context.search(userBase, filter, searchControls);
        //roop of retrieval result

        List users = new ArrayList();
        while (searchResultEnum.hasMore()) {
            SearchResult searchResult = (SearchResult) searchResultEnum.next();
            String dn = searchResult.getName() + "," + userBase;
            LDAPAccount user = createLDAPUser(dn, searchResult.getAttributes());
            users.add(user);
        }
        return users;
    }

    private List searchGroupMember(DirContext context, Map filters) throws NamingException {

        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        Set userList = new HashSet();
        String filter = buildFilter(filters);
        if (log.isInfoEnabled())
            log.info("Search User from " + userBase + " by " + filter);
        NamingEnumeration searchResultEnum = context.search(this.groupBase, filter, searchControls);

        while (searchResultEnum.hasMore()) {
            SearchResult searchResult = (SearchResult) searchResultEnum.next();
            Attributes attrs = searchResult.getAttributes();
            String dn = searchResult.getName() + "," + groupBase;
            String uniquememberAttrName = "uniqueMember";
            if (this.propAttrMap.containsKey("org_member")) {
                try {
                    uniquememberAttrName = (String) this.propAttrMap.get("org_member");
                } catch (Exception ex) {
                    //ignore
                }
            }
            Attribute uniquememberAttr = attrs.get(uniquememberAttrName);
            if (uniquememberAttr == null)
                continue;
            NamingEnumeration memberDNs = uniquememberAttr.getAll();
            while (memberDNs.hasMoreElements()) {
                //System.out.println(memberDNs[j]);
                userList.add(memberDNs.next());//DN of user
            }
        }

        List members = new ArrayList();

        for (Iterator userDns = userList.iterator(); userDns.hasNext();) {

            /* Next directory entry */
            String userDn = (String) userDns.next();
            Attributes userEntry = null;
            try {
                userEntry = context.getAttributes(userDn);//DN of user
            } catch (Exception e) {
                log.error(userDn + ": " + e.getMessage());
            }
            if (userEntry == null)
                continue;

            LDAPAccount user = createLDAPUser(userDn, userEntry);
            if (user.getUid() == null)
                continue;

            members.add(user);

        }

        return members;

    }

    private String buildFilter(Map filters) {
        StringBuffer filter = new StringBuffer();

        if (filters.size() > 1) {
            filter.append("(&");
        }
        for (Iterator it = filters.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry) it.next();
            filter.append("(").append(entry.getKey()).append("=").append(entry.getValue()).append("*)");
        }

        if (filters.size() > 1) {
            filter.append(")");
        }
        return filter.toString();
    }

    private String buildGroupFilterByDN(Map filters) {
        StringBuffer filter = new StringBuffer();

        if (filters.size() > 1) {
            filter.append("(&");
        }
        for (Iterator it = filters.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry) it.next();
            filter.append("(").append(entry.getKey()).append("=").append(entry.getValue()).append(")");
        }

        if (filters.size() > 1) {
            filter.append(")");
        }
        return filter.toString();
    }

    private String buildFilterByUid(Map filters) {
        StringBuffer filter = new StringBuffer();

        if (filters.size() > 1) {
            filter.append("(&");
        }
        for (Iterator it = filters.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry) it.next();
            filter.append("(").append(entry.getKey()).append("=").append(entry.getValue()).append(")");
        }

        if (filters.size() > 1) {
            filter.append(")");
        }
        return filter.toString();
    }

    private LDAPAccount createLDAPUser(String dn, Attributes attrs) throws NamingException {
        String uid = null;
        if (propAttrMap.containsKey("user_id")) {
            uid = getAttribute(attrs, propAttrMap.get("user_id"));
        } else {
            uid = getAttribute(attrs, accountAttr);
        }

        String mail = null;
        if (propAttrMap.containsKey("user_email"))
            mail = getAttribute(attrs, propAttrMap.get("user_email"));

        String displayName = null;
        if (userNameAttr != null) {
            displayName = getAttribute(attrs, this.userNameAttr);
        } else if (propAttrMap.containsKey("user_name")) {
            displayName = getAttribute(attrs, propAttrMap.get("user_name"));
        }

        String groupName = null;
        if (userGroupNameAttr != null)
            groupName = getAttribute(attrs, propAttrMap.get(userGroupNameAttr));

        LDAPAccount user = new LDAPAccount(dn, uid, mail, displayName, groupName);

        // case: multiple email -> implementation setter & getter emails here.
        List<String> mails = new ArrayList<String>();
        if (mail != null)
            mails.add(mail);
        user.setMails(mails);

        return user;
    }

    private String getAttribute(Attributes attrs, String name) throws NamingException {
        if (name == null)
            return null;

        Attribute attr = attrs.get(name);
        if (attr != null) {
            try {
                return (String) attr.get(0);
            } catch (ArrayIndexOutOfBoundsException ex) {
            }
        }

        return null;
    }

    private LDAPGroup createLDAPGroup(String dn, Attributes attrs) throws NamingException {
        String name;
        if (groupNameAttr != null) {
            name = getAttribute(attrs, groupNameAttr);
        } else {
            name = getAttribute(attrs, propAttrMap.get("org_name"));
        }

        return new LDAPGroup(dn, name, null);
    }

    public Collection<PrincipalDef> getPrincipalDefs() {
        return principalDefs;
    }
}