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