funkman 2003/09/04 12:59:47 Modified: catalina build.xml webapps/tomcat-docs realm-howto.xml catalina/src/share/org/apache/catalina/realm JNDIRealm.java Added: catalina/src/test/org/apache/catalina/realm JNDIRealmTestCase.java Log: Per http://marc.theaimsgroup.com/?l=tomcat-dev&m=106254937722504&w=2 Allow Multiple user patterns in JNDIRealm and doc patch. Patch provided by Jeff Tulley (jtulley at novell.com) Revision Changes Path 1.133 +11 -1 jakarta-tomcat-4.0/catalina/build.xml Index: build.xml =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/build.xml,v retrieving revision 1.132 retrieving revision 1.133 diff -u -r1.132 -r1.133 --- build.xml 12 Mar 2003 21:38:05 -0000 1.132 +++ build.xml 4 Sep 2003 19:59:46 -0000 1.133 @@ -979,7 +979,7 @@ <!-- ==================== TEST: Execute Unit Tests ====================== --> <target name="test" if="junit.present" description="Run all unit test cases" - depends="build-tests,test-dir-context,test-util"> + depends="build-tests,test-dir-context,test-realm,test-util"> </target> <target name="test-dir-context" if="junit.present"> @@ -1004,6 +1004,16 @@ </java> <delete file="${test.webapp.war}"/> + </target> + + <target name="test-realm" if="junit.present"> + + <echo message="Running Realm tests"/> + <java classname="${test.runner}" fork="yes" + failonerror="${test.failonerror}"> + <arg value="org.apache.catalina.realm.JNDIRealmTestCase"/> + <classpath refid="test.classpath"/> + </java> </target> <target name="test-util" if="junit.present"> 1.13 +56 -14 jakarta-tomcat-4.0/webapps/tomcat-docs/realm-howto.xml Index: realm-howto.xml =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/webapps/tomcat-docs/realm-howto.xml,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- realm-howto.xml 7 May 2003 15:56:00 -0000 1.12 +++ realm-howto.xml 4 Sep 2003 19:59:47 -0000 1.13 @@ -362,7 +362,7 @@ <li>Password to be recognized by Tomcat when the user logs in. This value may in cleartext or digested - see below for more information.</li> - </ul></li> + </ul></li> <li>There must be a table, referenced below as the <em>user roles</em> table, that contains one row for every valid role that is assigned to a particular user. It is legal for a user to have zero, one, or more than @@ -373,13 +373,13 @@ <li>Username to be recognized by Tomcat (same value as is specified in the <em>users</em> table).</li> <li>Role name of a valid role associated with this user.</li> - </ul></li> + </ul></li> </ul> <h3>Quick Start</h3> - + <p>To set up Tomcat to use DataSourceRealm, you will need to follow these steps:</p> -<ol> +<ol> <li>If you have not yet done so, create tables and columns in your database that conform to the requirements described above.</li> <li>Configure a database username and password for use by Tomcat, that has @@ -418,7 +418,7 @@ generate more detailed output. If not specified, the default debugging detail level is zero (0).</p> </attribute> - + <attribute name="digest" required="false"> <p>The digest algorithm used to store passwords in non-plaintext formats. Valid values are those accepted for the algorithm name by the @@ -426,18 +426,18 @@ <a href="#Digested Passwords">Digested Passwords</a> for more information. If not specified, passwords are stored in clear text.</p> </attribute> - + <attribute name="roleNameCol" required="true"> <p>The name of the column, in the <em>user roles</em> table, that contains the name of a role assigned to this user.</p> </attribute> - + <attribute name="userCredCol" required="true"> <p>The name of the column, in the <em>users</em> table, that contains the password for this user (either in clear text, or digested if the <code>digest</code> attribute is set).</p> </attribute> - + <attribute name="userNameCol" required="true"> <p>The name of the column, in the <em>users</em> and <em>user roles</em> tables, that contains the username of this user.</p> @@ -559,11 +559,19 @@ attribute containing the username that is presented for authentication.</p> -<p>Often the distinguished name of the user's entry contains the -username presented for authentication but is otherwise the same for -all users. In this case the <strong>userPattern</strong> attribute may -be used to specify the DN, with "{0}" marking where -the username should be substituted.</p> +<p>There are multiple options for specifying where to look for users. +One is through the use of <strong>userPattern</strong>. This is set +to the distinguished name of the user entry, but with "{0}" marking +where the username should be substituted. If you want Tomcat to +search for the username in multiple places, you can supply multiple +locations in the <strong>userPattern</strong>. This is done by +surrounding each separate location with parentheses. For example, +"(cn={0},ou=users1,o=myorg)(cn={0},ou=users2,o=myorg)" will result in +Tomcat looking in ou=users1,o=myorg, and then ou=users2,o=myorg for the +username passed in from the authentication process. You can also use +the standard LDAP "OR" search format, for instance +"(|(cn={0},o=myorg)({0}))". Note that, as in this example, you can +do both context-less and fully-typed logins using this technique.</p> <p>Otherwise the realm must search the directory to find a unique entry containing the username. The following attributes configure this @@ -831,7 +839,8 @@ directory entry, following the syntax supported by the <code>java.text.MessageFormat</code> class with <code>{0}</code> marking where the actual username should be - inserted. You can use this property instead of + inserted. Multiple search locations are achieved by separating + each with parentheses. You can use this property instead of <code>userSearch</code>, <code>userSubtree</code> and <code>userBase</code> when the distinguished name contains the username and is otherwise the same for all users.</p> @@ -908,6 +917,12 @@ objectClass: organizationalUnit ou: people +# Define another entry to contain admin users +# searches for admin users are based on this entry +dn: ou=admins,dc=mycompany,dc=com +objectClass: organizationalUnit +ou: admins + # Define a user entry for Janet Jones dn: uid=jjones,ou=people,dc=mycompany,dc=com objectClass: inetOrgPerson @@ -926,6 +941,15 @@ mail: [EMAIL PROTECTED] userPassword: fred +# Define a user entry for an admin user +dn: uid=jsmith,ou=admins,dc=mycompany,dc=com +objectClass: inetOrgPerson +uid: jsmith +sn: smith +cn: jean smith +mail: [EMAIL PROTECTED] +userPassword: jean + # Define an entry to contain LDAP groups # searches for roles are based on this entry dn: ou=groups,dc=mycompany,dc=com @@ -938,6 +962,7 @@ cn: tomcat uniqueMember: uid=jjones,ou=people,dc=mycompany,dc=com uniqueMember: uid=fbloggs,ou=people,dc=mycompany,dc=com +uniqueMember: uid=jsmith,ou=admins,dc=mycompany,dc=com # Define an entry for the "role1" role dn: cn=role1,ou=groups,dc=mycompany,dc=com @@ -967,6 +992,23 @@ <code>userPattern</code>, authenticate by binding to the directory with this DN and the password received from the user, and search the directory to find the user's roles.</p> + +<p>If you want the realm to look in multiple pre-determined locations, +you can use a similar notation, surrounding each location by parentheses:</p> +<source> +<Realm className="org.apache.catalina.realm.JNDIRealm" debug="99" + connectionURL="ldap://localhost:389" + userPattern="(uid={0},ou=people,dc=mycompany,dc=com)(uid={0},ou=admins,dc=mycompany,dc=com)" + roleBase="ou=groups,dc=mycompany,dc=com" + roleName="cn" + roleSearch="(uniqueMember={0})" +/> +</source> +<p>This pre-supposes that the User IDs in these two groups are unique; +the first <code>userPattern</code> that results in a valid user ID +will be the one used. Note that you can support typeless logins as +well by adding "({0})" to the userPattern string above. Then, Jean Smith +can log in as "jsmith", or "uid=jsmith,ou=admins,dc=mycompany,dc=com".</p> <p>Now suppose that users are expected to enter their email address rather than their userid when logging in. In this case the realm must 1.15 +134 -33 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/realm/JNDIRealm.java Index: JNDIRealm.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/realm/JNDIRealm.java,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- JNDIRealm.java 8 Aug 2003 16:40:13 -0000 1.14 +++ JNDIRealm.java 4 Sep 2003 19:59:47 -0000 1.15 @@ -6,7 +6,7 @@ * ==================================================================== * The Apache Software License, Version 1.1 * - * Copyright (c) 1999-2002 The Apache Software Foundation. All rights + * Copyright (c) 1999-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without @@ -69,8 +69,10 @@ import java.util.ArrayList; import java.util.Hashtable; import java.util.List; + import javax.naming.Context; import javax.naming.CommunicationException; +import javax.naming.InvalidNameException; import javax.naming.NameNotFoundException; import javax.naming.NamingEnumeration; import javax.naming.NamingException; @@ -298,6 +300,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 +319,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 +374,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 +744,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 +756,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 +780,7 @@ } + /** * Setter for property alternateURL. * @@ -870,21 +901,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 +979,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 +1010,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 +1220,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 { @@ -1566,7 +1627,50 @@ } + /** + * 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; + } } // ------------------------------------------------------ Private Classes @@ -1581,10 +1685,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; 1.1 jakarta-tomcat-4.0/catalina/src/test/org/apache/catalina/realm/JNDIRealmTestCase.java Index: JNDIRealmTestCase.java =================================================================== /* * $Header: /home/cvspublic/jakarta-tomcat-4.0/catalina/src/test/org/apache/catalina/realm/JNDIRealmTestCase.java * $Revision: 1.1 $ * $Date: 2003/09/04 19:59:47 $ * * ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.realm; import junit.framework.Assert; import junit.framework.TestCase; /** * @author Jeff Tulley */ public class JNDIRealmTestCase extends TestCase { public JNDIRealmTestCase(String arg0) { super(arg0); } public void testParseSimpleUserPattern() { JNDIRealm realm = new JNDIRealm(); String searchPath = "o=somepath"; String[] expected = { searchPath }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseSimpleUserPatternWithParens() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(o=somepath)"; String[] expected = { "o=somepath" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } // would somebody do this? Probably not, but it wouldn't hurt to support it for resiliency sake public void testParseSimpleUserPatternWithParensAndLDAPOr() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(|(o=somepath))"; String[] expected = { "o=somepath" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseSimpleUserPatternWithParensPathological() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(o=so\\(me\\)path)"; String[] expected = { "o=so\\(me\\)path" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseSimpleUserPatternWithParensReallyPathological() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(o=so\\(mepath\\))"; String[] expected = { "o=so\\(mepath\\)" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseTwoPartUserPattern() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(o=somepath)(o=someotherpath)"; String[] expected = { "o=somepath", "o=someotherpath" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } // people will probably try this, need to handle it public void testParseLegitLDAPTwoPartUserPattern() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(|(o=somepath)(o=someotherpath))"; String[] expected = { "o=somepath", "o=someotherpath" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseTwoPartUserPatternIgnoreWhitespace() { JNDIRealm realm = new JNDIRealm(); String searchPath = " (o=somepath) (o=someotherpath) "; String[] expected = { "o=somepath", "o=someotherpath" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } // people will probably try this, need to handle it public void testParseLegitLDAPTwoPartUserPatternIgnoreWhitespace() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(| (o=somepath) (o=someotherpath) )"; String[] expected = { "o=somepath", "o=someotherpath" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseTwoPartUserPatternPathological() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(o=somepath)(o=someo\\(ther\\(path)"; String[] expected = { "o=somepath", "o=someo\\(ther\\(path" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseLegitLDAPTwoPartUserPatternPathological() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(|(o=somepath)(o=someo\\(ther\\(path))"; String[] expected = { "o=somepath", "o=someo\\(ther\\(path" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseMultiPartUserPattern() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(o=somepath)(o=someotherpath)(o=somepath2)(o=someotherpath2)(o=somepath3)(o=someotherpath3)"; String[] expected = { "o=somepath", "o=someotherpath", "o=somepath2", "o=someotherpath2", "o=somepath3", "o=someotherpath3" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseComplexMultiPartUserPattern() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(cn={0},ou=ourou,o=somepath)(cn={0},ou=ourou,o=somepath2)(cn={0},ou=ourou,o=someotherpath2)"; String[] expected = { "cn={0},ou=ourou,o=somepath", "cn={0},ou=ourou,o=somepath2", "cn={0},ou=ourou,o=someotherpath2" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseLegitLDAPComplexMultiPartUserPattern() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(|(cn={0},ou=ourou,o=somepath)(cn={0},ou=ourou,o=somepath2)(cn={0},ou=ourou,o=someotherpath2))"; String[] expected = { "cn={0},ou=ourou,o=somepath", "cn={0},ou=ourou,o=somepath2", "cn={0},ou=ourou,o=someotherpath2" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseComplexMultiPartUserPatternEdirectorySyntax() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(|(cn={0}.ou=ourou.o=somepath)(cn={0}.ou=ourou.o=somepath2)(cn={0}.ou=ourou.o=someotherpath2))"; String[] expected = { "cn={0}.ou=ourou.o=somepath", "cn={0}.ou=ourou.o=somepath2", "cn={0}.ou=ourou.o=someotherpath2" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseComplexMultiPartUserPatternTypelessEdirectorySyntax() { JNDIRealm realm = new JNDIRealm(); String searchPath = "(|({0}.ourou.somepath)({0}.ourou.somepath2)({0}.ourou.someotherpath2))"; String[] expected = { "{0}.ourou.somepath", "{0}.ourou.somepath2", "{0}.ourou.someotherpath2" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseEmptyUserPattern() { JNDIRealm realm = new JNDIRealm(); String searchPath = ""; String[] expected = { "" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void testParseEmptyUserPatternWithParens() { JNDIRealm realm = new JNDIRealm(); String searchPath = "()"; String[] expected = { "" }; String[] actual = realm.parseUserPatternString(searchPath); assertStringArraysEquals(expected, actual); } public void assertStringArraysEquals(String[] expected, String[] actual) { Assert.assertTrue("not null", actual != null); Assert.assertEquals("array count is wrong", expected.length, actual.length); for (int i = 0; i < expected.length; i++) { Assert.assertEquals("element " + i + " is wrong", expected[i], actual[i]); } } public static void main(String[] args) { junit.textui.TestRunner.run(JNDIRealmTestCase.class); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]