FileDocCategorySizeDatePackage
UsersLDAPRepository.javaAPI DocApache James 2.3.129071Fri Jan 12 12:56:32 GMT 2007org.apache.james.userrepository

UsersLDAPRepository.java

/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you 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 org.apache.james.userrepository;

import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.james.Constants;
import org.apache.james.services.User;
import org.apache.james.services.UsersRepository;

import javax.naming.AuthenticationException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

/**
 * Implementation of a Repository to store users.
 *
 * This clas is a dummy for the proposal!
 *
 * @version This is $Revision: 494012 $
 */
public class UsersLDAPRepository
    extends AbstractLogEnabled
    implements UsersRepository, Serviceable, Configurable, Contextualizable, Initializable{

    private DirContext ctx;

    private String LDAPHost;
    private String rootNodeDN;
    private String rootURL;
    private String serverRDN;
    private String baseNodeDN;
    private String baseURL;
    private String mailAddressAttr;
    private String identAttr;
    private String authType;
    private String principal;
    private String password;
    private String usersDomain;
    private String membersAttr;
    private boolean manageGroupAttr;
    private String groupAttr;
    private boolean managePasswordAttr;
    private String passwordAttr;

    /**
     * @see org.apache.avalon.framework.context.Contextualizable#contextualize(Context)
     */
    public void contextualize(Context context)
        throws ContextException {
        usersDomain = (String)context.get(Constants.DEFAULT_DOMAIN);
    }

    /**
     * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
     */
    public void service(ServiceManager compMgr) {
        // this.comp = compMgr;
    }

    /**
     * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
     */
    public void configure(Configuration conf)
        throws ConfigurationException {

        LDAPHost = conf.getChild("LDAPServer").getValue();
        rootNodeDN = conf.getChild("LDAPRoot").getValue();
        serverRDN = conf.getChild("ThisServerRDN").getValue();
        mailAddressAttr
            = conf.getChild("MailAddressAttribute").getValue();
        identAttr = conf.getChild("IdentityAttribute").getValue();
        authType = conf.getChild("AuthenticationType").getValue();
        principal = conf.getChild("Principal").getValue();
        password = conf.getChild("Password").getValue();

        membersAttr = conf.getChild("MembersAttribute").getValue();
        manageGroupAttr
            = conf.getChild("ManageGroupAttribute").getValueAsBoolean( false );
        groupAttr = conf.getChild("GroupAttribute").getValue();
        managePasswordAttr = conf.getChild("ManagePasswordAttribute").getValueAsBoolean( false );
        passwordAttr = conf.getChild("PasswordAttribute").getValue();
    }

    public void setServerRoot() {
        StringBuffer serverRootBuffer =
            new StringBuffer(128)
                    .append(serverRDN)
                    .append(", ")
                    .append(rootNodeDN);
        this.setBase(serverRootBuffer.toString());
    }

    public void setBase(String base) {
        baseNodeDN = base;
    }

    /**
     * @see org.apache.avalon.framework.activity.Initializable#initialize()
     */
    public void initialize() throws Exception {
        //setServerRoot();
        StringBuffer urlBuffer =
            new StringBuffer(128)
                    .append(LDAPHost)
                    .append("/");
        rootURL = urlBuffer.toString() + rootNodeDN;
        baseURL = urlBuffer.toString() + baseNodeDN;

        getLogger().info("Creating initial context from " + baseURL);
        //System.out.println("Creating initial context from " + baseURL);

        Hashtable env = new Hashtable();
        env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
                "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(javax.naming.Context.PROVIDER_URL, baseURL);

        try {
            ctx = new InitialDirContext(env); // Could throw a NamingExcpetion
        } catch (Exception e) {
            getLogger().error("Exception creating InitialDirContext: ", e);
        }

        getLogger().info("Initial context initialized from " + baseURL);
    }



    public String getChildDestination(String childName) {

        String destination = null;
        String filter = "cn=" + childName;
        SearchControls ctls = new SearchControls();

        try {

            NamingEnumeration result  = ctx.search("", filter, ctls);

            if (result.hasMore()) {
                StringBuffer destinationBuffer =
                    new StringBuffer(128)
                            .append("cn=")
                            .append(childName)
                            .append(", ")
                            .append(baseNodeDN);
                destination = destinationBuffer.toString();
                getLogger().info("Pre-exisisting LDAP node: " + destination);
            } else {
                Attributes attrs = new BasicAttributes(true);
                Attribute objclass = new BasicAttribute("objectclass");
                objclass.add("top");
                objclass.add("rfc822MailGroup");
                attrs.put(objclass);
                Attribute cname = new BasicAttribute("cn");
                cname.add(childName);
                attrs.put(cname);
                Attribute owner = new BasicAttribute("owner");
                owner.add("JAMES-unassigned");
                attrs.put(owner);

                ctx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, authType);
                ctx.addToEnvironment(javax.naming.Context.SECURITY_PRINCIPAL, principal);
                ctx.addToEnvironment(javax.naming.Context.SECURITY_CREDENTIALS, password);

                ctx.createSubcontext("cn=" + childName, attrs);
                ctx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, "none");

                StringBuffer destinationBuffer =
                    new StringBuffer(128)
                            .append("cn=")
                            .append(childName)
                            .append(", ")
                            .append(baseNodeDN);
                destination = destinationBuffer.toString();
                getLogger().info("Created new LDAP node: " + destination);
            }
        } catch (NamingException e) {
            getLogger().error("Problem with child nodes " + e.getMessage(), e);
        }

        return destination;
    }

    /**
     * List users in repository.
     *
     * @return Iterator over a collection of Strings, each being one user in the repository.
     */
    public Iterator list() {

        List result = new ArrayList();
        // String filter = mailAddressAttr + "=*";
        String[] attrIDs = {membersAttr};

        try {
            Attribute members
                = ctx.getAttributes("", attrIDs).get(membersAttr);
            if (members != null) {
                NamingEnumeration enumeration = members.getAll();
                while (enumeration.hasMore()) {
                    result.add(enumeration.next());
                }
            }
        } catch (NamingException e) {
            getLogger().error("Problem listing mailboxes. " + e );

        }
        return result.iterator();
    }

    // Methods from interface UsersRepository --------------------------

    /**
     * Update the repository with the specified user object.  Unsupported for
     * this user repository type.
     *
     * @return false
     */
    public boolean addUser(User user) {
        return false;
    }

    public  User getUserByName(String name) {
        return new DefaultUser("dummy", "dummy");
    }

    public User getUserByNameCaseInsensitive(String name) {
        return getUserByName(name);
    }

    public boolean containsCaseInsensitive(String name) {
        return contains(name);
    }

    // TODO: This is in violation of the contract for the interface.
    //       Should only return null if the user doesn't exist.  Otherwise
    //       this should return a consistent string representation of the name
    public String getRealName(String name) {
        return null;
    }

    public boolean updateUser(User user) {
        return false;
    }

    /**
     * Adds userName to the MemberAttribute (specified in conf.xml) of this
     * node.
     * If ManageGroupAttribute (conf.xml) is TRUE then calls addGroupToUser.
     */
    public synchronized void addUser(String userName, Object attributes) {

        String[] attrIDs = {membersAttr};

        // First, add username to mailGroup at baseNode

        try {
            Attribute members = ctx.getAttributes("", attrIDs).get(membersAttr);


            if (members != null && members.contains(userName)) {//user already here
                StringBuffer infoBuffer =
                    new StringBuffer(64)
                            .append("Found ")
                            .append(userName)
                            .append(" already in mailGroup. ");
                getLogger().info(infoBuffer.toString());
                //System.out.println(infoBuffer.toString());

            } else {
                ctx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, authType);
                ctx.addToEnvironment(javax.naming.Context.SECURITY_PRINCIPAL, principal);
                ctx.addToEnvironment(javax.naming.Context.SECURITY_CREDENTIALS, password);

                ModificationItem[] mods = new ModificationItem[1];
                mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, new BasicAttribute(membersAttr, userName));

                ctx.modifyAttributes("", mods);

                ctx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, "none");
                StringBuffer infoBuffer =
                    new StringBuffer(128)
                            .append(userName)
                            .append(" added to mailGroup ")
                            .append(baseNodeDN);
                getLogger().info(infoBuffer.toString());
                //System.out.println(infoBuffer.toString());
            }
        } catch (NamingException e) {
            StringBuffer exceptionBuffer =
                new StringBuffer(256)
                        .append("Problem adding user ")
                        .append(userName)
                        .append(" to: ")
                        .append(baseNodeDN)
                        .append(e);
            getLogger().error(exceptionBuffer.toString());
        }

        // Add attributes to user objects, if necessary

        if (manageGroupAttr) {
            addGroupToUser(userName);
        }

