Index: java/org/apache/commons/httpclient/HttpState.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java,v retrieving revision 1.34 diff -u -r1.34 HttpState.java --- java/org/apache/commons/httpclient/HttpState.java 12 Jun 2004 22:47:23 -0000 1.34 +++ java/org/apache/commons/httpclient/HttpState.java 20 Jun 2004 13:09:43 -0000 @@ -355,24 +355,26 @@ * @return the credentials * */ - private static Credentials matchCredentials(HashMap map, AuthScope authscope) { - AuthScope key = authscope; - Credentials creds = (Credentials) map.get(key); - if (creds == null && authscope.getScheme() != null) { - key = new AuthScope(authscope.getHost(), authscope.getPort(), authscope.getRealm()); - creds = (Credentials) map.get(key); - } - if (creds == null && authscope.getRealm() != null) { - key = new AuthScope(authscope.getHost(), authscope.getPort()); - creds = (Credentials) map.get(key); - } - if (creds == null && authscope.getPort() >= 0) { - key = new AuthScope(authscope.getHost(), -1); - creds = (Credentials) map.get(key); - } - if (creds == null && authscope.getHost() != null) { - key = AuthScope.ANY; - creds = (Credentials) map.get(key); + private static Credentials matchCredentials(final HashMap map, final AuthScope authscope) { + // see if we get a direct hit + Credentials creds = (Credentials)map.get(authscope); + if (creds == null) { + // Nope. + // Do a full scan + int bestMatchFactor = -1; + AuthScope bestMatch = null; + Iterator items = map.keySet().iterator(); + while (items.hasNext()) { + AuthScope current = (AuthScope)items.next(); + int factor = authscope.match(current); + if (factor > bestMatchFactor) { + bestMatchFactor = factor; + bestMatch = current; + } + } + if (bestMatch != null) { + creds = (Credentials)map.get(bestMatch); + } } return creds; } Index: java/org/apache/commons/httpclient/auth/AuthScope.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthScope.java,v retrieving revision 1.1 diff -u -r1.1 AuthScope.java --- java/org/apache/commons/httpclient/auth/AuthScope.java 12 Jun 2004 22:47:23 -0000 1.1 +++ java/org/apache/commons/httpclient/auth/AuthScope.java 20 Jun 2004 13:09:44 -0000 @@ -103,10 +103,10 @@ public AuthScope(final String host, int port, final String realm, final String scheme) { - this.host = host; - this.port = port; - this.realm = realm; - this.scheme = scheme; + this.host = (host == null) ? ANY_HOST: host.toLowerCase(); + this.port = (port < 0) ? ANY_PORT: port; + this.realm = (realm == null) ? ANY_REALM: realm; + this.scheme = (scheme == null) ? ANY_SCHEME: scheme.toUpperCase();; } /** Creates a new credentials scope for the given @@ -198,37 +198,70 @@ return this.scheme; } - /** Determines if the given parameters match. Note that null acts as a - * wildcard so if either of the parameters are null, it is considered a match. + /** Determines if the given parameters are equal. * * @param p1 the parameter * @param p2 the other parameter - * @return boolean true if the parameters match, otherwise false. + * @return boolean true if the parameters are equal, otherwise false. */ - private static boolean paramsMatchIgnoreCase(final String p1, final String p2) { - return p1 == null || p2 == null || p1.equalsIgnoreCase(p2); + private static boolean paramsEqual(final String p1, final String p2) { + if (p1 == null) { + return p1 == p2; + } else { + return p1.equals(p2); + } } - /** Determines if the given parameters match. Note that null acts as a - * wildcard so if either of the parameters are null, it is considered a match. + /** Determines if the given parameters are equal. * * @param p1 the parameter * @param p2 the other parameter - * @return boolean true if the parameters match, otherwise false. + * @return boolean true if the parameters are equal, otherwise false. */ - private static boolean paramsMatch(final String p1, final String p2) { - return p1 == null || p2 == null || p1.equals(p2); + private static boolean paramsEqual(int p1, int p2) { + return p1 == p2; } - /** Determines if the given parameters match. Note that negative value acts as a - * wildcard so if either of the parameters are negative, it is considered a match. + /** + * Tests if the authentication scopes match. * - * @param p1 the parameter - * @param p2 the other parameter - * @return boolean true if the parameters match, otherwise false. + * @return the match factor. Negative value signifies no match. + * Non-negative signifies a match. The greater the returned value + * the closer the match. + * + * @since 3.0 */ - private static boolean paramsMatch(int p1, int p2) { - return p1 < 0 || p2 < 0 || p1 == p2; + public int match(final AuthScope that) { + int factor = 0; + if (paramsEqual(this.scheme, that.scheme)) { + factor += 1; + } else { + if (this.scheme != ANY_SCHEME && that.scheme != ANY_SCHEME) { + return -1; + } + } + if (paramsEqual(this.realm, that.realm)) { + factor += 2; + } else { + if (this.realm != ANY_REALM && that.realm != ANY_REALM) { + return -1; + } + } + if (paramsEqual(this.port, that.port)) { + factor += 4; + } else { + if (this.port != ANY_PORT && that.port != ANY_PORT) { + return -1; + } + } + if (paramsEqual(this.host, that.host)) { + factor += 8; + } else { + if (this.host != ANY_HOST && that.host != ANY_HOST) { + return -1; + } + } + return factor; } /** @@ -246,10 +279,10 @@ } AuthScope that = (AuthScope) o; return - paramsMatchIgnoreCase(this.host, that.host) - && paramsMatch(this.port, that.port) - && paramsMatch(this.realm, that.realm) - && paramsMatchIgnoreCase(this.scheme, that.scheme); + paramsEqual(this.host, that.host) + && paramsEqual(this.port, that.port) + && paramsEqual(this.realm, that.realm) + && paramsEqual(this.scheme, that.scheme); } /** @@ -278,6 +311,7 @@ } return buffer.toString(); } + /** * @see java.lang.Object#hashCode() */ Index: test/org/apache/commons/httpclient/TestHttpState.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpState.java,v retrieving revision 1.6 diff -u -r1.6 TestHttpState.java --- test/org/apache/commons/httpclient/TestHttpState.java 13 Jun 2004 12:13:08 -0000 1.6 +++ test/org/apache/commons/httpclient/TestHttpState.java 20 Jun 2004 13:09:45 -0000 @@ -30,6 +30,8 @@ package org.apache.commons.httpclient; +import org.apache.commons.httpclient.auth.AuthScope; + import junit.framework.*; /** @@ -39,17 +41,26 @@ * @author Rodney Waldhoff * @author Jeff Dever * @author Sean C. Sullivan + * @author Oleg Kalnichevski * * @version $Id: TestHttpState.java,v 1.6 2004/06/13 12:13:08 olegk Exp $ * */ public class TestHttpState extends TestCase { - public final Credentials creds1 = new UsernamePasswordCredentials("user1", "pass1"); - public final Credentials creds2 = new UsernamePasswordCredentials("user2", "pass2"); - - public final String realm1 = "realm1"; - public final String realm2 = "realm2"; + public final static Credentials CREDS1 = + new UsernamePasswordCredentials("user1", "pass1"); + public final static Credentials CREDS2 = + new UsernamePasswordCredentials("user2", "pass2"); + + public final static AuthScope SCOPE1 = + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "realm1"); + public final static AuthScope SCOPE2 = + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "realm2"); + public final static AuthScope BOGUS = + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "bogus"); + public final static AuthScope DEFSCOPE = + new AuthScope("host", AuthScope.ANY_PORT, "realm"); // ------------------------------------------------------------ Constructor @@ -74,10 +85,10 @@ public void testHttpStateCredentials() { HttpState state = new HttpState(); - state.setCredentials(realm1, null, creds1); - state.setCredentials(realm2, null, creds2); - assertEquals(creds1, state.getCredentials(realm1, null)); - assertEquals(creds2, state.getCredentials(realm2, null)); + state.setCredentials(SCOPE1, CREDS1); + state.setCredentials(SCOPE2, CREDS2); + assertEquals(CREDS1, state.getCredentials(SCOPE1)); + assertEquals(CREDS2, state.getCredentials(SCOPE2)); } public void testToString() @@ -91,44 +102,43 @@ state.addCookie(new Cookie("flub", "duck", "yuck")); assertNotNull(state.toString()); - state.setCredentials(realm1, null, creds1); + state.setCredentials(SCOPE1, CREDS1); assertNotNull(state.toString()); - state.setProxyCredentials(realm2, null, creds2); + state.setProxyCredentials(SCOPE2, CREDS2); assertNotNull(state.toString()); } public void testHttpStateNoCredentials() { HttpState state = new HttpState(); - assertEquals(null, state.getCredentials("bogus", null)); + assertEquals(null, state.getCredentials(BOGUS)); } public void testHttpStateDefaultCredentials() { HttpState state = new HttpState(); - state.setCredentials(null, null, creds1); - state.setCredentials(realm2, null, creds2); - assertEquals(creds1, state.getCredentials("bogus", null)); + state.setCredentials(AuthScope.ANY, CREDS1); + state.setCredentials(SCOPE2, CREDS2); + assertEquals(CREDS1, state.getCredentials(BOGUS)); } - public void testHttpStateProxyCredentials() { HttpState state = new HttpState(); - state.setProxyCredentials(realm1, null, creds1); - state.setProxyCredentials(realm2, null, creds2); - assertEquals(creds1, state.getProxyCredentials(realm1, null)); - assertEquals(creds2, state.getProxyCredentials(realm2, null)); + state.setProxyCredentials(SCOPE1, CREDS1); + state.setProxyCredentials(SCOPE2, CREDS2); + assertEquals(CREDS1, state.getProxyCredentials(SCOPE1)); + assertEquals(CREDS2, state.getProxyCredentials(SCOPE2)); } public void testHttpStateProxyNoCredentials() { HttpState state = new HttpState(); - assertEquals(null, state.getProxyCredentials("bogus", null)); + assertEquals(null, state.getProxyCredentials(BOGUS)); } public void testHttpStateProxyDefaultCredentials() { HttpState state = new HttpState(); - state.setProxyCredentials(null, null, creds1); - state.setProxyCredentials(realm2, null, creds2); - assertEquals(creds1, state.getProxyCredentials("bogus", null)); + state.setProxyCredentials(AuthScope.ANY, CREDS1); + state.setProxyCredentials(SCOPE2, CREDS2); + assertEquals(CREDS1, state.getProxyCredentials(BOGUS)); } // --------------------------------- Test Methods for Selecting Credentials @@ -136,65 +146,112 @@ public void testDefaultCredentials() throws Exception { HttpState state = new HttpState(); Credentials expected = new UsernamePasswordCredentials("name", "pass"); - state.setCredentials(null, null, expected); - Credentials got = state.getCredentials("realm", "host"); + state.setCredentials(AuthScope.ANY, expected); + Credentials got = state.getCredentials(DEFSCOPE); assertEquals(got, expected); } public void testRealmCredentials() throws Exception { HttpState state = new HttpState(); Credentials expected = new UsernamePasswordCredentials("name", "pass"); - state.setCredentials("realm", "host", expected); - Credentials got = state.getCredentials("realm", "host"); + state.setCredentials(DEFSCOPE, expected); + Credentials got = state.getCredentials(DEFSCOPE); assertEquals(expected, got); } public void testHostCredentials() throws Exception { HttpState state = new HttpState(); Credentials expected = new UsernamePasswordCredentials("name", "pass"); - state.setCredentials(null, "host", expected); - Credentials got = state.getCredentials("realm", "host"); - assertEquals(expected, got); - } - - public void testBothCredentials() throws Exception { - HttpState state = new HttpState(); - Credentials expected = new UsernamePasswordCredentials("name", "pass"); - state.setCredentials("realm", "host", expected); - Credentials got = state.getCredentials("realm", "host"); + state.setCredentials( + new AuthScope("host", AuthScope.ANY_PORT, AuthScope.ANY_REALM), expected); + Credentials got = state.getCredentials(DEFSCOPE); assertEquals(expected, got); } public void testWrongHostCredentials() throws Exception { HttpState state = new HttpState(); Credentials expected = new UsernamePasswordCredentials("name", "pass"); - state.setCredentials(null, "host1", expected); - Credentials got = state.getCredentials("realm", "host2"); + state.setCredentials( + new AuthScope("host1", AuthScope.ANY_PORT, "realm"), expected); + Credentials got = state.getCredentials( + new AuthScope("host2", AuthScope.ANY_PORT, "realm")); assertNotSame(expected, got); } public void testWrongRealmCredentials() throws Exception { HttpState state = new HttpState(); Credentials cred = new UsernamePasswordCredentials("name", "pass"); - state.setCredentials("realm1", "host", cred); - Credentials got = state.getCredentials("realm2", "host"); - assertNotSame(cred, got); - } - - public void testRealmSpoof() throws Exception { - HttpState state = new HttpState(); - Credentials cred = new UsernamePasswordCredentials("name", "pass"); - state.setCredentials(null, "admin.apache.org", cred); - Credentials got = state.getCredentials("admin.apache.org", "myhost"); + state.setCredentials( + new AuthScope("host", AuthScope.ANY_PORT, "realm1"), cred); + Credentials got = state.getCredentials( + new AuthScope("host", AuthScope.ANY_PORT, "realm2")); assertNotSame(cred, got); } + + // ------------------------------- Test Methods for matching Credentials - public void testRealmSpoof2() throws Exception { + public void testScopeMatching() { + AuthScope authscope1 = new AuthScope("somehost", 80, "somerealm", "somescheme"); + AuthScope authscope2 = new AuthScope("someotherhost", 80, "somerealm", "somescheme"); + assertTrue(authscope1.match(authscope2) < 0); + + int m1 = authscope1.match( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, "somescheme")); + int m2 = authscope1.match( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "somerealm", AuthScope.ANY_SCHEME)); + assertTrue(m2 > m1); + + m1 = authscope1.match( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, "somescheme")); + m2 = authscope1.match( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "somerealm", AuthScope.ANY_SCHEME)); + assertTrue(m2 > m1); + + m1 = authscope1.match( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "somerealm", "somescheme")); + m2 = authscope1.match( + new AuthScope(AuthScope.ANY_HOST, 80, AuthScope.ANY_REALM, AuthScope.ANY_SCHEME)); + assertTrue(m2 > m1); + + m1 = authscope1.match( + new AuthScope(AuthScope.ANY_HOST, 80, "somerealm", "somescheme")); + m2 = authscope1.match( + new AuthScope("somehost", AuthScope.ANY_PORT, AuthScope.ANY_REALM, AuthScope.ANY_SCHEME)); + assertTrue(m2 > m1); + + m1 = authscope1.match(AuthScope.ANY); + m2 = authscope1.match( + new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM, "somescheme")); + assertTrue(m2 > m1); + } + + public void testCredentialsMatching() { + Credentials creds1 = new UsernamePasswordCredentials("name1", "pass1"); + Credentials creds2 = new UsernamePasswordCredentials("name2", "pass2"); + Credentials creds3 = new UsernamePasswordCredentials("name3", "pass3"); + + AuthScope scope1 = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM); + AuthScope scope2 = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, "somerealm"); + AuthScope scope3 = new AuthScope("somehost", AuthScope.ANY_PORT, AuthScope.ANY_REALM); + HttpState state = new HttpState(); - Credentials cred = new UsernamePasswordCredentials("name", "pass"); - state.setCredentials(null, "whatever", cred); - Credentials got = state.getCredentials("nullwhatever", null); - assertNotSame(cred, got); - } + state.setCredentials(scope1, creds1); + state.setCredentials(scope2, creds2); + state.setCredentials(scope3, creds3); + + Credentials got = state.getCredentials( + new AuthScope("someotherhost", 80, "someotherrealm", "basic")); + Credentials expected = creds1; + assertEquals(expected, got); + + got = state.getCredentials( + new AuthScope("someotherhost", 80, "somerealm", "basic")); + expected = creds2; + assertEquals(expected, got); + got = state.getCredentials( + new AuthScope("somehost", 80, "someotherrealm", "basic")); + expected = creds3; + assertEquals(expected, got); + } }