rwaldhoff    01/08/08 08:29:05

  Modified:    httpclient/src/java/org/apache/commons/httpclient
                        Authenticator.java HttpClient.java State.java
               httpclient/src/test/org/apache/commons/httpclient
                        TestAuthenticator.java
  Log:
  Refactored the handling of Credentials in order to
  * support multiple basic authentication realms
  * store authentication Credentials as part of State rather than part of HttpClient
  
  Revision  Changes    Path
  1.3       +61 -21    
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Authenticator.java
  
  Index: Authenticator.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Authenticator.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Authenticator.java        2001/05/11 21:42:50     1.2
  +++ Authenticator.java        2001/08/08 15:29:05     1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Authenticator.java,v
 1.2 2001/05/11 21:42:50 jericho Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/05/11 21:42:50 $
  + * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/Authenticator.java,v
 1.3 2001/08/08 15:29:05 rwaldhoff Exp $
  + * $Revision: 1.3 $
  + * $Date: 2001/08/08 15:29:05 $
    *
    * ====================================================================
    *
  @@ -63,12 +63,17 @@
   
   package org.apache.commons.httpclient;
   
  +import java.util.StringTokenizer;
  +import java.util.NoSuchElementException;
  +import org.apache.commons.httpclient.log.*;
  +
   /**
    * Authenticate helper.
    *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Remy Maucherat</a>
    */
   public class Authenticator {
  +    static private final Log log = 
LogSource.getInstance("org.apache.commons.httpclient.Authenticator");
   
   
       // ----------------------------------------------------- Instance Variables
  @@ -89,17 +94,20 @@
        * @param state State
        * @param credentials Credentials to use to answser the challenge
        * @return String response to the challenge
  +     * @deprecated
        */
       public static String challengeResponse(State state,
                                              Credentials credentials)
           throws HttpException {
  +        log.debug("Authenticator.challengeResponse(State,Credentials)");
   
           if (credentials == null)
               throw new HttpException(HttpException.NO_CREDENTIALS_GIVEN);
   
           String challenge = state.getAuthenticateToken();
  -        if (challenge == null)
  +        if (challenge == null) {
               return null;
  +        }
   
           int space = challenge.indexOf(' ');
           if (space < 0)
  @@ -107,42 +115,74 @@
   
           String challengeName = challenge.substring(0, space);
   
  -        if (challengeName.equalsIgnoreCase("basic")) {
  +        if ("basic".equalsIgnoreCase(challengeName)) {
               return basic(state, credentials);
  -        } else if (challengeName.equalsIgnoreCase("digest")) {
  -            return digest(state, credentials);
  +        } else if ("digest".equalsIgnoreCase(challengeName)) {
  +            throw new UnsupportedOperationException("Digest authentication is not 
supported.");
           } else {
  +            throw new UnsupportedOperationException("Authentication type \"" + 
challengeName + "\" is not recognized.");
  +        }
  +    }
   
  +
  +    public static String challengeResponse(State state) throws HttpException {
  +        log.debug("Authenticator.challengeResponse(State)");
  +        String challenge = state.getAuthenticateToken();
  +        if (challenge == null) {
  +            return null;
           }
  -        return null;
   
  -    }
  +        StringTokenizer toker = new StringTokenizer(challenge);
  +        String challengeName = null;
  +        try {
  +            challengeName = toker.nextToken();
  +        } catch(NoSuchElementException e) {
  +            return null;
  +        }
   
  +        if ("basic".equalsIgnoreCase(challengeName)) {
  +            String realm = null;
  +            try {
  +                realm = toker.nextToken();
  +            } catch(NoSuchElementException e) {
  +                throw new HttpException("Expected realm name in basic 
authentication challenge.");
  +            }
  +            return basic(realm,state);
  +        } else if ("digest".equalsIgnoreCase(challengeName)) {
  +            throw new UnsupportedOperationException("Digest authentication is not 
supported.");
  +        } else {
  +            throw new UnsupportedOperationException("Authentication type \"" + 
challengeName + "\" is not recognized.");
  +        }
  +    }
   
       /**
        * Generate a basic response.
        *
        * @param credentials Credentials to use to answser the challenge
  +     * @deprecated
        */
       public static String basic(State state, Credentials credentials) {
  -
  +        log.debug("Authenticator.basic(State,Credentials)");
           String authString = credentials.getUserName() + ":"
               + credentials.getPassword();
           return "Basic " + new String(base64.encode(authString.getBytes()));
   
       }
  -
  -
  -    /**
  -     * Generate a basic response.
  -     *
  -     * @param credentials Credentials to use to answser the challenge
  -     */
  -    public static String digest(State state, Credentials credentials) {
  -
  -        return null;
   
  +    public static String basic(String realm, State state) throws HttpException {
  +        log.debug("Authenticator.basic(String,State)");
  +        Credentials cred = state.getCredentials(realm);
  +        if(null == cred) {
  +            if(log.isInfoEnabled()) {
  +                log.info("No credentials found for realm \"" + realm + "\", 
attempting to use default credentials.");
  +            }
  +            cred = state.getDefaultCredentials();
  +            if(null == cred) {
  +                throw new HttpException(HttpException.NO_CREDENTIALS_GIVEN);
  +            }
  +        }
  +        String authString = cred.getUserName() + ":" + cred.getPassword();
  +        return "Basic " + new String(base64.encode(authString.getBytes()));
       }
  -
   
   }
  
  
  
  1.25      +67 -16    
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpClient.java
  
  Index: HttpClient.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpClient.java,v
  retrieving revision 1.24
  retrieving revision 1.25
  diff -u -r1.24 -r1.25
  --- HttpClient.java   2001/08/02 23:00:58     1.24
  +++ HttpClient.java   2001/08/08 15:29:05     1.25
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpClient.java,v
 1.24 2001/08/02 23:00:58 rwaldhoff Exp $
  - * $Revision: 1.24 $
  - * $Date: 2001/08/02 23:00:58 $
  + * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpClient.java,v
 1.25 2001/08/08 15:29:05 rwaldhoff Exp $
  + * $Revision: 1.25 $
  + * $Date: 2001/08/08 15:29:05 $
    *
    * ====================================================================
    *
  @@ -107,7 +107,10 @@
        *             instead
        */
       public HttpClient(String user, String password) {
  -        setCredentials(new Credentials(user, password));
  +        if(null == state) {
  +            state = new State();
  +        }
  +        state.setDefaultCredentials(new Credentials(user, password));
       }
   
   
  @@ -135,12 +138,10 @@
        */
       public HttpClient(URL url, String user, String password) {
           this(url);
  -        /*
  -         * when this constructor is finally removed,
  -         * the endSession method should be altered
  -         * to null out the credentials
  -         */
  -        setCredentials(new Credentials(user, password));
  +        if(null == state) {
  +            state = new State();
  +        }
  +        state.setDefaultCredentials(new Credentials(user, password));
       }
   
   
  @@ -245,22 +246,45 @@
   
       /**
        * Set the credentials to use.
  +     * @deprecated
        */
       public void setCredentials(Credentials credentials) {
  -        this.credentials = credentials;
  +        if(null == state) {
  +            state = new State();
  +        }
  +        state.setDefaultCredentials(credentials);
       }
   
  +    public void setCredentials(String realm, Credentials credentials) {
  +        if(null == state) {
  +            state = new State();
  +        }
  +        state.setCredentials(realm,credentials);
  +    }
   
       /**
        * Get the credentials used.
  +     * @deprecated
        */
       public Credentials getCredentials() {
  -        return credentials;
  +        if(null == state) {
  +            return null;
  +        } else {
  +            return state.getDefaultCredentials();
  +        }
       }
   
  +    public Credentials getCredentials(String realm) {
  +        if(null == state) {
  +            return null;
  +        } else {
  +            return state.getCredentials(realm);
  +        }
  +    }
   
       /**
        * Set debug level.
  +     * @deprecated
        */
       public void setDebug(int debug) {
           this.debug = debug;
  @@ -309,19 +333,47 @@
   
       /**
        * Get the username.
  +     * @deprecated
        */
       public String getUserName() {
  -        return (credentials != null) ? credentials.getUserName() : null;
  +        if(null == state) {
  +            return null;
  +        } else {
  +            Credentials creds = state.getDefaultCredentials();
  +            return null == creds ? null : creds.getUserName();
  +        }
       }
   
  +    public String getUserName(String realm) {
  +        if(null == state) {
  +            return null;
  +        } else {
  +            Credentials creds = state.getCredentials(realm);
  +            return null == creds ? null : creds.getUserName();
  +        }
  +    }
   
       /**
        * Get the password.
  +     * @deprecated
        */
       public String getPassword() {
  -        return (credentials != null) ? credentials.getPassword() : null;
  +        if(null == state) {
  +            return null;
  +        } else {
  +            Credentials creds = state.getDefaultCredentials();
  +            return null == creds ? null : creds.getPassword();
  +        }
       }
   
  +    public String getPassword(String realm) {
  +        if(null == state) {
  +            return null;
  +        } else {
  +            Credentials creds = state.getCredentials(realm);
  +            return null == creds ? null : creds.getPassword();
  +        }
  +    }
   
       /**
        * Set stream interceptor.
  @@ -855,8 +907,7 @@
   
           if (state.getAuthenticateToken() != null) {
   
  -            String challengeResponse = Authenticator.challengeResponse
  -                (state, credentials);
  +            String challengeResponse = Authenticator.challengeResponse(state);
               if (challengeResponse != null) {
                   String authorizationHeader = "Authorization: " +  challengeResponse 
+ "\r\n";
                   wireLog.info(">> \"" + authorizationHeader + "\"");
  
  
  
  1.3       +80 -43    
jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/State.java
  
  Index: State.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/State.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- State.java        2001/07/22 19:28:33     1.2
  +++ State.java        2001/08/08 15:29:05     1.3
  @@ -1,13 +1,13 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/State.java,v
 1.2 2001/07/22 19:28:33 remm Exp $
  - * $Revision: 1.2 $
  - * $Date: 2001/07/22 19:28:33 $
  + * $Header: 
/home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/State.java,v
 1.3 2001/08/08 15:29:05 rwaldhoff Exp $
  + * $Revision: 1.3 $
  + * $Date: 2001/08/08 15:29:05 $
    *
    * ====================================================================
    *
    * The Apache Software License, Version 1.1
    *
  - * Copyright (c) 1999 The Apache Software Foundation.  All rights 
  + * Copyright (c) 1999 The Apache Software Foundation.  All rights
    * reserved.
    *
    * Redistribution and use in source and binary forms, with or without
  @@ -15,7 +15,7 @@
    * are met:
    *
    * 1. Redistributions of source code must retain the above copyright
  - *    notice, this list of conditions and the following disclaimer. 
  + *    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
  @@ -23,15 +23,15 @@
    *    distribution.
    *
    * 3. The end-user documentation included with the redistribution, if
  - *    any, must include the following acknowlegement:  
  - *       "This product includes software developed by the 
  + *    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 
  + *    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"
  @@ -59,38 +59,40 @@
    *
    * [Additional notices, if required by prior licensing conditions]
    *
  - */ 
  + */
   
   package org.apache.commons.httpclient;
   
   import java.io.IOException;
  -import java.util.Hashtable;
  +import java.util.HashMap;
   import java.util.Vector;
   import java.util.Enumeration;
   
   /**
    * Session state.
  - * 
  + *
    * @author <a href="mailto:[EMAIL PROTECTED]";>Remy Maucherat</a>
    */
   public class State {
  -    
  -    
  +
  +
       // ----------------------------------------------------- Instance Variables
  -    
  -    
  +
  +    protected HashMap credMap = new HashMap();
  +    protected Credentials defaultCred = null;
  +
       /**
        * Authenticate token.
        */
       protected String authenticateToken = null;
  -    
  -    
  +
  +
        /**
         * Cookies.
         */
       protected Vector cookies = new Vector();
  -    
  -    
  +
  +
       /**
        * URL encoding switch.
        */
  @@ -101,8 +103,8 @@
        * URL encoding charset.
        */
       protected String URLEncodingCharset = "UTF8";
  -    
  -    
  +
  +
       /**
        * URL decoding charset.
        */
  @@ -110,22 +112,22 @@
   
   
       // ------------------------------------------------------------- Properties
  -    
  -    
  +
  +
       /**
        * Add a cookie
        */
       public void addCookie(Cookie cookie) {
  -        
  +
           if (cookie != null) {
  -            
  +
               boolean found = false;
  -            
  +
               // let's remove the cookie if it's already saved
  -            for (Enumeration e = cookies.elements(); 
  +            for (Enumeration e = cookies.elements();
                    !found && e.hasMoreElements(); ) {
                   Cookie tmp = (Cookie) e.nextElement();
  -                if (cookie.getDomain().equals(tmp.getDomain()) && 
  +                if (cookie.getDomain().equals(tmp.getDomain()) &&
                       cookie.getName().equals(tmp.getName())) {
                       found = true;
                       cookies.removeElement(tmp);
  @@ -133,10 +135,10 @@
               }
               cookies.addElement(cookie);
           }
  -        
  +
       }
  -    
  -    
  +
  +
       /**
        * Add a number of cookies
        */
  @@ -147,34 +149,34 @@
               }
           }
       }
  -    
  -    
  +
  +
       // FIXME: this breaks encapsulation on the cookie vector
       public Vector getCookies() {
           return cookies;
       }
  -    
  -    
  +
  +
       /**
        * Authenticate token setter.
  -     * 
  +     *
        * @param authenticateToken Authenticate token
        */
       public void setAuthenticateToken(String authenticateToken) {
           this.authenticateToken = authenticateToken;
       }
  -    
  -    
  +
  +
       /**
        * Authenticate token accessor.
  -     * 
  +     *
        * @return String authenticate token
        */
       public String getAuthenticateToken() {
           return authenticateToken;
       }
  -    
  -    
  +
  +
       /**
        * Set the URL encoding flag.
        */
  @@ -198,10 +200,45 @@
           this.URLDecodingCharset = URLDecodingCharset;
       }
   
  +    /**
  +     * Set the {@link Credentials} for the given authentication realm.
  +     */
  +    public void setCredentials(String realm, Credentials credentials) {
  +        credMap.put(realm,credentials);
  +    }
  +
  +
  +    /**
  +     * Get the {@link Credentials} for the given authentication realm.
  +     */
  +    public Credentials getCredentials(String realm) {
  +        return (Credentials)(credMap.get(realm));
  +    }
   
  +    /**
  +     * Set the default {@link Credentials}, used when no
  +     * other realm is specified, or when no credential is
  +     * found to match the given realm.
  +     *
  +     * @deprecated Use
  +     * {@link 
#setCredentials(java.lang.String,org.apache.commons.httpclient.Credentials)}
  +     * instead.
  +     */
  +    public void setDefaultCredentials(Credentials credentials) {
  +        defaultCred = credentials;
  +    }
  +
  +
  +    /**
  +     * Get the defualt {@link Credentials}.
  +     */
  +    public Credentials getDefaultCredentials() {
  +        return defaultCred;
  +    }
  +
       // --------------------------------------------------------- Public Methods
  -    
  -    
  +
  +
       /**
        * URL encode.
        */
  
  
  
  1.2       +68 -8     
jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestAuthenticator.java
  
  Index: TestAuthenticator.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestAuthenticator.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TestAuthenticator.java    2001/08/07 17:42:21     1.1
  +++ TestAuthenticator.java    2001/08/08 15:29:05     1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestAuthenticator.java,v
 1.1 2001/08/07 17:42:21 rwaldhoff Exp $
  - * $Revision: 1.1 $
  - * $Date: 2001/08/07 17:42:21 $
  + * $Header: 
/home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestAuthenticator.java,v
 1.2 2001/08/08 15:29:05 rwaldhoff Exp $
  + * $Revision: 1.2 $
  + * $Date: 2001/08/08 15:29:05 $
    * ====================================================================
    * Copyright (C) The Apache Software Foundation. All rights reserved.
    *
  @@ -18,7 +18,7 @@
    * Unit tests for {@link Authenticator}.
    *
    * @author Rodney Waldhoff
  - * @version $Id: TestAuthenticator.java,v 1.1 2001/08/07 17:42:21 rwaldhoff Exp $
  + * @version $Id: TestAuthenticator.java,v 1.2 2001/08/08 15:29:05 rwaldhoff Exp $
    */
   public class TestAuthenticator extends TestCase {
   
  @@ -41,16 +41,76 @@
   
       // ----------------------------------------------------------- Test Methods
   
  -    public void testChallengeResponseWithNullCreds() {
  +    public void testBasicAuthenticationWithNoCreds() {
           State state = new State();
  +        state.setAuthenticateToken("Basic realm1");
           try {
  -            Authenticator.challengeResponse(state,null);
  -            fail("Should have thrown an HttpException");
  +            Authenticator.challengeResponse(state);
  +            fail("Should have thrown HttpException");
           } catch(HttpException e) {
  -        } catch(Throwable t) {
  -            fail("Should have thrown an HttpException, found " + t + " instead.");
  +            // expected
           }
       }
   
  +    public void testBasicAuthenticationWithNoRealm() {
  +        State state = new State();
  +        state.setAuthenticateToken("Basic");
  +        try {
  +            Authenticator.challengeResponse(state);
  +            fail("Should have thrown HttpException");
  +        } catch(HttpException e) {
  +            // expected
  +        }
  +    }
  +
  +    public void testBasicAuthenticationWithNoChallenge() throws Exception {
  +        State state = new State();
  +        assert(null == Authenticator.challengeResponse(state));
  +    }
  +
  +    public void testBasicAuthenticationWithNullState() throws Exception {
  +        try {
  +            Authenticator.challengeResponse(null);
  +            fail("Should have thrown NullPointerException");
  +        } catch(NullPointerException e) {
  +            // expected
  +        }
  +    }
  +
  +    public void testBasicAuthenticationWithDefaultCreds() throws Exception {
  +        State state = new State();
  +        state.setAuthenticateToken("Basic realm1");
  +        state.setDefaultCredentials(new Credentials("username","password"));
  +        String response = Authenticator.challengeResponse(state);
  +        String expected = "Basic " + new 
String(Base64.encode("username:password".getBytes()));
  +        assertEquals(expected,response);
  +    }
  +
  +    public void testBasicAuthentication() throws Exception {
  +        State state = new State();
  +        state.setAuthenticateToken("Basic realm1");
  +        state.setCredentials("realm1",new Credentials("username","password"));
  +        String response = Authenticator.challengeResponse(state);
  +        String expected = "Basic " + new 
String(Base64.encode("username:password".getBytes()));
  +        assertEquals(expected,response);
  +    }
  +
  +    public void testBasicAuthenticationWithMutlipleRealms() throws Exception {
  +        State state = new State();
  +        state.setCredentials("realm1",new Credentials("username","password"));
  +        state.setCredentials("realm2",new Credentials("uname2","password2"));
  +        {
  +            state.setAuthenticateToken("Basic realm1");
  +            String response = Authenticator.challengeResponse(state);
  +            String expected = "Basic " + new 
String(Base64.encode("username:password".getBytes()));
  +            assertEquals(expected,response);
  +        }
  +        {
  +            state.setAuthenticateToken("Basic realm2");
  +            String response = Authenticator.challengeResponse(state);
  +            String expected = "Basic " + new 
String(Base64.encode("uname2:password2".getBytes()));
  +            assertEquals(expected,response);
  +        }
  +    }
   
   }
  
  
  

Reply via email to