//        if (managePasswordAttr) {
//            String userPassword = (String) attributes; // Not yet implemented
//        }
    }
    
    /**
     * @see org.apache.james.services.UsersRepository#addUser(java.lang.String, java.lang.String)
     */
    public boolean addUser(String username, String password) {
        if (!contains(username)) {
            addUser(username, password);
            return contains(username);
        } else {
            return false;
        }
    }

    private void addGroupToUser(String userName) {
        String[] attrIDs = {membersAttr};

        Hashtable env = new Hashtable();
        env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(javax.naming.Context.PROVIDER_URL, rootURL);

        DirContext rootCtx = null;
        try {
            rootCtx = new InitialDirContext(env);

            String[] returnAttrs = {groupAttr};
            SearchControls ctls = new SearchControls();
            ctls.setReturningAttributes(attrIDs);
            ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            StringBuffer filterBuffer =
                new StringBuffer(128)
                        .append(mailAddressAttr)
                        .append("=")
                        .append(userName)
                        .append("@")
                        .append(usersDomain);
            String filter = filterBuffer.toString();

            NamingEnumeration enumeration  = rootCtx.search("", filter, ctls);

            if (enumeration.hasMore()) { // ie User is in Directory
                SearchResult newSr = (SearchResult)enumeration.next();
                String userDN = newSr.getName();
                Attribute servers = rootCtx.getAttributes(userDN, returnAttrs).get(groupAttr);


                if (servers != null && servers.contains(baseNodeDN)) {//server already registered for user
                    getLogger().info(baseNodeDN + " already in user's Groups. " );
                    //System.out.println(baseNodeDN + " already in user's Groups. ");

                } else {

                    rootCtx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, authType);
                    rootCtx.addToEnvironment(javax.naming.Context.SECURITY_PRINCIPAL, principal);
                    rootCtx.addToEnvironment(javax.naming.Context.SECURITY_CREDENTIALS, password);

                    rootCtx.modifyAttributes(userDN, DirContext.ADD_ATTRIBUTE, new BasicAttributes(groupAttr, baseNodeDN, true));

                    rootCtx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, "none");
                    getLogger().info(baseNodeDN + " added to user's groups ");
                    //System.out.println(baseNodeDN + " added to users' groups ");

                }

            } else {
                StringBuffer infoBuffer =
                    new StringBuffer(64)
                            .append("User ")
                            .append(userName)
                            .append(" not in directory.");
                getLogger().info(infoBuffer.toString());
                // System.out.println(infoBuffer.toString());

            }
        } catch (NamingException e) {
            getLogger().error("Problem adding group to user " + userName);
            //System.out.println("Problem adding group to user " + userName);
            //System.out.println(e.getMessage());
            //e.printStackTrace();
        } finally {
            closeDirContext(rootCtx);
        }
    }

    public synchronized void removeUser(String userName) {
        String[] attrIDs = {membersAttr};

        try {
            Attribute members = ctx.getAttributes("", attrIDs).get(membersAttr);
            if (members == null) {
                System.out.println("UsersLDAPRepository - Null list attribute.");

            } else  if (!members.contains(userName)) {//user not here
                getLogger().info(userName + " missing from mailGroup. ");
                //System.out.println(userName + " missing from mailGroup. ");

            } else {
                // First, remove username from mailGroup at baseNode

                ctx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, authType);
                ctx.addToEnvironment(javax.naming.Context.SECURITY_PRINCIPAL, principal);
                ctx.addToEnvironment(javax.naming.Context.SECURITY_CREDENTIALS, password);

                ModificationItem[] mods = new ModificationItem[1];
                mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, new BasicAttribute(membersAttr, userName));

                ctx.modifyAttributes("", mods);


                ctx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, "none");
                getLogger().info(userName + " removed from mailGroup. ");
                //System.out.println(userName + " removed from mailGroup. ");
            }
        } catch (NamingException e) {
            StringBuffer exceptionBuffer =
                new StringBuffer(256)
                        .append("Problem removing user ")
                        .append(userName)
                        .append(": ")
                        .append(e);
            getLogger().error(exceptionBuffer.toString());
            //System.out.println("Problem removing user " + userName);
            //System.out.println(e.getMessage());
            //e.printStackTrace();
        }
        if (manageGroupAttr) {
            removeGroupFromUser(userName);
        }

        if (managePasswordAttr) {
            // not yet implemented
        }

    }

    public void removeGroupFromUser(String userName) {

        Hashtable env = new Hashtable();
        env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(javax.naming.Context.PROVIDER_URL, rootURL);


        DirContext rootCtx = null;
        try {
            rootCtx = new InitialDirContext(env);

            // Find directory entry
            String[] returnAttrs = {groupAttr};
            SearchControls ctls = new SearchControls();
            ctls.setReturningAttributes(returnAttrs);
            ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            StringBuffer filterBuffer =
                new StringBuffer(128)
                        .append(mailAddressAttr)
                        .append("=")
                        .append(userName)
                        .append("@")
                        .append(usersDomain);
            String filter = filterBuffer.toString();

            NamingEnumeration enumeration  = rootCtx.search("", filter, ctls);

            if (enumeration.hasMore()) { // ie User is in Directory
                SearchResult newSr = (SearchResult)enumeration.next();
                String userDN = newSr.getName();

                System.out.println("Found user entry: " + userDN);

                Attribute servers = rootCtx.getAttributes(userDN, returnAttrs).get(groupAttr);
                if (servers == null) { //should not happen
                    getLogger().info("GroupAttribute missing from user: " + userName);
                    // System.out.println("GroupAttribute missing from user: " + userName );

                } else if (!servers.contains(baseNodeDN)) {//server not registered for user
                    getLogger().info(baseNodeDN + " missing from users' Groups. " );
                    //System.out.println(baseNodeDN + " missing from users' Groups. ");

                } else {

                    rootCtx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, authType);
                    rootCtx.addToEnvironment(javax.naming.Context.SECURITY_PRINCIPAL, principal);
                    rootCtx.addToEnvironment(javax.naming.Context.SECURITY_CREDENTIALS, password);

                    ModificationItem[] mods = new ModificationItem[1];
                    mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, new BasicAttribute(groupAttr, baseNodeDN));

                    rootCtx.modifyAttributes(userDN, mods);

                    //rootCtx.modifyAttributes(userDN, DirContext.REPLACE_ATTRIBUTE, changes);

                    rootCtx.addToEnvironment(javax.naming.Context.SECURITY_AUTHENTICATION, "none");
                    getLogger().info(baseNodeDN + " removed from users' groups " );
                    //System.out.println(baseNodeDN + " removed from users' groups ");

                }

            } else {
                StringBuffer infoBuffer =
                    new StringBuffer(64)
                            .append("User ")
                            .append(userName)
                            .append(" not in directory.");
                getLogger().info(infoBuffer.toString());
                //System.out.println(infoBuffer.toString());

            }
        } catch (NamingException e) {
            StringBuffer exceptionBuffer =
                new StringBuffer(256)
                        .append("Problem removing user ")
                        .append(userName)
                        .append(e);
            getLogger().error(exceptionBuffer.toString());
            //System.out.println("Problem removing user " + userName);
            //System.out.println(e.getMessage());
            //e.printStackTrace();
        } finally {
            closeDirContext(rootCtx);
            rootCtx = null;
        }
    }


    public boolean contains(String name) {
        boolean found = false;
        String[] attrIDs = {membersAttr};

        try {
            Attribute members = ctx.getAttributes("", attrIDs).get(membersAttr);
            if (members != null && members.contains(name)) {
                found = true;
                StringBuffer infoBuffer =
                    new StringBuffer(64)
                            .append("Found ")
                            .append(name)
                            .append(" in mailGroup. ");
                getLogger().info(infoBuffer.toString());
                //System.out.println(infoBuffer.toString());
            }
        } catch (NamingException e) {
            StringBuffer exceptionBuffer =
                new StringBuffer(256)
                        .append("Problem finding user ")
                        .append(name)
                        .append(" : ")
                        .append(e);
            getLogger().error(exceptionBuffer.toString());
            //System.out.println(exceptionBuffer.toString());
        }
        return found;
    }


    public boolean test(String name, String testPassword) {
        boolean result = false;
        boolean foundFlag = false;
        String userDN = null;

        try {
            String[] returnAttrs = {identAttr, passwordAttr};
            SearchControls ctls = new SearchControls();
            ctls.setReturningAttributes(returnAttrs);
            ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            StringBuffer filterBuffer = 
                new StringBuffer(128)
                        .append(mailAddressAttr)
                        .append("=")
                        .append(name)
                        .append("@")
                        .append(usersDomain);
            String filter = filterBuffer.toString();

            Hashtable env = new Hashtable();
            env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            env.put(javax.naming.Context.PROVIDER_URL, rootURL);
            DirContext rootCtx = null;

            try {
                rootCtx = new InitialDirContext(env);
    
                NamingEnumeration enumeration  = rootCtx.search("", filter, ctls);
                if (enumeration.hasMore()) { // ie User is in Directory
                    SearchResult sr = (SearchResult)enumeration.next();
                    String userRDN = sr.getName();
                    StringBuffer userDNBuffer =
                        new StringBuffer(128)
                                .append(userRDN)
                                .append(", ")
                                .append(rootNodeDN);
                    userDN = userDNBuffer.toString();
                    foundFlag = true;
                    //System.out.println("UserDN is : " + userDN);
                }
            } finally {
                closeDirContext(rootCtx);
            }
        } catch (Exception e) {
            StringBuffer exceptionBuffer =
                new StringBuffer(256)
                        .append("Problem finding user ")
                        .append(name)
                        .append(" for password test.")
                        .append(e); 
            getLogger().error(exceptionBuffer.toString());
            //e.getMessage();
            //e.printStackTrace();
        }

        if (foundFlag) { // ie User is in Directory
            Hashtable env2 = new Hashtable();
            env2.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            env2.put(javax.naming.Context.PROVIDER_URL, rootURL);
            env2.put(javax.naming.Context.SECURITY_AUTHENTICATION, "simple");
            env2.put(javax.naming.Context.SECURITY_PRINCIPAL, userDN);
            env2.put(javax.naming.Context.SECURITY_CREDENTIALS, testPassword);
            //System.out.println("Creating initial context from " + baseURL);

            DirContext testCtx = null;
            try {
                testCtx = new InitialDirContext(env2);
                result = true;

            } catch (AuthenticationException ae) {
                result = false;
                StringBuffer exceptionBuffer =
                    new StringBuffer(256)
                            .append("Attempt to authenticate with incorrect password for ")
                            .append(name)
                            .append(" : ")
                            .append(ae); 
                getLogger().error(exceptionBuffer.toString());
                //System.out.println(exceptionBuffer.toString());
                //System.out.println(ae.getMessage());
                //ae.printStackTrace();
            } catch (Exception e) {
                StringBuffer exceptionBuffer =
                    new StringBuffer(256)
                            .append("Problem checking password for ")
                            .append(name)
                            .append(" : ")
                            .append(e); 
                getLogger().error(exceptionBuffer.toString());
                //System.out.println(exceptionBuffer.toString());
                //System.out.println(e.getMessage());
                //e.printStackTrace();
            } finally {
                closeDirContext(testCtx);
            }
        }
        return result;

    }

    public int countUsers() {

        String[] attrIDs = {membersAttr};
        int result = -1;

        try {
            Attribute members = ctx.getAttributes("", attrIDs).get(membersAttr);
            if (members != null) {
                result = members.size();
            } else {
                result = 0;
            }
        } catch (NamingException e) {
            getLogger().error("Problem counting users: "  + e);
            //System.out.println("Problem counting users. ");
        }
        return result;
    }

    public String getDomains() {
        return usersDomain;
    }

    /**
     * Disposes of all open directory contexts
     *
     * @throws Exception if an error is encountered during shutdown
     */
    public void dispose() throws Exception {
        closeDirContext(ctx);
        ctx = null;
    }

    private void closeDirContext(DirContext ctx) {
        try {
            if (ctx != null) {
                ctx.close();
            }
        } catch (NamingException ne) {
            getLogger().warn("UsersLDAPRepository: Unexpected exception encountered while closing directory context: " + ne);
        }
    }
}