You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@manifoldcf.apache.org by kw...@apache.org on 2012/12/09 18:51:06 UTC

svn commit: r1419043 - in /manifoldcf/trunk: ./ connectors/ldap/connector/src/main/java/org/apache/manifoldcf/authorities/authorities/ldap/ connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/

Author: kwright
Date: Sun Dec  9 17:51:05 2012
New Revision: 1419043

URL: http://svn.apache.org/viewvc?rev=1419043&view=rev
Log:
Merge in changes for CONNECTORS-563.

Modified:
    manifoldcf/trunk/   (props changed)
    manifoldcf/trunk/CHANGES.txt
    manifoldcf/trunk/connectors/ldap/connector/src/main/java/org/apache/manifoldcf/authorities/authorities/ldap/LDAPAuthority.java
    manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_en_US.properties
    manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_ja_JP.properties

Propchange: manifoldcf/trunk/
------------------------------------------------------------------------------
  Merged /manifoldcf/branches/CONNECTORS-563:r1405810-1419032

Modified: manifoldcf/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/CHANGES.txt?rev=1419043&r1=1419042&r2=1419043&view=diff
==============================================================================
--- manifoldcf/trunk/CHANGES.txt (original)
+++ manifoldcf/trunk/CHANGES.txt Sun Dec  9 17:51:05 2012
@@ -3,6 +3,9 @@ $Id$
 
 ======================= 1.1-dev =====================
 
+CONNECTORS-563: Augment LDAP authority to include new features.
+(Maciej Li¿ewski)
+
 CONNECTORS-580: Remove build of commons-httpclient-mcf, and all
 ant and maven dependencies.
 (Karl Wright)
@@ -20,7 +23,7 @@ we have.
 
 CONNECTORS-577: Typo in language message properties which cause stack
 traces
-(Erlend Garåsen) 
+(Erlend Garåsen) 
 
 CONNECTORS-120: Port ManifoldCF to httpcomponents 4.2.2, from legacy
 commons-httpclient 3.1 (mcf edition).
@@ -48,7 +51,7 @@ CONNECTORS-568: Combined web.xml file co
 (Swami Rajamohan)
 
 CONNECTORS-562: Some web.xml files contain wrong declarations
-(Alex Ott, Erlend Garåsen)
+(Alex Ott, Erlend Garåsen)
 
 CONNECTORS-564: Configure velocity to use the ManifoldCF log
 configuration and the logger "velocity".
@@ -100,11 +103,11 @@ CONNECTORS-549: Wrong credentials not co
 
 CONNECTORS-548: Update maven build documentation to reflect limitations
 in how maven builds can be done.
-(Erlend Garåsen, Piergiorgio Lucidi, Karl Wright)
+(Erlend Garåsen, Piergiorgio Lucidi, Karl Wright)
 
 CONNECTORS-547: Web crawler, on encountering fatal error with no
 contents, would throw a StringIndexOutOfRange exception.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-545: web-proprietary wars need to be used by single-
 process proprietary example.
@@ -128,7 +131,7 @@ CONNECTORS-538: Maven build broken on Sh
 
 CONNECTORS-537: NPE when trying to display a history report when
 the corresponding connector is unregistered.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-533: Do not forgo xxx-README files in connector-lib-proprietary.
 (Karl Wright)
@@ -169,7 +172,7 @@ Wiki Connector.  Thanks to Maciej Lizews
 
 CONNECTORS-486: Optionally encrypt the file which the crawler configuration
 is exported to.
-(Erlend Garåsen)
+(Erlend Garåsen)
 
 CONNECTORS-523: Build a combined war that can be deployed alone
 on Tomcat and bring all of ManifoldCF's functionality with it.
@@ -307,7 +310,7 @@ the i18n work was done on it.
 
 CONNECTORS-461: Mime types and document length restrictions and
 commit option for Solr output connector need documentation
-(Erlend Garåsen)
+(Erlend Garåsen)
 
 CONNECTORS-483: Add NTLM proxy support for Web Connector.
 (Karl Wright)
@@ -360,7 +363,7 @@ Derby 10.8.1.1.
 (Karl Wright)
 
 CONNECTORS-467: Remove outdated taglib directives in web.xml
-(Erlend Garåsen)
+(Erlend Garåsen)
 
 CONNECTORS-468: Wrap ResourceBundle class so that missing keys
 in a resource bundle are dealt with in a friendlier way.
@@ -379,7 +382,7 @@ hopcounts, so that no references to dele
 
 CONNECTORS-462: Change "<" and ">" characters to HTML entities in
 connectors
-(Erlend Garåsen)
+(Erlend Garåsen)
 
 CONNECTORS-459: Upgrade to require JDK 1.6 across the board.  Get the
 (broken) maven build also working consistently with the ant build.
@@ -387,7 +390,7 @@ CONNECTORS-459: Upgrade to require JDK 1
 
 CONNECTORS-435: Replace incorrect Messages.getString() calls in the main
 UI with correct ones.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-460: Add multi-domain-controller support to the
 Active Directory authority.
@@ -407,7 +410,7 @@ build if they have the wrong svn client 
 
 CONNECTORS-430: An error should be returned if invalid seeds are typed
 into the seeds list for the web connector
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-449: Fix browser simulator so that UI tests work again.
 (Karl Wright)
@@ -422,7 +425,7 @@ how to work with the binary distribution
 
 CONNECTORS-432: Make sure quotations are in the javascript everywhere,
 not in the translation resources.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-431: Update pom.xml versions.
 (Karl Wright)
@@ -509,7 +512,7 @@ name as metadata.
 
 CONNECTORS-427: The default value for "include only host matching seeds"
 should be yes/checked
-(Erlend Garåsen)
+(Erlend Garåsen)
 
 CONNECTORS-288: Add Elastic Search connector.
 (Luca Stancapiano, Piergiorgio Lucidi, Karl Wright)
@@ -879,7 +882,7 @@ assistance!
 
 CONNECTORS-299: Handle Postgresql 9.1's newfound ability to throw
 deadlock conditions on transaction ends.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-298: Improve the SSL error message when no trusted
 certs are found.
