/* $Id$ */

/**
 * 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.manifoldcf.core.auth;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

import org.apache.commons.lang.StringUtils;
import org.apache.manifoldcf.core.interfaces.IAuth;
import org.apache.manifoldcf.core.interfaces.IThreadContext;
import org.apache.manifoldcf.core.system.Logging;
import org.apache.manifoldcf.core.system.ManifoldCF;
import org.apache.manifoldcf.core.interfaces.ManifoldCFException;
import org.apache.manifoldcf.core.interfaces.LockManagerFactory;

public class LdapAuthenticator implements IAuth {

  private static final String CONTEXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
  
  private static final String PROVIDER_URL_PROPERTY = "org.apache.manifoldcf.login.ldap.providerurl";
  private static final String SECURITY_AUTHENTICATION_TYPE = "org.apache.manifoldcf.login.ldap.securityauthenticationtype";
  private static final String SECURITY_PRINCIPLE = "org.apache.manifoldcf.login.ldap.securityprinciple";
  private static final String CONTEXT_SEARCH_QUERY = "org.apache.manifoldcf.login.ldap.contextsearchquery";
  private static final String SEARCH_ATTRIBUTE = "org.apache.manifoldcf.login.ldap.searchattribute";

  protected final String securityPrincipal;
  
  /** Constructor */
  public LdapAuthenticator(final IThreadContext threadContext)
    throws ManifoldCFException {
    securityPrincipal = LockManagerFactory.getStringProperty(threadContext,SECURITY_PRINCIPLE,"admin");
  }
  
  /**
   * @param userID
   * @param password
   * @return
   */
  private Hashtable<String, String> buildEnvironment(String userID,
      String password) {

    Hashtable<String, String> environment = new Hashtable<String, String>();

    environment.put(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY);

    environment.put(Context.PROVIDER_URL,
        ManifoldCF.getProperty(PROVIDER_URL_PROPERTY));

    environment.put(Context.SECURITY_AUTHENTICATION,
        ManifoldCF.getProperty(SECURITY_AUTHENTICATION_TYPE));
    environment.put(
        Context.SECURITY_PRINCIPAL,
        substituteUser(securityPrincipal, userID));
    environment.put(Context.SECURITY_CREDENTIALS, password);

    return environment;
  }

  /**
   * @param source
   * @param substitution
   * @return
   */
  private static String substituteUser(String source, String substitution) {
    return source.replace("$(userID)", substitution);
  }

  /**
   * @param userId
   * @param password
   * @return
   */
  @Override
  public boolean verifyLogin(final String userId, final String password)
    throws ManifoldCFException {
    boolean authenticated = false;

    if (StringUtils.isNotEmpty(userId) && StringUtils.isNotEmpty(password)) {

      try {

        Logging.misc
            .info("Authentication attempt for user = " + userId);

        // Create initial context
        DirContext ctx = new InitialDirContext(buildEnvironment(userId,
            password));

        NamingEnumeration results = null;
        try {

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

          results = ctx
              .search("",
                  substituteUser(ManifoldCF
                      .getProperty(CONTEXT_SEARCH_QUERY),
                      userId), controls);
          // is the user in the group?
          while (results.hasMore()) {
            SearchResult searchResult = (SearchResult) results
                .next();

            if (userId.equals(searchResult.getAttributes()
                .get(ManifoldCF.getProperty(SEARCH_ATTRIBUTE))
                .get())) {

              Logging.misc.info("Successfully authenticated : "
                  + userId);

              authenticated = true;
              break;
            }
          }

        } catch (Exception e) {
          Logging.misc.error("User not authenticated = " + userId
              + " exception = " + e.getMessage(), e);
          throw new ManifoldCFException("User not authenticated: "+e.getMessage(),e);
        } finally {

          if (results != null) {
            try {
              results.close();
            } catch (Exception e) {
              // do nothing
            }
          }
          if (ctx != null) {
            try {
              ctx.close();
            } catch (Exception e) {
              // do nothing
            }
          }
        }

      } catch (NamingException e) {
        Logging.misc.error("Exception authenticating user = " + userId
            + " exception = " + e.getMessage(), e);
        throw new ManifoldCFException("Exception authenticating user: "+e.getMessage(),e);
      }
    }
    return authenticated;
  }
  
  /** Check user capability */
  public boolean checkCapability(final String userId, final int capability)
    throws ManifoldCFException {
    // No current ability to distinguish roles
    return true;
  }

}
@@ -2275,16 +2275,202 @@
 ncipal;%0A
+  protected final String securityAuthenticationType;%0A  protected final String providerURLProperty;%0A  protected final String contextSearchQuery;%0A  protected final String searchAttribute;%0A
   %0A  /**
@@ -2680,13 +2680,442 @@
 LE,%22
-admin
+???%22);%0A    securityAuthenticationType = LockManagerFactory.getStringProperty(threadContext,SECURITY_AUTHENTICATION_TYPE,%22???%22);%0A    providerURLProperty = LockManagerFactory.getStringProperty(threadContext,PROVIDER_URL_PROPERTY,%22???%22);%0A    contextSearchQuery = LockManagerFactory.getStringProperty(threadContext,CONTEXT_SEARCH_QUERY,%22???%22);%0A    searchAttribute = LockManagerFactory.getStringProperty(threadContext,SEARCH_ATTRIBUTE,%22???
 %22);%0A
@@ -3484,53 +3484,27 @@
     
-ManifoldCF.getProperty(PROVIDER_URL_PROPERTY)
+providerURLProperty
 );%0A%0A
@@ -3568,60 +3568,34 @@
     
-ManifoldCF.getProperty(SECURITY_AUTHENTICATION_TYPE)
+securityAuthenticationType
 );%0A 
@@ -4796,75 +4796,26 @@
 ser(
-ManifoldCF%0A                      .getProperty(CONTEXT_SEARCH_QUERY)
+contextSearchQuery
 ,%0A  
@@ -5104,48 +5104,23 @@
 get(
-ManifoldCF.getProperty(SEARCH_ATTRIBUTE)
+searchAttribute
 )%0A  

Pick up all attributes from the right place