You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by fu...@apache.org on 2003/12/12 22:43:26 UTC

cvs commit: jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm JNDIRealm.java

funkman     2003/12/12 13:43:26

  Modified:    catalina/src/share/org/apache/catalina/realm JNDIRealm.java
  Log:
  Resync with 4.1 fixes, in particular - they include:
  BZ 23190 16541
  
  And Allow Multiple user patterns per
  http://marc.theaimsgroup.com/?l=tomcat-dev&m=106254937722504&w=2
  
  Revision  Changes    Path
  1.8       +185 -37   jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/JNDIRealm.java
  
  Index: JNDIRealm.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/realm/JNDIRealm.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- JNDIRealm.java	2 Sep 2003 21:22:05 -0000	1.7
  +++ JNDIRealm.java	12 Dec 2003 21:43:26 -0000	1.8
  @@ -61,6 +61,8 @@
    */
   
   
  +
  +
   package org.apache.catalina.realm;
   
   
  @@ -70,24 +72,25 @@
   import java.util.Hashtable;
   import java.util.List;
   
  -import javax.naming.AuthenticationException;
  -import javax.naming.CommunicationException;
   import javax.naming.Context;
  -import javax.naming.Name;
  +import javax.naming.CommunicationException;
  +import javax.naming.InvalidNameException;
   import javax.naming.NameNotFoundException;
  -import javax.naming.NameParser;
   import javax.naming.NamingEnumeration;
   import javax.naming.NamingException;
  +import javax.naming.NameParser;
  +import javax.naming.Name;
  +import javax.naming.AuthenticationException;
   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 org.apache.catalina.LifecycleException;
   import org.apache.catalina.util.Base64;
   
  +
   /**
    * <p>Implementation of <strong>Realm</strong> that works with a directory
    * server accessed via the Java Naming and Directory Interface (JNDI) APIs.
  @@ -298,6 +301,17 @@
   
   
       /**
  +     * A string of LDAP user patterns or paths, ":"-separated
  +     * These will be used to form the distinguished name of a
  +     * user, with "{0}" marking the spot where the specified username
  +     * goes.
  +     * This is similar to userPattern, but allows for multiple searches
  +     * for a user.
  +     */
  +    protected String[] userPatternArray = null;
  +
  +
  +    /**
        * The message format used to form the distinguished name of a
        * user, with "{0}" marking the spot where the specified username
        * goes.
  @@ -306,10 +320,10 @@
   
   
       /**
  -     * The MessageFormat object associated with the current
  -     * <code>userPattern</code>.
  +     * An array of MessageFormat objects associated with the current
  +     * <code>userPatternArray</code>.
        */
  -    protected MessageFormat userPatternFormat = null;
  +    protected MessageFormat[] userPatternFormatArray = null;
   
   
       /**
  @@ -361,6 +375,11 @@
        */
       protected int connectionAttempt = 0;
   
  +    /**
  +     * The current user pattern to be used for lookup and binding of a user.
  +     */
  +    protected int curUserPattern = 0;
  +
       // ------------------------------------------------------------- Properties
   
       /**
  @@ -726,6 +745,11 @@
   
       /**
        * Set the message format pattern for selecting users in this Realm.
  +     * This may be one simple pattern, or multiple patterns to be tried,
  +     * separated by parentheses. (for example, either "cn={0}", or
  +     * "(cn={0})(cn={0},o=myorg)" Full LDAP search strings are also supported,
  +     * but only the "OR", "|" syntax, so "(|(cn={0})(cn={0},o=myorg))" is
  +     * also valid. Complex search strings with &, etc are NOT supported.
        *
        * @param userPattern The new user pattern
        */
  @@ -733,12 +757,19 @@
   
           this.userPattern = userPattern;
           if (userPattern == null)
  -            userPatternFormat = null;
  -        else
  -            userPatternFormat = new MessageFormat(userPattern);
  -
  +            userPatternArray = null;
  +        else {
  +            userPatternArray = parseUserPatternString(userPattern);
  +            int len = this.userPatternArray.length;
  +            userPatternFormatArray = new MessageFormat[len];
  +            for (int i=0; i < len; i++) {
  +                userPatternFormatArray[i] =
  +                    new MessageFormat(userPatternArray[i]);
  +            }
  +        }
       }
   
  +
       /**
        * Getter for property alternateURL.
        *
  @@ -750,6 +781,7 @@
   
       }
   
  +
       /**
        * Setter for property alternateURL.
        *
  @@ -870,21 +902,50 @@
               || credentials == null || credentials.equals(""))
               return (null);
   
  -        // Retrieve user information
  -        User user = getUser(context, username);
  -        if (user == null)
  -            return (null);
  -
  -        // Check the user's credentials
  -        if (!checkCredentials(context, user, credentials))
  -            return (null);
  -
  -        // Search for additional roles
  -        List roles = getRoles(context, user);
  +        if (userPatternArray != null) {
  +            for (curUserPattern = 0;
  +                 curUserPattern < userPatternFormatArray.length;
  +                 curUserPattern++) {
  +                // Retrieve user information
  +                User user = getUser(context, username);
  +                if (user != null) {
  +                    try {
  +                        // Check the user's credentials
  +                        if (checkCredentials(context, user, credentials)) {
  +                            // Search for additional roles
  +                            List roles = getRoles(context, user);
  +                            return (new GenericPrincipal(this,
  +                                                         username,
  +                                                         credentials,
  +                                                         roles));
  +                        }
  +                    } catch (InvalidNameException ine) {
  +                        // Log the problem for posterity
  +                        log(sm.getString("jndiRealm.exception"), ine);
  +                        // ignore; this is probably due to a name not fitting
  +                        // the search path format exactly, as in a fully-
  +                        // qualified name being munged into a search path
  +                        // that already contains cn= or vice-versa
  +                    }
  +                }
  +            }
  +            return null;
  +        } else {
  +            // Retrieve user information
  +            User user = getUser(context, username);
  +            if (user == null)
  +                return (null);
  +
  +            // Check the user's credentials
  +            if (!checkCredentials(context, user, credentials))
  +                return (null);
   
  -        // Create and return a suitable Principal for this user
  -        return (new GenericPrincipal(this, username, credentials, roles));
  +            // Search for additional roles
  +            List roles = getRoles(context, user);
   
  +            // Create and return a suitable Principal for this user
  +            return (new GenericPrincipal(this, username, credentials, roles));
  +        }
       }
   
   
  @@ -919,7 +980,7 @@
           list.toArray(attrIds);
   
           // Use pattern or search for user entry
  -        if (userPatternFormat != null) {
  +        if (userPatternFormatArray != null) {
               user = getUserByPattern(context, username, attrIds);
           } else {
               user = getUserBySearch(context, username, attrIds);
  @@ -950,11 +1011,11 @@
           if (debug >= 2)
               log("lookupUser(" + username + ")");
   
  -        if (username == null || userPatternFormat == null)
  +        if (username == null || userPatternFormatArray[curUserPattern] == null)
               return (null);
   
           // Form the dn from the user pattern
  -        String dn = userPatternFormat.format(new String[] { username });
  +        String dn = userPatternFormatArray[curUserPattern].format(new String[] { username });
           if (debug >= 3) {
               log("  dn=" + dn);
           }
  @@ -1160,7 +1221,8 @@
                       password = password.substring(5);
                       md.reset();
                       md.update(credentials.getBytes());
  -                    String digestedPassword = new String(Base64.encode(md.digest()));
  +                    String digestedPassword =
  +                        new String(Base64.encode(md.digest()));
                       validated = password.equals(digestedPassword);
                   }
               } else {
  @@ -1279,6 +1341,7 @@
   
           // Set up parameters for an appropriate search
           String filter = roleFormat.format(new String[] { dn, username });
  +        filter = doRFC2254Encoding(filter);
           SearchControls controls = new SearchControls();
           if (roleSubtree)
               controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
  @@ -1566,6 +1629,94 @@
   
       }
   
  +    /**
  +     * Given a string containing LDAP patterns for user locations (separated by
  +     * parentheses in a pseudo-LDAP search string format -
  +     * "(location1)(location2)", returns an array of those paths.  Real LDAP
  +     * search strings are supported as well (though only the "|" "OR" type).
  +     *
  +     * @param userPatternString - a string LDAP search paths surrounded by
  +     * parentheses
  +     */
  +    protected String[] parseUserPatternString(String userPatternString) {
  +
  +        if (userPatternString != null) {
  +            ArrayList pathList = new ArrayList();
  +            int startParenLoc = userPatternString.indexOf('(');
  +            if (startParenLoc == -1) {
  +                // no parens here; return whole thing
  +                return new String[] {userPatternString};
  +            }
  +            int startingPoint = 0;
  +            while (startParenLoc > -1) {
  +                int endParenLoc = 0;
  +                // weed out escaped open parens and parens enclosing the
  +                // whole statement (in the case of valid LDAP search
  +                // strings: (|(something)(somethingelse))
  +                while ( (userPatternString.charAt(startParenLoc + 1) == '|') ||
  +                        (startParenLoc != 0 && userPatternString.charAt(startParenLoc - 1) == '\\') ) {
  +                    startParenLoc = userPatternString.indexOf("(", startParenLoc+1);
  +                }
  +                endParenLoc = userPatternString.indexOf(")", startParenLoc+1);
  +                // weed out escaped end-parens
  +                while (userPatternString.charAt(endParenLoc - 1) == '\\') {
  +                    endParenLoc = userPatternString.indexOf(")", endParenLoc+1);
  +                }
  +                String nextPathPart = userPatternString.substring
  +                    (startParenLoc+1, endParenLoc);
  +                pathList.add(nextPathPart);
  +                startingPoint = endParenLoc+1;
  +                startParenLoc = userPatternString.indexOf('(', startingPoint);
  +            }
  +            return (String[])pathList.toArray(new String[] {});
  +        }
  +        return null;
  +
  +    }
  +
  +
  +    /**
  +     * Given an LDAP search string, returns the string with certain characters
  +     * escaped according to RFC 2254 guidelines.
  +     * The character mapping is as follows:
  +     *     char ->  Replacement
  +     *    ---------------------------
  +     *     *  -> \2a
  +     *     (  -> \28
  +     *     )  -> \29
  +     *     \  -> \5c
  +     *     \0 -> \00
  +     * @param inString string to escape according to RFC 2254 guidelines
  +     * @return
  +     */
  +    protected String doRFC2254Encoding(String inString) {
  +        StringBuffer buf = new StringBuffer(inString.length());
  +        for (int i = 0; i < inString.length(); i++) {
  +            char c = inString.charAt(i);
  +            switch (c) {
  +                case '\\':
  +                    buf.append("\\5c");
  +                    break;
  +                case '*':
  +                    buf.append("\\2a");
  +                    break;
  +                case '(':
  +                    buf.append("\\28");
  +                    break;
  +                case ')':
  +                    buf.append("\\29");
  +                    break;
  +                case '\0':
  +                    buf.append("\\00");
  +                    break;
  +                default:
  +                    buf.append(c);
  +                    break;
  +            }
  +        }
  +        return buf.toString();
  +    }
  +
   
   }
   
  @@ -1581,10 +1732,7 @@
       ArrayList roles = null;
   
   
  -    User(String username,
  -             String dn,
  -             String password,
  -             ArrayList roles) {
  +    User(String username, String dn, String password, ArrayList roles) {
           this.username = username;
           this.dn = dn;
           this.password = password;
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org