@@ -1023,7 +1026,7 @@ RSS connector and Web connector.
 (Karl Wright)
 
 CONNECTORS-202: Add commit-within parameter to Solr output connector.
-(Jan Høydahl, Karl Wright)
+(Jan Høydahl, Karl Wright)
 
 CONNECTORS-251: Add target "download-dependencies" to build.xml.
 (Shinichiro Abe)
@@ -1076,7 +1079,7 @@ CONNECTORS-244: Add a fix to recover for
 
 CONNECTORS-243: Include most response headers in the metadata for
 each web connector document.
-(Jan Høydahl, Karl Wright)
+(Jan Høydahl, Karl Wright)
 
 CONNECTORS-224: Add OpenSearchServer output connector.
 (Emmanuel Keller, Karl Wright)
@@ -1140,11 +1143,11 @@ rapidly.
 
 CONNECTORS-223: Move test classes to be compatible with maven
 conventions.
-(Tobias Rübner, Karl Wright)
+(Tobias Rübner, Karl Wright)
 
 CONNECTORS-219: Update maven pom.xml files to include proper
 dependencies and version numbers.
-(Tobias Rübner)
+(Tobias Rübner)
 
 CONNECTORS-220: Fix long-standing issue with database connection
 problems.  Reset logic was insufficiently robust.
@@ -1170,7 +1173,7 @@ CONNECTORS-214: Add output connector sup
 mime type, URL, and document length.  Hook this up to the web and RSS
 connectors, and add mime type and maximum length fields to the Solr
 connector.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-212: Failure during notify should send job back to
 ReadyForNotify state, not ReadyForDelete state.
@@ -1205,14 +1208,14 @@ work for HSQLDB.
 (Karl Wright)
 
 CONNECTORS-203: Move to Java 1.5 compilation and conventions.
-(Erlend Garåsen, Tommaso Teofili, Karl Wright et al)
+(Erlend Garåsen, Tommaso Teofili, Karl Wright et al)
 
 CONNECTORS-201: Add activity interface ICarrydownActivity to allow
 sharing of functionality across IVersionActivity and IProcessActivity.
 (Karl Wright)
 
 CONNECTORS-200: Interpret TikaExceptions as being permanent failures.
-(Erlend Garåsen, Shinichiro Abe, Karl Wright)
+(Erlend Garåsen, Shinichiro Abe, Karl Wright)
 
 CONNECTORS-199: Modify site to point to new 0.2-incubating release.
 (Karl Wright)
@@ -1227,11 +1230,11 @@ test-output-postgresql folders.
 
 CONNECTORS-174: The standard logging.ini file for the Quick Start should
 set a log format that includes at least date and time.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-194: Forrest doc build always gets an error because of relative
 references to javadoc roots.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-196: Active directory authority did not work if login name
 and common name differed.
@@ -1306,7 +1309,7 @@ corrupt entity references.
 Release Date:  See http://incubator.apache.org/connectors for the official release date.
 
 CONNECTORS-188: Change the eol-style attribute to LF for all the .sh files.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 Web site updated to reflect proposed release.
 
@@ -1332,7 +1335,7 @@ to handle non-XML responses
 (Fuad Efendi, Karl Wright)
 
 CONNECTORS-159: Add support for external PostgreSQL server
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-165: Upgrade to jetty 6.1.26, plus patches, which is what
 Solr is using.
@@ -1374,7 +1377,7 @@ a parameter for the password when creati
 (Nicolas Max, Karl Wright)
 
 CONNECTORS-153: Add support for robots meta tag to web connector.
-(Erlend Garåsen, Karl Wright)
+(Erlend Garåsen, Karl Wright)
 
 CONNECTORS-152: Refactor notification thread and states to make the framework
 more resilient.

Modified: manifoldcf/trunk/connectors/ldap/connector/src/main/java/org/apache/manifoldcf/authorities/authorities/ldap/LDAPAuthority.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/ldap/connector/src/main/java/org/apache/manifoldcf/authorities/authorities/ldap/LDAPAuthority.java?rev=1419043&r1=1419042&r2=1419043&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/ldap/connector/src/main/java/org/apache/manifoldcf/authorities/authorities/ldap/LDAPAuthority.java (original)
+++ manifoldcf/trunk/connectors/ldap/connector/src/main/java/org/apache/manifoldcf/authorities/authorities/ldap/LDAPAuthority.java Sun Dec  9 17:51:05 2012
@@ -7,9 +7,9 @@
  * "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
+ * http://www.apache.org/licenses/LICENSE-2.0
  * 
-* Unless required by applicable law or agreed to in writing, software
+ * 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
@@ -17,17 +17,16 @@
  */
 package org.apache.manifoldcf.authorities.authorities.ldap;
 
-import org.apache.manifoldcf.core.interfaces.*;
-import org.apache.manifoldcf.agents.interfaces.*;
-import org.apache.manifoldcf.authorities.interfaces.*;
-import org.apache.manifoldcf.authorities.system.Logging;
-import org.apache.manifoldcf.authorities.system.ManifoldCF;
-
 import java.io.*;
 import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 import javax.naming.*;
-import javax.naming.ldap.*;
 import javax.naming.directory.*;
+import javax.naming.ldap.*;
+import org.apache.manifoldcf.authorities.interfaces.*;
+import org.apache.manifoldcf.authorities.system.ManifoldCF;
+import org.apache.manifoldcf.core.interfaces.*;
 import org.apache.manifoldcf.ui.util.Encoder;
 
 /**
@@ -66,7 +65,15 @@ public class LDAPAuthority extends org.a
   private String groupBase;
   private String groupSearch;
   private String groupNameAttr;
-    
+  private boolean groupMemberDN;
+  private boolean addUserRecord;
+  private String userNameAttr;
+
+  private long responseLifetime = 60000L; //60sec
+  private int LRUsize = 1000;
+  /** Cache manager. */
+  private ICacheManager cacheManager = null;
+
   /**
    * Constructor.
    */
