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 2003/12/10 22:04:13 UTC
cvs commit: jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/server ProxyAuthRequestHandler.java
olegk 2003/12/10 13:04:13
Modified: httpclient/src/java/org/apache/commons/httpclient
ConnectMethod.java HttpMethodBase.java
HttpMethodDirector.java
httpclient/src/java/org/apache/commons/httpclient/auth
AuthChallengeParser.java AuthScheme.java
AuthSchemeBase.java BasicScheme.java
DigestScheme.java HttpAuthenticator.java
NTLMScheme.java RFC2617Scheme.java
httpclient/src/test/org/apache/commons/httpclient
TestAuthenticator.java
httpclient/src/test/org/apache/commons/httpclient/server
ProxyAuthRequestHandler.java
Log:
Changelog:
* Another attempt at fixing NTLM proxy + basic host authentication (R: #24352)
* Plug-in mechanism for authentication modules
* AuthModule interface implementing authentication modules can now be
instantiated using default (parameter-less) constructor
* Authentication modules can now retain limited state information (the state is
retained within the lifetime of the method director)
* Authentication scheme selection routine can be easily parameterized
* Yet another massive refactoring of HttpMethodDirector
Contributed by Oleg Kalnichevski
Reviewed By Michael Becke
Revision Changes Path
1.23 +4 -5 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/ConnectMethod.java
Index: ConnectMethod.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/ConnectMethod.java,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- ConnectMethod.java 10 Nov 2003 23:19:49 -0000 1.22
+++ ConnectMethod.java 10 Dec 2003 21:04:13 -0000 1.23
@@ -171,7 +171,6 @@
+ "HttpConnection)");
addUserAgentRequestHeader(state, conn);
addHostRequestHeader(state, conn);
- addProxyAuthorizationRequestHeader(state, conn);
addProxyConnectionHeader(state, conn);
}
1.191 +7 -95 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java
Index: HttpMethodBase.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v
retrieving revision 1.190
retrieving revision 1.191
diff -u -r1.190 -r1.191
--- HttpMethodBase.java 13 Nov 2003 22:24:46 -0000 1.190
+++ HttpMethodBase.java 10 Dec 2003 21:04:13 -0000 1.191
@@ -68,12 +68,10 @@
import java.io.IOException;
import java.io.InputStream;
-import org.apache.commons.httpclient.auth.AuthScheme;
-import org.apache.commons.httpclient.auth.HttpAuthenticator;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.cookie.CookieSpec;
import org.apache.commons.httpclient.cookie.MalformedCookieException;
-import org.apache.commons.httpclient.params.*;
+import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.util.EncodingUtil;
import org.apache.commons.logging.Log;
@@ -146,12 +144,6 @@
/** Response trailer headers, if any. */
private HeaderGroup responseTrailerHeaders = new HeaderGroup();
- /** Actual authentication realm */
- private String realm = null;
-
- /** Actual proxy authentication realm */
- private String proxyRealm = null;
-
/** Path of the HTTP method. */
private String path = null;
@@ -1027,8 +1019,6 @@
path = null;
followRedirects = false;
doAuthentication = true;
- realm = null;
- proxyRealm = null;
queryString = null;
getRequestHeaderGroup().clear();
getResponseHeaderGroup().clear();
@@ -1096,45 +1086,6 @@
/**
- * Generates <tt>Authorization</tt> request header if needed, as long as no
- * <tt>Authorization</tt> request header already exists.
- *
- * @param state the {@link HttpState state} information associated with this method
- * @param conn the {@link HttpConnection connection} used to execute
- * this HTTP method
- *
- * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
- * can be recovered from.
- * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
- * cannot be recovered from.
- */
- protected void addAuthorizationRequestHeader(HttpState state,
- HttpConnection conn)
- throws IOException, HttpException {
- LOG.trace("enter HttpMethodBase.addAuthorizationRequestHeader("
- + "HttpState, HttpConnection)");
-
- // add authorization header, if needed
- if (getRequestHeader(HttpAuthenticator.WWW_AUTH_RESP) == null) {
- Header[] challenges = getResponseHeaderGroup().getHeaders(
- HttpAuthenticator.WWW_AUTH);
- if (challenges.length > 0) {
- try {
- AuthScheme authscheme = HttpAuthenticator.selectAuthScheme(challenges);
- HttpAuthenticator.authenticate(authscheme, this, conn, state);
- } catch (HttpException e) {
- // log and move on
- if (LOG.isErrorEnabled()) {
- LOG.error(e.getMessage(), e);
- }
- }
- }
- }
- }
-
-
-
- /**
* Generates <tt>Cookie</tt> request headers for those {@link Cookie cookie}s
* that match the given host, port and path.
*
@@ -1232,43 +1183,6 @@
}
/**
- * Generates <tt>Proxy-Authorization</tt> request header if needed, as long as no
- * <tt>Proxy-Authorization</tt> request header already exists.
- *
- * @param state the {@link HttpState state} information associated with this method
- * @param conn the {@link HttpConnection connection} used to execute
- * this HTTP method
- *
- * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
- * can be recovered from.
- * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
- * cannot be recovered from.
- */
- protected void addProxyAuthorizationRequestHeader(HttpState state,
- HttpConnection conn)
- throws IOException, HttpException {
- LOG.trace("enter HttpMethodBase.addProxyAuthorizationRequestHeader("
- + "HttpState, HttpConnection)");
-
- // add proxy authorization header, if needed
- if (getRequestHeader(HttpAuthenticator.PROXY_AUTH_RESP) == null) {
- Header[] challenges = getResponseHeaderGroup().getHeaders(
- HttpAuthenticator.PROXY_AUTH);
- if (challenges.length > 0) {
- try {
- AuthScheme authscheme = HttpAuthenticator.selectAuthScheme(challenges);
- HttpAuthenticator.authenticateProxy(authscheme, this, conn, state);
- } catch (HttpException e) {
- // log and move on
- if (LOG.isErrorEnabled()) {
- LOG.error(e.getMessage(), e);
- }
- }
- }
- }
- }
-
- /**
* Generates <tt>Proxy-Connection: Keep-Alive</tt> request header when
* communicating via a proxy server.
*
@@ -1326,8 +1240,6 @@
addUserAgentRequestHeader(state, conn);
addHostRequestHeader(state, conn);
addCookieRequestHeader(state, conn);
- addAuthorizationRequestHeader(state, conn);
- addProxyAuthorizationRequestHeader(state, conn);
addProxyConnectionHeader(state, conn);
}
@@ -2155,7 +2067,7 @@
* @return proxy authentication realm
*/
public String getProxyAuthenticationRealm() {
- return this.proxyRealm;
+ return null;
}
/**
@@ -2167,7 +2079,7 @@
* @return authentication realm
*/
public String getAuthenticationRealm() {
- return this.realm;
+ return null;
}
/**
1.11 +192 -102 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodDirector.java
Index: HttpMethodDirector.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodDirector.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- HttpMethodDirector.java 19 Nov 2003 21:11:16 -0000 1.10
+++ HttpMethodDirector.java 10 Dec 2003 21:04:13 -0000 1.11
@@ -64,15 +64,22 @@
package org.apache.commons.httpclient;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
+import org.apache.commons.httpclient.auth.AuthChallengeParser;
+import org.apache.commons.httpclient.auth.AuthPolicy;
import org.apache.commons.httpclient.auth.AuthScheme;
import org.apache.commons.httpclient.auth.AuthenticationException;
import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
import org.apache.commons.httpclient.auth.HttpAuthenticator;
import org.apache.commons.httpclient.auth.MalformedChallengeException;
-import org.apache.commons.httpclient.params.*;
+import org.apache.commons.httpclient.params.HttpClientParams;
+import org.apache.commons.httpclient.params.HttpParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -107,11 +114,19 @@
/** Proxy Realms that we tried to authenticate to */
private Set proxyRealms = null;
- /** Actual authentication realm */
- private String realm = null;
+ /** Actual authentication scheme */
+ private AuthScheme authScheme = null;
- /** Actual proxy authentication realm */
- private String proxyRealm = null;
+ /** Actual proxy authentication scheme */
+ private AuthScheme proxyAuthScheme = null;
+
+ //TODO: to be parameterized
+ private static final List AUTH_PREFERENCES = new ArrayList(3);
+ static {
+ AUTH_PREFERENCES.add(AuthPolicy.NTLM);
+ AUTH_PREFERENCES.add(AuthPolicy.DIGEST);
+ AUTH_PREFERENCES.add(AuthPolicy.BASIC);
+ }
public HttpMethodDirector(
final HttpConnectionManager connectionManager,
@@ -484,7 +499,12 @@
//invalidate the list of authentication attempts
this.realms.clear();
//remove exisitng authentication headers
- method.removeRequestHeader(HttpAuthenticator.WWW_AUTH_RESP);
+ if ((this.proxyAuthScheme != null) && (this.proxyAuthScheme.isConnectionBased())) {
+ method.removeRequestHeader(HttpAuthenticator.PROXY_AUTH);
+ }
+ method.removeRequestHeader(HttpAuthenticator.WWW_AUTH_RESP);
+ //Invalidate present authentication scheme
+ this.authScheme = null;
//update the current location with the redirect location.
//avoiding use of URL.getPath() and URL.getQuery() to keep
//jdk1.2 comliance.
@@ -514,109 +534,179 @@
LOG.trace("enter HttpMethodBase.processAuthenticationResponse("
+ "HttpState, HttpConnection)");
+ if ((this.proxyAuthScheme != null) && (this.proxyAuthScheme.isConnectionBased())) {
+ method.removeRequestHeader(HttpAuthenticator.PROXY_AUTH);
+ }
+ if ((this.authScheme != null) && (this.authScheme.isConnectionBased())) {
+ method.removeRequestHeader(HttpAuthenticator.WWW_AUTH);
+ }
boolean authenticated = false;
- int statusCode = method.getStatusCode();
- // handle authentication required
- Header[] challenges = null;
- Set realmsUsed = null;
- String host = null;
- switch (statusCode) {
- case HttpStatus.SC_UNAUTHORIZED:
- challenges = method.getResponseHeaders(HttpAuthenticator.WWW_AUTH);
- realmsUsed = realms;
- host = this.conn.getVirtualHost();
- if (host == null) {
- host = this.conn.getHost();
- }
- break;
- case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
- challenges = method.getResponseHeaders(HttpAuthenticator.PROXY_AUTH);
- realmsUsed = proxyRealms;
- host = this.conn.getProxyHost();
- break;
+ try {
+ switch (method.getStatusCode()) {
+ case HttpStatus.SC_UNAUTHORIZED:
+ Map challenges = AuthChallengeParser.parseChallenges(
+ method.getResponseHeaders(HttpAuthenticator.WWW_AUTH));
+ if (challenges.isEmpty()) {
+ return false;
+ }
+ if (this.authScheme != null) {
+ processChallenge(this.authScheme, challenges);
+ } else {
+ this.authScheme = processChallenge(challenges);
+ if (this.authScheme == null) {
+ return false;
+ }
+ }
+ String host = this.conn.getVirtualHost();
+ if (host == null) {
+ host = this.conn.getHost();
+ }
+ if (previousAttemptFailed(this.realms, host,
+ this.authScheme)) {
+ return false;
+ }
+ method.removeRequestHeader(HttpAuthenticator.WWW_AUTH_RESP);
+ authenticated = HttpAuthenticator.authenticate(
+ this.authScheme, method, this.conn, this.state);
+ break;
+ case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
+ Map proxyChallenges = AuthChallengeParser.parseChallenges(
+ method.getResponseHeaders(HttpAuthenticator.PROXY_AUTH));
+ if (proxyChallenges.isEmpty()) {
+ return false;
+ }
+ if (this.proxyAuthScheme != null) {
+ processChallenge(this.proxyAuthScheme, proxyChallenges);
+ } else {
+ this.proxyAuthScheme = processChallenge(proxyChallenges);
+ if (this.proxyAuthScheme == null) {
+ return false;
+ }
+ }
+ if (previousAttemptFailed(this.proxyRealms, this.conn.getProxyHost(),
+ this.proxyAuthScheme)) {
+ return false;
+ }
+ method.removeRequestHeader(HttpAuthenticator.PROXY_AUTH_RESP);
+ authenticated = HttpAuthenticator.authenticateProxy(
+ proxyAuthScheme, method, this.conn, this.state);
+ break;
+ }
+ } catch (MalformedChallengeException e) {
+ if (LOG.isErrorEnabled()) {
+ LOG.error(e.getMessage(), e);
+ }
+ return false;
+ } catch (CredentialsNotAvailableException e) {
+ if (LOG.isWarnEnabled()) {
+ LOG.warn(e.getMessage());
+ }
+ return false;
+ } catch (AuthenticationException e) {
+ if (LOG.isErrorEnabled()) {
+ LOG.error(e.getMessage(), e);
+ }
+ return false;
}
- // if there was a header requesting authentication
- if (challenges.length > 0) {
- AuthScheme authscheme = null;
- try {
- authscheme = HttpAuthenticator.selectAuthScheme(challenges);
- } catch (MalformedChallengeException e) {
- if (LOG.isErrorEnabled()) {
- LOG.error(e.getMessage(), e);
- }
- return false;
- } catch (UnsupportedOperationException e) {
- if (LOG.isErrorEnabled()) {
- LOG.error(e.getMessage(), e);
- }
- return false;
- }
-
- StringBuffer buffer = new StringBuffer();
- buffer.append(host);
- buffer.append('#');
- buffer.append(authscheme.getID());
- String realm = buffer.toString();
+ if (!authenticated) {
+ // won't be able to authenticate to this challenge
+ // without additional information
+ LOG.debug("Credentials required to respond to authorization" +
+ " challenge are not available");
+ } else {
+ LOG.debug("Credentials required to respond to authorization" +
+ " challenge have been provided");
+ // let's try it again, using the credentials
+ }
+ return authenticated;
+ }
- if (realmsUsed.contains(realm)) {
+ private void processChallenge(final AuthScheme authscheme, final Map challenges)
+ throws MalformedChallengeException, AuthenticationException
+ {
+ String id = authscheme.getSchemeName();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Using present authentication scheme: " + id);
+ }
+ String challenge = (String) challenges.get(id.toLowerCase());
+ if (challenge == null) {
+ throw new AuthenticationException(id +
+ " authorization challenge expected, but not found");
+ }
+ authscheme.processChallenge(challenge);
+ }
+
+ private AuthScheme processChallenge(final Map challenges)
+ throws MalformedChallengeException, AuthenticationException {
+ AuthScheme authscheme = null;
+ String challenge = null;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Supported authentication schemes in the order of preference: "
+ + AUTH_PREFERENCES);
+ }
+ Iterator item = AUTH_PREFERENCES.iterator();
+ while (item.hasNext()) {
+ String id = (String) item.next();
+ challenge = (String) challenges.get(id.toLowerCase());
+ if (challenge != null) {
if (LOG.isInfoEnabled()) {
- buffer = new StringBuffer();
- buffer.append("Already tried to authenticate with '");
- buffer.append(authscheme.getRealm());
- buffer.append("' authentication realm at ");
- buffer.append(host);
- buffer.append(", but still receiving: ");
- buffer.append(method.getStatusLine().toString());
- LOG.info(buffer.toString());
+ LOG.info(id + " authentication scheme selected");
+ }
+ authscheme = AuthPolicy.getAuthScheme(id);
+ if (authscheme == null) {
+ throw new AuthenticationException("Requested authorization scheme " +
+ id + " is not supported");
}
- return false;
+ // Looks like we got something
+ break;
} else {
- realmsUsed.add(realm);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Challenge for " + id + " authentication scheme not available");
+ // Try again
+ }
}
+ }
+ if (authscheme == null) {
+ // If none selected, something is wrong, warn and leave
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("None of the following challenges can be responsed to: "
+ + challenges);
+ }
+ } else {
+ // Process the challenge
+ authscheme.processChallenge(challenge);
+ }
+ return authscheme;
+ }
- method.removeRequestHeader(HttpAuthenticator.WWW_AUTH_RESP);
- method.removeRequestHeader(HttpAuthenticator.PROXY_AUTH_RESP);
- try {
- //remove preemptive header and reauthenticate
- switch (statusCode) {
- case HttpStatus.SC_UNAUTHORIZED:
- authenticated = HttpAuthenticator.authenticate(
- authscheme, method, this.conn, this.state);
- this.realm = authscheme.getRealm();
- break;
- case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
- authenticated = HttpAuthenticator.authenticateProxy(
- authscheme, method, this.conn, this.state);
- this.proxyRealm = authscheme.getRealm();
- break;
- }
- } catch (CredentialsNotAvailableException e) {
- if (LOG.isWarnEnabled()) {
- LOG.warn(e.getMessage());
- }
- return false; // finished request
- } catch (AuthenticationException e) {
- if (LOG.isErrorEnabled()) {
- LOG.error(e.getMessage(), e);
- }
- return false; // finished request
- }
- if (!authenticated) {
- // won't be able to authenticate to this challenge
- // without additional information
- LOG.debug("HttpMethodBase.execute(): Server demands "
- + "authentication credentials, but none are "
- + "available, so aborting.");
- } else {
- LOG.debug("HttpMethodBase.execute(): Server demanded "
- + "authentication credentials, will try again.");
- // let's try it again, using the credentials
- }
- }
- return authenticated;
- }
+ private boolean previousAttemptFailed(final Set realms, final String host, final AuthScheme authscheme) {
+ // See if authentication against the given realm & host has
+ // already been attempted
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(host);
+ buffer.append('#');
+ buffer.append(authscheme.getID());
+ String realm = buffer.toString();
+
+ if (realms.contains(realm)) {
+ // Already tried. Give up
+ if (LOG.isInfoEnabled()) {
+ buffer = new StringBuffer();
+ buffer.append("Attempt to authenticate with '");
+ buffer.append(authscheme.getRealm());
+ buffer.append("' authentication realm at ");
+ buffer.append(host);
+ buffer.append(" failed");
+ LOG.info(buffer.toString());
+ }
+ return true;
+ } else {
+ realms.add(realm);
+ return false;
+ }
+ }
/**
* Tests if the {@link HttpMethod method} requires a redirect to another location.
*
1.7 +30 -3 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthChallengeParser.java
Index: AuthChallengeParser.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthChallengeParser.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- AuthChallengeParser.java 13 Jul 2003 21:29:05 -0000 1.6
+++ AuthChallengeParser.java 10 Dec 2003 21:04:13 -0000 1.7
@@ -67,6 +67,7 @@
import java.util.List;
import java.util.Map;
+import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.util.ParameterParser;
@@ -139,4 +140,30 @@
}
return map;
}
+
+ /**
+ * Extracts a map of challenges ordered by authentication scheme name
+ *
+ * @param headers the array of authorization challenges
+ * @return a map of authorization challenges
+ *
+ * @throws MalformedChallengeException if any of challenge strings
+ * is malformed
+ *
+ * @since 2.0beta1
+ */
+ public static Map parseChallenges(final Header[] headers)
+ throws MalformedChallengeException {
+ if (headers == null) {
+ throw new IllegalArgumentException("Array of challenges may not be null");
+ }
+ String challenge = null;
+ Map challengemap = new HashMap(headers.length);
+ for (int i = 0; i < headers.length; i++) {
+ challenge = headers[i].getValue();
+ String s = AuthChallengeParser.extractScheme(challenge);
+ challengemap.put(s, challenge);
+ }
+ return challengemap;
+ }
}
1.5 +33 -6 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthScheme.java
Index: AuthScheme.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthScheme.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- AuthScheme.java 22 Apr 2003 17:00:25 -0000 1.4
+++ AuthScheme.java 10 Dec 2003 21:04:13 -0000 1.5
@@ -67,23 +67,31 @@
/**
* <p>
- * This interface represents an abstract authentication scheme.
+ * This interface represents an abstract challenge-response oriented
+ * authentication scheme.
* </p>
* <p>
* An authentication scheme should be able to support the following
* functions:
* <ul>
+ * <li>Parse and process the challenge sent by the targer server
+ * in response to request for a protected resource
* <li>Provide its textual designation
* <li>Provide its parameters, if available
* <li>Provide the realm this authentication scheme is applicable to,
* if available
* <li>Generate authorization string for the given set of credentials,
* request method and URI as specificed in the HTTP request line
+ * in response to the actual authorization challenge
* </ul>
* </p>
* <p>
* Authentication schemes may ignore method name and URI parameters
- * if they are relevant for the given authentication mechanism
+ * if they are not relevant for the given authentication mechanism
+ * </p>
+ * <p>
+ * Authentication schemes may be stateful involving a series of
+ * challenge-response exchanges
* </p>
*
* @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
@@ -93,6 +101,15 @@
*/
public interface AuthScheme {
+
+ /**
+ * Processes the given challenge token. Some authentication schemes
+ * may involve multiple challenge-response exchanges. Such schemes must be able
+ * to maintain the state information when dealing with sequential challenges
+ *
+ * @param the challenge string
+ */
+ void processChallenge(final String challenge) throws MalformedChallengeException;
/**
* Returns textual designation of the given authentication scheme.
@@ -136,10 +153,20 @@
* returned value may be null.
*/
String getID();
+
+ /**
+ * Tests if the authentication scheme is provides authorization on a per
+ * connection basis instead of usual per request basis
+ *
+ * @return <tt>true</tt> if the scheme is connection based, <tt>false</tt>
+ * if the scheme is request based.
+ */
+ boolean isConnectionBased();
/**
* Produces an authorization string for the given set of {@link Credentials},
- * method name and URI using the given authentication scheme.
+ * method name and URI using the given authentication scheme in response to
+ * the actual authorization challenge.
*
* @param credentials The set of credentials to be used for athentication
* @param method The name of the method that requires authorization.
1.4 +7 -3 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthSchemeBase.java
Index: AuthSchemeBase.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/AuthSchemeBase.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- AuthSchemeBase.java 6 Apr 2003 22:31:53 -0000 1.3
+++ AuthSchemeBase.java 10 Dec 2003 21:04:13 -0000 1.4
@@ -68,6 +68,7 @@
* Abstract authentication scheme class that implements {@link AuthScheme}
* interface and provides a default contstructor.
* </p>
+ * @deprecated No longer used
*
* @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
*/
@@ -85,6 +86,9 @@
*
* @throws MalformedChallengeException is thrown if the authentication challenge
* is malformed
+ *
+ * @deprecated Use parameterless constructor and {@link AuthScheme#challenge(String)}
+ * method
*/
public AuthSchemeBase(final String challenge)
throws MalformedChallengeException {
1.8 +23 -3 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/BasicScheme.java
Index: BasicScheme.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/BasicScheme.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- BasicScheme.java 14 Nov 2003 02:28:49 -0000 1.7
+++ BasicScheme.java 10 Dec 2003 21:04:13 -0000 1.8
@@ -91,12 +91,23 @@
private static final Log LOG = LogFactory.getLog(BasicScheme.class);
/**
+ * Default constructor for the basic authetication scheme.
+ *
+ */
+ public BasicScheme() {
+ super();
+ }
+
+ /**
* Constructor for the basic authetication scheme.
*
* @param challenge authentication challenge
*
* @throws MalformedChallengeException is thrown if the authentication challenge
* is malformed
+ *
+ * @deprecated Use parameterless constructor and {@link AuthScheme#challenge(String)}
+ * method
*/
public BasicScheme(final String challenge) throws MalformedChallengeException {
super(challenge);
@@ -140,6 +151,15 @@
+ credentials.getClass().getName());
}
return BasicScheme.authenticate(usernamepassword);
+ }
+
+ /**
+ * Returns <tt>false</tt>. Basic authentication scheme is request based.
+ *
+ * @return <tt>false</tt>.
+ */
+ public boolean isConnectionBased() {
+ return false;
}
/**
1.12 +67 -5 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/DigestScheme.java
Index: DigestScheme.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/DigestScheme.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- DigestScheme.java 3 Oct 2003 20:57:36 -0000 1.11
+++ DigestScheme.java 10 Dec 2003 21:04:13 -0000 1.12
@@ -120,6 +120,14 @@
private String cnonce;
/**
+ * Default constructor for the digest authetication scheme.
+ *
+ */
+ public DigestScheme() {
+ super();
+ }
+
+ /**
* Gets an ID based upon the realm and the nonce value. This ensures that requests
* to the same realm with different nonce values will succeed. This differentiation
* allows servers to request re-authentication using a fresh nonce value.
@@ -136,12 +144,15 @@
}
/**
- * Constructor for the digest authentication scheme.
+ * Constructor for the digest authetication scheme.
*
- * @param challenge The authentication challenge
+ * @param challenge authentication challenge
*
* @throws MalformedChallengeException is thrown if the authentication challenge
* is malformed
+ *
+ * @deprecated Use parameterless constructor and {@link AuthScheme#challenge(String)}
+ * method
*/
public DigestScheme(final String challenge)
throws MalformedChallengeException {
@@ -177,6 +188,48 @@
cnonce = createCnonce();
}
+ /**
+ * Processes the Digest challenge.
+ *
+ * @param the challenge string
+ *
+ * @throws MalformedChallengeException is thrown if the authentication challenge
+ * is malformed
+ */
+ public void processChallenge(final String challenge)
+ throws MalformedChallengeException {
+ super.processChallenge(challenge);
+
+ if (getParameter("nonce") == null) {
+ throw new MalformedChallengeException("missing nonce in challange");
+ }
+
+ boolean unsupportedQop = false;
+ // qop parsing
+ String qop = getParameter("qop");
+ if (qop != null) {
+ StringTokenizer tok = new StringTokenizer(qop,",");
+ while (tok.hasMoreTokens()) {
+ String variant = tok.nextToken().trim();
+ if (variant.equals("auth")) {
+ qopVariant = QOP_AUTH;
+ break; //that's our favourite, because auth-int is unsupported
+ } else if (variant.equals("auth-int")) {
+ qopVariant = QOP_AUTH_INT;
+ } else {
+ unsupportedQop = true;
+ LOG.warn("Unsupported qop detected: "+ variant);
+ }
+ }
+ }
+
+ if (unsupportedQop && (qopVariant == QOP_MISSING)) {
+ throw new MalformedChallengeException("None of the qop methods is supported");
+ }
+
+ cnonce = createCnonce();
+ }
+
/**
* Returns textual designation of the digest authentication scheme.
@@ -185,6 +238,15 @@
*/
public String getSchemeName() {
return "digest";
+ }
+
+ /**
+ * Returns <tt>false</tt>. Digest authentication scheme is request based.
+ *
+ * @return <tt>false</tt>.
+ */
+ public boolean isConnectionBased() {
+ return false;
}
/**
1.14 +9 -8 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/HttpAuthenticator.java
Index: HttpAuthenticator.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/HttpAuthenticator.java,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- HttpAuthenticator.java 2 Nov 2003 12:10:28 -0000 1.13
+++ HttpAuthenticator.java 10 Dec 2003 21:04:13 -0000 1.14
@@ -63,12 +63,13 @@
package org.apache.commons.httpclient.auth;
-import java.util.Map;
import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpMethod;
-import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.logging.Log;
@@ -110,19 +111,16 @@
*/
public static final String WWW_AUTH = "WWW-Authenticate";
-
/**
* The www authenticate response header.
*/
public static final String WWW_AUTH_RESP = "Authorization";
-
/**
* The proxy authenticate challange header.
*/
public static final String PROXY_AUTH = "Proxy-Authenticate";
-
/**
* The proxy authenticate response header.
*/
@@ -145,6 +143,9 @@
* challenge is malformed
* @throws UnsupportedOperationException when none of challenge types
* available is supported.
+ *
+ * @deprecated Use {@link AuthChallengeParser#parseChallenges(Header[])} and
+ * {@link AuthPolicy#getAuthScheme(String)}
*/
public static AuthScheme selectAuthScheme(final Header[] challenges)
throws MalformedChallengeException {
1.12 +34 -5 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/NTLMScheme.java
Index: NTLMScheme.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/NTLMScheme.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- NTLMScheme.java 16 Aug 2003 00:41:24 -0000 1.11
+++ NTLMScheme.java 10 Dec 2003 21:04:13 -0000 1.12
@@ -81,7 +81,7 @@
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
* @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
*/
-public class NTLMScheme extends AuthSchemeBase {
+public class NTLMScheme implements AuthScheme {
/** Log object for this class. */
private static final Log LOG = LogFactory.getLog(NTLMScheme.class);
@@ -90,6 +90,14 @@
private String ntlmchallenge = null;
/**
+ * Default constructor for the NTLM authentication scheme.
+ *
+ */
+ public NTLMScheme() {
+ super();
+ }
+
+ /**
* Constructor for the NTLM authentication scheme.
*
* @param challenge The authentication challenge
@@ -98,7 +106,19 @@
* is malformed
*/
public NTLMScheme(final String challenge) throws MalformedChallengeException {
- super(challenge);
+ super();
+ processChallenge(challenge);
+ }
+
+ /**
+ * Processes the NTLM challenge.
+ *
+ * @param the challenge string
+ *
+ * @throws MalformedChallengeException is thrown if the authentication challenge
+ * is malformed
+ */
+ public void processChallenge(final String challenge) throws MalformedChallengeException {
String s = AuthChallengeParser.extractScheme(challenge);
if (!s.equalsIgnoreCase(getSchemeName())) {
throw new MalformedChallengeException("Invalid NTLM challenge: " + challenge);
@@ -167,6 +187,15 @@
throw new IllegalArgumentException("Parameter name may not be null");
}
return null;
+ }
+
+ /**
+ * Returns <tt>true</tt>. NTLM authentication scheme is connection based.
+ *
+ * @return <tt>true</tt>.
+ */
+ public boolean isConnectionBased() {
+ return true;
}
/**
1.5 +30 -5 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/RFC2617Scheme.java
Index: RFC2617Scheme.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/auth/RFC2617Scheme.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- RFC2617Scheme.java 22 Apr 2003 17:00:25 -0000 1.4
+++ RFC2617Scheme.java 10 Dec 2003 21:04:13 -0000 1.5
@@ -74,7 +74,7 @@
*
* @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
*/
-public abstract class RFC2617Scheme extends AuthSchemeBase {
+public abstract class RFC2617Scheme implements AuthScheme {
/**
* Authentication parameter map.
@@ -84,13 +84,38 @@
/**
* Default constructor for RFC2617 compliant authetication schemes.
*
+ */
+ public RFC2617Scheme() {
+ super();
+ }
+
+ /**
+ * Default constructor for RFC2617 compliant authetication schemes.
+ *
* @param challenge authentication challenge
*
* @throws MalformedChallengeException is thrown if the authentication challenge
* is malformed
+ *
+ * @deprecated Use parameterless constructor and {@link AuthScheme#challenge(String)}
+ * method
*/
public RFC2617Scheme(final String challenge) throws MalformedChallengeException {
- super(challenge);
+ super();
+ processChallenge(challenge);
+ }
+
+ /**
+ * Processes the given challenge token. Some authentication schemes
+ * may involve multiple challenge-response exchanges. Such schemes must be able
+ * to maintain the state information when dealing with sequential challenges
+ *
+ * @param the challenge string
+ *
+ * @throws MalformedChallengeException is thrown if the authentication challenge
+ * is malformed
+ */
+ public void processChallenge(final String challenge) throws MalformedChallengeException {
String s = AuthChallengeParser.extractScheme(challenge);
if (!s.equalsIgnoreCase(getSchemeName())) {
throw new MalformedChallengeException(
1.35 +44 -35 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.34
retrieving revision 1.35
diff -u -r1.34 -r1.35
--- TestAuthenticator.java 24 Nov 2003 22:17:02 -0000 1.34
+++ TestAuthenticator.java 10 Dec 2003 21:04:13 -0000 1.35
@@ -131,7 +131,8 @@
HttpState state = new HttpState();
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
try {
- AuthScheme authscheme = new BasicScheme(challenge);
+ AuthScheme authscheme = new BasicScheme();
+ authscheme.processChallenge(challenge);
HttpAuthenticator.authenticate(authscheme, method, null, state);
fail("Should have thrown HttpException");
} catch(HttpException e) {
@@ -144,7 +145,8 @@
HttpState state = new HttpState();
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
try {
- AuthScheme authscheme = new BasicScheme(challenge);
+ AuthScheme authscheme = new BasicScheme();
+ authscheme.processChallenge(challenge);
HttpAuthenticator.authenticate(authscheme, method, null, state);
fail("Should have thrown HttpException");
} catch(HttpException e) {
@@ -157,7 +159,8 @@
HttpState state = new HttpState();
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
try {
- AuthScheme authscheme = new BasicScheme(challenge);
+ AuthScheme authscheme = new BasicScheme();
+ authscheme.processChallenge(challenge);
HttpAuthenticator.authenticate(authscheme, method, null, state);
fail("Should have thrown HttpException");
} catch(HttpException e) {
@@ -169,7 +172,8 @@
String challenge = "Basic realm=\"realm1\"";
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
try {
- AuthScheme authscheme = new BasicScheme(challenge);
+ AuthScheme authscheme = new BasicScheme();
+ authscheme.processChallenge(challenge);
HttpAuthenticator.authenticate(authscheme, method, null, null);
fail("Should have thrown IllegalArgumentException");
} catch(IllegalArgumentException e) {
@@ -177,23 +181,13 @@
}
}
- public void testInvalidAuthenticationScheme() throws Exception {
- String challenge = "invalid realm=\"realm1\"";
- try{
- HttpAuthenticator.selectAuthScheme(
- new Header[] { new Header("WWW-Authenticate", challenge) });
- fail("Should have thrown UnsupportedOperationException");
- }catch (UnsupportedOperationException uoe){
- // expected
- }
- }
-
public void testBasicAuthenticationCaseInsensitivity() throws Exception {
String challenge = "bAsIc ReAlM=\"realm1\"";
HttpState state = new HttpState();
state.setCredentials(null, null, new UsernamePasswordCredentials("username","password"));
HttpMethod method = new SimpleHttpMethod(new Header("WwW-AuThEnTiCaTe", challenge));
- AuthScheme authscheme = new BasicScheme(challenge);
+ AuthScheme authscheme = new BasicScheme();
+ authscheme.processChallenge(challenge);
assertTrue(HttpAuthenticator.authenticate(authscheme, method, null, state));
assertTrue(null != method.getRequestHeader("Authorization"));
String expected = "Basic " + HttpConstants.getString(Base64.encode(HttpConstants.getBytes("username:password")));
@@ -216,7 +210,8 @@
HttpState state = new HttpState();
state.setCredentials("realm", null, new UsernamePasswordCredentials("username","password"));
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
- AuthScheme authscheme = new BasicScheme(challenge);
+ AuthScheme authscheme = new BasicScheme();
+ authscheme.processChallenge(challenge);
assertTrue(HttpAuthenticator.authenticate(authscheme, method, null, state));
assertTrue(null != method.getRequestHeader("Authorization"));
String expected = "Basic " + HttpConstants.getString(Base64.encode(HttpConstants.getBytes("username:password")));
@@ -240,8 +235,10 @@
HttpState state = new HttpState();
state.setCredentials("realm1", null, new UsernamePasswordCredentials("username","password"));
state.setCredentials("realm2", null, new UsernamePasswordCredentials("uname2","password2"));
- AuthScheme authscheme1 = new BasicScheme(challenge1);
- AuthScheme authscheme2 = new BasicScheme(challenge2);
+ AuthScheme authscheme1 = new BasicScheme();
+ authscheme1.processChallenge(challenge1);
+ AuthScheme authscheme2 = new BasicScheme();
+ authscheme2.processChallenge(challenge2);
{
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate",challenge1));
assertTrue(HttpAuthenticator.authenticate(authscheme1, method, null, state));
@@ -284,7 +281,8 @@
HttpState state = new HttpState();
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
try {
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
HttpAuthenticator.authenticate(authscheme, method, null, state);
fail("Should have thrown HttpException");
} catch(HttpException e) {
@@ -297,7 +295,8 @@
HttpState state = new HttpState();
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
try {
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
HttpAuthenticator.authenticate(authscheme, method, null, state);
fail("Should have thrown HttpException");
} catch(HttpException e) {
@@ -310,7 +309,8 @@
HttpState state = new HttpState();
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
try {
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
HttpAuthenticator.authenticate(authscheme, method, null, state);
fail("Should have thrown HttpException");
} catch(HttpException e) {
@@ -322,7 +322,8 @@
String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
try {
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
HttpAuthenticator.authenticate(authscheme, method, null, null);
fail("Should have thrown IllegalArgumentException");
} catch(IllegalArgumentException e) {
@@ -336,7 +337,8 @@
UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
state.setCredentials(null, null, cred);
HttpMethod method = new SimpleHttpMethod(new Header("WwW-AuThEnTiCaTe", challenge));
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
assertTrue(HttpAuthenticator.authenticate(authscheme, method, null, state));
assertTrue(null != method.getRequestHeader("Authorization"));
}
@@ -348,7 +350,8 @@
UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
state.setCredentials(null, null, cred);
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
assertTrue(HttpAuthenticator.authenticate(authscheme, method, null, state));
assertTrue(null != method.getRequestHeader("Authorization"));
Map table = AuthChallengeParser.extractParams(method.getRequestHeader("Authorization").getValue());
@@ -365,7 +368,8 @@
UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username","password");
state.setCredentials(null, null, cred);
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
assertTrue(HttpAuthenticator.authenticate(authscheme, method, null, state));
assertTrue(null != method.getRequestHeader("Authorization"));
Map table = AuthChallengeParser.extractParams(method.getRequestHeader("Authorization").getValue());
@@ -423,8 +427,10 @@
state.setCredentials("realm1", null, cred);
UsernamePasswordCredentials cred2 = new UsernamePasswordCredentials("uname2","password2");
state.setCredentials("realm2", null, cred2);
- AuthScheme authscheme1 = new DigestScheme(challenge1);
- AuthScheme authscheme2 = new DigestScheme(challenge2);
+ AuthScheme authscheme1 = new DigestScheme();
+ authscheme1.processChallenge(challenge1);
+ AuthScheme authscheme2 = new DigestScheme();
+ authscheme2.processChallenge(challenge2);
{
HttpMethod method = new SimpleHttpMethod(new Header("WWW-Authenticate",challenge1));
assertTrue(HttpAuthenticator.authenticate(authscheme1, method, null, state));
@@ -471,7 +477,8 @@
UsernamePasswordCredentials cred =
new UsernamePasswordCredentials(username, password);
state.setCredentials(realm, null, cred);
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
HttpMethod method =
new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
assertTrue(HttpAuthenticator.authenticate(
@@ -512,7 +519,8 @@
UsernamePasswordCredentials cred =
new UsernamePasswordCredentials(username, password);
state.setCredentials(realm, null, cred);
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
HttpMethod method =
new SimpleHttpMethod(new Header("WWW-Authenticate", challenge));
assertTrue(HttpAuthenticator.authenticate(
@@ -554,7 +562,8 @@
new UsernamePasswordCredentials(username, password);
state.setCredentials(realm, null, cred);
try {
- AuthScheme authscheme = new DigestScheme(challenge);
+ AuthScheme authscheme = new DigestScheme();
+ authscheme.processChallenge(challenge);
fail("MalformedChallengeException exception expected due to invalid qop value");
} catch(MalformedChallengeException e) {
/* expected */
1.3 +5 -4 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/server/ProxyAuthRequestHandler.java
Index: ProxyAuthRequestHandler.java
===================================================================
RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/server/ProxyAuthRequestHandler.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- ProxyAuthRequestHandler.java 5 Dec 2003 21:23:15 -0000 1.2
+++ ProxyAuthRequestHandler.java 10 Dec 2003 21:04:13 -0000 1.3
@@ -142,7 +142,8 @@
// TODO Auto-generated method stub
BasicScheme scheme;
try {
- scheme = new BasicScheme("basic realm=test");
+ scheme = new BasicScheme();
+ scheme.processChallenge("basic realm=test");
String expectedAuthString = scheme.authenticate(credentials, null, null);
return expectedAuthString.equals(clientAuth.getValue());
} catch (MalformedChallengeException e) {
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org