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);
+ }
+ }
}