@@ -74,6 +81,16 @@ public class LDAPAuthority extends org.a
   }
 
   /**
+   * Set thread context.
+   */
+  @Override
+  public void setThreadContext(IThreadContext tc)
+    throws ManifoldCFException {
+    super.setThreadContext(tc);
+    cacheManager = CacheManagerFactory.make(tc);
+  }
+
+  /**
    * Connect. The configuration parameters are included.
    *
    * @param configParams are the configuration parameters for this connection.
@@ -82,6 +99,21 @@ public class LDAPAuthority extends org.a
   public void connect(ConfigParams configParams) {
     super.connect(configParams);
     parameters = configParams;
+
+    // We get the parameters here, so we can check them in case they are missing
+    serverName = configParams.getParameter( "ldapServerName" );
+    serverPort = configParams.getParameter( "ldapServerPort" );
+    serverBase = configParams.getParameter( "ldapServerBase" );
+
+    userBase = configParams.getParameter( "ldapUserBase" );
+    userSearch = configParams.getParameter( "ldapUserSearch" );
+    groupBase = configParams.getParameter( "ldapGroupBase" );
+    groupSearch = configParams.getParameter( "ldapGroupSearch" );
+    groupNameAttr = configParams.getParameter( "ldapGroupNameAttr" );
+    userNameAttr = configParams.getParameter( "ldapUserNameAttr" );
+    
+    groupMemberDN = "1".equals(getParam(configParams, "ldapGroupMemberDn", ""));
+    addUserRecord = "1".equals(getParam(configParams, "ldapAddUserRecord", ""));
   }
 
   // All methods below this line will ONLY be called if a connect() call succeeded
@@ -93,37 +125,48 @@ public class LDAPAuthority extends org.a
   protected LdapContext getSession()
     throws ManifoldCFException
   {
-    // We get the parameters here, so we can check them in case they are missing
-    serverName = parameters.getParameter( "ldapServerName" );
-    if (serverName == null || serverName.length() == 0)
+    if (serverName == null || serverName.length() == 0) {
       throw new ManifoldCFException("Server name parameter missing but required");
-    serverPort = parameters.getParameter( "ldapServerPort" );
-    if (serverPort == null || serverPort.length() == 0)
+    }
+    if (serverPort == null || serverPort.length() == 0) {
       throw new ManifoldCFException("Server port parameter missing but required");
-    serverBase = parameters.getParameter( "ldapServerBase" );
-    if (serverBase == null)
+    }
+    if (serverBase == null) {
       throw new ManifoldCFException("Server base parameter missing but required");
-
-    userBase = parameters.getParameter( "ldapUserBase" );
-    if (userBase == null)
+    }
+    if (userBase == null) {
       throw new ManifoldCFException("User base parameter missing but required");
-    userSearch = parameters.getParameter( "ldapUserSearch" );
-    if (userSearch == null || userSearch.length() == 0)
+    }
+    if (userSearch == null || userSearch.length() == 0) {
       throw new ManifoldCFException("User search expression missing but required");
-    groupBase = parameters.getParameter( "ldapGroupBase" );
-    if (groupBase == null)
+    }
+    if (groupBase == null) {
       throw new ManifoldCFException("Group base parameter missing but required");
-    groupSearch = parameters.getParameter( "ldapGroupSearch" );
-    if (groupSearch == null || groupSearch.length() == 0)
+    }
+    if (groupSearch == null || groupSearch.length() == 0) {
       throw new ManifoldCFException("Group search expression missing but required");
-    groupNameAttr = parameters.getParameter( "ldapGroupNameAttr" );
-    if (groupNameAttr == null || groupNameAttr.length() == 0)
+    }
+    if (groupNameAttr == null || groupNameAttr.length() == 0) {
       throw new ManifoldCFException("Group name attribute missing but required");
+    }
+    if (userNameAttr == null || userNameAttr.length() == 0) {
+      throw new ManifoldCFException("User name attribute missing but required");
+    }
 
     Hashtable env = new Hashtable();
     env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
     env.put(Context.PROVIDER_URL, "ldap://"+serverName+":"+serverPort+"/"+serverBase);
 
+    //get bind credentials
+    String bindUser = getParam(parameters, "ldapBindUser", null);
+    String bindPass = getParam(parameters, "ldapBindPass", null);
+    if (bindPass != null && bindUser != null) {
+      bindPass = ManifoldCF.deobfuscate(bindPass);
+      env.put(Context.SECURITY_AUTHENTICATION, "simple");
+      env.put(Context.SECURITY_PRINCIPAL, bindUser);
+      env.put(Context.SECURITY_CREDENTIALS, bindPass);
+    }
+
     try {
       if (session == null) {
         session = new InitialLdapContext(env, null);
@@ -145,7 +188,6 @@ public class LDAPAuthority extends org.a
       sessionExpirationTime = -1L;
       throw new ManifoldCFException("Naming error: "+e.getMessage(),e);
     }
-
   }
     
   /**
@@ -155,7 +197,7 @@ public class LDAPAuthority extends org.a
   public String check()
     throws ManifoldCFException {
     disconnectSession();
-    LdapContext session = getSession();
+    LdapContext fSession = getSession();
     // MHL for a real check of all the search etc.
     return super.check();
   }
@@ -166,15 +208,15 @@ public class LDAPAuthority extends org.a
   @Override
   public void poll()
     throws ManifoldCFException {
-    if (session != null && System.currentTimeMillis() > sessionExpirationTime)
+    if (session != null && System.currentTimeMillis() > sessionExpirationTime) {
       disconnectSession();
+    }
     super.poll();
   }
 
   /** Disconnect a session.
   */
