Return-Path: Delivered-To: apmail-jakarta-commons-httpclient-dev-archive@www.apache.org Received: (qmail 91151 invoked from network); 18 Mar 2004 21:54:57 -0000 Received: from daedalus.apache.org (HELO mail.apache.org) (208.185.179.12) by minotaur-2.apache.org with SMTP; 18 Mar 2004 21:54:57 -0000 Received: (qmail 41287 invoked by uid 500); 18 Mar 2004 21:54:21 -0000 Delivered-To: apmail-jakarta-commons-httpclient-dev-archive@jakarta.apache.org Received: (qmail 41248 invoked by uid 500); 18 Mar 2004 21:54:21 -0000 Mailing-List: contact commons-httpclient-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Commons HttpClient Project" Reply-To: "Commons HttpClient Project" Delivered-To: mailing list commons-httpclient-dev@jakarta.apache.org Received: (qmail 41082 invoked from network); 18 Mar 2004 21:54:20 -0000 Received: from unknown (HELO mail1.bluewin.ch) (195.186.1.74) by daedalus.apache.org with SMTP; 18 Mar 2004 21:54:20 -0000 Received: from [192.168.0.2] (81.62.16.144) by mail1.bluewin.ch (Bluewin AG 7.0.027) id 40559959000AA233; Thu, 18 Mar 2004 21:54:25 +0000 Subject: [PATCH] Yet another refactoring of authentication logic "Oops I did it again" From: Oleg Kalnichevski Reply-To: olegk@apache.org To: Jakarta Commons HttpClient mailing list , Vincent Massol Content-Type: multipart/mixed; boundary="=-vnHNPYcupp8oqxt5PCc1" Message-Id: <1079646860.20374.36.camel@localhost.localdomain> Mime-Version: 1.0 X-Mailer: Ximian Evolution 1.4.5 (1.4.5-7) Date: Thu, 18 Mar 2004 22:54:20 +0100 X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N --=-vnHNPYcupp8oqxt5PCc1 Content-Type: text/plain Content-Transfer-Encoding: 7bit Folks, I believe that most of you have already been suspecting that tinkering with the authentication framework is a sort of Russian traditional sport. Well, almost ;-) Prompted by the 2.0 incompatibility discovered by Vincent Massol (Specials thanks go to Gump and all the Gump Meisters) I went over the authentication code one more time and made yet another series of changes * I factored out the authentication challenge processing logic from the HttpMethodDirector to a class of its own. Thanks to that authentication challenge processing can now be tested separately. Test cases provided. * HttpMethodDirector no longer intervenes if Authorization & Proxy-Authorization are set manually by the user. Custom authorization headers are never overwritten * Introduced a new class called AuthState that represents the authentication process state that contains all the authentication details. Basically it is just a convenience wrapper around the authentication scheme interface. * Proxy and host authentication state objects moved to the HTTP method level, so that they can be queried by the user to find out the details about authentication that has been performed by the HttpMethodDirector. With the current implementation all the details of proxy and host authentication are contained within the HttpMethodDirector instance, which exists only within the lifetime of HttpClient#executeMethod() execution. As soon as the method returns, the respective HttpMethodDirector instance gets GCed along with the authentication details * More test cases Let me know what you think Oleg PS: I hope this is going to be the last round of changes in the authentication logic. I can live with it now (until 4.0, of course) --=-vnHNPYcupp8oqxt5PCc1 Content-Disposition: attachment; filename=auth.patch Content-Type: text/x-patch; name=auth.patch; charset=us-ascii Content-Transfer-Encoding: quoted-printable Index: java/org/apache/commons/httpclient/HttpMethod.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/co= mmons/httpclient/HttpMethod.java,v retrieving revision 1.34 diff -u -r1.34 HttpMethod.java --- java/org/apache/commons/httpclient/HttpMethod.java 22 Feb 2004 18:08:45= -0000 1.34 +++ java/org/apache/commons/httpclient/HttpMethod.java 18 Mar 2004 21:11:09= -0000 @@ -34,6 +34,7 @@ import java.io.IOException; import java.io.InputStream; =20 +import org.apache.commons.httpclient.auth.AuthState; import org.apache.commons.httpclient.params.*; =20 =20 @@ -211,6 +212,13 @@ void removeRequestHeader(String headerName); =20 /** + * Removes the given request header. + *=20 + * @param header the header + */ + void removeRequestHeader(Header header); + + /** * Returns true if the HTTP method should automatically follo= w HTTP redirects=20 * (status code 302, etc.), false otherwise. *=20 @@ -531,5 +539,23 @@ * @param handler the methodRetryHandler to use when this method execu= ted */ public void setMethodRetryHandler(MethodRetryHandler handler); + + /** + * Returns the target host {@link AuthState authentication state} + *=20 + * @return host authentication state + *=20 + * @since 3.0 + */ + public AuthState getHostAuthState(); + + /** + * Returns the proxy {@link AuthState authentication state} + *=20 + * @return host authentication state + *=20 + * @since 3.0 + */ + public AuthState getProxyAuthState(); =20 } Index: java/org/apache/commons/httpclient/HttpMethodBase.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/co= mmons/httpclient/HttpMethodBase.java,v retrieving revision 1.201 diff -u -r1.201 HttpMethodBase.java --- java/org/apache/commons/httpclient/HttpMethodBase.java 9 Mar 2004 14:01= :02 -0000 1.201 +++ java/org/apache/commons/httpclient/HttpMethodBase.java 18 Mar 2004 21:1= 1:15 -0000 @@ -36,6 +36,7 @@ import java.io.IOException; import java.io.InputStream; =20 +import org.apache.commons.httpclient.auth.AuthState; import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.commons.httpclient.cookie.CookieSpec; import org.apache.commons.httpclient.cookie.MalformedCookieException; @@ -139,6 +140,12 @@ /** HTTP protocol parameters. */ private HttpMethodParams params =3D new HttpMethodParams(); =20 + /** Host authentication state */ + private AuthState hostAuthState =3D new AuthState(); + + /** Proxy authentication state */ + private AuthState proxyAuthState =3D new AuthState(); + /** True if this method has already been executed. */ private boolean used =3D false; =20 @@ -998,6 +1005,8 @@ responseBody =3D null; recoverableExceptionCount =3D 0; connectionCloseForced =3D false; + hostAuthState.invalidate(); + proxyAuthState.invalidate(); } =20 /** @@ -1040,6 +1049,18 @@ } =20 } + =20 + /** + * Removes the given request header. + *=20 + * @param header the header + */ + public void removeRequestHeader(final Header header) { + if (header =3D=3D null) { + return; + } + getRequestHeaderGroup().removeHeader(header); + } =20 // ---------------------------------------------------------------- Qu= eries =20 @@ -2039,27 +2060,27 @@ } =20 /** - * @deprecated no longer used - *=20 * Returns proxy authentication realm, if it has been used during auth= entication process.=20 * Otherwise returns null. *=20 * @return proxy authentication realm + *=20 + * @deprecated use #getProxyAuthState() */ public String getProxyAuthenticationRealm() { - return null; + return this.proxyAuthState.getRealm(); } =20 /** - * @deprecated no longer used - *=20 * Returns authentication realm, if it has been used during authentica= tion process.=20 * Otherwise returns null. *=20 * @return authentication realm + *=20 + * @deprecated use #getHostAuthState() */ public String getAuthenticationRealm() { - return null; + return this.hostAuthState.getRealm(); } =20 /** @@ -2241,4 +2262,25 @@ this.responseStream =3D responseStream; } =20 + /** + * Returns the target host {@link AuthState authentication state} + *=20 + * @return host authentication state + *=20 + * @since 3.0 + */ + public AuthState getHostAuthState() { + return this.hostAuthState; + } + + /** + * Returns the proxy {@link AuthState authentication state} + *=20 + * @return host authentication state + *=20 + * @since 3.0 + */ + public AuthState getProxyAuthState() { + return this.proxyAuthState; + } } Index: java/org/apache/commons/httpclient/HttpMethodDirector.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/co= mmons/httpclient/HttpMethodDirector.java,v retrieving revision 1.18 diff -u -r1.18 HttpMethodDirector.java --- java/org/apache/commons/httpclient/HttpMethodDirector.java 22 Feb 2004 = 18:08:46 -0000 1.18 +++ java/org/apache/commons/httpclient/HttpMethodDirector.java 18 Mar 2004 = 21:11:17 -0000 @@ -32,13 +32,13 @@ package org.apache.commons.httpclient; =20 import java.io.IOException; -import java.util.Collection; -import java.util.Iterator; import java.util.Map; =20 +import org.apache.commons.httpclient.auth.AuthChallengeException; import org.apache.commons.httpclient.auth.AuthChallengeParser; -import org.apache.commons.httpclient.auth.AuthPolicy; +import org.apache.commons.httpclient.auth.AuthChallengeProcessor; import org.apache.commons.httpclient.auth.AuthScheme; +import org.apache.commons.httpclient.auth.AuthState; import org.apache.commons.httpclient.auth.AuthenticationException; import org.apache.commons.httpclient.auth.CredentialsProvider; import org.apache.commons.httpclient.auth.CredentialsNotAvailableException= ; @@ -93,20 +93,11 @@ private static final int AUTH_NOT_REQUIRED =3D Integer.MAX_VALUE; =20 /** Actual state of authentication process */ - private int authState =3D AUTH_UNINITIATED; + private int authProcess =3D AUTH_UNINITIATED; =20 - /** Actual authentication scheme */ - private AuthScheme authScheme =3D null; + /** Authentication processor */ + private AuthChallengeProcessor authProcessor =3D null; =20 - /** Whether preemtive authentication is attempted */ - private boolean authPreemptive =3D false;=20 - =20 - /** Actual proxy authentication scheme */ - private AuthScheme proxyAuthScheme =3D null; - - /** Whether preemtive proxy authentication is attempted */ - private boolean proxyAuthPreemptive =3D false;=20 - =20 public HttpMethodDirector( final HttpConnectionManager connectionManager, final HostConfiguration hostConfiguration, @@ -118,6 +109,7 @@ this.hostConfiguration =3D hostConfiguration; this.params =3D params; this.state =3D state; + this.authProcessor =3D new AuthChallengeProcessor(this.params); } =20 =09 @@ -128,9 +120,7 @@ * @throws HttpException */ public void executeMethod(final HttpMethod method) throws IOException,= HttpException { - =20 - if (method =3D=3D null) - { + if (method =3D=3D null) { throw new IllegalArgumentException("Method may not be null"); } method.getParams().setDefaults(this.params); @@ -161,12 +151,10 @@ || this.state.isAuthenticationPreemptive())=20 { LOG.debug("Preemptively sending default basic cred= entials"); - this.authState =3D AUTH_PREEMPTIVE; - this.authScheme =3D AuthPolicy.getAuthScheme("basi= c"); - this.authPreemptive =3D true; + this.authProcess =3D AUTH_PREEMPTIVE; + method.getHostAuthState().setPreemptive(); if (this.conn.isProxied()) { - this.proxyAuthPreemptive =3D true; - this.proxyAuthScheme =3D AuthPolicy.getAuthSch= eme("basic"); + method.getProxyAuthState().setPreemptive(); } } } @@ -188,7 +176,7 @@ retry =3D true; } } else { - this.authState =3D AUTH_NOT_REQUIRED; + this.authProcess =3D AUTH_NOT_REQUIRED; } if (!retry) { break; @@ -237,14 +225,33 @@ } } =20 + + private boolean cleanAuthHeaders(final HttpMethod method, final String= name) { + Header[] authheaders =3D method.getRequestHeaders(name); + boolean clean =3D true; + for (int i =3D 0; i < authheaders.length; i++) { + Header authheader =3D authheaders[i]; + if (authheader.isAutogenerated()) { + method.removeRequestHeader(authheader); + } else { + clean =3D false; + } + } + return clean; + } =20 + private void authenticateHost(final HttpMethod method) throws Authenti= cationException { // Clean up existing authentication headers - method.removeRequestHeader(WWW_AUTH_RESP); - if ((this.authScheme !=3D null)=20 - && ((this.authState =3D=3D AUTH_WWW_REQUIRED)=20 - || (!this.authScheme.isConnectionBased()))) - { + if (!cleanAuthHeaders(method, WWW_AUTH_RESP)) { + // User defined authentication header(s) present + return; + } + AuthScheme authscheme =3D method.getHostAuthState().getAuthScheme(= ); + if (authscheme =3D=3D null) { + return; + } + if ((this.authProcess =3D=3D AUTH_WWW_REQUIRED) || (!authscheme.is= ConnectionBased())) { String host =3D conn.getVirtualHost(); if (host =3D=3D null) { host =3D conn.getHost(); @@ -252,14 +259,14 @@ int port =3D conn.getPort(); HttpAuthRealm realm =3D new HttpAuthRealm( host, port,=20 - this.authScheme.getRealm(),=20 - this.authScheme.getSchemeName()); =20 + authscheme.getRealm(),=20 + authscheme.getSchemeName()); =20 if (LOG.isDebugEnabled()) { LOG.debug("Authenticating with " + realm); } Credentials credentials =3D this.state.getCredentials(realm); if (credentials !=3D null) { - String authstring =3D this.authScheme.authenticate(credent= ials, method); + String authstring =3D authscheme.authenticate(credentials,= method); if (authstring !=3D null) { method.addRequestHeader(new Header(WWW_AUTH_RESP, auth= string, true)); } @@ -272,21 +279,26 @@ =20 private void authenticateProxy(final HttpMethod method) throws Authent= icationException { // Clean up existing authentication headers - method.removeRequestHeader(PROXY_AUTH_RESP); - if ((this.proxyAuthScheme !=3D null)=20 - && ((this.authState =3D=3D AUTH_PROXY_REQUIRED)=20 - || (!this.proxyAuthScheme.isConnectionBased()))) - { + // Clean up existing authentication headers + if (!cleanAuthHeaders(method, PROXY_AUTH_RESP)) { + // User defined authentication header(s) present + return; + } + AuthScheme authscheme =3D method.getProxyAuthState().getAuthScheme= (); + if (authscheme =3D=3D null) { + return; + } + if ((this.authProcess =3D=3D AUTH_PROXY_REQUIRED) || (!authscheme.= isConnectionBased())) { HttpAuthRealm realm =3D new HttpAuthRealm( conn.getProxyHost(), conn.getProxyPort(),=20 - this.proxyAuthScheme.getRealm(),=20 - this.proxyAuthScheme.getSchemeName()); =20 + authscheme.getRealm(),=20 + authscheme.getSchemeName()); =20 if (LOG.isDebugEnabled()) { LOG.debug("Authenticating with " + realm); } Credentials credentials =3D this.state.getProxyCredentials(rea= lm); if (credentials !=3D null) { - String authstring =3D this.proxyAuthScheme.authenticate(cr= edentials, method); + String authstring =3D authscheme.authenticate(credentials,= method); if (authstring !=3D null) { method.addRequestHeader(new Header(PROXY_AUTH_RESP, au= thstring, true)); } @@ -416,7 +428,7 @@ retry =3D true; } } else { - this.authState =3D AUTH_NOT_REQUIRED; + this.authProcess =3D AUTH_NOT_REQUIRED; } if (!retry) { break; @@ -467,6 +479,8 @@ this.connectMethod.getResponseHeaderGroup(), this.connectMethod.getResponseBodyAsStream() ); + method.getProxyAuthState().setAuthScheme( + this.connectMethod.getProxyAuthState().getAuthScheme()); this.connectMethod =3D null; } else { releaseConnection =3D true; @@ -537,7 +551,7 @@ } =20 //And finally invalidate the actual authentication scheme - this.authScheme =3D null;=20 + method.getHostAuthState().invalidate();=20 return true; } =20 @@ -574,21 +588,24 @@ private boolean processWWWAuthChallenge(final HttpMethod method) throws MalformedChallengeException, AuthenticationException =20 { - if (this.authPreemptive) { - this.authScheme =3D null; - this.authPreemptive =3D false; + AuthState authstate =3D method.getHostAuthState(); + if (authstate.isPreemptive()) { + authstate.invalidate(); } Map challenges =3D AuthChallengeParser.parseChallenges( method.getResponseHeaders(WWW_AUTH_CHALLENGE)); if (challenges.isEmpty()) { return false;=20 } - if (this.authScheme !=3D null) { - processChallenge(this.authScheme, challenges); - } else { - this.authScheme =3D processChallenge(challenges); + AuthScheme authscheme =3D null; + try { + authscheme =3D this.authProcessor.processChallenge(authstate, = challenges); + } catch (AuthChallengeException e) { + if (LOG.isWarnEnabled()) { + LOG.warn(e.getMessage()); + } } - if (this.authScheme =3D=3D null) { + if (authscheme =3D=3D null) { return false; } String host =3D conn.getVirtualHost(); @@ -598,13 +615,13 @@ int port =3D conn.getPort(); HttpAuthRealm realm =3D new HttpAuthRealm( host, port,=20 - this.authScheme.getRealm(),=20 - this.authScheme.getSchemeName()); =20 + authscheme.getRealm(),=20 + authscheme.getSchemeName()); =20 =20 - if ((this.authState =3D=3D AUTH_WWW_REQUIRED)=20 - && (this.authScheme.isComplete())) { + if ((this.authProcess =3D=3D AUTH_WWW_REQUIRED) && (authscheme.isC= omplete())) { // Already tried and failed - Credentials credentials =3D promptForCredentials(method.getPar= ams(), realm); + Credentials credentials =3D promptForCredentials( + authscheme, method.getParams(), realm); if (credentials =3D=3D null) { if (LOG.isInfoEnabled()) { LOG.info("Failure authenticating with " + realm); @@ -614,11 +631,12 @@ return true; } } else { - this.authState =3D AUTH_WWW_REQUIRED; + this.authProcess =3D AUTH_WWW_REQUIRED; =20 Credentials credentials =3D this.state.getCredentials(realm); if (credentials =3D=3D null) { - credentials =3D promptForCredentials(method.getParams(), r= ealm); + credentials =3D promptForCredentials( + authscheme, method.getParams(), realm); } if (credentials =3D=3D null) { if (LOG.isInfoEnabled()) { @@ -634,32 +652,35 @@ private boolean processProxyAuthChallenge(final HttpMethod method) throws MalformedChallengeException, AuthenticationException { =20 - if (this.proxyAuthPreemptive) { - this.proxyAuthScheme =3D null; - this.proxyAuthPreemptive =3D false; + AuthState authstate =3D method.getProxyAuthState(); + if (authstate.isPreemptive()) { + authstate.invalidate(); } Map proxyChallenges =3D AuthChallengeParser.parseChallenges( method.getResponseHeaders(PROXY_AUTH_CHALLENGE)); if (proxyChallenges.isEmpty()) { return false;=20 } - if (this.proxyAuthScheme !=3D null) { - processChallenge(this.proxyAuthScheme, proxyChallenges); - } else { - this.proxyAuthScheme =3D processChallenge(proxyChallenges); + AuthScheme authscheme =3D null; + try { + authscheme =3D this.authProcessor.processChallenge(authstate, = proxyChallenges); + } catch (AuthChallengeException e) { + if (LOG.isWarnEnabled()) { + LOG.warn(e.getMessage()); + } } - if (this.proxyAuthScheme =3D=3D null) { + if (authscheme =3D=3D null) { return false; } HttpAuthRealm realm =3D new HttpAuthRealm( conn.getProxyHost(), conn.getProxyPort(),=20 - this.proxyAuthScheme.getRealm(),=20 - this.proxyAuthScheme.getSchemeName()); =20 + authscheme.getRealm(),=20 + authscheme.getSchemeName()); =20 =20 - if ((this.authState =3D=3D AUTH_PROXY_REQUIRED)=20 - && (this.proxyAuthScheme.isComplete())) { + if ((this.authProcess =3D=3D AUTH_PROXY_REQUIRED) && (authscheme.i= sComplete())) { // Already tried and failed - Credentials credentials =3D promptForProxyCredentials(method.g= etParams(), realm); + Credentials credentials =3D promptForProxyCredentials( + authscheme, method.getParams(), realm); if (credentials =3D=3D null) { if (LOG.isInfoEnabled()) { LOG.info("Failure authenticating with " + realm); @@ -669,11 +690,12 @@ return true; } } else { - this.authState =3D AUTH_PROXY_REQUIRED; + this.authProcess =3D AUTH_PROXY_REQUIRED; =20 Credentials credentials =3D this.state.getProxyCredentials(rea= lm); if (credentials =3D=3D null) { - credentials =3D promptForProxyCredentials(method.getParams= (), realm); + credentials =3D promptForProxyCredentials( + authscheme, method.getParams(), realm); } if (credentials =3D=3D null) { if (LOG.isInfoEnabled()) { @@ -686,71 +708,6 @@ } } =20 - private void processChallenge(final AuthScheme authscheme, final Map c= hallenges) - throws MalformedChallengeException, AuthenticationException - { =20 - String id =3D authscheme.getSchemeName(); - if (LOG.isDebugEnabled()) { - LOG.debug("Using present authentication scheme: " + id); - } - String challenge =3D (String) challenges.get(id.toLowerCase()); - if (challenge =3D=3D null) { - throw new AuthenticationException(id +=20 - " authorization challenge expected, but not found"); - } - authscheme.processChallenge(challenge); - } - =20 - private AuthScheme processChallenge(final Map challenges) - throws MalformedChallengeException, AuthenticationException { =20 - - Collection authPrefs =3D (Collection) this.params.getParameter( - AuthPolicy= .AUTH_SCHEME_PRIORITY); - if (authPrefs =3D=3D null || authPrefs.isEmpty()) { - authPrefs =3D AuthPolicy.getDefaultAuthPrefs(); =20 - } - =20 - AuthScheme authscheme =3D null; - String challenge =3D null; - if (LOG.isDebugEnabled()) { - LOG.debug("Supported authentication schemes in the order of pr= eference: "=20 - + authPrefs); - } - Iterator item =3D authPrefs.iterator(); - while (item.hasNext()) { - String id =3D (String) item.next(); - challenge =3D (String) challenges.get(id.toLowerCase());=20 - if (challenge !=3D null) { - if (LOG.isInfoEnabled()) { - LOG.info(id + " authentication scheme selected"); - } - authscheme =3D AuthPolicy.getAuthScheme(id); - if (authscheme =3D=3D null) { =20 - throw new AuthenticationException("Requested authoriza= tion scheme " + - id + " is not supported"); - } - // Looks like we got something - break; - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Challenge for " + id + " authentication sch= eme not available"); - // Try again - } - } - } - if (authscheme =3D=3D null) { - // If none selected, something is wrong, warn and leave - if (LOG.isWarnEnabled()) { - LOG.warn("None of the following challenges can be response= d to: " - + challenges); - } - } else { - // Process the challenge - authscheme.processChallenge(challenge); - } - return authscheme; - } - /** * Tests if the {@link HttpMethod method} requires a redirect to anoth= er location. *=20 @@ -802,6 +759,7 @@ } =20 private Credentials promptForCredentials( + final AuthScheme authScheme, final HttpParams params,=20 final HttpAuthRealm realm) { @@ -811,7 +769,7 @@ if (credProvider !=3D null) { try { creds =3D credProvider.getCredentials( - this.authScheme, realm.getHost(), realm.getPort(), fal= se); + authScheme, realm.getHost(), realm.getPort(), false); } catch (CredentialsNotAvailableException e) { LOG.warn(e.getMessage()); } @@ -826,6 +784,7 @@ } =20 private Credentials promptForProxyCredentials( + final AuthScheme authScheme, final HttpParams params, final HttpAuthRealm realm)=20 { @@ -835,7 +794,7 @@ if (credProvider !=3D null) { try { creds =3D credProvider.getCredentials( - this.proxyAuthScheme, realm.getHost(), realm.getPort()= , true); + authScheme, realm.getHost(), realm.getPort(), true); } catch (CredentialsNotAvailableException e) { LOG.warn(e.getMessage()); } @@ -875,33 +834,5 @@ */ public HttpParams getParams() { return this.params; - } - - /** - * Returns the authentication scheme used to authenticate with the=20 - * target host. - *=20 - * @return the authentication scheme used to authenticate with the=20 - * target host. Null is returned if target host authentication - * was not required. - *=20 - * @since 2.1 - */ - public AuthScheme getAuthScheme() { - return authScheme; - } - - /** - * Returns the authentication scheme used to authenticate with the=20 - * proxy host. - *=20 - * @return the authentication scheme used to authenticate with the=20 - * proxy host. Null is returned if proxy authentication was not=20 - * required. - *=20 - * @since 2.1 - */ - public AuthScheme getProxyAuthScheme() { - return proxyAuthScheme; } } Index: java/org/apache/commons/httpclient/auth/AuthChallengeException.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: java/org/apache/commons/httpclient/auth/AuthChallengeException.ja= va diff -N java/org/apache/commons/httpclient/auth/AuthChallengeException.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ java/org/apache/commons/httpclient/auth/AuthChallengeException.java 18 = Mar 2004 21:11:18 -0000 @@ -0,0 +1,70 @@ +/* + * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache= /commons/httpclient/auth/AuthenticationException.java,v 1.4 2004/02/22 18:0= 8:47 olegk Exp $ + * $Revision: 1.4 $ + * $Date: 2004/02/22 18:08:47 $ + * + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * Copyright 2002-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implie= d. + * See the License for the specific language governing permissions and + * limitations under the License. + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + +package org.apache.commons.httpclient.auth; + +/** + * Signals a failure processing authentication challenge + * + * @author Oleg Kalnichevski + *=20 + * @since 3.0 + */ +public class AuthChallengeException extends AuthenticationException { + + /** + * Creates a new AuthChallengeException with a null detail me= ssage.=20 + */ + public AuthChallengeException() { + super(); + } + + /** + * Creates a new AuthChallengeException with the specified message. + *=20 + * @param message the exception detail message + */ + public AuthChallengeException(String message) { + super(message); + } + + /** + * Creates a new AuthChallengeException with the specified detail mess= age and cause. + *=20 + * @param message the exception detail message + * @param cause the Throwable that caused this exception, or = null + * if the cause is unavailable, unknown, or not a Throwable + */ + public AuthChallengeException(String message, Throwable cause) { + super(message, cause); + } + +} Index: java/org/apache/commons/httpclient/auth/AuthChallengeProcessor.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: java/org/apache/commons/httpclient/auth/AuthChallengeProcessor.ja= va diff -N java/org/apache/commons/httpclient/auth/AuthChallengeProcessor.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ java/org/apache/commons/httpclient/auth/AuthChallengeProcessor.java 18 = Mar 2004 21:11:18 -0000 @@ -0,0 +1,165 @@ +/* + * $Header$ + * $Revision$ + * $Date$ + * + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * Copyright 2002-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implie= d. + * See the License for the specific language governing permissions and + * limitations under the License. + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + +package org.apache.commons.httpclient.auth; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.httpclient.params.HttpParams; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class provides utility methods for processing HTTP www and proxy a= uthentication=20 + * challenges. + *=20 + * @author Oleg Kalnichevski + *=20 + * @since 3.0 + */ +public final class AuthChallengeProcessor { + + private static final Log LOG =3D LogFactory.getLog(AuthChallengeProces= sor.class); + + private HttpParams params =3D null; + =20 + /** + * Creates an authentication challenge processor with the given {@link= HttpParams HTTP=20 + * parameters} + *=20 + * @param params the {@link HttpParams HTTP parameters} used by this p= rocessor + */ + public AuthChallengeProcessor(final HttpParams params) { + super(); + if (params =3D=3D null) { + throw new IllegalArgumentException("Parameter collection may n= ot be null"); + } + this.params =3D params; + } + + /** + * Determines the preferred {@link AuthScheme authentication scheme} t= hat can be used=20 + * to respond to the given collection of challenges. + *=20 + * @param challenges the collection of authentication challenges + *=20 + * @return the preferred {@link AuthScheme authentication scheme} + *=20 + * @throws AuthChallengeException if the preferred authentication sche= me=20 + * cannot be determined or is not supported + */ + public AuthScheme selectAuthScheme(final Map challenges) throws AuthCh= allengeException { + if (challenges =3D=3D null) { + throw new IllegalArgumentException("Challenge map may not be n= ull");=20 + } + Collection authPrefs =3D (Collection) this.params.getParameter( + AuthPolicy.AUTH_SCHEME_PRIORITY); + if (authPrefs =3D=3D null || authPrefs.isEmpty()) { + authPrefs =3D AuthPolicy.getDefaultAuthPrefs(); =20 + } + if (LOG.isDebugEnabled()) { + LOG.debug("Supported authentication schemes in the order of pr= eference: "=20 + + authPrefs); + } + AuthScheme authscheme =3D null; + String challenge =3D null; + Iterator item =3D authPrefs.iterator(); + while (item.hasNext()) { + String id =3D (String) item.next(); + challenge =3D (String) challenges.get(id.toLowerCase());=20 + if (challenge !=3D null) { + if (LOG.isInfoEnabled()) { + LOG.info(id + " authentication scheme selected"); + } + try { + authscheme =3D AuthPolicy.getAuthScheme(id); + } catch (IllegalStateException e) { + throw new AuthChallengeException(e.getMessage()); + } + break; + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Challenge for " + id + " authentication sch= eme not available"); + // Try again + } + } + } + if (authscheme =3D=3D null) { + // If none selected, something is wrong + throw new AuthChallengeException( + "Unable to respond to any of these challenges: " + + challenges); + } + return authscheme; + } + + /** + * Processes the given collection of challenges and updates the=20 + * {@link AuthState state} of the authentication process. + *=20 + * @param challenges the collection of authentication challenges + *=20 + * @return the {@link AuthScheme authentication scheme} used to=20 + * process the challenge + *=20 + * @throws AuthChallengeException if authentication challenges cannot = be=20 + * successfully processed or the preferred authentication scheme canno= t + * be determined + */ + public AuthScheme processChallenge(final AuthState state, final Map ch= allenges) + throws MalformedChallengeException, AuthenticationException + { =20 + if (state =3D=3D null) { + throw new IllegalArgumentException("Authentication state may n= ot be null");=20 + } + if (challenges =3D=3D null) { + throw new IllegalArgumentException("Challenge map may not be n= ull");=20 + } + if (state.getAuthScheme() =3D=3D null) { + // Authentication not attempted before + state.setAuthScheme(selectAuthScheme(challenges)); + } + AuthScheme authscheme =3D state.getAuthScheme(); + String id =3D authscheme.getSchemeName(); + if (LOG.isDebugEnabled()) { + LOG.debug("Using authentication scheme: " + id); + } + String challenge =3D (String) challenges.get(id.toLowerCase()); + if (challenge =3D=3D null) { + throw new AuthenticationException(id +=20 + " authorization challenge expected, but not found"); + } + authscheme.processChallenge(challenge); + return authscheme; + } +} Index: java/org/apache/commons/httpclient/auth/AuthState.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: java/org/apache/commons/httpclient/auth/AuthState.java diff -N java/org/apache/commons/httpclient/auth/AuthState.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ java/org/apache/commons/httpclient/auth/AuthState.java 18 Mar 2004 21:1= 1:18 -0000 @@ -0,0 +1,117 @@ +/* + * $Header$ + * $Revision$ + * $Date$ + * + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implie= d. + * See the License for the specific language governing permissions and + * limitations under the License. + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + +package org.apache.commons.httpclient.auth; + +/** + * This class provides detailed information about the state of the + * authentication process. + *=20 + * @author Oleg Kalnichevski + * @since 3.0 + */ +public class AuthState { + + /** Actual authentication scheme */ + private AuthScheme authScheme =3D null; + + /** Whether preemtive authentication is attempted */ + private boolean preemptive =3D false;=20 + =20 + /** + * Default constructor. + *=20 + */ + public AuthState() { + super(); + } + + /** + * Preemptively assigns Basic authentication scheme. + */ + public void setPreemptive() { + if (this.authScheme !=3D null) { + throw new IllegalStateException("Authentication state already = initialized"); + } + this.authScheme =3D AuthPolicy.getAuthScheme("basic"); + this.preemptive =3D true; + } + + /** + * Invalidates the authentication state by resetting its parameters. + */ + public void invalidate() { + this.authScheme =3D null; + this.preemptive =3D false; + } + =20 + /** + * Tests if preemptive authentication is used. + *=20 + * @return true if using the default Basic {@link AuthScheme=20 + * authentication scheme}, false otherwise. + */ + public boolean isPreemptive() { + return this.preemptive; + } + =20 + /** + * Assigns the given {@link AuthScheme authentication scheme}. + *=20 + * @param authScheme the {@link AuthScheme authentication scheme} + */ + public void setAuthScheme(final AuthScheme authScheme) { + this.authScheme =3D authScheme; + this.preemptive =3D false; + } + + /** + * Returns the {@link AuthScheme authentication scheme}. + *=20 + * @return {@link AuthScheme authentication scheme} + */ + public AuthScheme getAuthScheme() { + return authScheme; + } + =20 + /** + * Returns the authentication realm. + *=20 + * @return the name of the authentication realm + */ + public String getRealm() { + if (this.authScheme !=3D null) { + return this.authScheme.getRealm(); + } else { + return null; + } + } +} Index: test/org/apache/commons/httpclient/TestChallengeParser.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: test/org/apache/commons/httpclient/TestChallengeParser.java diff -N test/org/apache/commons/httpclient/TestChallengeParser.java --- test/org/apache/commons/httpclient/TestChallengeParser.java 22 Feb 2004= 18:08:49 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,80 +0,0 @@ -/* - * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - * - * Copyright 1999-2004 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implie= d. - * See the License for the specific language governing permissions and - * limitations under the License. - * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - * - * [Additional notices, if required by prior licensing conditions] - * - */ - -package org.apache.commons.httpclient; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; -import java.util.Map; -import org.apache.commons.httpclient.auth.AuthChallengeParser; -import org.apache.commons.httpclient.auth.MalformedChallengeException;=20 - -/** - * Unit tests for {@link AuthChallengeParser}. - * - * @author Oleg Kalnichevski - */ -public class TestChallengeParser extends TestCase { - - // ------------------------------------------------------------ Constr= uctor - public TestChallengeParser(String testName) { - super(testName); - } - - // -------------------------------------------------------------------= Main - public static void main(String args[]) { - String[] testCaseName =3D { TestChallengeParser.class.getName() }; - junit.textui.TestRunner.main(testCaseName); - } - - // ------------------------------------------------------- TestCase Me= thods - - public static Test suite() { - return new TestSuite(TestChallengeParser.class); - } - - - public void testParsingChallenge() { - String challenge =3D=20 - "Basic realm=3D\"realm1\", test, test1 =3D stuff, test2 =3D \"= stuff, stuff\", test3=3D\"crap"; - String scheme =3D null; - Map elements =3D null; - try { - scheme =3D AuthChallengeParser.extractScheme(challenge); - elements =3D AuthChallengeParser.extractParams(challenge); - } catch (MalformedChallengeException e) { - fail("Unexpected exception: " + e.toString()); - } - assertEquals("basic", scheme); - assertEquals("realm1", elements.get("realm")); - assertEquals(null, elements.get("test")); - assertEquals("stuff", elements.get("test1")); - assertEquals("stuff, stuff", elements.get("test2")); - assertEquals("\"crap", elements.get("test3")); - } -} Index: test/org/apache/commons/httpclient/TestExceptions.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/co= mmons/httpclient/TestExceptions.java,v retrieving revision 1.3 diff -u -r1.3 TestExceptions.java --- test/org/apache/commons/httpclient/TestExceptions.java 22 Feb 2004 18:0= 8:49 -0000 1.3 +++ test/org/apache/commons/httpclient/TestExceptions.java 18 Mar 2004 21:1= 1:19 -0000 @@ -34,6 +34,8 @@ import java.io.PrintWriter; import java.io.StringWriter; =20 +import org.apache.commons.httpclient.auth.*; + import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; Index: test/org/apache/commons/httpclient/TestNoHost.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/co= mmons/httpclient/TestNoHost.java,v retrieving revision 1.32 diff -u -r1.32 TestNoHost.java --- test/org/apache/commons/httpclient/TestNoHost.java 27 Feb 2004 19:01:33= -0000 1.32 +++ test/org/apache/commons/httpclient/TestNoHost.java 18 Mar 2004 21:11:20= -0000 @@ -35,6 +35,8 @@ import junit.framework.TestSuite; =20 import org.apache.commons.httpclient.auth.TestBasicAuth; +import org.apache.commons.httpclient.auth.TestChallengeParser; +import org.apache.commons.httpclient.auth.TestChallengeProcessor; =20 /** * Tests that don't require any external host. @@ -61,6 +63,7 @@ suite.addTest(TestParameterParser.suite()); suite.addTest(TestHeaderElement.suite()); suite.addTest(TestChallengeParser.suite()); + suite.addTest(TestChallengeProcessor.suite()); suite.addTest(TestAuthenticator.suite()); suite.addTest(TestBasicAuth.suite()); suite.addTest(TestHttpUrlMethod.suite()); Index: test/org/apache/commons/httpclient/auth/TestBasicAuth.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/co= mmons/httpclient/auth/TestBasicAuth.java,v retrieving revision 1.1 diff -u -r1.1 TestBasicAuth.java --- test/org/apache/commons/httpclient/auth/TestBasicAuth.java 27 Feb 2004 = 19:04:32 -0000 1.1 +++ test/org/apache/commons/httpclient/auth/TestBasicAuth.java 18 Mar 2004 = 21:11:21 -0000 @@ -216,6 +216,10 @@ this.client.executeMethod(httpget); assertNotNull(httpget.getStatusLine()); assertEquals(HttpStatus.SC_UNAUTHORIZED, httpget.getStatusLine= ().getStatusCode()); + AuthState authstate =3D httpget.getHostAuthState(); + assertNotNull(authstate.getAuthScheme()); + assertTrue(authstate.getAuthScheme() instanceof BasicScheme); + assertEquals("test", authstate.getRealm()); } finally { httpget.releaseConnection(); } @@ -262,6 +266,10 @@ String expected =3D "Basic " + EncodingUtil.getAsciiString( Base64.encodeBase64(EncodingUtil.getAsciiBytes("test:test"))); assertEquals(expected, auth.getValue()); + AuthState authstate =3D httpget.getHostAuthState(); + assertNotNull(authstate.getAuthScheme()); + assertTrue(authstate.getAuthScheme() instanceof BasicScheme); + assertEquals("test", authstate.getRealm()); } =20 public void testBasicAuthentication() throws Exception { @@ -286,6 +294,10 @@ String expected =3D "Basic " + EncodingUtil.getAsciiString( Base64.encodeBase64(EncodingUtil.getAsciiBytes("test:test"))); assertEquals(expected, auth.getValue()); + AuthState authstate =3D httpget.getHostAuthState(); + assertNotNull(authstate.getAuthScheme()); + assertTrue(authstate.getAuthScheme() instanceof BasicScheme); + assertEquals("test", authstate.getRealm()); } =20 public void testBasicAuthenticationWithInvalidCredentials() throws Exc= eption { @@ -305,6 +317,10 @@ } assertNotNull(httpget.getStatusLine()); assertEquals(HttpStatus.SC_FORBIDDEN, httpget.getStatusLine().getS= tatusCode()); + AuthState authstate =3D httpget.getHostAuthState(); + assertNotNull(authstate.getAuthScheme()); + assertTrue(authstate.getAuthScheme() instanceof BasicScheme); + assertEquals("test", authstate.getRealm()); } =20 public void testBasicAuthenticationWithMutlipleRealms() throws Excepti= on { @@ -335,6 +351,10 @@ String expected =3D "Basic " + EncodingUtil.getAsciiString( Base64.encodeBase64(EncodingUtil.getAsciiBytes("test:test"= ))); assertEquals(expected, auth.getValue()); + AuthState authstate =3D httpget.getHostAuthState(); + assertNotNull(authstate.getAuthScheme()); + assertTrue(authstate.getAuthScheme() instanceof BasicScheme); + assertEquals("test", authstate.getRealm()); } { this.server.setHttpService(new BasicAuthService2()); @@ -351,6 +371,10 @@ String expected =3D "Basic " + EncodingUtil.getAsciiString( Base64.encodeBase64(EncodingUtil.getAsciiBytes("test2:test= 2"))); assertEquals(expected, auth.getValue()); + AuthState authstate =3D httpget.getHostAuthState(); + assertNotNull(authstate.getAuthScheme()); + assertTrue(authstate.getAuthScheme() instanceof BasicScheme); + assertEquals("test2", authstate.getRealm()); } } =20 @@ -373,6 +397,11 @@ String expected =3D "Basic " + EncodingUtil.getAsciiString( Base64.encodeBase64(EncodingUtil.getAsciiBytes("test:test"))); assertEquals(expected, auth.getValue()); + AuthState authstate =3D httpget.getHostAuthState(); + assertNotNull(authstate.getAuthScheme()); + assertTrue(authstate.getAuthScheme() instanceof BasicScheme); + assertNull(authstate.getRealm()); + assertTrue(authstate.isPreemptive()); } =20 public void testBasicAuthenticationCaseInsensitivity() throws Exceptio= n { @@ -397,5 +426,21 @@ String expected =3D "Basic " + EncodingUtil.getAsciiString( Base64.encodeBase64(EncodingUtil.getAsciiBytes("test:test"))); assertEquals(expected, auth.getValue()); + } + + + public void testCustomAuthorizationHeader() throws Exception { + String authResponse =3D "Basic " + EncodingUtil.getAsciiString( + Base64.encodeBase64(EncodingUtil.getAsciiBytes("test:test"))); + this.server.setHttpService(new BasicAuthService()); + GetMethod httpget =3D new GetMethod("/test/"); + httpget.addRequestHeader(new Header("Authorization", authResponse)= ); + try { + this.client.executeMethod(httpget); + } finally { + httpget.releaseConnection(); + } + assertNotNull(httpget.getStatusLine()); + assertEquals(HttpStatus.SC_OK, httpget.getStatusLine().getStatusCo= de()); } } Index: test/org/apache/commons/httpclient/auth/TestChallengeParser.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: test/org/apache/commons/httpclient/auth/TestChallengeParser.java diff -N test/org/apache/commons/httpclient/auth/TestChallengeParser.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/org/apache/commons/httpclient/auth/TestChallengeParser.java 18 Mar= 2004 21:11:21 -0000 @@ -0,0 +1,83 @@ +/* + * $Header$ + * $Revision$ + * $Date$ + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implie= d. + * See the License for the specific language governing permissions and + * limitations under the License. + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + +package org.apache.commons.httpclient.auth; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import java.util.Map; +import org.apache.commons.httpclient.auth.AuthChallengeParser; +import org.apache.commons.httpclient.auth.MalformedChallengeException;=20 + +/** + * Unit tests for {@link AuthChallengeParser}. + * + * @author Oleg Kalnichevski + */ +public class TestChallengeParser extends TestCase { + + // ------------------------------------------------------------ Constr= uctor + public TestChallengeParser(String testName) { + super(testName); + } + + // -------------------------------------------------------------------= Main + public static void main(String args[]) { + String[] testCaseName =3D { TestChallengeParser.class.getName() }; + junit.textui.TestRunner.main(testCaseName); + } + + // ------------------------------------------------------- TestCase Me= thods + + public static Test suite() { + return new TestSuite(TestChallengeParser.class); + } + + + public void testParsingChallenge() { + String challenge =3D=20 + "Basic realm=3D\"realm1\", test, test1 =3D stuff, test2 =3D \"= stuff, stuff\", test3=3D\"crap"; + String scheme =3D null; + Map elements =3D null; + try { + scheme =3D AuthChallengeParser.extractScheme(challenge); + elements =3D AuthChallengeParser.extractParams(challenge); + } catch (MalformedChallengeException e) { + fail("Unexpected exception: " + e.toString()); + } + assertEquals("basic", scheme); + assertEquals("realm1", elements.get("realm")); + assertEquals(null, elements.get("test")); + assertEquals("stuff", elements.get("test1")); + assertEquals("stuff, stuff", elements.get("test2")); + assertEquals("\"crap", elements.get("test3")); + } +} Index: test/org/apache/commons/httpclient/auth/TestChallengeProcessor.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: test/org/apache/commons/httpclient/auth/TestChallengeProcessor.ja= va diff -N test/org/apache/commons/httpclient/auth/TestChallengeProcessor.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/org/apache/commons/httpclient/auth/TestChallengeProcessor.java 18 = Mar 2004 21:11:22 -0000 @@ -0,0 +1,173 @@ +/* + * $Header$ + * $Revision$ + * $Date$ + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implie= d. + * See the License for the specific language governing permissions and + * limitations under the License. + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ + +package org.apache.commons.httpclient.auth; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.commons.httpclient.params.DefaultHttpParams; +import org.apache.commons.httpclient.params.HttpParams; + +/** + * Unit tests for {@link testParsingChallenge}. + * + * @author Oleg Kalnichevski + */ +public class TestChallengeProcessor extends TestCase { + + // ------------------------------------------------------------ Constr= uctor + public TestChallengeProcessor(String testName) { + super(testName); + } + + // -------------------------------------------------------------------= Main + public static void main(String args[]) { + String[] testCaseName =3D { TestChallengeProcessor.class.getName()= }; + junit.textui.TestRunner.main(testCaseName); + } + + // ------------------------------------------------------- TestCase Me= thods + + public static Test suite() { + return new TestSuite(TestChallengeProcessor.class); + } + + + public void testChallengeSelection() throws Exception { + List authPrefs =3D new ArrayList(3); + authPrefs.add(AuthPolicy.NTLM); + authPrefs.add(AuthPolicy.DIGEST); + authPrefs.add(AuthPolicy.BASIC); + HttpParams httpparams =3D new DefaultHttpParams();=20 + httpparams.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs= ); + =20 + AuthChallengeProcessor processor =3D new AuthChallengeProcessor(ht= tpparams); + + Map map =3D new HashMap();=20 + map.put("unknown", "unknown realm=3D\"whatever\""); + map.put("basic", "basic realm=3D\"whatever\""); + =20 + AuthScheme authscheme =3D processor.selectAuthScheme(map); + assertTrue(authscheme instanceof BasicScheme); + } + + + public void testInvalidChallenge() throws Exception { + List authPrefs =3D new ArrayList(3); + authPrefs.add("unsupported1"); + authPrefs.add("unsupported2"); + HttpParams httpparams =3D new DefaultHttpParams();=20 + httpparams.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs= ); + =20 + AuthChallengeProcessor processor =3D new AuthChallengeProcessor(ht= tpparams); + + Map map =3D new HashMap();=20 + map.put("unsupported1", "unsupported1 realm=3D\"whatever\""); + map.put("unsupported2", "unsupported2 realm=3D\"whatever\""); + try { + AuthScheme authscheme =3D processor.selectAuthScheme(map); + fail("AuthChallengeException should have been thrown"); + } catch (AuthChallengeException e) { + //ignore + } + } + + + public void testUnsupportedChallenge() throws Exception { + List authPrefs =3D new ArrayList(3); + authPrefs.add(AuthPolicy.NTLM); + authPrefs.add(AuthPolicy.BASIC); + authPrefs.add(AuthPolicy.DIGEST); + HttpParams httpparams =3D new DefaultHttpParams();=20 + httpparams.setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs= ); + =20 + AuthChallengeProcessor processor =3D new AuthChallengeProcessor(ht= tpparams); + + Map map =3D new HashMap();=20 + map.put("unsupported1", "unsupported1 realm=3D\"whatever\""); + map.put("unsupported2", "unsupported2 realm=3D\"whatever\""); + =20 + try { + AuthScheme authscheme =3D processor.selectAuthScheme(map); + fail("AuthChallengeException should have been thrown"); + } catch (AuthChallengeException e) { + //expected + } + } + + public void testChallengeProcessing() throws Exception { + HttpParams httpparams =3D new DefaultHttpParams();=20 + AuthChallengeProcessor processor =3D new AuthChallengeProcessor(ht= tpparams); + + Map map =3D new HashMap();=20 + map.put("basic", "basic realm=3D\"whatever\", param=3D\"value\""); + =20 + AuthState authstate =3D new AuthState(); + =20 + AuthScheme authscheme =3D processor.processChallenge(authstate, ma= p); + assertTrue(authscheme instanceof BasicScheme); + assertEquals("whatever", authscheme.getRealm()); + assertEquals(authscheme, authstate.getAuthScheme()); + assertEquals("value", authscheme.getParameter("param")); + } + + public void testInvalidChallengeProcessing() throws Exception { + HttpParams httpparams =3D new DefaultHttpParams();=20 + AuthChallengeProcessor processor =3D new AuthChallengeProcessor(ht= tpparams); + + Map map =3D new HashMap();=20 + map.put("basic", "basic realm=3D\"whatever\", param=3D\"value\""); + =20 + AuthState authstate =3D new AuthState(); + =20 + AuthScheme authscheme =3D processor.processChallenge(authstate, ma= p); + assertTrue(authscheme instanceof BasicScheme); + assertEquals("whatever", authscheme.getRealm()); + assertEquals(authscheme, authstate.getAuthScheme()); + assertEquals("value", authscheme.getParameter("param")); + + Map map2 =3D new HashMap();=20 + map2.put("ntlm", "NTLM"); + try { + // Basic authentication scheme expected + authscheme =3D processor.processChallenge(authstate, map2); + fail("AuthenticationException should have been thrown"); + } catch (AuthenticationException e) { + //expected + } + } +} --=-vnHNPYcupp8oqxt5PCc1 Content-Type: text/plain; charset=us-ascii --------------------------------------------------------------------- To unsubscribe, e-mail: commons-httpclient-dev-unsubscribe@jakarta.apache.org For additional commands, e-mail: commons-httpclient-dev-help@jakarta.apache.org --=-vnHNPYcupp8oqxt5PCc1--