You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by ol...@apache.org on 2004/06/23 08:50:25 UTC

cvs commit: jakarta-commons/httpclient/src/test/org/apache/commons/httpclient TestHttpState.java

olegk       2004/06/22 23:50:25

  Modified:    httpclient/src/java/org/apache/commons/httpclient
                        HttpState.java
               httpclient/src/java/org/apache/commons/httpclient/auth
                        AuthScope.java
               httpclient/src/test/org/apache/commons/httpclient
                        TestHttpState.java
  Log:
  PR #29540 (HttpState#matchCredentials is broken)
  
  Changelog:
  
  * Fixes credentials scope matching algorithm
  
  Contributed by Oleg Kalnichevski
  Reviewed by Michael Becke
  
  Revision  Changes    Path
  1.35      +32 -30    jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java
  
  Index: HttpState.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java,v
  retrieving revision 1.34
  retrieving revision 1.35
  diff -u -r1.34 -r1.35
  --- HttpState.java	12 Jun 2004 22:47:23 -0000	1.34
  +++ HttpState.java	23 Jun 2004 06:50:25 -0000	1.35
  @@ -310,7 +310,7 @@
        * have been explictly supplied for the challenging host. Any previous 
        * credentials for the given realm on the given host will be overwritten.
        * 
  -     * @param scope the authentication scope
  +     * @param realm the authentication realm
        * @param host the host the realm belongs to
        * @param credentials the authentication {@link Credentials credentials} 
        * for the given realm.
  @@ -330,7 +330,7 @@
        * Sets the {@link Credentials credentials} for the given authentication 
        * scope. Any previous credentials for the given scope will be overwritten.
        * 
  -     * @param scope the {@link AuthScope authentication scope}
  +     * @param authscope the {@link AuthScope authentication scope}
        * @param credentials the authentication {@link Credentials credentials} 
        * for the given scope.
        * 
  @@ -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;
       }
  @@ -389,7 +391,7 @@
        * the default Credentials.  If there are no default credentials, return
        * <code>null</code>.
        *
  -     * @param scope the authentication scope
  +     * @param realm the authentication realm
        * @param host the host the realm is on
        * @return the credentials 
        * 
  @@ -407,7 +409,7 @@
       /**
        * Get the {@link Credentials credentials} for the given authentication scope.
        *
  -     * @param scope the {@link AuthScope authentication scope}
  +     * @param authscope the {@link AuthScope authentication scope}
        * @return the credentials 
        * 
        * @see #setCredentials(AuthScope, Credentials)
  @@ -433,7 +435,7 @@
        * credentials for the given proxy realm on the given proxy host will be 
        * overwritten.
        *
  -     * @param scope the authentication scope
  +     * @param realm the authentication realm
        * @param proxyHost the proxy host
        * @param credentials the authentication credentials for the given realm
        * 
  @@ -455,7 +457,7 @@
        * Sets the {@link Credentials proxy credentials} for the given authentication 
        * realm. Any previous credentials for the given realm will be overwritten.
        * 
  -     * @param scope the {@link AuthScope authentication scope}
  +     * @param authscope the {@link AuthScope authentication scope}
        * @param credentials the authentication {@link Credentials credentials} 
        * for the given realm.
        * 
  @@ -486,7 +488,7 @@
        * the default Credentials.  If there are no default credentials, return
        * <code>null</code>.
        * 
  -     * @param scope the authentication scope
  +     * @param realm the authentication realm
        * @param proxyHost the proxy host the realm is on
        * @return the credentials 
        * @see #setProxyCredentials(String, String, Credentials)
  @@ -502,7 +504,7 @@
       /**
        * Get the {@link Credentials proxy credentials} for the given authentication scope.
        *
  -     * @param scope the {@link AuthScope authentication scope}
  +     * @param authscope the {@link AuthScope authentication scope}
        * @return the credentials 
        * 
        * @see #setProxyCredentials(AuthScope, Credentials)
  
  
  
  1.2       +63 -29    jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthScope.java
  
  Index: AuthScope.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthScope.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- AuthScope.java	12 Jun 2004 22:47:23 -0000	1.1
  +++ AuthScope.java	23 Jun 2004 06:50:25 -0000	1.2
  @@ -32,7 +32,7 @@
   /** 
    * The class represents an authentication scope consisting of a host name,
    * a port number, a realm name and an authentication scheme name which 
  - * {@link Credentials} apply to.
  + * {@link org.apache.commons.httpclient.Credentials} apply to.
    * 
    * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
    * @author <a href="mailto:adrian@intencha.com">Adrian Sutton</a>
  @@ -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 <tt>null</tt> acts as a
  -     * wildcard so if either of the parameters are <tt>null</tt>, 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 <tt>null</tt> acts as a
  -     * wildcard so if either of the parameters are <tt>null</tt>, 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()
        */
  
  
  
  1.7       +117 -60   jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpState.java
  
  Index: TestHttpState.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpState.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- TestHttpState.java	13 Jun 2004 12:13:08 -0000	1.6
  +++ TestHttpState.java	23 Jun 2004 06:50:25 -0000	1.7
  @@ -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 <a href="mailto:jsdever@apache.org">Jeff Dever</a>
    * @author Sean C. Sullivan
  + * @author Oleg Kalnichevski
    * 
    * @version $Id$
    * 
    */
   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);
  +    }
   }
  
  
  

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