-  protected void disconnectSession()
-  {
+  protected void disconnectSession() {
     if (session != null) {
       try {
         session.close();
@@ -183,16 +225,8 @@ public class LDAPAuthority extends org.a
       }
       session = null;
       sessionExpirationTime = -1L;
+
     }
-    
-    serverName = null;
-    serverPort = null;
-    serverBase = null;
-    userBase = null;
-    userSearch = null;
-    groupBase = null;
-    groupSearch = null;
-    groupNameAttr = null;
   }
     
   /**
@@ -204,8 +238,37 @@ public class LDAPAuthority extends org.a
     throws ManifoldCFException {
     disconnectSession();
     super.disconnect();
+    // Zero out all the stuff that we want to be sure we don't use again
+    serverName = null;
+    serverPort = null;
+    serverBase = null;
+    userBase = null;
+    userSearch = null;
+    groupBase = null;
+    groupSearch = null;
+    groupNameAttr = null;
+    userNameAttr = null;
+
+  }
+
+  protected String createCacheConnectionString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(serverName).append(":").append(serverPort).append("/").append(serverBase);
+    return sb.toString();
   }
 
+  protected String createUserSearchString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(userBase).append("|").append(userSearch).append("|").append(userNameAttr).append("|").append(addUserRecord ? 'Y' : 'N');
+    return sb.toString();
+  }
+
+  protected String createGroupSearchString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(groupBase).append("|").append(groupSearch).append("|").append(groupNameAttr).append("|").append(groupMemberDN ? 'Y' : 'N');
+    return sb.toString();
+  }
+  
   /**
   * Obtain the access tokens for a given user name.
   *
@@ -217,16 +280,63 @@ public class LDAPAuthority extends org.a
   @Override
   public AuthorizationResponse getAuthorizationResponse(String userName)
     throws ManifoldCFException {
+    
+    getSession();
+    // Construct a cache description object
+    ICacheDescription objectDescription = new LdapAuthorizationResponseDescription(userName,
+      createCacheConnectionString(), createUserSearchString(), createGroupSearchString(), this.responseLifetime, this.LRUsize);
+
+    // Enter the cache
+    ICacheHandle ch = cacheManager.enterCache(new ICacheDescription[]{objectDescription}, null, null);
+    try {
+      ICacheCreateHandle createHandle = cacheManager.enterCreateSection(ch);
+      try {
+        // Lookup the object
+        AuthorizationResponse response = (AuthorizationResponse) cacheManager.lookupObject(createHandle, objectDescription);
+        if (response != null) {
+          return response;
+        }
+        // Create the object.
+        response = getAuthorizationResponseUncached(userName);
+        // Save it in the cache
+        cacheManager.saveObject(createHandle, objectDescription, response);
+        // And return it...
+        return response;
+      } finally {
+        cacheManager.leaveCreateSection(createHandle);
+      }
+    } finally {
+      cacheManager.leaveCache(ch);
+    }
+  }
+
+  protected AuthorizationResponse getAuthorizationResponseUncached(String userName)
+    throws ManifoldCFException {
     LdapContext session = getSession();
     try {
-      //Get DistinguishedName (for this method we are using DomainPart as a searchBase ie: DC=qa-ad-76,DC=metacarta,DC=com")
-      String usrDN = getDistinguishedName(session, userName);
-      if (usrDN == null) {
+      //find user in LDAP tree
+      SearchResult usrRecord = getUserEntry(session, userName);
+      if (usrRecord == null) {
         return userNotFoundResponse;
       }
 
+      ArrayList theGroups = new ArrayList();
+
+      String usrName = userName;
+      if (userNameAttr != null && !"".equals(userNameAttr)) {
+        if (usrRecord.getAttributes() != null) {
+          Attribute attr = usrRecord.getAttributes().get(userNameAttr);
+          if (attr != null) {
+            usrName = attr.get().toString();
+          }
+        }
+      }
+      if (addUserRecord) {
+        theGroups.add(usrName);
+      }
+
       //specify the LDAP search filter
-      String searchFilter = groupSearch.replaceAll( "\\{0\\}", ldapEscape(usrDN));
+      String searchFilter = groupSearch.replaceAll("\\{0\\}", escapeLDAPSearchFilter(groupMemberDN ? usrRecord.getNameInNamespace() : usrName));
       SearchControls searchCtls = new SearchControls();
       searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
       String returnedAtts[] = {groupNameAttr};
@@ -235,8 +345,6 @@ public class LDAPAuthority extends org.a
       //Search for tokens.  Since every user *must* have a SID, the "no user" detection should be safe.
       NamingEnumeration answer = session.search(groupBase, searchFilter, searchCtls);
 
-      ArrayList theGroups = new ArrayList();
-        
       while (answer.hasMoreElements()) {
         SearchResult sr = (SearchResult) answer.next();
         Attributes attrs = sr.getAttributes();
@@ -414,15 +522,27 @@ public class LDAPAuthority extends org.a
   @Override
   public void outputConfigurationBody(IThreadContext threadContext, IHTTPOutput out, Locale locale, ConfigParams parameters, String tabName)
     throws ManifoldCFException, IOException {
-    String serverName = getParam( parameters, "ldapServerName", "");
-    String serverPort = getParam( parameters, "ldapServerPort", "389");
-    String serverBase = getParam( parameters, "ldapServerBase", "");
-    String userBase = getParam( parameters, "ldapUserBase", "ou=People" );
-    String userSearch = getParam( parameters, "ldapUserSearch", "(&(objectClass=inetOrgPerson)(uid={0}))" );
-    String groupBase = getParam( parameters, "ldapGroupBase", "ou=Groups" );
-    String groupSearch = getParam( parameters, "ldapGroupSearch", "(&(objectClass=groupOfNames)(member={0}))" );
-    String groupNameAttr = getParam( parameters, "ldapGroupNameAttr", "cn" );
-        
+    String fServerName = getParam( parameters, "ldapServerName", "");
+    String fServerPort = getParam( parameters, "ldapServerPort", "389");
+    String fServerBase = getParam( parameters, "ldapServerBase", "");
+    
+    String fUserBase = getParam( parameters, "ldapUserBase", "ou=People" );
+    String fUserSearch = getParam( parameters, "ldapUserSearch", "(&(objectClass=inetOrgPerson)(uid={0}))" );
+    String fUserNameAttr = getParam(parameters, "ldapUserNameAttr", "uid");
+    boolean fAddUserRecord = "1".equals(getParam(parameters, "ldapAddUserRecord", ""));
+    
+    String fGroupBase = getParam( parameters, "ldapGroupBase", "ou=Groups" );
+    String fGroupSearch = getParam( parameters, "ldapGroupSearch", "(&(objectClass=groupOfNames)(member={0}))" );
+    String fGroupNameAttr = getParam( parameters, "ldapGroupNameAttr", "cn" );
+    boolean fGroupMemberDN = "1".equals(getParam(parameters, "ldapGroupMemberDn", ""));
+    
+    String fBindUser = getParam(parameters, "ldapBindUser", "");
+    String fBindPass = getParam(parameters, "ldapBindPass", null);
+    if (fBindPass != null)
+      fBindPass = ManifoldCF.deobfuscate(fBindPass);
+    else
+      fBindPass = "";
+
     if (tabName.equals(Messages.getString(locale,"LDAP.LDAP"))) {
       out.print(
 "<table class=\"displaytable\">\n"+
@@ -430,55 +550,87 @@ public class LDAPAuthority extends org.a
                     
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPServerNameColon")+"</nobr></td>\n"+
-"  <td class=\"value\"><input type=\"text\" size=\"32\" name=\"ldapServerName\" value=\""+Encoder.attributeEscape(serverName)+"\"/></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"32\" name=\"ldapServerName\" value=\""+Encoder.attributeEscape(fServerName)+"\"/></td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPServerPortColon")+"</nobr></td>\n"+
-"  <td class=\"value\"><input type=\"text\" size=\"5\" name=\"ldapServerPort\" value=\""+Encoder.attributeEscape(serverPort)+"\"/></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"5\" name=\"ldapServerPort\" value=\""+Encoder.attributeEscape(fServerPort)+"\"/></td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPServerBaseColon")+"</nobr></td>\n"+
-"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapServerBase\" value=\""+Encoder.attributeEscape(serverBase)+"\"/></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapServerBase\" value=\""+Encoder.attributeEscape(fServerBase)+"\"/></td>\n"+
+" </tr>\n"+
+
+" <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPBindUserColon")+"</nobr></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapBindUser\" value=\""+Encoder.attributeEscape(fBindUser)+"\"/></td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPBindPasswordColon")+"</nobr></td>\n"+
+"  <td class=\"value\"><input type=\"password\" size=\"64\" name=\"ldapBindPass\" value=\""+Encoder.attributeEscape(fBindPass)+"\"/></td>\n"+
+" </tr>\n"+
+
+" <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+
+" <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.UserSearchBaseColon")+"</nobr></td>\n"+
-"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapUserBase\" value=\""+Encoder.attributeEscape(userBase)+"\"/></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapUserBase\" value=\""+Encoder.attributeEscape(fUserBase)+"\"/></td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.UserSearchFilterColon")+"</nobr></td>\n"+
-"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapUserSearch\" value=\""+Encoder.attributeEscape(userSearch)+"\"/></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapUserSearch\" value=\""+Encoder.attributeEscape(fUserSearch)+"\"/></td>\n"+
+" </tr>\n"+
+
+" <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.AddUserAuthColon")+"</nobr></td>\n"+
+"  <td class=\"value\"><input type=\"checkbox\" value=\"1\" name=\"ldapAddUserRecord\" " + (fAddUserRecord ? "checked=\"true\"" : "") + "/></td>\n"+
+" </tr>\n"+
+
+" <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.UserNameAttrColon")+"</nobr></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapUserNameAttr\" value=\"" + Encoder.attributeEscape(fUserNameAttr) + "\"/></td>\n"+
 " </tr>\n"+
 
+" <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n" +
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.GroupSearchBaseColon")+"</nobr></td>\n"+
-"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapGroupBase\" value=\""+Encoder.attributeEscape(groupBase)+"\"/></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapGroupBase\" value=\""+Encoder.attributeEscape(fGroupBase)+"\"/></td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.GroupSearchFilterColon")+"</nobr></td>\n"+
-"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapGroupSearch\" value=\""+Encoder.attributeEscape(groupSearch)+"\"/></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapGroupSearch\" value=\""+Encoder.attributeEscape(fGroupSearch)+"\"/></td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.GroupNameAttributeColon")+"</nobr></td>\n"+
-"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapGroupNameAttr\" value=\""+Encoder.attributeEscape(groupNameAttr)+"\"/></td>\n"+
+"  <td class=\"value\"><input type=\"text\" size=\"64\" name=\"ldapGroupNameAttr\" value=\""+Encoder.attributeEscape(fGroupNameAttr)+"\"/></td>\n"+
+" </tr>\n"+
+
+" <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.GroupMemberDnColon")+"</nobr></td>\n"+
+"  <td class=\"value\"><input type=\"checkbox\" value=\"1\" name=\"ldapGroupMemberDn\" " + (fGroupMemberDN ? "checked=\"true\"" : "") + "/></td>\n"+
 " </tr>\n"+
 
 "</table>\n"
       );
     } else {
-      out.print( "<input type=\"hidden\" name=\"ldapServerName\" value=\""+Encoder.attributeEscape(serverName)+"\"/>\n");
-      out.print( "<input type=\"hidden\" name=\"ldapServerPort\" value=\""+Encoder.attributeEscape(serverPort)+"\"/>\n");
-      out.print( "<input type=\"hidden\" name=\"ldapServerBase\" value=\""+Encoder.attributeEscape(serverBase)+"\"/>\n");
-      out.print( "<input type=\"hidden\" name=\"ldapUserBase\" value=\""+Encoder.attributeEscape(userBase)+"\"/>\n");
-      out.print( "<input type=\"hidden\" name=\"ldapUserSearch\" value=\""+Encoder.attributeEscape(userSearch)+"\"/>\n");
-      out.print( "<input type=\"hidden\" name=\"ldapGroupBase\" value=\""+Encoder.attributeEscape(groupBase)+"\"/>\n");
-      out.print( "<input type=\"hidden\" name=\"ldapGroupSearch\" value=\""+Encoder.attributeEscape(groupSearch)+"\"/>\n");
-      out.print( "<input type=\"hidden\" name=\"ldapGroupNameAttr\" value=\""+Encoder.attributeEscape(groupNameAttr)+"\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapServerName\" value=\"" + Encoder.attributeEscape(fServerName) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapServerPort\" value=\"" + Encoder.attributeEscape(fServerPort) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapServerBase\" value=\"" + Encoder.attributeEscape(fServerBase) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapBindUser\" value=\"" + Encoder.attributeEscape(fBindUser) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapBindPass\" value=\"" + Encoder.attributeEscape(fBindPass) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapUserBase\" value=\"" + Encoder.attributeEscape(fUserBase) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapUserSearch\" value=\"" + Encoder.attributeEscape(fUserSearch) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapGroupBase\" value=\"" + Encoder.attributeEscape(fGroupBase) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapGroupSearch\" value=\"" + Encoder.attributeEscape(fGroupSearch) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapGroupNameAttr\" value=\"" + Encoder.attributeEscape(fGroupNameAttr) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapUserNameAttr\" value=\"" + Encoder.attributeEscape(fUserNameAttr) + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapAddUserRecord\" value=\"" + (fAddUserRecord ? "1" : "0") + "\"/>\n");
+      out.print("<input type=\"hidden\" name=\"ldapGroupMemberDn\" value=\"" + (fGroupMemberDN ? "1" : "0") + "\"/>\n");
     }
   }
 
@@ -499,6 +651,14 @@ public class LDAPAuthority extends org.a
     return true;
   }
 
+  private void copyParam2(IPostParameters variableContext, ConfigParams parameters, String name) {
+    String val = variableContext.getParameter(name);
+    if (val == null) {
+      val = "";
+    }
+    parameters.setParameter(name, val);
+  }
+
   /**
   * Process a configuration post. This method is called at the start of the
   * authority connector's configuration page, whenever there is a possibility
@@ -523,9 +683,19 @@ public class LDAPAuthority extends org.a
     copyParam(variableContext, parameters, "ldapServerBase" );
     copyParam(variableContext, parameters, "ldapUserBase" );
     copyParam(variableContext, parameters, "ldapUserSearch" );
+    copyParam(variableContext, parameters, "ldapUserNameAttr" );
     copyParam(variableContext, parameters, "ldapGroupBase" );
     copyParam(variableContext, parameters, "ldapGroupSearch" );
     copyParam(variableContext, parameters, "ldapGroupNameAttr" );
+    
+    copyParam(variableContext, parameters, "ldapGroupMemberDn");
+    copyParam(variableContext, parameters, "ldapAddUserRecord");
+    copyParam(variableContext, parameters, "ldapBindUser");
+    String bindPass = variableContext.getParameter("ldapBindPass");
+    if (bindPass != null) {
+      parameters.setParameter("ldapBindPass", ManifoldCF.obfuscate(bindPass));
+    }
+
     return null;
   }
 
@@ -544,57 +714,88 @@ public class LDAPAuthority extends org.a
   @Override
   public void viewConfiguration(IThreadContext threadContext, IHTTPOutput out, Locale locale, ConfigParams parameters)
     throws ManifoldCFException, IOException {
-    String serverName = getViewParam( parameters, "ldapServerName" );
-    String serverPort = getViewParam( parameters, "ldapServerPort" );
-    String serverBase = getViewParam( parameters, "ldapServerBase" );
-
-    String userBase = getViewParam( parameters, "ldapUserBase" );
-    String userSearch = getViewParam( parameters, "ldapUserSearch" );
-    String groupBase = getViewParam( parameters, "ldapGroupBase" );
-    String groupSearch = getViewParam( parameters, "ldapGroupSearch" );
-    String groupNameAttr = getViewParam( parameters, "ldapGroupNameAttr" );
+    String f_serverName = getViewParam( parameters, "ldapServerName" );
+    String f_serverPort = getViewParam( parameters, "ldapServerPort" );
+    String f_serverBase = getViewParam( parameters, "ldapServerBase" );
+    String f_bindUser = getViewParam(parameters, "ldapBindUser");
+
+    String f_userBase = getViewParam( parameters, "ldapUserBase" );
+    String f_userSearch = getViewParam( parameters, "ldapUserSearch" );
+    String f_groupBase = getViewParam( parameters, "ldapGroupBase" );
+    String f_groupSearch = getViewParam( parameters, "ldapGroupSearch" );
+    String f_groupNameAttr = getViewParam( parameters, "ldapGroupNameAttr" );
+    
+    String f_userNameAttr = getViewParam(parameters, "ldapUserNameAttr");
+    boolean f_groupMemberDN = "1".equals(getViewParam(parameters, "ldapGroupMemberDn"));
+    boolean f_addUserRecord = "1".equals(getViewParam(parameters, "ldapAddUserRecord"));
+
     out.print(
 "<table class=\"displaytable\">\n"+
 " <tr><td class=\"separator\" colspan=\"2\"><hr/></td></tr>\n"+
                     
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPServerNameColon")+"</nobr></td>\n"+
-"  <td class=\"value\">"+Encoder.bodyEscape(serverName)+"</td>\n"+
+"  <td class=\"value\">"+Encoder.bodyEscape(f_serverName)+"</td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPServerPortColon")+"</nobr></td>\n"+
-"  <td class=\"value\">"+Encoder.bodyEscape(serverPort)+"</td>\n"+
+"  <td class=\"value\">"+Encoder.bodyEscape(f_serverPort)+"</td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPServerBaseColon")+"</nobr></td>\n"+
-"  <td class=\"value\">"+Encoder.bodyEscape(serverBase)+"</td>\n"+
+"  <td class=\"value\">"+Encoder.bodyEscape(f_serverBase)+"</td>\n"+
+" </tr>\n"+
+
+" <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPBindUserColon")+"</nobr></td>\n"+
+"  <td class=\"value\">"+Encoder.bodyEscape(f_bindUser)+"</td>\n"+
+" </tr>\n"+
+
+" <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.LDAPBindPasswordColon")+"</nobr></td>\n"+
+"  <td class=\"value\">*******</td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.UserSearchBaseColon")+"</nobr></td>\n"+
-"  <td class=\"value\">"+Encoder.bodyEscape(userBase)+"</td>\n"+
+"  <td class=\"value\">"+Encoder.bodyEscape(f_userBase)+"</td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.UserSearchFilterColon")+"</nobr></td>\n"+
-"  <td class=\"value\">"+Encoder.bodyEscape(userSearch)+"</td>\n"+
+"  <td class=\"value\">"+Encoder.bodyEscape(f_userSearch)+"</td>\n"+
+" </tr>\n"+
+
+" <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.AddUserAuthColon")+"</nobr></td>\n"+
+"  <td class=\"value\">" + (f_addUserRecord ? "Y" : "N") + "</td>\n"+
+" </tr>\n"+
+
+" <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.UserNameAttrColon")+"</nobr></td>\n"+
+"  <td class=\"value\">" + Encoder.bodyEscape(f_userNameAttr) + "</td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.GroupSearchBaseColon")+"</nobr></td>\n"+
-"  <td class=\"value\">"+Encoder.bodyEscape(groupBase)+"</td>\n"+
+"  <td class=\"value\">"+Encoder.bodyEscape(f_groupBase)+"</td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.GroupSearchFilterColon")+"</nobr></td>\n"+
-"  <td class=\"value\">"+Encoder.bodyEscape(groupSearch)+"</td>\n"+
+"  <td class=\"value\">"+Encoder.bodyEscape(f_groupSearch)+"</td>\n"+
 " </tr>\n"+
 
 " <tr>\n"+
 "  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.GroupNameAttributeColon")+"</nobr></td>\n"+
-"  <td class=\"value\">"+Encoder.bodyEscape(groupNameAttr)+"</td>\n"+
+"  <td class=\"value\">"+Encoder.bodyEscape(f_groupNameAttr)+"</td>\n"+
+" </tr>\n"+
+
+" <tr>\n"+
+"  <td class=\"description\"><nobr>"+Messages.getBodyString(locale,"LDAP.GroupMemberDnColon")+"</nobr></td>\n"+
+"  <td class=\"value\">"+(f_groupMemberDN?"Y":"N")+"</td>\n"+
 " </tr>\n"+
 
 "</table>\n"
@@ -603,28 +804,25 @@ public class LDAPAuthority extends org.a
 
   // Protected methods
   /**
-  * Obtain the DistinguishedName for a given user logon name.
-  *
-  * @param ctx is the ldap context to use.
-  * @param userName (Domain Logon Name) is the user name or identifier.
-  * @param searchBase (Full Domain Name for the search ie:
-  * DC=qa-ad-76,DC=metacarta,DC=com)
-  * @return DistinguishedName for given domain user logon name. (Should
-  * throws an exception if user is not found.)
-  */
-  protected String getDistinguishedName(LdapContext ctx, String userName)
+   * Obtain the user LDAP record for a given user logon name.
+   *
+   * @param ctx is the ldap context to use.
+   * @param userName (Domain Logon Name) is the user name or identifier.
+   * @param searchBase (Full Domain Name for the search ie:
+   * DC=qa-ad-76,DC=metacarta,DC=com)
+   * @return SearchResult for given domain user logon name. (Should throws
+   * an exception if user is not found.)
+   */
+  protected SearchResult getUserEntry(LdapContext ctx, String userName)
     throws ManifoldCFException {
-    String returnedAtts[] = {"dn"};
-    String searchFilter = userSearch.replaceAll( "\\{0\\}", ldapEscape(userName));
+    String searchFilter = userSearch.replaceAll("\\{0\\}", escapeDN(userName));
     SearchControls searchCtls = new SearchControls();
-    searchCtls.setReturningAttributes(returnedAtts);
     searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
 
     try {
       NamingEnumeration answer = ctx.search(userBase, searchFilter, searchCtls);
-      while (answer.hasMoreElements()) {
-        SearchResult sr = (SearchResult) answer.next();
-        return sr.getNameInNamespace();
+      if (answer.hasMoreElements()) {
+        return (SearchResult) answer.next();
       }
       return null;
     } catch (Exception e) {
@@ -633,8 +831,8 @@ public class LDAPAuthority extends org.a
   }
 
   /**
-  * LDAP escape a string.
-  */
+   * LDAP escape a string.
+   */
   protected static String ldapEscape(String input) {
     //Add escape sequence to all commas
     StringBuilder sb = new StringBuilder();
@@ -651,4 +849,170 @@ public class LDAPAuthority extends org.a
     }
     return sb.toString();
   }
+
+  public static String escapeDN(String name) {
+    StringBuilder sb = new StringBuilder(); // If using JDK >= 1.5 consider using StringBuilder
+    if ((name.length() > 0) && ((name.charAt(0) == ' ') || (name.charAt(0) == '#'))) {
+      sb.append('\\'); // add the leading backslash if needed
+    }
+    for (int i = 0; i < name.length(); i++) {
+      char curChar = name.charAt(i);
+      switch (curChar) {
+        case '\\':
+          sb.append("\\\\");
+          break;
+        case ',':
+          sb.append("\\,");
+          break;
+        case '+':
+          sb.append("\\+");
+          break;
+        case '"':
+          sb.append("\\\"");
+          break;
+        case '<':
+          sb.append("\\<");
+          break;
+        case '>':
+          sb.append("\\>");
+          break;
+        case ';':
+          sb.append("\\;");
+          break;
+        default:
+          sb.append(curChar);
+      }
+    }
+    if ((name.length() > 1) && (name.charAt(name.length() - 1) == ' ')) {
+      sb.insert(sb.length() - 1, '\\'); // add the trailing backslash if needed
+    }
+    return sb.toString();
+  }
+
+  public static String escapeLDAPSearchFilter(String filter) {
+    StringBuilder sb = new StringBuilder(); // If using JDK >= 1.5 consider using StringBuilder
+    for (int i = 0; i < filter.length(); i++) {
+      char curChar = filter.charAt(i);
+      switch (curChar) {
+        case '\\':
+          sb.append("\\5c");
+          break;
+        case '*':
+          sb.append("\\2a");
+          break;
+        case '(':
+          sb.append("\\28");
+          break;
+        case ')':
+          sb.append("\\29");
+          break;
+        case '\u0000':
+          sb.append("\\00");
+          break;
+        default:
+          sb.append(curChar);
+      }
+    }
+    return sb.toString();
+  }
+  
+  protected static StringSet emptyStringSet = new StringSet();
+
+  /**
+   * This is the cache object descriptor for cached access tokens from this
+   * connector.
+   */
+  protected class LdapAuthorizationResponseDescription extends org.apache.manifoldcf.core.cachemanager.BaseDescription {
+
+    /**
+     * The user name
+     */
+    protected String userName;
+    /**
+     * LDAP connection string with server name and base DN
+     */
+    protected String connectionString;
+    /**
+     * User search definition
+     */
+    protected String userSearch;
+    /**
+     * Group search definition
+     */
+    protected String groupSearch;
+    /**
+     * The response lifetime
+     */
+    protected long responseLifetime;
+    /**
+     * The expiration time
+     */
+    protected long expirationTime = -1;
+
+    /**
+     * Constructor.
+     */
+    public LdapAuthorizationResponseDescription(String userName, String connectionString, String userSearch, String groupSearch, long responseLifetime, int LRUsize) {
+      super("LDAPAuthority", LRUsize);
+      this.userName = userName;
+      this.connectionString = connectionString;
+      this.userSearch = userSearch;
+      this.groupSearch = groupSearch;
+      this.responseLifetime = responseLifetime;
+    }
+
+    /**
+     * Return the invalidation keys for this object.
+     */
+    public StringSet getObjectKeys() {
+      return emptyStringSet;
+    }
+
+    /**
+     * Get the critical section name, used for synchronizing the creation of the
+     * object
+     */
+    public String getCriticalSectionName() {
+      StringBuilder sb = new StringBuilder(getClass().getName());
+      sb.append("-").append(userName).append("-").append(connectionString);
+      return sb.toString();
+    }
+
+    /**
+     * Return the object expiration interval
+     */
+    @Override
+    public long getObjectExpirationTime(long currentTime) {
+      if (expirationTime == -1) {
+        expirationTime = currentTime + responseLifetime;
+      }
+      return expirationTime;
+    }
+
+    @Override
+    public int hashCode() {
+      return userName.hashCode() + connectionString.hashCode() + userSearch.hashCode() + groupSearch.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (!(o instanceof LdapAuthorizationResponseDescription)) {
+        return false;
+      }
+      LdapAuthorizationResponseDescription ard = (LdapAuthorizationResponseDescription) o;
+      if (!ard.userName.equals(userName)) {
+        return false;
+      }
+      if (!ard.connectionString.equals(connectionString)) {
+        return false;
+      }
+      if (!ard.userSearch.equals(userSearch)) {
+        return false;
+      }
+      if (!ard.groupSearch.equals(groupSearch)) {
+        return false;
+      }
+      return true;
+    }
+  }
 }

Modified: manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_en_US.properties
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_en_US.properties?rev=1419043&r1=1419042&r2=1419043&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_en_US.properties (original)
+++ manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_en_US.properties Sun Dec  9 17:51:05 2012
@@ -17,11 +17,16 @@ LDAP.LDAP=LDAP
 LDAP.LDAPServerNameColon=LDAP server name:
 LDAP.LDAPServerPortColon=LDAP server port:
 LDAP.LDAPServerBaseColon=LDAP server base (e.g. 'dc=office,dc=com'):
+LDAP.LDAPBindUserColon=Bind to server as user (leave empty if not needed):
+LDAP.LDAPBindPasswordColon=Bind to server with password:
 LDAP.UserSearchBaseColon=User search base:
 LDAP.UserSearchFilterColon=User search filter:
 LDAP.GroupSearchBaseColon=Group search base:
 LDAP.GroupSearchFilterColon=Group search filter:
 LDAP.GroupNameAttributeColon=Group name attribute:
+LDAP.AddUserAuthColon=Add user as authorization token:
+LDAP.UserNameAttrColon=User name attribute:
+LDAP.GroupMemberDnColon=Member attribute is DN:
 
 LDAP.ServerNameCannotBeBlank=Server name cannot be blank
 LDAP.ServerPortCannotBeBlank=Server port cannot be blank

Modified: manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_ja_JP.properties
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_ja_JP.properties?rev=1419043&r1=1419042&r2=1419043&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_ja_JP.properties (original)
+++ manifoldcf/trunk/connectors/ldap/connector/src/main/native2ascii/org/apache/manifoldcf/authorities/authorities/ldap/common_ja_JP.properties Sun Dec  9 17:51:05 2012
@@ -17,11 +17,16 @@ LDAP.LDAP=LDAP
 LDAP.LDAPServerNameColon=LDAPサーバ名:
 LDAP.LDAPServerPortColon=LDAPサーバポート:
 LDAP.LDAPServerBaseColon=LDAPサーバベース (例 'dc=office,dc=com'):
+LDAP.LDAPBindUserColon=Bindユーザ (必要なければ空白):
+LDAP.LDAPBindPasswordColon=Bindパスワード:
 LDAP.UserSearchBaseColon=Userサーチベース:
 LDAP.UserSearchFilterColon=Userサーチフィルタ:
 LDAP.GroupSearchBaseColon=Groupサーチベース:
 LDAP.GroupSearchFilterColon=Groupサーチフィルタ:
 LDAP.GroupNameAttributeColon=Group名属性:
+LDAP.AddUserAuthColon=認証トークンとしてユーザを追加:
+LDAP.UserNameAttrColon=ユーザ名属性:
+LDAP.GroupMemberDnColon=メンバ属性がDN:
 
 LDAP.ServerNameCannotBeBlank=サーバ名は空白にできません
 LDAP.ServerPortCannotBeBlank=サーバポートは空白にできません