AuthScheme API changes; User credentials made optional for those schemes that do not require them

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1692371 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2015-07-23 10:00:46 +00:00
parent e7190e7177
commit d3bcbedec7
52 changed files with 974 additions and 1433 deletions

View File

@ -29,18 +29,13 @@
import java.io.IOException; import java.io.IOException;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthScope;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials; import org.apache.http.auth.Credentials;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.auth.NTCredentials; import org.apache.http.auth.NTCredentials;
import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache; import org.apache.http.client.AuthCache;
@ -151,12 +146,14 @@ public Executor auth(final String host, final Credentials creds) {
} }
public Executor authPreemptive(final HttpHost host) { public Executor authPreemptive(final HttpHost host) {
final BasicScheme basicScheme = new BasicScheme(); if (this.credentialsStore != null) {
try { final Credentials credentials = this.credentialsStore.getCredentials(new AuthScope(host));
basicScheme.processChallenge(ChallengeType.TARGET, new AuthChallenge("basic", null, Collections.<NameValuePair>emptyList())); if (credentials == null) {
} catch (final MalformedChallengeException ignore) { final BasicScheme basicScheme = new BasicScheme();
basicScheme.initPreemptive(credentials);
this.authCache.put(host, basicScheme);
}
} }
this.authCache.put(host, basicScheme);
return this; return this;
} }
@ -168,12 +165,14 @@ public Executor authPreemptive(final String host) {
} }
public Executor authPreemptiveProxy(final HttpHost proxy) { public Executor authPreemptiveProxy(final HttpHost proxy) {
final BasicScheme basicScheme = new BasicScheme(); if (this.credentialsStore != null) {
try { final Credentials credentials = this.credentialsStore.getCredentials(new AuthScope(proxy));
basicScheme.processChallenge(ChallengeType.PROXY, new AuthChallenge("basic", null, Collections.<NameValuePair>emptyList())); if (credentials == null) {
} catch (final MalformedChallengeException ignore) { final BasicScheme basicScheme = new BasicScheme();
basicScheme.initPreemptive(credentials);
this.authCache.put(proxy, basicScheme);
}
} }
this.authCache.put(proxy, basicScheme);
return this; return this;
} }

View File

@ -65,10 +65,10 @@ public interface HttpCacheStorage {
void removeEntry(String key) throws IOException; void removeEntry(String key) throws IOException;
/** /**
* Atomically applies the given callback to update an existing cache * Atomically applies the given callback to processChallenge an existing cache
* entry under a given key. * entry under a given key.
* @param key indicates which entry to modify * @param key indicates which entry to modify
* @param callback performs the update; see * @param callback performs the processChallenge; see
* {@link HttpCacheUpdateCallback} for details, but roughly the * {@link HttpCacheUpdateCallback} for details, but roughly the
* callback expects to be handed the current entry and will return * callback expects to be handed the current entry and will return
* the new value for the entry. * the new value for the entry.

View File

@ -28,7 +28,7 @@
/** /**
* Signals that {@link HttpCacheStorage} encountered an error performing an * Signals that {@link HttpCacheStorage} encountered an error performing an
* update operation. * processChallenge operation.
* *
* @since 4.1 * @since 4.1
*/ */

View File

@ -165,7 +165,7 @@ public HttpCacheEntry update(final HttpCacheEntry existing) throws IOException {
try { try {
storage.updateEntry(parentURI, callback); storage.updateEntry(parentURI, callback);
} catch (final HttpCacheUpdateException e) { } catch (final HttpCacheUpdateException e) {
log.warn("Could not update key [" + parentURI + "]", e); log.warn("Could not processChallenge key [" + parentURI + "]", e);
} }
} }
@ -189,7 +189,7 @@ public HttpCacheEntry update(final HttpCacheEntry existing)
try { try {
storage.updateEntry(parentCacheKey, callback); storage.updateEntry(parentCacheKey, callback);
} catch (final HttpCacheUpdateException e) { } catch (final HttpCacheUpdateException e) {
log.warn("Could not update key [" + parentCacheKey + "]", e); log.warn("Could not processChallenge key [" + parentCacheKey + "]", e);
} }
} }

View File

@ -113,7 +113,7 @@ public class CacheConfig implements Cloneable {
public final static int DEFAULT_MAX_CACHE_ENTRIES = 1000; public final static int DEFAULT_MAX_CACHE_ENTRIES = 1000;
/** Default setting for the number of retries on a failed /** Default setting for the number of retries on a failed
* cache update * cache processChallenge
*/ */
public final static int DEFAULT_MAX_UPDATE_RETRIES = 1; public final static int DEFAULT_MAX_UPDATE_RETRIES = 1;
@ -234,7 +234,7 @@ public int getMaxCacheEntries() {
} }
/** /**
* Returns the number of times to retry a cache update on failure * Returns the number of times to retry a cache processChallenge on failure
*/ */
public int getMaxUpdateRetries(){ public int getMaxUpdateRetries(){
return maxUpdateRetries; return maxUpdateRetries;
@ -400,7 +400,7 @@ public Builder setMaxCacheEntries(final int maxCacheEntries) {
} }
/** /**
* Sets the number of times to retry a cache update on failure * Sets the number of times to retry a cache processChallenge on failure
*/ */
public Builder setMaxUpdateRetries(final int maxUpdateRetries) { public Builder setMaxUpdateRetries(final int maxUpdateRetries) {
this.maxUpdateRetries = maxUpdateRetries; this.maxUpdateRetries = maxUpdateRetries;

View File

@ -48,7 +48,7 @@
/** /**
* Update a {@link HttpCacheEntry} with new or updated information based on the latest * Update a {@link HttpCacheEntry} with new or updated information based on the latest
* 304 status response from the Server. Use the {@link HttpResponse} to perform * 304 status response from the Server. Use the {@link HttpResponse} to perform
* the update. * the processChallenge.
* *
* @since 4.1 * @since 4.1
*/ */

View File

@ -712,7 +712,7 @@ private HttpCacheEntry getUpdatedVariantEntry(
responseEntry = responseCache.updateVariantCacheEntry(target, conditionalRequest, responseEntry = responseCache.updateVariantCacheEntry(target, conditionalRequest,
matchedEntry, backendResponse, requestDate, responseDate, matchingVariant.getCacheKey()); matchedEntry, backendResponse, requestDate, responseDate, matchingVariant.getCacheKey());
} catch (final IOException ioe) { } catch (final IOException ioe) {
log.warn("Could not update cache entry", ioe); log.warn("Could not processChallenge cache entry", ioe);
} finally { } finally {
backendResponse.close(); backendResponse.close();
} }
@ -726,7 +726,7 @@ private void tryToUpdateVariantMap(
try { try {
responseCache.reuseVariantEntryFor(target, request, matchingVariant); responseCache.reuseVariantEntryFor(target, request, matchingVariant);
} catch (final IOException ioe) { } catch (final IOException ioe) {
log.warn("Could not update cache entry to reuse variant", ioe); log.warn("Could not processChallenge cache entry to reuse variant", ioe);
} }
} }

View File

@ -98,7 +98,7 @@ private void updateValue(final String identifier) {
* *
* In case there is a lot of contention on that identifier, a thread * In case there is a lot of contention on that identifier, a thread
* might starve. Thus it gives up after a certain number of failed * might starve. Thus it gives up after a certain number of failed
* update tries. * processChallenge tries.
*/ */
for (int i = 0; i < MAX_UPDATE_TRIES; i++) { for (int i = 0; i < MAX_UPDATE_TRIES; i++) {
final FailureCacheValue oldValue = storage.get(identifier); final FailureCacheValue oldValue = storage.get(identifier);

View File

@ -159,7 +159,7 @@ HttpCacheEntry updateVariantCacheEntry(HttpHost target, HttpRequest request,
* @param target host of the upstream client request * @param target host of the upstream client request
* @param req request sent by upstream client * @param req request sent by upstream client
* @param variant variant cache entry to reuse * @param variant variant cache entry to reuse
* @throws IOException may be thrown during cache update * @throws IOException may be thrown during cache processChallenge
*/ */
void reuseVariantEntryFor(HttpHost target, final HttpRequest req, void reuseVariantEntryFor(HttpHost target, final HttpRequest req,
final Variant variant) throws IOException; final Variant variant) throws IOException;

View File

@ -156,6 +156,6 @@ public synchronized void updateEntry(final String key, final HttpCacheUpdateCall
} }
} }
}while(numRetries <= maxUpdateRetries); }while(numRetries <= maxUpdateRetries);
throw new HttpCacheUpdateException("Failed to update"); throw new HttpCacheUpdateException("Failed to processChallenge");
} }
} }

View File

@ -268,6 +268,6 @@ public void updateEntry(final String url, final HttpCacheUpdateCallback callback
} }
} while (numRetries <= maxUpdateRetries); } while (numRetries <= maxUpdateRetries);
throw new HttpCacheUpdateException("Failed to update"); throw new HttpCacheUpdateException("Failed to processChallenge");
} }
} }

View File

@ -1208,7 +1208,7 @@ public void testSendsAllVariantEtagsInConditionalRequest()
} }
/* "If the entity-tag of the new response matches that of an existing /* "If the entity-tag of the new response matches that of an existing
* entry, the new response SHOULD be used to update the header fields * entry, the new response SHOULD be used to processChallenge the header fields
* of the existing entry, and the result MUST be returned to the * of the existing entry, and the result MUST be returned to the
* client." * client."
* *

View File

@ -2198,8 +2198,8 @@ public void testNotModifiedOfNonCachedEntityShouldRevalidateWithUnconditionalGET
} }
/* /*
* "If a cache uses a received 304 response to update a cache entry, the * "If a cache uses a received 304 response to processChallenge a cache entry, the
* cache MUST update the entry to reflect any new field values given in the * cache MUST processChallenge the entry to reflect any new field values given in the
* response. * response.
* *
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
@ -4539,7 +4539,7 @@ public void testCannotServeFromCacheForVaryStar() throws Exception {
* the ETag header field in its 304 (Not Modified) response to * the ETag header field in its 304 (Not Modified) response to
* tell the cache which entry is appropriate. If the entity-tag of * tell the cache which entry is appropriate. If the entity-tag of
* the new response matches that of an existing entry, the new * the new response matches that of an existing entry, the new
* response SHOULD be used to update the header fields of the * response SHOULD be used to processChallenge the header fields of the
* existing entry, and the result MUST be returned to the client. * existing entry, and the result MUST be returned to the client.
* *
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6

View File

@ -168,7 +168,7 @@ public HttpCacheEntry update(final HttpCacheEntry old){
when(mockCache.get(key)).thenReturn(existingElement); when(mockCache.get(key)).thenReturn(existingElement);
when(mockSerializer.readFrom(isA(InputStream.class))).thenReturn(existingValue); when(mockSerializer.readFrom(isA(InputStream.class))).thenReturn(existingValue);
// update // processChallenge
mockSerializer.writeTo(same(updatedValue), isA(OutputStream.class)); mockSerializer.writeTo(same(updatedValue), isA(OutputStream.class));
when(mockCache.replace(same(existingElement), isA(Element.class))).thenReturn(true); when(mockCache.replace(same(existingElement), isA(Element.class))).thenReturn(true);
@ -231,7 +231,7 @@ public HttpCacheEntry update(final HttpCacheEntry old){
when(mockCache.get(key)).thenReturn(existingElement); when(mockCache.get(key)).thenReturn(existingElement);
when(mockSerializer.readFrom(isA(InputStream.class))).thenReturn(existingValue); when(mockSerializer.readFrom(isA(InputStream.class))).thenReturn(existingValue);
// update but fail // processChallenge but fail
when(mockCache.replace(same(existingElement), isA(Element.class))).thenReturn(false); when(mockCache.replace(same(existingElement), isA(Element.class))).thenReturn(false);
try{ try{

View File

@ -1,108 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.auth.win;
import java.io.Serializable;
import java.security.Principal;
import org.apache.http.annotation.Immutable;
import org.apache.http.auth.Credentials;
import com.sun.jna.platform.win32.Secur32.EXTENDED_NAME_FORMAT;
import com.sun.jna.platform.win32.Secur32Util;
/**
* Returns the current Windows user credentials
* <p>
* EXPERIMENTAL
* </p>
*
* @since 4.4
*/
@Immutable
public final class CurrentWindowsCredentials implements Credentials, Serializable, Principal {
private static final long serialVersionUID = 4361166468529298169L;
public static final CurrentWindowsCredentials INSTANCE = new CurrentWindowsCredentials();
/**
* Get the SAM-compatible username of the currently logged-on user.
*
* @return String.
*/
public static String getCurrentUsername() {
return Secur32Util.getUserNameEx(EXTENDED_NAME_FORMAT.NameSamCompatible);
}
private CurrentWindowsCredentials() {
}
@Override
public Principal getUserPrincipal() {
return this;
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
return getClass().equals(o.getClass());
}
@Override
public String toString() {
return getCurrentUsername();
}
/**
* Returns an empty password
*/
@Override
public String getPassword() {
return "";
}
@Override
public String getName() {
return getCurrentUsername();
}
}

View File

@ -1,76 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.auth.win;
import org.apache.http.annotation.ThreadSafe;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsStore;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.util.Args;
/**
* {@link CredentialsStore} implementation that always returns
* {@link org.apache.http.impl.auth.win.CurrentWindowsCredentials} instance to NTLM
* and SPNego authentication challenges.
* <p>
* EXPERIMENTAL
* </p>
*
* @since 4.4
*/
@ThreadSafe
public class WindowsCredentialsProvider implements CredentialsStore {
private final CredentialsStore provider;
public WindowsCredentialsProvider(final CredentialsStore provider) {
this.provider = Args.notNull(provider, "Credentials provider");
}
@Override
public Credentials getCredentials(final AuthScope authscope) {
final String scheme = authscope.getScheme();
if (AuthSchemes.NTLM.equalsIgnoreCase(scheme) || AuthSchemes.SPNEGO.equalsIgnoreCase(scheme)) {
return CurrentWindowsCredentials.INSTANCE;
} else {
return provider.getCredentials(authscope);
}
}
@Override
public void setCredentials(final AuthScope authscope, final Credentials credentials) {
provider.setCredentials(authscope, credentials);
}
@Override
public void clear() {
provider.clear();
}
}

View File

@ -26,29 +26,28 @@
*/ */
package org.apache.http.impl.auth.win; package org.apache.http.impl.auth.win;
import java.security.Principal;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
import org.apache.http.annotation.NotThreadSafe; import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType; import org.apache.http.auth.BasicUserPrincipal;
import org.apache.http.auth.Credentials; import org.apache.http.auth.CredentialsProvider;
import org.apache.http.auth.InvalidCredentialsException;
import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.routing.RouteInfo; import org.apache.http.conn.routing.RouteInfo;
import org.apache.http.impl.auth.NonStandardAuthScheme;
import org.apache.http.message.BufferedHeader;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.apache.http.util.CharArrayBuffer; import org.apache.http.util.Args;
import com.sun.jna.platform.win32.Secur32; import com.sun.jna.platform.win32.Secur32;
import com.sun.jna.platform.win32.Secur32Util;
import com.sun.jna.platform.win32.Sspi; import com.sun.jna.platform.win32.Sspi;
import com.sun.jna.platform.win32.Sspi.CredHandle; import com.sun.jna.platform.win32.Sspi.CredHandle;
import com.sun.jna.platform.win32.Sspi.CtxtHandle; import com.sun.jna.platform.win32.Sspi.CtxtHandle;
@ -70,7 +69,7 @@
* @since 4.4 * @since 4.4
*/ */
@NotThreadSafe @NotThreadSafe
public class WindowsNegotiateScheme extends NonStandardAuthScheme { public class WindowsNegotiateScheme implements AuthScheme {
private final Log log = LogFactory.getLog(getClass()); private final Log log = LogFactory.getLog(getClass());
@ -78,6 +77,7 @@ public class WindowsNegotiateScheme extends NonStandardAuthScheme {
private final String scheme; private final String scheme;
private final String servicePrincipalName; private final String servicePrincipalName;
private String challenge;
private CredHandle clientCred; private CredHandle clientCred;
private CtxtHandle sspiContext; private CtxtHandle sspiContext;
private boolean continueNeeded; private boolean continueNeeded;
@ -119,7 +119,7 @@ public void finalize() throws Throwable {
} }
@Override @Override
public String getSchemeName() { public String getName() {
return scheme; return scheme;
} }
@ -128,11 +128,20 @@ public boolean isConnectionBased() {
return true; return true;
} }
@Override
public String getRealm() {
return null;
}
@Override @Override
public void processChallenge( public void processChallenge(
final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException { final AuthChallenge authChallenge,
update(challengeType, authChallenge); final HttpContext context) throws MalformedChallengeException {
final String challenge = getChallenge(); Args.notNull(authChallenge, "AuthChallenge");
if (authChallenge.getValue() == null) {
throw new MalformedChallengeException("Missing auth challenge");
}
challenge = authChallenge.getValue();
if (challenge.isEmpty()) { if (challenge.isEmpty()) {
if (clientCred != null) { if (clientCred != null) {
dispose(); // run cleanup first before throwing an exception otherwise can leak OS resources dispose(); // run cleanup first before throwing an exception otherwise can leak OS resources
@ -144,24 +153,38 @@ public void processChallenge(
} }
@Override @Override
public Header authenticate( public boolean isResponseReady(
final Credentials credentials, final HttpHost host,
final CredentialsProvider credentialsProvider,
final HttpContext context) throws AuthenticationException {
return true;
}
/**
* Get the SAM-compatible username of the currently logged-on user.
*
* @return String.
*/
public static String getCurrentUsername() {
return Secur32Util.getUserNameEx(Secur32.EXTENDED_NAME_FORMAT.NameSamCompatible);
}
@Override
public Principal getPrinciple() {
return new BasicUserPrincipal(getCurrentUsername());
}
@Override
public String generateAuthResponse(
final HttpHost host,
final HttpRequest request, final HttpRequest request,
final HttpContext context) throws AuthenticationException { final HttpContext context) throws AuthenticationException {
final String challenge = getChallenge();
final String response; final String response;
if (clientCred == null) { if (clientCred == null) {
// ?? We don't use the credentials, should we allow anything?
if (!(credentials instanceof CurrentWindowsCredentials)) {
throw new InvalidCredentialsException(
"Credentials cannot be used for " + getSchemeName() + " authentication: "
+ credentials.getClass().getName());
}
// client credentials handle // client credentials handle
try { try {
final String username = CurrentWindowsCredentials.getCurrentUsername(); final String username = getCurrentUsername();
final TimeStamp lifetime = new TimeStamp(); final TimeStamp lifetime = new TimeStamp();
clientCred = new CredHandle(); clientCred = new CredHandle();
@ -202,18 +225,7 @@ public Header authenticate(
} }
} }
} }
return scheme + " " + response;
final CharArrayBuffer buffer = new CharArrayBuffer(scheme.length() + 30);
if (isProxy()) {
buffer.append(HttpHeaders.PROXY_AUTHORIZATION);
} else {
buffer.append(HttpHeaders.AUTHORIZATION);
}
buffer.append(": ");
buffer.append(scheme); // NTLM or Negotiate
buffer.append(" ");
buffer.append(response);
return new BufferedHeader(buffer);
} }
private void failAuthCleanup() { private void failAuthCleanup() {
@ -281,7 +293,7 @@ String getToken(
} }
@Override @Override
public boolean isComplete() { public boolean isChallengeComplete() {
return !continueNeeded; return !continueNeeded;
} }

View File

@ -29,13 +29,11 @@
import java.util.Locale; import java.util.Locale;
import org.apache.http.auth.AuthSchemeProvider; import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.AuthSchemes;
import org.apache.http.config.Registry; import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder; import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.auth.BasicSchemeFactory; import org.apache.http.impl.auth.BasicSchemeFactory;
import org.apache.http.impl.auth.DigestSchemeFactory; import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.win.WindowsCredentialsProvider;
import org.apache.http.impl.auth.win.WindowsNTLMSchemeFactory; import org.apache.http.impl.auth.win.WindowsNTLMSchemeFactory;
import org.apache.http.impl.auth.win.WindowsNegotiateSchemeFactory; import org.apache.http.impl.auth.win.WindowsNegotiateSchemeFactory;
@ -74,9 +72,7 @@ private static HttpClientBuilder createBuilder() {
.register(AuthSchemes.NTLM, new WindowsNTLMSchemeFactory(null)) .register(AuthSchemes.NTLM, new WindowsNTLMSchemeFactory(null))
.register(AuthSchemes.SPNEGO, new WindowsNegotiateSchemeFactory(null)) .register(AuthSchemes.SPNEGO, new WindowsNegotiateSchemeFactory(null))
.build(); .build();
final CredentialsProvider credsProvider = new WindowsCredentialsProvider(new SystemDefaultCredentialsProvider());
return HttpClientBuilder.create() return HttpClientBuilder.create()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultAuthSchemeRegistry(authSchemeRegistry); .setDefaultAuthSchemeRegistry(authSchemeRegistry);
} else { } else {
return HttpClientBuilder.create(); return HttpClientBuilder.create();

View File

@ -36,7 +36,6 @@
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeProvider; import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
@ -44,7 +43,6 @@
import org.apache.http.config.RegistryBuilder; import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
import org.apache.http.impl.client.WinHttpClients; import org.apache.http.impl.client.WinHttpClients;
import org.apache.http.localserver.LocalServerTestBase; import org.apache.http.localserver.LocalServerTestBase;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
@ -105,10 +103,7 @@ public AuthScheme create(final HttpContext context) {
return new WindowsNegotiateSchemeGetTokenFail(AuthSchemes.SPNEGO, "HTTP/example.com"); return new WindowsNegotiateSchemeGetTokenFail(AuthSchemes.SPNEGO, "HTTP/example.com");
} }
}).build(); }).build();
final CredentialsProvider credsProvider =
new WindowsCredentialsProvider(new SystemDefaultCredentialsProvider());
final CloseableHttpClient customClient = HttpClientBuilder.create() final CloseableHttpClient customClient = HttpClientBuilder.create()
.setDefaultCredentialsProvider(credsProvider)
.setDefaultAuthSchemeRegistry(authSchemeRegistry).build(); .setDefaultAuthSchemeRegistry(authSchemeRegistry).build();
final HttpHost target = start(); final HttpHost target = start();

View File

@ -27,7 +27,6 @@
package org.apache.http.examples.client; package org.apache.http.examples.client;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache; import org.apache.http.client.AuthCache;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
@ -35,7 +34,6 @@
import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
@ -52,18 +50,13 @@ public class ClientPreemptiveBasicAuthentication {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
HttpHost target = new HttpHost("localhost", 80, "http"); HttpHost target = new HttpHost("localhost", 80, "http");
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("username", "password"));
try (CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider).build()) {
// Create AuthCache instance // Create AuthCache instance
AuthCache authCache = new BasicAuthCache(); AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local // Generate BASIC scheme object and add it to the local auth cache
// auth cache
BasicScheme basicAuth = new BasicScheme(); BasicScheme basicAuth = new BasicScheme();
basicAuth.initPreemptive(new UsernamePasswordCredentials("username", "password"));
authCache.put(target, basicAuth); authCache.put(target, basicAuth);
// Add AuthCache to the execution context // Add AuthCache to the execution context

View File

@ -27,7 +27,6 @@
package org.apache.http.examples.client; package org.apache.http.examples.client;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache; import org.apache.http.client.AuthCache;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
@ -35,7 +34,6 @@
import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.DigestScheme; import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
@ -53,23 +51,14 @@ public class ClientPreemptiveDigestAuthentication {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
HttpHost target = new HttpHost("localhost", 80, "http"); HttpHost target = new HttpHost("localhost", 80, "http");
BasicCredentialsProvider credsProvider = new BasicCredentialsProvider(); try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("username", "password"));
try (CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build()) {
// Create AuthCache instance // Create AuthCache instance
AuthCache authCache = new BasicAuthCache(); AuthCache authCache = new BasicAuthCache();
// Generate DIGEST scheme object, initialize it and add it to the local // Generate DIGEST scheme object, initialize it and add it to the local auth cache
// auth cache
DigestScheme digestAuth = new DigestScheme(); DigestScheme digestAuth = new DigestScheme();
// Suppose we already know the realm name // Suppose we already know the realm name and the expected nonce value
digestAuth.overrideParamter("realm", "some realm"); digestAuth.initPreemptive(new UsernamePasswordCredentials("username", "password"), "whatever", "realm");
// Suppose we already know the expected nonce value
digestAuth.overrideParamter("nonce", "whatever");
authCache.put(target, digestAuth); authCache.put(target, digestAuth);
// Add AuthCache to the execution context // Add AuthCache to the execution context

View File

@ -1,63 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
*/
package org.apache.http.auth;
import org.apache.http.annotation.Immutable;
import org.apache.http.util.Args;
/**
* @since 4.2
*/
@Immutable
public final class AuthOption {
private final AuthScheme authScheme;
private final Credentials creds;
public AuthOption(final AuthScheme authScheme, final Credentials creds) {
super();
Args.notNull(authScheme, "Auth scheme");
Args.notNull(creds, "User credentials");
this.authScheme = authScheme;
this.creds = creds;
}
public AuthScheme getAuthScheme() {
return this.authScheme;
}
public Credentials getCredentials() {
return this.creds;
}
@Override
public String toString() {
return this.authScheme.toString();
}
}

View File

@ -26,69 +26,106 @@
*/ */
package org.apache.http.auth; package org.apache.http.auth;
import org.apache.http.Header; import java.security.Principal;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
/** /**
* This interface represents an abstract challenge-response oriented * This interface represents an abstract challenge-response oriented authentication scheme.
* authentication scheme.
* <p> * <p>
* Authentication schemes may be stateful involving a series of * Authentication schemes can be either request or connection based. The former are
* challenge-response exchanges. * expected to provide an authorization response with each request message while the latter
* is executed only once and applies to the underlying connection for its entire life span.
* Care must be taken when re-using connections authorized through a connection based
* authentication scheme and they may carry a particular security context and be authorized
* for a particular user identity. It is important that such schemes always provide
* the user identity they represent through the {@link #getPrinciple()} method.
* <p>
* Authentication scheme are expected to transition through a series of standard phases or
* states.
* <p>
* Authentication scheme starts off its life cycle with no context and no specific state.
* <p>
* The {@link #processChallenge(AuthChallenge, HttpContext)} method is called to
* process an authentication challenge received either from the target server or a proxy.
* The authentication scheme transitions to CHALLENGED state and is expected to validate
* the token passed to it as a parameter and initialize its internal state based on
* challenge details. Standard authentication schemes are expected to provide a realm
* attribute in the challenge. {@link #getRealm()} can be called to obtain an identifier
* of the realm that requires authorization.
* <p>
* Once the challenge has been fully processed the {@link #isResponseReady(HttpHost,
* CredentialsProvider, HttpContext)} method to determine whether the scheme is capable of
* generating a authorization response based on its current state and it holds user credentials
* required to do so. If this method returns {@code false} the authentication is considered
* to be in FAILED state and no authorization response. Otherwise the scheme is considered
* to be in RESPONSE_READY state.
* <p>
* Once the scheme is ready to respond to the challenge the {@link #generateAuthResponse(
* HttpHost, HttpRequest, HttpContext)} method to generate a response token, which will
* be sent to the opposite endpoint in the subsequent request message.
* <p>
* Certain non-standard schemes may involve multiple challenge / response exchanges to
* fully establish a shared context and complete the authentication process. Authentication
* schemes are required to return {@code true} {@link #isChallengeComplete()} once the
* handshake is considered complete.
* <p>
* The authentication scheme is considered successfully completed and in SUCCESS state
* if the opposite endpoint accepts the request message containing the authorization
* response and responds with a message indicating no authentication failure .
* If the opposite endpoint sends status code 401 or 407 in response to a request message
* containing the terminal authorization response, the scheme is considered unsuccessful
* and in FAILED state.
* *
* @since 4.0 * @since 4.0
*/ */
public interface AuthScheme { 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 challengeType the challenge type
* @param authChallenge the auth challenge
*
* @since 5.0
*/
void processChallenge(
ChallengeType challengeType,
AuthChallenge authChallenge) throws MalformedChallengeException;
/**
* Produces an authorization string for the given set of {@link Credentials}.
*
* @param credentials The credentials to be used for authentication
* @param request The request being authenticated
* @param context HTTP context
* @throws AuthenticationException if authorization string cannot
* be generated due to an authentication failure
*
* @return authorization header
*
* @since 5.0
*/
Header authenticate(
Credentials credentials,
HttpRequest request,
HttpContext context) throws AuthenticationException;
/** /**
* Returns textual designation of the given authentication scheme. * Returns textual designation of the given authentication scheme.
* *
* @return the name of the given authentication scheme * @return the name of the given authentication scheme
*/ */
String getSchemeName(); String getName();
/** /**
* Returns authentication parameter with the given name, if available. * Determines if the authentication scheme is expected to provide an authorization response
* on a per connection basis instead of the standard per request basis
* *
* @param name The name of the parameter to be returned * @return {@code true} if the scheme is connection based, {@code false}
* * if the scheme is request based.
* @return the parameter with the given name
*/ */
String getParameter(final String name); boolean isConnectionBased();
/**
* Processes the given auth challenge. Some authentication schemes may involve multiple
* challenge-response exchanges. Such schemes must be able to maintain internal state
* when dealing with sequential challenges
*
* @param authChallenge the auth challenge
* @param context HTTP context
* @throws MalformedChallengeException in case the auth challenge is incomplete,
* malformed or otherwise invalid.
* @since 5.0
*/
void processChallenge(
AuthChallenge authChallenge,
HttpContext context) throws MalformedChallengeException;
/**
* Authentication process may involve a series of challenge-response exchanges.
* This method tests if the authorization process has been fully completed (either
* successfully or unsuccessfully), that is, all the required authorization
* challenges have been processed in their entirety.
*
* @return {@code true} if the authentication process has been completed,
* {@code false} otherwise.
*
* @since 5.0
*/
boolean isChallengeComplete();
/** /**
* Returns authentication realm. If the concept of an authentication * Returns authentication realm. If the concept of an authentication
@ -100,23 +137,58 @@ Header authenticate(
String getRealm(); String getRealm();
/** /**
* Tests if the authentication scheme is provides authorization on a per * Determines whether or not an authorization response can be generated based on
* connection basis instead of usual per request basis * the actual authentication state. Generally the outcome of this method will depend
* upon availability of user credentials necessary to produce an authorization
* response.
* *
* @return {@code true} if the scheme is connection based, {@code false} * @param credentialsProvider The credentials to be used for authentication
* if the scheme is request based. * @param context HTTP context
* @throws AuthenticationException if authorization string cannot
* be generated due to an authentication failure
*
* @return {@code true} if an authorization response can be generated and
* the authentication handshake can proceed, {@code false} otherwise.
*
* @since 5.0
*/ */
boolean isConnectionBased(); boolean isResponseReady(
HttpHost host,
CredentialsProvider credentialsProvider,
HttpContext context) throws AuthenticationException;
/** /**
* Authentication process may involve a series of challenge-response exchanges. * Returns {@link Principal} whose credentials are used to generate
* This method tests if the authorization process has been completed, either * an authentication response. Connection based schemes are required
* successfully or unsuccessfully, that is, all the required authorization * to return a user {@link Principal} if authorization applies to
* challenges have been processed in their entirety. * for the entire life span of connection.
* @return user principle
* *
* @return {@code true} if the authentication process has been completed, * @see #isConnectionBased()
* {@code false} otherwise. *
* @since 5.0
*/ */
boolean isComplete(); Principal getPrinciple();
/**
* Generates an authorization response based on the current state. Some authentication
* schemes may need to load user credentials required to generate an authorization
* response from a {@link CredentialsProvider} prior to this method call.
*
* @param request The request being authenticated
* @param context HTTP context
* @throws AuthenticationException if authorization string cannot
* be generated due to an authentication failure
*
* @return authorization header
*
* @see #isResponseReady(HttpHost, CredentialsProvider, HttpContext)
*
* @since 5.0
*/
String generateAuthResponse(
HttpHost host,
HttpRequest request,
HttpContext context) throws AuthenticationException;
} }

View File

@ -45,11 +45,8 @@ public class AuthState {
/** Actual authentication scheme */ /** Actual authentication scheme */
private AuthScheme authScheme; private AuthScheme authScheme;
/** Credentials selected for authentication */
private Credentials credentials;
/** Available auth options */ /** Available auth options */
private Queue<AuthOption> authOptions; private Queue<AuthScheme> authOptions;
public AuthState() { public AuthState() {
super(); super();
@ -65,7 +62,6 @@ public void reset() {
this.state = AuthProtocolState.UNCHALLENGED; this.state = AuthProtocolState.UNCHALLENGED;
this.authOptions = null; this.authOptions = null;
this.authScheme = null; this.authScheme = null;
this.credentials = null;
} }
/** /**
@ -90,71 +86,48 @@ public AuthScheme getAuthScheme() {
} }
/** /**
* Returns actual {@link Credentials}. May be null. * Updates the auth state with {@link AuthScheme} and clears auth options.
*/
public Credentials getCredentials() {
return this.credentials;
}
/**
* Updates the auth state with {@link AuthScheme} and {@link Credentials}.
* *
* @param authScheme auth scheme. May not be null. * @param authScheme auth scheme. May not be null.
* @param credentials user crednetials. May not be null.
* *
* @since 4.2 * @since 4.2
*/ */
public void update(final AuthScheme authScheme, final Credentials credentials) { public void update(final AuthScheme authScheme) {
Args.notNull(authScheme, "Auth scheme"); Args.notNull(authScheme, "Auth scheme");
Args.notNull(credentials, "Credentials");
this.authScheme = authScheme; this.authScheme = authScheme;
this.credentials = credentials;
this.authOptions = null; this.authOptions = null;
} }
/** /**
* Returns available {@link AuthOption}s. May be null. * Returns available auth options. May be null.
* *
* @since 4.2 * @since 4.2
*/ */
public Queue<AuthOption> getAuthOptions() { public Queue<AuthScheme> getAuthOptions() {
return this.authOptions; return this.authOptions;
} }
/** /**
* Returns {@code true} if {@link AuthOption}s are available, {@code false} * Updates the auth state with a queue of auth options.
* otherwise.
*
* @since 4.2
*/
public boolean hasAuthOptions() {
return this.authOptions != null && !this.authOptions.isEmpty();
}
/**
* Updates the auth state with a queue of {@link AuthOption}s.
* *
* @param authOptions a queue of auth options. May not be null or empty. * @param authOptions a queue of auth options. May not be null or empty.
* *
* @since 4.2 * @since 4.2
*/ */
public void update(final Queue<AuthOption> authOptions) { public void update(final Queue<AuthScheme> authOptions) {
Args.notEmpty(authOptions, "Queue of auth options"); Args.notEmpty(authOptions, "Queue of auth options");
this.authOptions = authOptions; this.authOptions = authOptions;
this.authScheme = null; this.authScheme = null;
this.credentials = null;
} }
@Override @Override
public String toString() { public String toString() {
final StringBuilder buffer = new StringBuilder(); final StringBuilder buffer = new StringBuilder();
buffer.append("state:").append(this.state).append(";"); buffer.append("[").append(this.state);
if (this.authScheme != null) { if (this.authScheme != null) {
buffer.append("auth scheme:").append(this.authScheme.getSchemeName()).append(";"); buffer.append(" ").append(this.authScheme);
}
if (this.credentials != null) {
buffer.append("credentials present");
} }
buffer.append("]");
return buffer.toString(); return buffer.toString();
} }

View File

@ -38,9 +38,7 @@
import org.apache.http.annotation.Immutable; import org.apache.http.annotation.Immutable;
import org.apache.http.auth.AuthProtocolState; import org.apache.http.auth.AuthProtocolState;
import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState; import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.CredentialsProvider; import org.apache.http.auth.CredentialsProvider;
import org.apache.http.client.AuthCache; import org.apache.http.client.AuthCache;
import org.apache.http.conn.routing.RouteInfo; import org.apache.http.conn.routing.RouteInfo;
@ -106,7 +104,10 @@ public void process(final HttpRequest request, final HttpContext context)
if (targetState != null && targetState.getState() == AuthProtocolState.UNCHALLENGED) { if (targetState != null && targetState.getState() == AuthProtocolState.UNCHALLENGED) {
final AuthScheme authScheme = authCache.get(target); final AuthScheme authScheme = authCache.get(target);
if (authScheme != null) { if (authScheme != null) {
doPreemptiveAuth(target, authScheme, targetState, credsProvider); if (this.log.isDebugEnabled()) {
this.log.debug("Re-using cached '" + authScheme.getName() + "' auth scheme for " + target);
}
targetState.update(authScheme);
} }
} }
@ -115,34 +116,12 @@ public void process(final HttpRequest request, final HttpContext context)
if (proxy != null && proxyState != null && proxyState.getState() == AuthProtocolState.UNCHALLENGED) { if (proxy != null && proxyState != null && proxyState.getState() == AuthProtocolState.UNCHALLENGED) {
final AuthScheme authScheme = authCache.get(proxy); final AuthScheme authScheme = authCache.get(proxy);
if (authScheme != null) { if (authScheme != null) {
doPreemptiveAuth(proxy, authScheme, proxyState, credsProvider); if (this.log.isDebugEnabled()) {
this.log.debug("Re-using cached '" + authScheme.getName() + "' auth scheme for " + proxy);
}
proxyState.update(authScheme);
} }
} }
} }
private void doPreemptiveAuth(
final HttpHost host,
final AuthScheme authScheme,
final AuthState authState,
final CredentialsProvider credsProvider) {
final String schemeName = authScheme.getSchemeName();
if (this.log.isDebugEnabled()) {
this.log.debug("Re-using cached '" + schemeName + "' auth scheme for " + host);
}
final AuthScope authScope = new AuthScope(host, AuthScope.ANY_REALM, schemeName);
final Credentials creds = credsProvider.getCredentials(authScope);
if (creds != null) {
if ("BASIC".equalsIgnoreCase(authScheme.getSchemeName())) {
authState.setState(AuthProtocolState.CHALLENGED);
} else {
authState.setState(AuthProtocolState.SUCCESS);
}
authState.update(authScheme, creds);
} else {
this.log.debug("No credentials for preemptive authentication");
}
}
} }

View File

@ -41,7 +41,7 @@
public interface CookieAttributeHandler { public interface CookieAttributeHandler {
/** /**
* Parse the given cookie attribute value and update the corresponding * Parse the given cookie attribute value and processChallenge the corresponding
* {@link org.apache.http.cookie.Cookie} property. * {@link org.apache.http.cookie.Cookie} property.
* *
* @param cookie {@link org.apache.http.cookie.Cookie} to be updated * @param cookie {@link org.apache.http.cookie.Cookie} to be updated

View File

@ -54,7 +54,7 @@ private int getPathLength(final Cookie cookie) {
public int compare(final Cookie c1, final Cookie c2) { public int compare(final Cookie c1, final Cookie c2) {
final int l1 = getPathLength(c1); final int l1 = getPathLength(c1);
final int l2 = getPathLength(c2); final int l2 = getPathLength(c2);
//TODO: update this class once Cookie interface has been expended with #getCreationTime method //TODO: processChallenge this class once Cookie interface has been expended with #getCreationTime method
final int result = l2 - l1; final int result = l2 - l1;
if (result == 0 && c1 instanceof BasicClientCookie && c2 instanceof BasicClientCookie) { if (result == 0 && c1 instanceof BasicClientCookie && c2 instanceof BasicClientCookie) {
final Date d1 = ((BasicClientCookie) c1).getCreationDate(); final Date d1 = ((BasicClientCookie) c1).getCreationDate();

View File

@ -30,23 +30,29 @@
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.ObjectStreamException; import java.io.ObjectStreamException;
import java.io.Serializable;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.Principal;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.http.Consts; import org.apache.http.Consts;
import org.apache.http.Header; import org.apache.http.HttpHost;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.annotation.NotThreadSafe; import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials; import org.apache.http.auth.Credentials;
import org.apache.http.auth.CredentialsProvider;
import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.message.BufferedHeader;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args; import org.apache.http.util.Args;
import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.CharsetUtils; import org.apache.http.util.CharsetUtils;
import org.apache.http.util.EncodingUtils; import org.apache.http.util.EncodingUtils;
@ -56,17 +62,21 @@
* @since 4.0 * @since 4.0
*/ */
@NotThreadSafe @NotThreadSafe
public class BasicScheme extends StandardAuthScheme { public class BasicScheme implements AuthScheme, Serializable {
private static final long serialVersionUID = -1931571557597830536L; private static final long serialVersionUID = -1931571557597830536L;
private final Map<String, String> paramMap;
private transient Charset charset; private transient Charset charset;
private boolean complete; private boolean complete;
private String username;
private String password;
/** /**
* @since 4.3 * @since 4.3
*/ */
public BasicScheme(final Charset charset) { public BasicScheme(final Charset charset) {
this.paramMap = new HashMap<>();
this.charset = charset != null ? charset : Consts.ASCII; this.charset = charset != null ? charset : Consts.ASCII;
this.complete = false; this.complete = false;
} }
@ -75,54 +85,88 @@ public BasicScheme() {
this(Consts.ASCII); this(Consts.ASCII);
} }
public void initPreemptive(final Credentials credentials) {
if (credentials != null) {
this.username = credentials.getUserPrincipal().getName();
this.password = credentials.getPassword();
} else {
this.username = null;
this.password = null;
}
}
@Override @Override
public String getSchemeName() { public String getName() {
return "basic"; return "basic";
} }
public void processChallenge(
final ChallengeType challengeType,
final AuthChallenge authChallenge) throws MalformedChallengeException {
update(challengeType, authChallenge);
this.complete = true;
}
@Override
public boolean isComplete() {
return this.complete;
}
@Override @Override
public boolean isConnectionBased() { public boolean isConnectionBased() {
return false; return false;
} }
@Override @Override
public Header authenticate( public String getRealm() {
final Credentials credentials, return this.paramMap.get("realm");
final HttpRequest request, }
@Override
public void processChallenge(
final AuthChallenge authChallenge,
final HttpContext context) throws MalformedChallengeException {
this.paramMap.clear();
final List<NameValuePair> params = authChallenge.getParams();
if (params != null) {
for (NameValuePair param: params) {
this.paramMap.put(param.getName().toLowerCase(Locale.ROOT), param.getValue());
}
}
this.complete = true;
}
@Override
public boolean isChallengeComplete() {
return this.complete;
}
@Override
public boolean isResponseReady(
final HttpHost host,
final CredentialsProvider credentialsProvider,
final HttpContext context) throws AuthenticationException { final HttpContext context) throws AuthenticationException {
Args.notNull(credentials, "Credentials"); Args.notNull(host, "Auth host");
Args.notNull(request, "HTTP request"); Args.notNull(credentialsProvider, "CredentialsProvider");
final CharArrayBuffer buffer = new CharArrayBuffer(32);
if (isProxy()) { final Credentials credentials = credentialsProvider.getCredentials(new AuthScope(host, getRealm(), getName()));
buffer.append(HttpHeaders.PROXY_AUTHORIZATION); if (credentials != null) {
this.username = credentials.getUserPrincipal().getName();
this.password = credentials.getPassword();
return true;
} else { } else {
buffer.append(HttpHeaders.AUTHORIZATION); this.username = null;
this.password = null;
return false;
} }
buffer.append(": Basic "); }
final StringBuilder tmp = new StringBuilder(); @Override
tmp.append(credentials.getUserPrincipal().getName()); public Principal getPrinciple() {
tmp.append(":"); return null;
tmp.append((credentials.getPassword() == null) ? "null" : credentials.getPassword()); }
@Override
public String generateAuthResponse(
final HttpHost host,
final HttpRequest request,
final HttpContext context) throws AuthenticationException {
final StringBuilder buffer = new StringBuilder();
buffer.append(this.username);
buffer.append(":");
buffer.append(this.password);
final Base64 base64codec = new Base64(0); final Base64 base64codec = new Base64(0);
final byte[] base64password = base64codec.encode(EncodingUtils.getBytes(tmp.toString(), charset.name())); final byte[] encodedCreds = base64codec.encode(EncodingUtils.getBytes(buffer.toString(), charset.name()));
return "Basic " + new String(encodedCreds, 0, encodedCreds.length, Consts.ASCII);
buffer.append(base64password, 0, base64password.length);
return new BufferedHeader(buffer);
} }
private void writeObject(final ObjectOutputStream out) throws IOException { private void writeObject(final ObjectOutputStream out) throws IOException {
@ -142,4 +186,9 @@ private void readObject(final ObjectInputStream in) throws IOException, ClassNot
private void readObjectNoData() throws ObjectStreamException { private void readObjectNoData() throws ObjectStreamException {
} }
@Override
public String toString() {
return this.paramMap.toString();
}
} }

View File

@ -27,30 +27,35 @@
package org.apache.http.impl.auth; package org.apache.http.impl.auth;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.Principal;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Formatter; import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import org.apache.http.Header;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHeaders; import org.apache.http.HttpHost;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
import org.apache.http.NameValuePair;
import org.apache.http.annotation.NotThreadSafe; import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials; import org.apache.http.auth.Credentials;
import org.apache.http.auth.CredentialsProvider;
import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.message.BasicHeaderValueFormatter; import org.apache.http.message.BasicHeaderValueFormatter;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.apache.http.message.BufferedHeader;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args; import org.apache.http.util.Args;
import org.apache.http.util.CharArrayBuffer; import org.apache.http.util.CharArrayBuffer;
@ -71,7 +76,7 @@
* @since 4.0 * @since 4.0
*/ */
@NotThreadSafe @NotThreadSafe
public class DigestScheme extends StandardAuthScheme { public class DigestScheme implements AuthScheme, Serializable {
private static final long serialVersionUID = 3883908186234566916L; private static final long serialVersionUID = 3883908186234566916L;
@ -86,49 +91,37 @@ public class DigestScheme extends StandardAuthScheme {
'e', 'f' 'e', 'f'
}; };
/** Whether the digest authentication process is complete */
private boolean complete;
private static final int QOP_UNKNOWN = -1; private static final int QOP_UNKNOWN = -1;
private static final int QOP_MISSING = 0; private static final int QOP_MISSING = 0;
private static final int QOP_AUTH_INT = 1; private static final int QOP_AUTH_INT = 1;
private static final int QOP_AUTH = 2; private static final int QOP_AUTH = 2;
private final Map<String, String> paramMap;
private boolean complete;
private String lastNonce; private String lastNonce;
private long nounceCount; private long nounceCount;
private String cnonce; private String cnonce;
private String a1; private String a1;
private String a2; private String a2;
private String username;
private String password;
public DigestScheme() { public DigestScheme() {
this.paramMap = new HashMap<>();
this.complete = false; this.complete = false;
} }
@Override public void initPreemptive(final Credentials credentials, final String cnonce, final String realm) {
public void processChallenge( Args.notNull(credentials, "Credentials");
final ChallengeType challengeType, this.username = credentials.getUserPrincipal().getName();
final AuthChallenge authChallenge) throws MalformedChallengeException { this.password = credentials.getPassword();
Args.notNull(challengeType, "ChallengeType"); this.paramMap.put("cnonce", cnonce);
Args.notNull(authChallenge, "AuthChallenge"); this.paramMap.put("realm", realm);
update(challengeType, authChallenge);
if (getParameters().isEmpty()) {
throw new MalformedChallengeException("Missing digest auth parameters");
}
this.complete = true;
} }
@Override @Override
public boolean isComplete() { public String getName() {
final String s = getParameter("stale");
if ("true".equalsIgnoreCase(s)) {
return false;
} else {
return this.complete;
}
}
@Override
public String getSchemeName() {
return "digest"; return "digest";
} }
@ -137,28 +130,79 @@ public boolean isConnectionBased() {
return false; return false;
} }
public void overrideParamter(final String name, final String value) { @Override
getParameters().put(name, value); public String getRealm() {
return this.paramMap.get("realm");
} }
@Override @Override
public Header authenticate( public void processChallenge(
final Credentials credentials, final AuthChallenge authChallenge,
final HttpContext context) throws MalformedChallengeException {
Args.notNull(authChallenge, "AuthChallenge");
this.paramMap.clear();
final List<NameValuePair> params = authChallenge.getParams();
if (params != null) {
for (NameValuePair param: params) {
this.paramMap.put(param.getName().toLowerCase(Locale.ROOT), param.getValue());
}
}
if (this.paramMap.isEmpty()) {
throw new MalformedChallengeException("Missing digest auth parameters");
}
this.complete = true;
}
@Override
public boolean isChallengeComplete() {
final String s = this.paramMap.get("stale");
if ("true".equalsIgnoreCase(s)) {
return false;
} else {
return this.complete;
}
}
@Override
public boolean isResponseReady(
final HttpHost host,
final CredentialsProvider credentialsProvider,
final HttpContext context) throws AuthenticationException {
Args.notNull(host, "Auth host");
Args.notNull(credentialsProvider, "CredentialsProvider");
final Credentials credentials = credentialsProvider.getCredentials(new AuthScope(host, getRealm(), getName()));
if (credentials != null) {
this.username = credentials.getUserPrincipal().getName();
this.password = credentials.getPassword();
return true;
} else {
this.username = null;
this.password = null;
return false;
}
}
@Override
public Principal getPrinciple() {
return null;
}
@Override
public String generateAuthResponse(
final HttpHost host,
final HttpRequest request, final HttpRequest request,
final HttpContext context) throws AuthenticationException { final HttpContext context) throws AuthenticationException {
Args.notNull(credentials, "Credentials");
Args.notNull(request, "HTTP request"); Args.notNull(request, "HTTP request");
if (getParameter("realm") == null) { if (this.paramMap.get("realm") == null) {
throw new AuthenticationException("missing realm in challenge"); throw new AuthenticationException("missing realm");
} }
if (getParameter("nonce") == null) { if (this.paramMap.get("nonce") == null) {
throw new AuthenticationException("missing nonce in challenge"); throw new AuthenticationException("missing nonce");
} }
// Add method name and request-URI to the parameter map return createDigestResponse(request);
getParameters().put("methodname", request.getRequestLine().getMethod());
getParameters().put("uri", request.getRequestLine().getUri());
return createDigestHeader(credentials, request);
} }
private static MessageDigest createMessageDigest( private static MessageDigest createMessageDigest(
@ -172,15 +216,14 @@ private static MessageDigest createMessageDigest(
} }
} }
private Header createDigestHeader( private String createDigestResponse(final HttpRequest request) throws AuthenticationException {
final Credentials credentials,
final HttpRequest request) throws AuthenticationException { final String uri = request.getRequestLine().getUri();
final String uri = getParameter("uri"); final String method = request.getRequestLine().getMethod();
final String realm = getParameter("realm"); final String realm = this.paramMap.get("realm");
final String nonce = getParameter("nonce"); final String nonce = this.paramMap.get("nonce");
final String opaque = getParameter("opaque"); final String opaque = this.paramMap.get("opaque");
final String method = getParameter("methodname"); String algorithm = this.paramMap.get("algorithm");
String algorithm = getParameter("algorithm");
// If an algorithm is not specified, default to MD5. // If an algorithm is not specified, default to MD5.
if (algorithm == null) { if (algorithm == null) {
algorithm = "MD5"; algorithm = "MD5";
@ -188,7 +231,7 @@ private Header createDigestHeader(
final Set<String> qopset = new HashSet<>(8); final Set<String> qopset = new HashSet<>(8);
int qop = QOP_UNKNOWN; int qop = QOP_UNKNOWN;
final String qoplist = getParameter("qop"); final String qoplist = this.paramMap.get("qop");
if (qoplist != null) { if (qoplist != null) {
final StringTokenizer tok = new StringTokenizer(qoplist, ","); final StringTokenizer tok = new StringTokenizer(qoplist, ",");
while (tok.hasMoreTokens()) { while (tok.hasMoreTokens()) {
@ -208,7 +251,7 @@ private Header createDigestHeader(
throw new AuthenticationException("None of the qop methods is supported: " + qoplist); throw new AuthenticationException("None of the qop methods is supported: " + qoplist);
} }
String charset = getParameter("charset"); String charset = this.paramMap.get("charset");
if (charset == null) { if (charset == null) {
charset = "ISO-8859-1"; charset = "ISO-8859-1";
} }
@ -225,9 +268,6 @@ private Header createDigestHeader(
throw new AuthenticationException("Unsuppported digest algorithm: " + digAlg); throw new AuthenticationException("Unsuppported digest algorithm: " + digAlg);
} }
final String uname = credentials.getUserPrincipal().getName();
final String pwd = credentials.getPassword();
if (nonce.equals(this.lastNonce)) { if (nonce.equals(this.lastNonce)) {
nounceCount++; nounceCount++;
} else { } else {
@ -255,7 +295,7 @@ private Header createDigestHeader(
// calculated one per session // calculated one per session
sb.setLength(0); sb.setLength(0);
sb.append(uname).append(':').append(realm).append(':').append(pwd); sb.append(username).append(':').append(realm).append(':').append(password);
final String checksum = encode(digester.digest(EncodingUtils.getBytes(sb.toString(), charset))); final String checksum = encode(digester.digest(EncodingUtils.getBytes(sb.toString(), charset)));
sb.setLength(0); sb.setLength(0);
sb.append(checksum).append(':').append(nonce).append(':').append(cnonce); sb.append(checksum).append(':').append(nonce).append(':').append(cnonce);
@ -263,7 +303,7 @@ private Header createDigestHeader(
} else { } else {
// unq(username-value) ":" unq(realm-value) ":" passwd // unq(username-value) ":" unq(realm-value) ":" passwd
sb.setLength(0); sb.setLength(0);
sb.append(uname).append(':').append(realm).append(':').append(pwd); sb.append(username).append(':').append(realm).append(':').append(password);
a1 = sb.toString(); a1 = sb.toString();
} }
@ -323,15 +363,10 @@ private Header createDigestHeader(
final String digest = encode(digester.digest(EncodingUtils.getAsciiBytes(digestValue))); final String digest = encode(digester.digest(EncodingUtils.getAsciiBytes(digestValue)));
final CharArrayBuffer buffer = new CharArrayBuffer(128); final CharArrayBuffer buffer = new CharArrayBuffer(128);
if (isProxy()) { buffer.append("Digest ");
buffer.append(HttpHeaders.PROXY_AUTHORIZATION);
} else {
buffer.append(HttpHeaders.AUTHORIZATION);
}
buffer.append(": Digest ");
final List<BasicNameValuePair> params = new ArrayList<>(20); final List<BasicNameValuePair> params = new ArrayList<>(20);
params.add(new BasicNameValuePair("username", uname)); params.add(new BasicNameValuePair("username", username));
params.add(new BasicNameValuePair("realm", realm)); params.add(new BasicNameValuePair("realm", realm));
params.add(new BasicNameValuePair("nonce", nonce)); params.add(new BasicNameValuePair("nonce", nonce));
params.add(new BasicNameValuePair("uri", uri)); params.add(new BasicNameValuePair("uri", uri));
@ -358,7 +393,7 @@ private Header createDigestHeader(
|| "algorithm".equals(name)); || "algorithm".equals(name));
BasicHeaderValueFormatter.INSTANCE.formatNameValuePair(buffer, param, !noQuotes); BasicHeaderValueFormatter.INSTANCE.formatNameValuePair(buffer, param, !noQuotes);
} }
return new BufferedHeader(buffer); return buffer.toString();
} }
String getCnonce() { String getCnonce() {
@ -405,4 +440,9 @@ public static String createCnonce() {
return encode(tmp); return encode(tmp);
} }
@Override
public String toString() {
return this.paramMap.toString();
}
} }

View File

@ -28,28 +28,25 @@
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.Principal;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
import org.apache.http.annotation.NotThreadSafe; import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials; import org.apache.http.auth.Credentials;
import org.apache.http.auth.CredentialsProvider;
import org.apache.http.auth.InvalidCredentialsException; import org.apache.http.auth.InvalidCredentialsException;
import org.apache.http.auth.KerberosCredentials; import org.apache.http.auth.KerberosCredentials;
import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.message.BufferedHeader;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args; import org.apache.http.util.Args;
import org.apache.http.util.CharArrayBuffer;
import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential; import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSException;
@ -61,7 +58,7 @@
* @since 4.2 * @since 4.2
*/ */
@NotThreadSafe @NotThreadSafe
public abstract class GGSSchemeBase extends NonStandardAuthScheme { public abstract class GGSSchemeBase implements AuthScheme {
enum State { enum State {
UNINITIATED, UNINITIATED,
@ -77,8 +74,8 @@ enum State {
/** Authentication process state */ /** Authentication process state */
private State state; private State state;
private GSSCredential gssCredential;
/** base64 decoded challenge **/ private String challenge;
private byte[] token; private byte[] token;
GGSSchemeBase(final boolean stripPort, final boolean useCanonicalHostname) { GGSSchemeBase(final boolean stripPort, final boolean useCanonicalHostname) {
@ -93,19 +90,25 @@ enum State {
} }
GGSSchemeBase() { GGSSchemeBase() {
this(true,true); this(true, true);
} }
@Override
public String getRealm() {
return null;
}
@Override
public void processChallenge( public void processChallenge(
final ChallengeType challengeType, final AuthChallenge authChallenge,
final AuthChallenge authChallenge) throws MalformedChallengeException { final HttpContext context) throws MalformedChallengeException {
update(challengeType, authChallenge); Args.notNull(authChallenge, "AuthChallenge");
if (authChallenge.getValue() == null) {
throw new MalformedChallengeException("Missing auth challenge");
}
this.challenge = authChallenge.getValue();
if (state == State.UNINITIATED) { if (state == State.UNINITIATED) {
final String challenge = getChallenge();
token = Base64.decodeBase64(challenge.getBytes()); token = Base64.decodeBase64(challenge.getBytes());
if (log.isDebugEnabled()) {
log.debug("Received token '" + token + "' from the auth server");
}
state = State.CHALLENGE_RECEIVED; state = State.CHALLENGE_RECEIVED;
} else { } else {
log.debug("Authentication already attempted"); log.debug("Authentication already attempted");
@ -117,17 +120,10 @@ protected GSSManager getManager() {
return GSSManager.getInstance(); return GSSManager.getInstance();
} }
protected byte[] generateGSSToken(
final byte[] input, final Oid oid, final String authServer) throws GSSException {
return generateGSSToken(input, oid, authServer, null);
}
/** /**
* @since 4.4 * @since 4.4
*/ */
protected byte[] generateGSSToken( protected byte[] generateGSSToken(final byte[] input, final Oid oid, final String authServer) throws GSSException {
final byte[] input, final Oid oid, final String authServer,
final Credentials credentials) throws GSSException {
byte[] inputBuff = input; byte[] inputBuff = input;
if (inputBuff == null) { if (inputBuff == null) {
inputBuff = new byte[0]; inputBuff = new byte[0];
@ -135,13 +131,6 @@ protected byte[] generateGSSToken(
final GSSManager manager = getManager(); final GSSManager manager = getManager();
final GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE); final GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE);
final GSSCredential gssCredential;
if (credentials instanceof KerberosCredentials) {
gssCredential = ((KerberosCredentials) credentials).getGSSCredential();
} else {
gssCredential = null;
}
final GSSContext gssContext = manager.createContext( final GSSContext gssContext = manager.createContext(
serverName.canonicalize(oid), oid, gssCredential, GSSContext.DEFAULT_LIFETIME); serverName.canonicalize(oid), oid, gssCredential, GSSContext.DEFAULT_LIFETIME);
gssContext.requestMutualAuth(true); gssContext.requestMutualAuth(true);
@ -152,43 +141,52 @@ protected byte[] generateGSSToken(
/** /**
* @since 4.4 * @since 4.4
*/ */
protected abstract byte[] generateToken( protected abstract byte[] generateToken(byte[] input, String authServer) throws GSSException;
byte[] input, String authServer, Credentials credentials) throws GSSException;
@Override @Override
public boolean isComplete() { public boolean isChallengeComplete() {
return this.state == State.TOKEN_GENERATED || this.state == State.FAILED; return this.state == State.TOKEN_GENERATED || this.state == State.FAILED;
} }
@Override @Override
public Header authenticate( public boolean isResponseReady(
final Credentials credentials, final HttpHost host,
final CredentialsProvider credentialsProvider,
final HttpContext context) throws AuthenticationException {
Args.notNull(host, "Auth host");
Args.notNull(credentialsProvider, "CredentialsProvider");
final Credentials credentials = credentialsProvider.getCredentials(new AuthScope(host, null, getName()));
if (credentials instanceof KerberosCredentials) {
this.gssCredential = ((KerberosCredentials) credentials).getGSSCredential();
} else {
this.gssCredential = null;
}
return true;
}
@Override
public Principal getPrinciple() {
return null;
}
@Override
public String generateAuthResponse(
final HttpHost host,
final HttpRequest request, final HttpRequest request,
final HttpContext context) throws AuthenticationException { final HttpContext context) throws AuthenticationException {
Args.notNull(host, "HTTP host");
Args.notNull(request, "HTTP request"); Args.notNull(request, "HTTP request");
switch (state) { switch (state) {
case UNINITIATED: case UNINITIATED:
throw new AuthenticationException(getSchemeName() + " authentication has not been initiated"); throw new AuthenticationException(getName() + " authentication has not been initiated");
case FAILED: case FAILED:
throw new AuthenticationException(getSchemeName() + " authentication has failed"); throw new AuthenticationException(getName() + " authentication has failed");
case CHALLENGE_RECEIVED: case CHALLENGE_RECEIVED:
try { try {
final HttpRoute route = (HttpRoute) context.getAttribute(HttpClientContext.HTTP_ROUTE);
if (route == null) {
throw new AuthenticationException("Connection route is not available");
}
HttpHost host;
if (isProxy()) {
host = route.getProxyHost();
if (host == null) {
host = route.getTargetHost();
}
} else {
host = route.getTargetHost();
}
final String authServer; final String authServer;
String hostname = host.getHostName(); String hostname = host.getHostName();
if (this.useCanonicalHostname){ if (this.useCanonicalHostname){
try { try {
//TODO: uncomment this statement and delete the resolveCanonicalHostname, //TODO: uncomment this statement and delete the resolveCanonicalHostname,
@ -208,7 +206,7 @@ public Header authenticate(
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("init " + authServer); log.debug("init " + authServer);
} }
token = generateToken(token, authServer, credentials); token = generateToken(token, authServer);
state = State.TOKEN_GENERATED; state = State.TOKEN_GENERATED;
} catch (final GSSException gsse) { } catch (final GSSException gsse) {
state = State.FAILED; state = State.FAILED;
@ -233,15 +231,7 @@ public Header authenticate(
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Sending response '" + tokenstr + "' back to the auth server"); log.debug("Sending response '" + tokenstr + "' back to the auth server");
} }
final CharArrayBuffer buffer = new CharArrayBuffer(32); return "Negotiate " + tokenstr;
if (isProxy()) {
buffer.append(HttpHeaders.PROXY_AUTHORIZATION);
} else {
buffer.append(HttpHeaders.AUTHORIZATION);
}
buffer.append(": Negotiate ");
buffer.append(tokenstr);
return new BufferedHeader(buffer);
default: default:
throw new IllegalStateException("Illegal state: " + state); throw new IllegalStateException("Illegal state: " + state);
} }
@ -256,4 +246,9 @@ private String resolveCanonicalHostname(final String host) throws UnknownHostExc
return canonicalServer; return canonicalServer;
} }
@Override
public String toString() {
return "[" + this.state + " " + challenge + ']';
}
} }

View File

@ -47,20 +47,18 @@
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.ParseException; import org.apache.http.ParseException;
import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthOption;
import org.apache.http.auth.AuthProtocolState; import org.apache.http.auth.AuthProtocolState;
import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState; import org.apache.http.auth.AuthState;
import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType; import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.CredentialsProvider; import org.apache.http.auth.CredentialsProvider;
import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.client.AuthCache; import org.apache.http.client.AuthCache;
import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.AuthenticationStrategy;
import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.ParserCursor; import org.apache.http.message.ParserCursor;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Asserts; import org.apache.http.util.Asserts;
@ -195,12 +193,12 @@ public boolean prepareAuthResponse(
case UNCHALLENGED: case UNCHALLENGED:
final AuthScheme authScheme = authState.getAuthScheme(); final AuthScheme authScheme = authState.getAuthScheme();
if (authScheme != null) { if (authScheme != null) {
final String id = authScheme.getSchemeName(); final String id = authScheme.getName();
final AuthChallenge challenge = challengeMap.get(id.toLowerCase(Locale.ROOT)); final AuthChallenge challenge = challengeMap.get(id.toLowerCase(Locale.ROOT));
if (challenge != null) { if (challenge != null) {
this.log.debug("Authorization challenge processed"); this.log.debug("Authorization challenge processed");
try { try {
authScheme.processChallenge(challengeType, challenge); authScheme.processChallenge(challenge, context);
} catch (MalformedChallengeException ex) { } catch (MalformedChallengeException ex) {
if (this.log.isWarnEnabled()) { if (this.log.isWarnEnabled()) {
this.log.warn(ex.getMessage()); this.log.warn(ex.getMessage());
@ -209,7 +207,7 @@ public boolean prepareAuthResponse(
authState.reset(); authState.reset();
return false; return false;
} }
if (authScheme.isComplete()) { if (authScheme.isChallengeComplete()) {
this.log.debug("Authentication failed"); this.log.debug("Authentication failed");
clearCache(host, clientContext); clearCache(host, clientContext);
authState.reset(); authState.reset();
@ -233,22 +231,16 @@ public boolean prepareAuthResponse(
return false; return false;
} }
final Queue<AuthOption> authOptions = new LinkedList<>(); final Queue<AuthScheme> authOptions = new LinkedList<>();
for (AuthScheme authScheme: preferredSchemes) { for (AuthScheme authScheme: preferredSchemes) {
try { try {
final String id = authScheme.getSchemeName(); final String id = authScheme.getName();
final AuthChallenge challenge = challengeMap.get(id.toLowerCase(Locale.ROOT)); final AuthChallenge challenge = challengeMap.get(id.toLowerCase(Locale.ROOT));
authScheme.processChallenge(challengeType, challenge); authScheme.processChallenge(challenge, context);
final AuthScope authScope = new AuthScope( if (authScheme.isResponseReady(host, credsProvider, context)) {
host.getHostName(), authOptions.add(authScheme);
host.getPort(),
authScheme.getRealm(),
authScheme.getSchemeName());
final Credentials credentials = credsProvider.getCredentials(authScope);
if (credentials != null) {
authOptions.add(new AuthOption(authScheme, credentials));
} }
} catch (MalformedChallengeException ex) { } catch (AuthenticationException | MalformedChallengeException ex) {
if (this.log.isWarnEnabled()) { if (this.log.isWarnEnabled()) {
this.log.warn(ex.getMessage()); this.log.warn(ex.getMessage());
} }
@ -267,11 +259,12 @@ public boolean prepareAuthResponse(
} }
public void addAuthResponse( public void addAuthResponse(
final HttpHost host,
final ChallengeType challengeType,
final HttpRequest request, final HttpRequest request,
final AuthState authState, final AuthState authState,
final HttpContext context) throws HttpException, IOException { final HttpContext context) throws HttpException, IOException {
AuthScheme authScheme = authState.getAuthScheme(); AuthScheme authScheme = authState.getAuthScheme();
Credentials creds = authState.getCredentials();
switch (authState.getState()) { switch (authState.getState()) {
case FAILURE: case FAILURE:
return; return;
@ -285,19 +278,20 @@ public void addAuthResponse(
Asserts.notNull(authScheme, "AuthScheme"); Asserts.notNull(authScheme, "AuthScheme");
break; break;
case CHALLENGED: case CHALLENGED:
final Queue<AuthOption> authOptions = authState.getAuthOptions(); final Queue<AuthScheme> authOptions = authState.getAuthOptions();
if (authOptions != null) { if (authOptions != null) {
while (!authOptions.isEmpty()) { while (!authOptions.isEmpty()) {
final AuthOption authOption = authOptions.remove(); authScheme = authOptions.remove();
authScheme = authOption.getAuthScheme(); authState.update(authScheme);
creds = authOption.getCredentials();
authState.update(authScheme, creds);
if (this.log.isDebugEnabled()) { if (this.log.isDebugEnabled()) {
this.log.debug("Generating response to an authentication challenge using " this.log.debug("Generating response to an authentication challenge using "
+ authScheme.getSchemeName() + " scheme"); + authScheme.getName() + " scheme");
} }
try { try {
final Header header = doAuth(authScheme, creds, request, context); final String authResponse = authScheme.generateAuthResponse(host, request, context);
final Header header = new BasicHeader(
challengeType == ChallengeType.TARGET ? HttpHeaders.AUTHORIZATION : HttpHeaders.PROXY_AUTHORIZATION,
authResponse);
request.addHeader(header); request.addHeader(header);
break; break;
} catch (final AuthenticationException ex) { } catch (final AuthenticationException ex) {
@ -314,7 +308,10 @@ public void addAuthResponse(
} }
if (authScheme != null) { if (authScheme != null) {
try { try {
final Header header = doAuth(authScheme, creds, request, context); final String authResponse = authScheme.generateAuthResponse(host, request, context);
final Header header = new BasicHeader(
challengeType == ChallengeType.TARGET ? HttpHeaders.AUTHORIZATION : HttpHeaders.PROXY_AUTHORIZATION,
authResponse);
request.addHeader(header); request.addHeader(header);
} catch (final AuthenticationException ex) { } catch (final AuthenticationException ex) {
if (this.log.isErrorEnabled()) { if (this.log.isErrorEnabled()) {
@ -325,7 +322,7 @@ public void addAuthResponse(
} }
private boolean isCachable(final AuthScheme authScheme) { private boolean isCachable(final AuthScheme authScheme) {
final String schemeName = authScheme.getSchemeName(); final String schemeName = authScheme.getName();
return schemeName.equalsIgnoreCase(AuthSchemes.BASIC) || return schemeName.equalsIgnoreCase(AuthSchemes.BASIC) ||
schemeName.equalsIgnoreCase(AuthSchemes.DIGEST); schemeName.equalsIgnoreCase(AuthSchemes.DIGEST);
} }
@ -335,7 +332,7 @@ private void updateCache(final HttpHost host, final AuthScheme authScheme, final
final AuthCache authCache = clientContext.getAuthCache(); final AuthCache authCache = clientContext.getAuthCache();
if (authCache != null) { if (authCache != null) {
if (this.log.isDebugEnabled()) { if (this.log.isDebugEnabled()) {
this.log.debug("Caching '" + authScheme.getSchemeName() + "' auth scheme for " + host); this.log.debug("Caching '" + authScheme.getName() + "' auth scheme for " + host);
} }
authCache.put(host, authScheme); authCache.put(host, authScheme);
} }
@ -353,12 +350,4 @@ private void clearCache(final HttpHost host, final HttpClientContext clientConte
} }
} }
private Header doAuth(
final AuthScheme authScheme,
final Credentials creds,
final HttpRequest request,
final HttpContext context) throws AuthenticationException {
return authScheme.authenticate(creds, request, context);
}
} }

View File

@ -27,7 +27,6 @@
package org.apache.http.impl.auth; package org.apache.http.impl.auth;
import org.apache.http.annotation.NotThreadSafe; import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.Credentials;
import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid; import org.ietf.jgss.Oid;
@ -57,13 +56,13 @@ public KerberosScheme() {
} }
@Override @Override
public String getSchemeName() { public String getName() {
return "Kerberos"; return "Kerberos";
} }
@Override @Override
protected byte[] generateToken(final byte[] input, final String authServer, final Credentials credentials) throws GSSException { protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException {
return generateGSSToken(input, new Oid(KERBEROS_OID), authServer, credentials); return generateGSSToken(input, new Oid(KERBEROS_OID), authServer);
} }
@Override @Override

View File

@ -1574,7 +1574,7 @@ static class HMACMD5 {
i++; i++;
} }
// Very important: update the digest with the ipad buffer // Very important: processChallenge the digest with the ipad buffer
md5.reset(); md5.reset();
md5.update(ipad); md5.update(ipad);

View File

@ -26,21 +26,21 @@
*/ */
package org.apache.http.impl.auth; package org.apache.http.impl.auth;
import org.apache.http.Header; import java.security.Principal;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
import org.apache.http.annotation.NotThreadSafe; import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials; import org.apache.http.auth.Credentials;
import org.apache.http.auth.InvalidCredentialsException; import org.apache.http.auth.CredentialsProvider;
import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.auth.NTCredentials; import org.apache.http.auth.NTCredentials;
import org.apache.http.message.BufferedHeader;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args; import org.apache.http.util.Args;
import org.apache.http.util.CharArrayBuffer;
/** /**
* NTLM is a proprietary authentication scheme developed by Microsoft * NTLM is a proprietary authentication scheme developed by Microsoft
@ -49,7 +49,7 @@
* @since 4.0 * @since 4.0
*/ */
@NotThreadSafe @NotThreadSafe
public class NTLMScheme extends NonStandardAuthScheme { public class NTLMScheme implements AuthScheme {
enum State { enum State {
UNINITIATED, UNINITIATED,
@ -63,6 +63,8 @@ enum State {
private final NTLMEngine engine; private final NTLMEngine engine;
private State state; private State state;
private String challenge;
private NTCredentials credentials;
public NTLMScheme(final NTLMEngine engine) { public NTLMScheme(final NTLMEngine engine) {
super(); super();
@ -79,7 +81,7 @@ public NTLMScheme() {
} }
@Override @Override
public String getSchemeName() { public String getName() {
return "ntlm"; return "ntlm";
} }
@ -88,13 +90,21 @@ public boolean isConnectionBased() {
return true; return true;
} }
@Override
public String getRealm() {
return null;
}
@Override @Override
public void processChallenge( public void processChallenge(
final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException { final AuthChallenge authChallenge,
Args.notNull(challengeType, "ChallengeType"); final HttpContext context) throws MalformedChallengeException {
Args.notNull(authChallenge, "AuthChallenge"); Args.notNull(authChallenge, "AuthChallenge");
final String value = authChallenge.getValue(); if (authChallenge.getValue() == null) {
if (value == null || value.isEmpty()) { throw new MalformedChallengeException("Missing auth challenge");
}
this.challenge = authChallenge.getValue();
if (this.challenge == null || this.challenge.isEmpty()) {
if (this.state == State.UNINITIATED) { if (this.state == State.UNINITIATED) {
this.state = State.CHALLENGE_RECEIVED; this.state = State.CHALLENGE_RECEIVED;
} else { } else {
@ -111,51 +121,66 @@ public void processChallenge(
} }
@Override @Override
public Header authenticate( public boolean isResponseReady(
final Credentials credentials, final HttpHost host,
final CredentialsProvider credentialsProvider,
final HttpContext context) throws AuthenticationException {
Args.notNull(host, "Auth host");
Args.notNull(credentialsProvider, "CredentialsProvider");
final Credentials credentials = credentialsProvider.getCredentials(new AuthScope(host, null, getName()));
if (credentials instanceof NTCredentials) {
this.credentials = (NTCredentials) credentials;
return true;
} else {
return false;
}
}
@Override
public Principal getPrinciple() {
return this.credentials != null ? this.credentials.getUserPrincipal() : null;
}
@Override
public String generateAuthResponse(
final HttpHost host,
final HttpRequest request, final HttpRequest request,
final HttpContext context) throws AuthenticationException { final HttpContext context) throws AuthenticationException {
final NTCredentials ntcredentials; if (this.credentials == null) {
try { throw new AuthenticationException("NT credentials not available");
ntcredentials = (NTCredentials) credentials;
} catch (final ClassCastException e) {
throw new InvalidCredentialsException(
"Credentials cannot be used for NTLM authentication: "
+ credentials.getClass().getName());
} }
final String response; final String response;
if (this.state == State.FAILED) { if (this.state == State.FAILED) {
throw new AuthenticationException("NTLM authentication failed"); throw new AuthenticationException("NTLM authentication failed");
} else if (this.state == State.CHALLENGE_RECEIVED) { } else if (this.state == State.CHALLENGE_RECEIVED) {
response = this.engine.generateType1Msg( response = this.engine.generateType1Msg(
ntcredentials.getNetbiosDomain(), this.credentials.getNetbiosDomain(),
ntcredentials.getWorkstation()); this.credentials.getWorkstation());
this.state = State.MSG_TYPE1_GENERATED; this.state = State.MSG_TYPE1_GENERATED;
} else if (this.state == State.MSG_TYPE2_RECEVIED) { } else if (this.state == State.MSG_TYPE2_RECEVIED) {
response = this.engine.generateType3Msg( response = this.engine.generateType3Msg(
ntcredentials.getUserName(), this.credentials.getUserName(),
ntcredentials.getPassword(), this.credentials.getPassword(),
ntcredentials.getNetbiosDomain(), this.credentials.getNetbiosDomain(),
ntcredentials.getWorkstation(), this.credentials.getWorkstation(),
getChallenge()); this.challenge);
this.state = State.MSG_TYPE3_GENERATED; this.state = State.MSG_TYPE3_GENERATED;
} else { } else {
throw new AuthenticationException("Unexpected state: " + this.state); throw new AuthenticationException("Unexpected state: " + this.state);
} }
final CharArrayBuffer buffer = new CharArrayBuffer(32); return "NTLM " + response;
if (isProxy()) {
buffer.append(HttpHeaders.PROXY_AUTHORIZATION);
} else {
buffer.append(HttpHeaders.AUTHORIZATION);
}
buffer.append(": NTLM ");
buffer.append(response);
return new BufferedHeader(buffer);
} }
@Override @Override
public boolean isComplete() { public boolean isChallengeComplete() {
return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED; return this.state == State.MSG_TYPE3_GENERATED || this.state == State.FAILED;
} }
@Override
public String toString() {
return "[" + this.state + " " + challenge + ']';
}
} }

View File

@ -1,70 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.auth;
import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.MalformedChallengeException;
public abstract class NonStandardAuthScheme implements AuthScheme {
private ChallengeType challengeType;
private String challenge;
public boolean isProxy() {
return this.challengeType != null && this.challengeType == ChallengeType.PROXY;
}
protected void update(final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException{
if (authChallenge.getValue() == null) {
throw new MalformedChallengeException("Missing auth challenge");
}
this.challengeType = challengeType;
this.challenge = authChallenge.getValue();
}
protected String getChallenge() {
return this.challenge;
}
@Override
public String getParameter(final String name) {
return null;
}
@Override
public String getRealm() {
return null;
}
@Override
public String toString() {
return getSchemeName() + "(" + this.challengeType + ") " + this.challenge;
}
}

View File

@ -27,7 +27,6 @@
package org.apache.http.impl.auth; package org.apache.http.impl.auth;
import org.apache.http.annotation.NotThreadSafe; import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.Credentials;
import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid; import org.ietf.jgss.Oid;
@ -58,13 +57,13 @@ public SPNegoScheme() {
} }
@Override @Override
public String getSchemeName() { public String getName() {
return "Negotiate"; return "Negotiate";
} }
@Override @Override
protected byte[] generateToken(final byte[] input, final String authServer, final Credentials credentials) throws GSSException { protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException {
return generateGSSToken(input, new Oid(SPNEGO_OID), authServer, credentials); return generateGSSToken(input, new Oid(SPNEGO_OID), authServer);
} }
@Override @Override

View File

@ -1,111 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.auth;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.MalformedChallengeException;
/**
* Abstract authentication scheme class that lays foundation for standard HTTP authentication schemes and
* provides capabilities common to all authentication schemes defined in the HTTP specification.
*
* @since 4.0
*/
@NotThreadSafe
public abstract class StandardAuthScheme implements AuthScheme, Serializable {
private static final long serialVersionUID = -2845454858205884623L;
private final Map<String, String> paramMap;
private ChallengeType challengeType;
/**
* @since 4.3
*/
public StandardAuthScheme() {
super();
this.paramMap = new LinkedHashMap<>();
}
protected void update(final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException {
final List<NameValuePair> params = authChallenge.getParams();
this.challengeType = challengeType;
if (params != null) {
for (NameValuePair param: params) {
this.paramMap.put(param.getName().toLowerCase(Locale.ROOT), param.getValue());
}
}
}
@Override
public String getRealm() {
return getParameter("realm");
}
protected Map<String, String> getParameters() {
return this.paramMap;
}
/**
* Returns authentication parameter with the given name, if available.
*
* @param name The name of the parameter to be returned
*
* @return the parameter with the given name
*/
@Override
public String getParameter(final String name) {
if (name == null) {
return null;
}
return this.paramMap.get(name.toLowerCase(Locale.ROOT));
}
/**
* Returns {@code true} if authenticating against a proxy, {@code false}
* otherwise.
*/
public boolean isProxy() {
return this.challengeType != null && this.challengeType == ChallengeType.PROXY;
}
@Override
public String toString() {
return getSchemeName() + "(" + this.challengeType + ") " + this.paramMap;
}
}

View File

@ -34,7 +34,6 @@
import org.apache.http.annotation.Immutable; import org.apache.http.annotation.Immutable;
import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthState; import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.UserTokenHandler; import org.apache.http.client.UserTokenHandler;
import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ManagedHttpClientConnection; import org.apache.http.conn.ManagedHttpClientConnection;
@ -90,11 +89,8 @@ public Object getUserToken(final HttpContext context) {
private static Principal getAuthPrincipal(final AuthState authState) { private static Principal getAuthPrincipal(final AuthState authState) {
final AuthScheme scheme = authState.getAuthScheme(); final AuthScheme scheme = authState.getAuthScheme();
if (scheme != null && scheme.isComplete() && scheme.isConnectionBased()) { if (scheme != null && scheme.isConnectionBased()) {
final Credentials creds = authState.getCredentials(); return scheme.getPrinciple();
if (creds != null) {
return creds.getUserPrincipal();
}
} }
return null; return null;
} }

View File

@ -177,7 +177,7 @@ public Socket tunnel(
conn.bind(socket); conn.bind(socket);
} }
this.authenticator.addAuthResponse(connect, this.proxyAuthState, context); this.authenticator.addAuthResponse(proxy, ChallengeType.PROXY, connect, this.proxyAuthState, context);
response = this.requestExec.execute(connect, conn, context); response = this.requestExec.execute(connect, conn, context);

View File

@ -248,13 +248,15 @@ public CloseableHttpResponse execute(
if (this.log.isDebugEnabled()) { if (this.log.isDebugEnabled()) {
this.log.debug("Target auth state: " + targetAuthState.getState()); this.log.debug("Target auth state: " + targetAuthState.getState());
} }
this.authenticator.addAuthResponse(request, targetAuthState, context); this.authenticator.addAuthResponse(
route.getTargetHost(), ChallengeType.TARGET, request, targetAuthState, context);
} }
if (!request.containsHeader(HttpHeaders.PROXY_AUTHORIZATION) && !route.isTunnelled()) { if (!request.containsHeader(HttpHeaders.PROXY_AUTHORIZATION) && !route.isTunnelled()) {
if (this.log.isDebugEnabled()) { if (this.log.isDebugEnabled()) {
this.log.debug("Proxy auth state: " + proxyAuthState.getState()); this.log.debug("Proxy auth state: " + proxyAuthState.getState());
} }
this.authenticator.addAuthResponse(request, proxyAuthState, context); this.authenticator.addAuthResponse(
route.getProxyHost(), ChallengeType.PROXY, request, proxyAuthState, context);
} }
response = requestExecutor.execute(request, managedConn, context); response = requestExecutor.execute(request, managedConn, context);
@ -423,7 +425,7 @@ void establishRoute(
* The connection must be established to the (last) proxy. * The connection must be established to the (last) proxy.
* A CONNECT request for tunnelling through the proxy will * A CONNECT request for tunnelling through the proxy will
* be created and sent, the response received and checked. * be created and sent, the response received and checked.
* This method does <i>not</i> update the connection with * This method does <i>not</i> processChallenge the connection with
* information about the tunnel, that is left to the caller. * information about the tunnel, that is left to the caller.
*/ */
private boolean createTunnelToTarget( private boolean createTunnelToTarget(
@ -455,7 +457,7 @@ private boolean createTunnelToTarget(
} }
connect.removeHeaders(HttpHeaders.PROXY_AUTHORIZATION); connect.removeHeaders(HttpHeaders.PROXY_AUTHORIZATION);
this.authenticator.addAuthResponse(connect, proxyAuthState, context); this.authenticator.addAuthResponse(proxy, ChallengeType.PROXY, connect, proxyAuthState, context);
response = this.requestExecutor.execute(connect, managedConn, context); response = this.requestExecutor.execute(connect, managedConn, context);

View File

@ -57,7 +57,7 @@
* Request executor in the request execution chain that is responsible * Request executor in the request execution chain that is responsible
* for implementation of HTTP specification requirements. * for implementation of HTTP specification requirements.
* Internally this executor relies on a {@link HttpProcessor} to populate * Internally this executor relies on a {@link HttpProcessor} to populate
* requisite HTTP request headers, process HTTP response headers and update * requisite HTTP request headers, process HTTP response headers and processChallenge
* session state in {@link HttpClientContext}. * session state in {@link HttpClientContext}.
* <p> * <p>
* Further responsibilities such as communication with the opposite * Further responsibilities such as communication with the opposite

View File

@ -113,9 +113,7 @@ public void testPreemptiveTargetAndProxyAuth() throws Exception {
final HttpRequestInterceptor interceptor = new RequestAuthCache(); final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, context); interceptor.process(request, context);
Assert.assertNotNull(this.targetState.getAuthScheme()); Assert.assertNotNull(this.targetState.getAuthScheme());
Assert.assertSame(this.creds1, this.targetState.getCredentials());
Assert.assertNotNull(this.proxyState.getAuthScheme()); Assert.assertNotNull(this.proxyState.getAuthScheme());
Assert.assertSame(this.creds2, this.proxyState.getCredentials());
} }
@Test @Test
@ -138,9 +136,7 @@ public void testCredentialsProviderNotSet() throws Exception {
final HttpRequestInterceptor interceptor = new RequestAuthCache(); final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, context); interceptor.process(request, context);
Assert.assertNull(this.targetState.getAuthScheme()); Assert.assertNull(this.targetState.getAuthScheme());
Assert.assertNull(this.targetState.getCredentials());
Assert.assertNull(this.proxyState.getAuthScheme()); Assert.assertNull(this.proxyState.getAuthScheme());
Assert.assertNull(this.proxyState.getCredentials());
} }
@Test @Test
@ -158,9 +154,7 @@ public void testAuthCacheNotSet() throws Exception {
final HttpRequestInterceptor interceptor = new RequestAuthCache(); final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, context); interceptor.process(request, context);
Assert.assertNull(this.targetState.getAuthScheme()); Assert.assertNull(this.targetState.getAuthScheme());
Assert.assertNull(this.targetState.getCredentials());
Assert.assertNull(this.proxyState.getAuthScheme()); Assert.assertNull(this.proxyState.getAuthScheme());
Assert.assertNull(this.proxyState.getCredentials());
} }
@Test @Test
@ -180,36 +174,7 @@ public void testAuthCacheEmpty() throws Exception {
final HttpRequestInterceptor interceptor = new RequestAuthCache(); final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, context); interceptor.process(request, context);
Assert.assertNull(this.targetState.getAuthScheme()); Assert.assertNull(this.targetState.getAuthScheme());
Assert.assertNull(this.targetState.getCredentials());
Assert.assertNull(this.proxyState.getAuthScheme()); Assert.assertNull(this.proxyState.getAuthScheme());
Assert.assertNull(this.proxyState.getCredentials());
}
@Test
public void testNoMatchingCredentials() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/");
this.credProvider.clear();
final HttpClientContext context = HttpClientContext.create();
context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credProvider);
context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, this.target);
context.setAttribute(HttpClientContext.HTTP_ROUTE, new HttpRoute(this.target, null, this.proxy, false));
context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, this.targetState);
context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, this.proxyState);
final AuthCache authCache = new BasicAuthCache();
authCache.put(this.target, this.authscheme1);
authCache.put(this.proxy, this.authscheme2);
context.setAttribute(HttpClientContext.AUTH_CACHE, authCache);
final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, context);
Assert.assertNull(this.targetState.getAuthScheme());
Assert.assertNull(this.targetState.getCredentials());
Assert.assertNull(this.proxyState.getAuthScheme());
Assert.assertNull(this.proxyState.getCredentials());
} }
@Test @Test
@ -230,16 +195,14 @@ public void testAuthSchemeAlreadySet() throws Exception {
context.setAttribute(HttpClientContext.AUTH_CACHE, authCache); context.setAttribute(HttpClientContext.AUTH_CACHE, authCache);
this.targetState.setState(AuthProtocolState.CHALLENGED); this.targetState.setState(AuthProtocolState.CHALLENGED);
this.targetState.update(new BasicScheme(), new UsernamePasswordCredentials("user3", "secret3")); this.targetState.update(new BasicScheme());
this.proxyState.setState(AuthProtocolState.CHALLENGED); this.proxyState.setState(AuthProtocolState.CHALLENGED);
this.proxyState.update(new BasicScheme(), new UsernamePasswordCredentials("user4", "secret4")); this.proxyState.update(new BasicScheme());
final HttpRequestInterceptor interceptor = new RequestAuthCache(); final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, context); interceptor.process(request, context);
Assert.assertNotSame(this.authscheme1, this.targetState.getAuthScheme()); Assert.assertNotSame(this.authscheme1, this.targetState.getAuthScheme());
Assert.assertNotSame(this.creds1, this.targetState.getCredentials());
Assert.assertNotSame(this.authscheme2, this.proxyState.getAuthScheme()); Assert.assertNotSame(this.authscheme2, this.proxyState.getAuthScheme());
Assert.assertNotSame(this.creds2, this.proxyState.getCredentials());
} }
} }

View File

@ -34,17 +34,15 @@
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.http.Consts; import org.apache.http.Consts;
import org.apache.http.Header; import org.apache.http.HttpHost;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.ChallengeType; import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpRequest;
import org.apache.http.message.ParserCursor; import org.apache.http.message.ParserCursor;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.CharArrayBuffer; import org.apache.http.util.CharArrayBuffer;
import org.apache.http.util.EncodingUtils; import org.apache.http.util.EncodingUtils;
import org.junit.Assert; import org.junit.Assert;
@ -69,7 +67,7 @@ public void testBasicAuthenticationEmptyChallenge() throws Exception {
final String challenge = "Basic"; final String challenge = "Basic";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final AuthScheme authscheme = new BasicScheme(); final AuthScheme authscheme = new BasicScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
Assert.assertNull(authscheme.getRealm()); Assert.assertNull(authscheme.getRealm());
} }
@ -81,56 +79,66 @@ public void testBasicAuthenticationWith88591Chars() throws Exception {
buffer.append((char)germanChar); buffer.append((char)germanChar);
} }
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "some realm", null);
final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("dh", buffer.toString()); final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("dh", buffer.toString());
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(authScope, creds);
final BasicScheme authscheme = new BasicScheme(Consts.ISO_8859_1); final BasicScheme authscheme = new BasicScheme(Consts.ISO_8859_1);
Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpContext context = new BasicHttpContext(); final String authResponse = authscheme.generateAuthResponse(host, request, null);
final Header header = authscheme.authenticate(creds, request, context); Assert.assertEquals("Basic ZGg65C32Lfw=", authResponse);
Assert.assertEquals("Basic ZGg65C32Lfw=", header.getValue());
} }
@Test @Test
public void testBasicAuthentication() throws Exception { public void testBasicAuthentication() throws Exception {
final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("testuser", "testpass");
final AuthChallenge authChallenge = parse("Basic realm=\"test\""); final AuthChallenge authChallenge = parse("Basic realm=\"test\"");
final BasicScheme authscheme = new BasicScheme(); final BasicScheme authscheme = new BasicScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "test", null);
final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("testuser", "testpass");
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(authScope, creds);
final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpContext context = new BasicHttpContext(); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final Header authResponse = authscheme.authenticate(creds, request, context); final String authResponse = authscheme.generateAuthResponse(host, request, null);
final String expected = "Basic " + EncodingUtils.getAsciiString( final String expected = "Basic " + EncodingUtils.getAsciiString(
Base64.encodeBase64(EncodingUtils.getAsciiBytes("testuser:testpass"))); Base64.encodeBase64(EncodingUtils.getAsciiBytes("testuser:testpass")));
Assert.assertEquals(HttpHeaders.AUTHORIZATION, authResponse.getName()); Assert.assertEquals(expected, authResponse);
Assert.assertEquals(expected, authResponse.getValue());
Assert.assertEquals("test", authscheme.getRealm()); Assert.assertEquals("test", authscheme.getRealm());
Assert.assertTrue(authscheme.isComplete()); Assert.assertTrue(authscheme.isChallengeComplete());
Assert.assertFalse(authscheme.isConnectionBased()); Assert.assertFalse(authscheme.isConnectionBased());
} }
@Test @Test
public void testBasicProxyAuthentication() throws Exception { public void testBasicProxyAuthentication() throws Exception {
final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("testuser", "testpass");
final AuthChallenge authChallenge = parse("Basic realm=\"test\""); final AuthChallenge authChallenge = parse("Basic realm=\"test\"");
final BasicScheme authscheme = new BasicScheme(); final BasicScheme authscheme = new BasicScheme();
authscheme.processChallenge(ChallengeType.PROXY, authChallenge); authscheme.processChallenge(authChallenge, null);
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "test", null);
final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("testuser", "testpass");
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(authScope, creds);
final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpContext context = new BasicHttpContext(); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final Header authResponse = authscheme.authenticate(creds, request, context); final String authResponse = authscheme.generateAuthResponse(host, request, null);
final String expected = "Basic " + EncodingUtils.getAsciiString( final String expected = "Basic " + EncodingUtils.getAsciiString(
Base64.encodeBase64(EncodingUtils.getAsciiBytes("testuser:testpass"))); Base64.encodeBase64(EncodingUtils.getAsciiBytes("testuser:testpass")));
Assert.assertEquals(HttpHeaders.PROXY_AUTHORIZATION, authResponse.getName()); Assert.assertEquals(expected, authResponse);
Assert.assertEquals(expected, authResponse.getValue());
Assert.assertEquals("test", authscheme.getRealm()); Assert.assertEquals("test", authscheme.getRealm());
Assert.assertTrue(authscheme.isComplete()); Assert.assertTrue(authscheme.isChallengeComplete());
Assert.assertFalse(authscheme.isConnectionBased()); Assert.assertFalse(authscheme.isConnectionBased());
} }
@ -139,7 +147,7 @@ public void testSerialization() throws Exception {
final AuthChallenge authChallenge = parse("Basic realm=\"test\""); final AuthChallenge authChallenge = parse("Basic realm=\"test\"");
final BasicScheme basicScheme = new BasicScheme(); final BasicScheme basicScheme = new BasicScheme();
basicScheme.processChallenge(ChallengeType.PROXY, authChallenge); basicScheme.processChallenge(authChallenge, null);
final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
final ObjectOutputStream out = new ObjectOutputStream(buffer); final ObjectOutputStream out = new ObjectOutputStream(buffer);
@ -149,10 +157,9 @@ public void testSerialization() throws Exception {
final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw)); final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw));
final BasicScheme authScheme = (BasicScheme) in.readObject(); final BasicScheme authScheme = (BasicScheme) in.readObject();
Assert.assertEquals(basicScheme.getSchemeName(), authScheme.getSchemeName()); Assert.assertEquals(basicScheme.getName(), authScheme.getName());
Assert.assertEquals(basicScheme.getRealm(), authScheme.getRealm()); Assert.assertEquals(basicScheme.getRealm(), authScheme.getRealm());
Assert.assertEquals(basicScheme.isComplete(), authScheme.isComplete()); Assert.assertEquals(basicScheme.isChallengeComplete(), authScheme.isChallengeComplete());
Assert.assertEquals(true, basicScheme.isProxy());
} }
@Test @Test
@ -167,10 +174,9 @@ public void testSerializationUnchallenged() throws Exception {
final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw)); final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw));
final BasicScheme authScheme = (BasicScheme) in.readObject(); final BasicScheme authScheme = (BasicScheme) in.readObject();
Assert.assertEquals(basicScheme.getSchemeName(), authScheme.getSchemeName()); Assert.assertEquals(basicScheme.getName(), authScheme.getName());
Assert.assertEquals(basicScheme.getRealm(), authScheme.getRealm()); Assert.assertEquals(basicScheme.getRealm(), authScheme.getRealm());
Assert.assertEquals(basicScheme.isComplete(), authScheme.isComplete()); Assert.assertEquals(basicScheme.isChallengeComplete(), authScheme.isChallengeComplete());
Assert.assertEquals(false, basicScheme.isProxy());
} }
} }

View File

@ -36,26 +36,25 @@
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.http.Header;
import org.apache.http.HeaderElement; import org.apache.http.HeaderElement;
import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest; import org.apache.http.HttpRequest;
import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials; import org.apache.http.auth.Credentials;
import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.entity.InputStreamEntity; import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.message.BasicHeaderValueParser; import org.apache.http.message.BasicHeaderValueParser;
import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpRequest;
import org.apache.http.message.ParserCursor; import org.apache.http.message.ParserCursor;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.CharArrayBuffer; import org.apache.http.util.CharArrayBuffer;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -78,27 +77,33 @@ private static AuthChallenge parse(final String s) {
public void testDigestAuthenticationEmptyChallenge1() throws Exception { public void testDigestAuthenticationEmptyChallenge1() throws Exception {
final AuthChallenge authChallenge = parse("Digest"); final AuthChallenge authChallenge = parse("Digest");
final AuthScheme authscheme = new DigestScheme(); final AuthScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
} }
@Test(expected=MalformedChallengeException.class) @Test(expected=MalformedChallengeException.class)
public void testDigestAuthenticationEmptyChallenge2() throws Exception { public void testDigestAuthenticationEmptyChallenge2() throws Exception {
final AuthChallenge authChallenge = parse("Digest "); final AuthChallenge authChallenge = parse("Digest ");
final AuthScheme authscheme = new DigestScheme(); final AuthScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
} }
@Test @Test
public void testDigestAuthenticationWithDefaultCreds() throws Exception { public void testDigestAuthenticationWithDefaultCreds() throws Exception {
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final Credentials cred = new UsernamePasswordCredentials("username","password");
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
final HttpContext context = new BasicHttpContext(); authscheme.processChallenge(authChallenge, null);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge);
final Header authResponse = authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
Assert.assertTrue(authscheme.isComplete()); final String authResponse = authscheme.generateAuthResponse(host, request, null);
Assert.assertTrue(authscheme.isChallengeComplete());
Assert.assertFalse(authscheme.isConnectionBased()); Assert.assertFalse(authscheme.isConnectionBased());
final Map<String, String> table = parseAuthResponse(authResponse); final Map<String, String> table = parseAuthResponse(authResponse);
@ -111,14 +116,20 @@ public void testDigestAuthenticationWithDefaultCreds() throws Exception {
@Test @Test
public void testDigestAuthentication() throws Exception { public void testDigestAuthentication() throws Exception {
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final Credentials cred = new UsernamePasswordCredentials("username","password");
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
final HttpContext context = new BasicHttpContext(); authscheme.processChallenge(authChallenge, null);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge);
final Header authResponse = authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table = parseAuthResponse(authResponse); final Map<String, String> table = parseAuthResponse(authResponse);
Assert.assertEquals("username", table.get("username")); Assert.assertEquals("username", table.get("username"));
@ -130,20 +141,29 @@ public void testDigestAuthentication() throws Exception {
@Test @Test
public void testDigestAuthenticationInvalidInput() throws Exception { public void testDigestAuthenticationInvalidInput() throws Exception {
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final Credentials cred = new UsernamePasswordCredentials("username","password");
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
final HttpContext context = new BasicHttpContext(); authscheme.processChallenge(authChallenge, null);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge);
try { try {
authscheme.authenticate(null, request, context); authscheme.isResponseReady(null, credentialsProvider, null);
Assert.fail("IllegalArgumentException should have been thrown"); Assert.fail("IllegalArgumentException should have been thrown");
} catch (final IllegalArgumentException ex) { } catch (final IllegalArgumentException ex) {
} }
try { try {
authscheme.authenticate(cred, null, context); authscheme.isResponseReady(host, null, null);
Assert.fail("IllegalArgumentException should have been thrown");
} catch (final IllegalArgumentException ex) {
}
try {
authscheme.generateAuthResponse(host, null, null);
Assert.fail("IllegalArgumentException should have been thrown"); Assert.fail("IllegalArgumentException should have been thrown");
} catch (final IllegalArgumentException ex) { } catch (final IllegalArgumentException ex) {
} }
@ -151,16 +171,22 @@ public void testDigestAuthenticationInvalidInput() throws Exception {
@Test @Test
public void testDigestAuthenticationWithSHA() throws Exception { public void testDigestAuthenticationWithSHA() throws Exception {
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", " + final String challenge = "Digest realm=\"realm1\", " +
"nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
"algorithm=SHA"; "algorithm=SHA";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final Credentials cred = new UsernamePasswordCredentials("username","password");
final HttpContext context = new BasicHttpContext();
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
final Header authResponse = authscheme.authenticate(cred, request, context);
Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table = parseAuthResponse(authResponse); final Map<String, String> table = parseAuthResponse(authResponse);
Assert.assertEquals("username", table.get("username")); Assert.assertEquals("username", table.get("username"));
@ -172,14 +198,20 @@ public void testDigestAuthenticationWithSHA() throws Exception {
@Test @Test
public void testDigestAuthenticationWithQueryStringInDigestURI() throws Exception { public void testDigestAuthenticationWithQueryStringInDigestURI() throws Exception {
final HttpRequest request = new BasicHttpRequest("Simple", "/?param=value");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpRequest request = new BasicHttpRequest("Simple", "/?param=value");
final Credentials cred = new UsernamePasswordCredentials("username","password");
final HttpContext context = new BasicHttpContext();
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
final Header authResponse = authscheme.authenticate(cred, request, context);
Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table = parseAuthResponse(authResponse); final Map<String, String> table = parseAuthResponse(authResponse);
Assert.assertEquals("username", table.get("username")); Assert.assertEquals("username", table.get("username"));
@ -189,64 +221,40 @@ public void testDigestAuthenticationWithQueryStringInDigestURI() throws Exceptio
Assert.assertEquals("a847f58f5fef0bc087bcb9c3eb30e042", table.get("response")); Assert.assertEquals("a847f58f5fef0bc087bcb9c3eb30e042", table.get("response"));
} }
@Test
public void testDigestAuthenticationWithMultipleRealms() throws Exception {
final String challenge1 = "Digest realm=\"realm1\", nonce=\"abcde\"";
final String challenge2 = "Digest realm=\"realm2\", nonce=\"123546\"";
final Credentials cred = new UsernamePasswordCredentials("username","password");
final Credentials cred2 = new UsernamePasswordCredentials("uname2","password2");
final AuthChallenge authChallenge1 = parse(challenge1);
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpContext context = new BasicHttpContext();
final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge1);
Header authResponse = authscheme.authenticate(cred, request, context);
Map<String, String> table = parseAuthResponse(authResponse);
Assert.assertEquals("username", table.get("username"));
Assert.assertEquals("realm1", table.get("realm"));
Assert.assertEquals("/", table.get("uri"));
Assert.assertEquals("abcde", table.get("nonce"));
Assert.assertEquals("786f500303eac1478f3c2865e676ed68", table.get("response"));
final AuthChallenge authChallenge2 = parse(challenge2);
final DigestScheme authscheme2 = new DigestScheme();
authscheme2.processChallenge(ChallengeType.TARGET, authChallenge2);
authResponse = authscheme2.authenticate(cred2, request, context);
table = parseAuthResponse(authResponse);
Assert.assertEquals("uname2", table.get("username"));
Assert.assertEquals("realm2", table.get("realm"));
Assert.assertEquals("/", table.get("uri"));
Assert.assertEquals("123546", table.get("nonce"));
Assert.assertEquals("0283edd9ef06a38b378b3b74661391e9", table.get("response"));
}
@Test(expected=AuthenticationException.class) @Test(expected=AuthenticationException.class)
public void testDigestAuthenticationNoRealm() throws Exception { public void testDigestAuthenticationNoRealm() throws Exception {
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest no-realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final String challenge = "Digest no-realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpContext context = new BasicHttpContext();
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
final Credentials cred = new UsernamePasswordCredentials("username","password"); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final HttpRequest request = new BasicHttpRequest("Simple", "/"); authscheme.generateAuthResponse(host, request, null);
authscheme.authenticate(cred, request, context);
} }
@Test(expected=AuthenticationException.class) @Test(expected=AuthenticationException.class)
public void testDigestAuthenticationNoNonce() throws Exception { public void testDigestAuthenticationNoNonce() throws Exception {
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", no-nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; final String challenge = "Digest realm=\"realm1\", no-nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpContext context = new BasicHttpContext();
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
final Credentials cred = new UsernamePasswordCredentials("username","password"); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final HttpRequest request = new BasicHttpRequest("Simple", "/"); authscheme.generateAuthResponse(host, request, null);
authscheme.authenticate(cred, request, context);
} }
/** /**
@ -261,6 +269,13 @@ public void testDigestAuthenticationMD5Sess() throws Exception {
final String password="password"; final String password="password";
final String nonce="e273f1776275974f1a120d8b92c5b3cb"; final String nonce="e273f1776275974f1a120d8b92c5b3cb";
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, realm, null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials(username, password);
credentialsProvider.setCredentials(authScope, creds);
final String challenge="Digest realm=\"" + realm + "\", " final String challenge="Digest realm=\"" + realm + "\", "
+ "nonce=\"" + nonce + "\", " + "nonce=\"" + nonce + "\", "
+ "opaque=\"SomeString\", " + "opaque=\"SomeString\", "
@ -270,17 +285,14 @@ public void testDigestAuthenticationMD5Sess() throws Exception {
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final Credentials cred = new UsernamePasswordCredentials(username, password);
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpContext context = new BasicHttpContext();
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
final Header authResponse = authscheme.authenticate(cred, request, context);
final String response = authResponse.getValue();
Assert.assertTrue(response.indexOf("nc=00000001") > 0); // test for quotes Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
Assert.assertTrue(response.indexOf("qop=auth") > 0); // test for quotes final String authResponse = authscheme.generateAuthResponse(host, request, null);
Assert.assertTrue(authResponse.indexOf("nc=00000001") > 0); // test for quotes
Assert.assertTrue(authResponse.indexOf("qop=auth") > 0); // test for quotes
final Map<String, String> table = parseAuthResponse(authResponse); final Map<String, String> table = parseAuthResponse(authResponse);
Assert.assertEquals(username, table.get("username")); Assert.assertEquals(username, table.get("username"));
@ -308,6 +320,13 @@ public void testDigestAuthenticationMD5SessNoQop() throws Exception {
final String password="password"; final String password="password";
final String nonce="e273f1776275974f1a120d8b92c5b3cb"; final String nonce="e273f1776275974f1a120d8b92c5b3cb";
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, realm, null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials(username, password);
credentialsProvider.setCredentials(authScope, creds);
final String challenge="Digest realm=\"" + realm + "\", " final String challenge="Digest realm=\"" + realm + "\", "
+ "nonce=\"" + nonce + "\", " + "nonce=\"" + nonce + "\", "
+ "opaque=\"SomeString\", " + "opaque=\"SomeString\", "
@ -316,14 +335,10 @@ public void testDigestAuthenticationMD5SessNoQop() throws Exception {
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final Credentials cred = new UsernamePasswordCredentials(username, password);
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpContext context = new BasicHttpContext();
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
final Header authResponse = authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table = parseAuthResponse(authResponse); final Map<String, String> table = parseAuthResponse(authResponse);
Assert.assertEquals(username, table.get("username")); Assert.assertEquals(username, table.get("username"));
@ -350,6 +365,13 @@ public void testDigestAuthenticationMD5SessUnknownQop() throws Exception {
final String password="password"; final String password="password";
final String nonce="e273f1776275974f1a120d8b92c5b3cb"; final String nonce="e273f1776275974f1a120d8b92c5b3cb";
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, realm, null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials(username, password);
credentialsProvider.setCredentials(authScope, creds);
final String challenge="Digest realm=\"" + realm + "\", " final String challenge="Digest realm=\"" + realm + "\", "
+ "nonce=\"" + nonce + "\", " + "nonce=\"" + nonce + "\", "
+ "opaque=\"SomeString\", " + "opaque=\"SomeString\", "
@ -360,12 +382,10 @@ public void testDigestAuthenticationMD5SessUnknownQop() throws Exception {
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
final Credentials cred = new UsernamePasswordCredentials(username, password); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final HttpRequest request = new BasicHttpRequest("Simple", "/"); authscheme.generateAuthResponse(host, request, null);
final HttpContext context = new BasicHttpContext();
authscheme.authenticate(cred, request, context);
} }
/** /**
@ -380,6 +400,13 @@ public void testDigestAuthenticationUnknownAlgo() throws Exception {
final String password="password"; final String password="password";
final String nonce="e273f1776275974f1a120d8b92c5b3cb"; final String nonce="e273f1776275974f1a120d8b92c5b3cb";
final HttpRequest request = new BasicHttpRequest("Simple", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, realm, null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials(username, password);
credentialsProvider.setCredentials(authScope, creds);
final String challenge="Digest realm=\"" + realm + "\", " final String challenge="Digest realm=\"" + realm + "\", "
+ "nonce=\"" + nonce + "\", " + "nonce=\"" + nonce + "\", "
+ "opaque=\"SomeString\", " + "opaque=\"SomeString\", "
@ -390,12 +417,10 @@ public void testDigestAuthenticationUnknownAlgo() throws Exception {
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
final Credentials cred = new UsernamePasswordCredentials(username, password); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final HttpRequest request = new BasicHttpRequest("Simple", "/"); authscheme.generateAuthResponse(host, request, null);
final HttpContext context = new BasicHttpContext();
authscheme.authenticate(cred, request, context);
} }
@Test @Test
@ -404,17 +429,16 @@ public void testDigestAuthenticationWithStaleNonce() throws Exception {
"nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", stale=\"true\""; "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", stale=\"true\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final AuthScheme authscheme = new DigestScheme(); final AuthScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.processChallenge(authChallenge, null);
Assert.assertFalse(authscheme.isComplete()); Assert.assertFalse(authscheme.isChallengeComplete());
} }
private static Map<String, String> parseAuthResponse(final Header authResponse) { private static Map<String, String> parseAuthResponse(final String authResponse) {
final String s = authResponse.getValue(); if (!authResponse.startsWith("Digest ")) {
if (!s.startsWith("Digest ")) {
return null; return null;
} }
final HeaderElement[] elements = BasicHeaderValueParser.parseElements(s.substring(7), null); final HeaderElement[] elements = BasicHeaderValueParser.parseElements(authResponse.substring(7), null);
final Map<String, String> map = new HashMap<>(elements.length); final Map<String, String> map = new HashMap<>(elements.length);
for (final HeaderElement element : elements) { for (final HeaderElement element : elements) {
map.put(element.getName(), element.getValue()); map.put(element.getName(), element.getValue());
@ -424,50 +448,73 @@ private static Map<String, String> parseAuthResponse(final Header authResponse)
@Test @Test
public void testDigestNouceCount() throws Exception { public void testDigestNouceCount() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge1 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth"; final String challenge1 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
final AuthChallenge authChallenge1 = parse(challenge1); final AuthChallenge authChallenge1 = parse(challenge1);
final HttpRequest request = new BasicHttpRequest("GET", "/");
final Credentials cred = new UsernamePasswordCredentials("username","password");
final HttpContext context = new BasicHttpContext();
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge1); authscheme.processChallenge(authChallenge1, null);
final Header authResponse1 = authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse1 = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table1 = parseAuthResponse(authResponse1); final Map<String, String> table1 = parseAuthResponse(authResponse1);
Assert.assertEquals("00000001", table1.get("nc")); Assert.assertEquals("00000001", table1.get("nc"));
final Header authResponse2 = authscheme.authenticate(cred, request, context);
Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse2 = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table2 = parseAuthResponse(authResponse2); final Map<String, String> table2 = parseAuthResponse(authResponse2);
Assert.assertEquals("00000002", table2.get("nc")); Assert.assertEquals("00000002", table2.get("nc"));
final String challenge2 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth"; final String challenge2 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
final AuthChallenge authChallenge2 = parse(challenge2); final AuthChallenge authChallenge2 = parse(challenge2);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge2); authscheme.processChallenge(authChallenge2, null);
final Header authResponse3 = authscheme.authenticate(cred, request, context);
Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse3 = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table3 = parseAuthResponse(authResponse3); final Map<String, String> table3 = parseAuthResponse(authResponse3);
Assert.assertEquals("00000003", table3.get("nc")); Assert.assertEquals("00000003", table3.get("nc"));
final String challenge3 = "Digest realm=\"realm1\", nonce=\"e273f1776275974f1a120d8b92c5b3cb\", qop=auth"; final String challenge3 = "Digest realm=\"realm1\", nonce=\"e273f1776275974f1a120d8b92c5b3cb\", qop=auth";
final AuthChallenge authChallenge3 = parse(challenge3); final AuthChallenge authChallenge3 = parse(challenge3);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge3); authscheme.processChallenge(authChallenge3, null);
final Header authResponse4 = authscheme.authenticate(cred, request, context);
Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse4 = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table4 = parseAuthResponse(authResponse4); final Map<String, String> table4 = parseAuthResponse(authResponse4);
Assert.assertEquals("00000001", table4.get("nc")); Assert.assertEquals("00000001", table4.get("nc"));
} }
@Test @Test
public void testDigestMD5SessA1AndCnonceConsistency() throws Exception { public void testDigestMD5SessA1AndCnonceConsistency() throws Exception {
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "subnet.domain.com", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final HttpRequest request = new BasicHttpRequest("GET", "/");
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge1 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " + final String challenge1 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " +
"charset=utf-8, realm=\"subnet.domain.com\""; "charset=utf-8, realm=\"subnet.domain.com\"";
final AuthChallenge authChallenge1 = parse(challenge1); final AuthChallenge authChallenge1 = parse(challenge1);
final HttpRequest request = new BasicHttpRequest("GET", "/");
final Credentials cred = new UsernamePasswordCredentials("username","password");
final HttpContext context = new BasicHttpContext();
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
authscheme.processChallenge(ChallengeType.TARGET, authChallenge1); authscheme.processChallenge(authChallenge1, null);
final Header authResponse1 = authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse1 = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table1 = parseAuthResponse(authResponse1); final Map<String, String> table1 = parseAuthResponse(authResponse1);
Assert.assertEquals("00000001", table1.get("nc")); Assert.assertEquals("00000001", table1.get("nc"));
final String cnonce1 = authscheme.getCnonce(); final String cnonce1 = authscheme.getCnonce();
final String sessionKey1 = authscheme.getA1(); final String sessionKey1 = authscheme.getA1();
final Header authResponse2 = authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse2 = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table2 = parseAuthResponse(authResponse2); final Map<String, String> table2 = parseAuthResponse(authResponse2);
Assert.assertEquals("00000002", table2.get("nc")); Assert.assertEquals("00000002", table2.get("nc"));
final String cnonce2 = authscheme.getCnonce(); final String cnonce2 = authscheme.getCnonce();
@ -479,8 +526,9 @@ public void testDigestMD5SessA1AndCnonceConsistency() throws Exception {
final String challenge2 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " + final String challenge2 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " +
"charset=utf-8, realm=\"subnet.domain.com\""; "charset=utf-8, realm=\"subnet.domain.com\"";
final AuthChallenge authChallenge2 = parse(challenge2); final AuthChallenge authChallenge2 = parse(challenge2);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge2); authscheme.processChallenge(authChallenge2, null);
final Header authResponse3 = authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse3 = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table3 = parseAuthResponse(authResponse3); final Map<String, String> table3 = parseAuthResponse(authResponse3);
Assert.assertEquals("00000003", table3.get("nc")); Assert.assertEquals("00000003", table3.get("nc"));
@ -493,8 +541,9 @@ public void testDigestMD5SessA1AndCnonceConsistency() throws Exception {
final String challenge3 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"fedcba0987654321\", " + final String challenge3 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"fedcba0987654321\", " +
"charset=utf-8, realm=\"subnet.domain.com\""; "charset=utf-8, realm=\"subnet.domain.com\"";
final AuthChallenge authChallenge3 = parse(challenge3); final AuthChallenge authChallenge3 = parse(challenge3);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge3); authscheme.processChallenge(authChallenge3, null);
final Header authResponse4 = authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final String authResponse4 = authscheme.generateAuthResponse(host, request, null);
final Map<String, String> table4 = parseAuthResponse(authResponse4); final Map<String, String> table4 = parseAuthResponse(authResponse4);
Assert.assertEquals("00000001", table4.get("nc")); Assert.assertEquals("00000001", table4.get("nc"));
@ -533,16 +582,21 @@ public void testHttpEntityDigest() throws Exception {
@Test @Test
public void testDigestAuthenticationQopAuthInt() throws Exception { public void testDigestAuthenticationQopAuthInt() throws Exception {
final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
request.setEntity(new StringEntity("abc\u00e4\u00f6\u00fcabc", HTTP.DEF_CONTENT_CHARSET));
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
"qop=\"auth,auth-int\""; "qop=\"auth,auth-int\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
request.setEntity(new StringEntity("abc\u00e4\u00f6\u00fcabc", HTTP.DEF_CONTENT_CHARSET));
final Credentials cred = new UsernamePasswordCredentials("username","password");
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
final HttpContext context = new BasicHttpContext(); authscheme.processChallenge(authChallenge, null);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final Header authResponse = authscheme.authenticate(cred, request, context); final String authResponse = authscheme.generateAuthResponse(host, request, null);
Assert.assertEquals("Post:/:acd2b59cd01c7737d8069015584c6cac", authscheme.getA2()); Assert.assertEquals("Post:/:acd2b59cd01c7737d8069015584c6cac", authscheme.getA2());
@ -556,15 +610,20 @@ public void testDigestAuthenticationQopAuthInt() throws Exception {
@Test @Test
public void testDigestAuthenticationQopAuthIntNullEntity() throws Exception { public void testDigestAuthenticationQopAuthIntNullEntity() throws Exception {
final HttpRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
"qop=\"auth,auth-int\""; "qop=\"auth,auth-int\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
final Credentials cred = new UsernamePasswordCredentials("username","password");
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
final HttpContext context = new BasicHttpContext(); authscheme.processChallenge(authChallenge, null);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final Header authResponse = authscheme.authenticate(cred, request, context); final String authResponse = authscheme.generateAuthResponse(host, request, null);
Assert.assertEquals("Post:/:d41d8cd98f00b204e9800998ecf8427e", authscheme.getA2()); Assert.assertEquals("Post:/:d41d8cd98f00b204e9800998ecf8427e", authscheme.getA2());
@ -578,16 +637,21 @@ public void testDigestAuthenticationQopAuthIntNullEntity() throws Exception {
@Test @Test
public void testDigestAuthenticationQopAuthOrAuthIntNonRepeatableEntity() throws Exception { public void testDigestAuthenticationQopAuthOrAuthIntNonRepeatableEntity() throws Exception {
final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1));
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
"qop=\"auth,auth-int\""; "qop=\"auth,auth-int\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1));
final Credentials cred = new UsernamePasswordCredentials("username","password");
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
final HttpContext context = new BasicHttpContext(); authscheme.processChallenge(authChallenge, null);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
final Header authResponse = authscheme.authenticate(cred, request, context); final String authResponse = authscheme.generateAuthResponse(host, request, null);
Assert.assertEquals("Post:/", authscheme.getA2()); Assert.assertEquals("Post:/", authscheme.getA2());
@ -601,32 +665,43 @@ public void testDigestAuthenticationQopAuthOrAuthIntNonRepeatableEntity() throws
@Test @Test
public void testParameterCaseSensitivity() throws Exception { public void testParameterCaseSensitivity() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "-", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest Realm=\"-\", " + final String challenge = "Digest Realm=\"-\", " +
"nonce=\"YjYuNGYyYmJhMzUuY2I5ZDhlZDE5M2ZlZDM 1Mjk3NGJkNTIyYjgyNTcwMjQ=\", " + "nonce=\"YjYuNGYyYmJhMzUuY2I5ZDhlZDE5M2ZlZDM 1Mjk3NGJkNTIyYjgyNTcwMjQ=\", " +
"opaque=\"98700A3D9CE17065E2246B41035C6609\", qop=\"auth\""; "opaque=\"98700A3D9CE17065E2246B41035C6609\", qop=\"auth\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpRequest request = new BasicHttpRequest("GET", "/");
final Credentials cred = new UsernamePasswordCredentials("username","password");
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
final HttpContext context = new BasicHttpContext(); authscheme.processChallenge(authChallenge, null);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge);
Assert.assertEquals("-", authscheme.getRealm()); Assert.assertEquals("-", authscheme.getRealm());
authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
authscheme.generateAuthResponse(host, request, null);
} }
@Test(expected=AuthenticationException.class) @Test(expected=AuthenticationException.class)
public void testDigestAuthenticationQopIntOnlyNonRepeatableEntity() throws Exception { public void testDigestAuthenticationQopIntOnlyNonRepeatableEntity() throws Exception {
final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1));
final HttpHost host = new HttpHost("somehost", 80);
final AuthScope authScope = new AuthScope(host, "realm1", null);
final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final Credentials creds = new UsernamePasswordCredentials("username","password");
credentialsProvider.setCredentials(authScope, creds);
final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
"qop=\"auth-int\""; "qop=\"auth-int\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1));
final Credentials cred = new UsernamePasswordCredentials("username","password");
final DigestScheme authscheme = new DigestScheme(); final DigestScheme authscheme = new DigestScheme();
final HttpContext context = new BasicHttpContext(); authscheme.processChallenge(authChallenge, null);
authscheme.processChallenge(ChallengeType.TARGET, authChallenge);
authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isResponseReady(host, credentialsProvider, null));
authscheme.generateAuthResponse(host, request, null);
} }
@Test @Test
@ -635,7 +710,7 @@ public void testSerialization() throws Exception {
"qop=\"auth,auth-int\""; "qop=\"auth,auth-int\"";
final AuthChallenge authChallenge = parse(challenge); final AuthChallenge authChallenge = parse(challenge);
final DigestScheme digestScheme = new DigestScheme(); final DigestScheme digestScheme = new DigestScheme();
digestScheme.processChallenge(ChallengeType.TARGET, authChallenge); digestScheme.processChallenge(authChallenge, null);
final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
final ObjectOutputStream out = new ObjectOutputStream(buffer); final ObjectOutputStream out = new ObjectOutputStream(buffer);
@ -645,9 +720,9 @@ public void testSerialization() throws Exception {
final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw)); final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw));
final DigestScheme authScheme = (DigestScheme) in.readObject(); final DigestScheme authScheme = (DigestScheme) in.readObject();
Assert.assertEquals(digestScheme.getSchemeName(), authScheme.getSchemeName()); Assert.assertEquals(digestScheme.getName(), authScheme.getName());
Assert.assertEquals(digestScheme.getRealm(), authScheme.getRealm()); Assert.assertEquals(digestScheme.getRealm(), authScheme.getRealm());
Assert.assertEquals(digestScheme.isComplete(), authScheme.isComplete()); Assert.assertEquals(digestScheme.isChallengeComplete(), authScheme.isChallengeComplete());
Assert.assertEquals(digestScheme.getA1(), authScheme.getA1()); Assert.assertEquals(digestScheme.getA1(), authScheme.getA1());
Assert.assertEquals(digestScheme.getA2(), authScheme.getA2()); Assert.assertEquals(digestScheme.getA2(), authScheme.getA2());
Assert.assertEquals(digestScheme.getCnonce(), authScheme.getCnonce()); Assert.assertEquals(digestScheme.getCnonce(), authScheme.getCnonce());

View File

@ -35,7 +35,6 @@
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion; import org.apache.http.HttpVersion;
import org.apache.http.auth.AuthOption;
import org.apache.http.auth.AuthProtocolState; import org.apache.http.auth.AuthProtocolState;
import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeProvider; import org.apache.http.auth.AuthSchemeProvider;
@ -44,11 +43,12 @@
import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType; import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials; import org.apache.http.auth.Credentials;
import org.apache.http.auth.CredentialsProvider;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache; import org.apache.http.client.AuthCache;
import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Lookup; import org.apache.http.config.Lookup;
import org.apache.http.config.RegistryBuilder; import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultAuthenticationStrategy; import org.apache.http.impl.client.DefaultAuthenticationStrategy;
import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpRequest;
@ -68,8 +68,7 @@ public class TestHttpAuthenticator {
private AuthScheme authScheme; private AuthScheme authScheme;
private HttpContext context; private HttpContext context;
private HttpHost defaultHost; private HttpHost defaultHost;
private Credentials credentials; private CredentialsProvider credentialsProvider;
private BasicCredentialsProvider credentialsProvider;
private Lookup<AuthSchemeProvider> authSchemeRegistry; private Lookup<AuthSchemeProvider> authSchemeRegistry;
private AuthCache authCache; private AuthCache authCache;
private HttpAuthenticator httpAuthenticator; private HttpAuthenticator httpAuthenticator;
@ -78,14 +77,12 @@ public class TestHttpAuthenticator {
public void setUp() throws Exception { public void setUp() throws Exception {
this.authState = new AuthState(); this.authState = new AuthState();
this.authScheme = Mockito.mock(AuthScheme.class); this.authScheme = Mockito.mock(AuthScheme.class);
Mockito.when(this.authScheme.getSchemeName()).thenReturn("Basic"); Mockito.when(this.authScheme.getName()).thenReturn("Basic");
Mockito.when(this.authScheme.isComplete()).thenReturn(Boolean.TRUE); Mockito.when(this.authScheme.isChallengeComplete()).thenReturn(Boolean.TRUE);
this.context = new BasicHttpContext(); this.context = new BasicHttpContext();
this.defaultHost = new HttpHost("localhost", 80); this.defaultHost = new HttpHost("localhost", 80);
this.context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, this.defaultHost); this.context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, this.defaultHost);
this.credentials = Mockito.mock(Credentials.class); this.credentialsProvider = Mockito.mock(CredentialsProvider.class);
this.credentialsProvider = new BasicCredentialsProvider();
this.credentialsProvider.setCredentials(AuthScope.ANY, this.credentials);
this.context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credentialsProvider); this.context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credentialsProvider);
this.authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create() this.authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register("basic", new BasicSchemeFactory()) .register("basic", new BasicSchemeFactory())
@ -111,7 +108,7 @@ public void testAuthenticationRequestedAfterSuccess() throws Exception {
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test"); response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test");
this.authState.update(this.authScheme, this.credentials); this.authState.update(this.authScheme);
this.authState.setState(AuthProtocolState.SUCCESS); this.authState.setState(AuthProtocolState.SUCCESS);
Assert.assertTrue(this.httpAuthenticator.isChallenged( Assert.assertTrue(this.httpAuthenticator.isChallenged(
@ -132,7 +129,7 @@ public void testAuthenticationNotRequestedUnchallenged() throws Exception {
@Test @Test
public void testAuthenticationNotRequestedSuccess1() throws Exception { public void testAuthenticationNotRequestedSuccess1() throws Exception {
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
this.authState.update(this.authScheme, this.credentials); this.authState.update(this.authScheme);
this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.setState(AuthProtocolState.CHALLENGED);
Assert.assertFalse(this.httpAuthenticator.isChallenged( Assert.assertFalse(this.httpAuthenticator.isChallenged(
@ -145,7 +142,7 @@ public void testAuthenticationNotRequestedSuccess1() throws Exception {
@Test @Test
public void testAuthenticationNotRequestedSuccess2() throws Exception { public void testAuthenticationNotRequestedSuccess2() throws Exception {
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
this.authState.update(this.authScheme, this.credentials); this.authState.update(this.authScheme);
this.authState.setState(AuthProtocolState.HANDSHAKE); this.authState.setState(AuthProtocolState.HANDSHAKE);
Assert.assertFalse(this.httpAuthenticator.isChallenged( Assert.assertFalse(this.httpAuthenticator.isChallenged(
@ -163,20 +160,47 @@ public void testAuthentication() throws Exception {
response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"realm1\", nonce=\"1234\""));
response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "whatever realm=\"realm1\", stuff=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "whatever realm=\"realm1\", stuff=\"1234\""));
final Credentials credentials = new UsernamePasswordCredentials("user:pass");
Mockito.when(this.credentialsProvider.getCredentials(Mockito.<AuthScope>any())).thenReturn(credentials);
final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
Assert.assertTrue(this.httpAuthenticator.prepareAuthResponse( Assert.assertTrue(this.httpAuthenticator.prepareAuthResponse(
host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context));
Assert.assertEquals(AuthProtocolState.CHALLENGED, this.authState.getState()); Assert.assertEquals(AuthProtocolState.CHALLENGED, this.authState.getState());
final Queue<AuthOption> options = this.authState.getAuthOptions(); final Queue<AuthScheme> options = this.authState.getAuthOptions();
Assert.assertNotNull(options); Assert.assertNotNull(options);
final AuthOption option1 = options.poll(); final AuthScheme authScheme1 = options.poll();
Assert.assertNotNull(option1); Assert.assertNotNull(authScheme1);
Assert.assertEquals("digest", option1.getAuthScheme().getSchemeName()); Assert.assertEquals("digest", authScheme1.getName());
final AuthOption option2 = options.poll(); final AuthScheme authScheme2 = options.poll();
Assert.assertNotNull(option2); Assert.assertNotNull(authScheme2);
Assert.assertEquals("basic", option2.getAuthScheme().getSchemeName()); Assert.assertEquals("basic", authScheme2.getName());
Assert.assertNull(options.poll());
}
@Test
public void testAuthenticationCredentialsForBasic() throws Exception {
final HttpHost host = new HttpHost("somehost", 80);
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"test\""));
response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"realm1\", nonce=\"1234\""));
final Credentials credentials = new UsernamePasswordCredentials("user:pass");
Mockito.when(this.credentialsProvider.getCredentials(new AuthScope(host, "test", "basic"))).thenReturn(credentials);
final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
Assert.assertTrue(this.httpAuthenticator.prepareAuthResponse(
host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context));
Assert.assertEquals(AuthProtocolState.CHALLENGED, this.authState.getState());
final Queue<AuthScheme> options = this.authState.getAuthOptions();
Assert.assertNotNull(options);
final AuthScheme authScheme1 = options.poll();
Assert.assertNotNull(authScheme1);
Assert.assertEquals("basic", authScheme1.getName());
Assert.assertNull(options.poll()); Assert.assertNull(options.poll());
} }
@ -211,8 +235,6 @@ public void testAuthenticationNoCredentials() throws Exception {
response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"test\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"test\""));
response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"realm1\", nonce=\"1234\""));
this.credentialsProvider.clear();
final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
Assert.assertFalse(this.httpAuthenticator.prepareAuthResponse( Assert.assertFalse(this.httpAuthenticator.prepareAuthResponse(
@ -227,7 +249,7 @@ public void testAuthenticationFailed() throws Exception {
response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"realm1\", nonce=\"1234\""));
this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.setState(AuthProtocolState.CHALLENGED);
this.authState.update(this.authScheme, this.credentials); this.authState.update(this.authScheme);
final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
@ -267,12 +289,11 @@ public void testAuthenticationFailure() throws Exception {
final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.setState(AuthProtocolState.CHALLENGED);
this.authState.update(new BasicScheme(), this.credentials); this.authState.update(new BasicScheme());
Assert.assertFalse(this.httpAuthenticator.prepareAuthResponse( Assert.assertFalse(this.httpAuthenticator.prepareAuthResponse(
host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context));
Assert.assertEquals(AuthProtocolState.FAILURE, this.authState.getState()); Assert.assertEquals(AuthProtocolState.FAILURE, this.authState.getState());
Assert.assertNull(this.authState.getCredentials());
} }
@Test @Test
@ -286,7 +307,7 @@ public void testAuthenticationHandshaking() throws Exception {
final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.setState(AuthProtocolState.CHALLENGED);
this.authState.update(new DigestScheme(), this.credentials); this.authState.update(new DigestScheme());
Assert.assertTrue(this.httpAuthenticator.prepareAuthResponse( Assert.assertTrue(this.httpAuthenticator.prepareAuthResponse(
host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context));
@ -301,20 +322,23 @@ public void testAuthenticationNoMatchingChallenge() throws Exception {
response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"realm1\", nonce=\"1234\""));
response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "whatever realm=\"realm1\", stuff=\"1234\"")); response.addHeader(new BasicHeader(HttpHeaders.WWW_AUTHENTICATE, "whatever realm=\"realm1\", stuff=\"1234\""));
final Credentials credentials = new UsernamePasswordCredentials("user:pass");
Mockito.when(this.credentialsProvider.getCredentials(new AuthScope(host, "realm1", "digest"))).thenReturn(credentials);
final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.setState(AuthProtocolState.CHALLENGED);
this.authState.update(new BasicScheme(), this.credentials); this.authState.update(new BasicScheme());
Assert.assertTrue(this.httpAuthenticator.prepareAuthResponse( Assert.assertTrue(this.httpAuthenticator.prepareAuthResponse(
host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context));
Assert.assertEquals(AuthProtocolState.CHALLENGED, this.authState.getState()); Assert.assertEquals(AuthProtocolState.CHALLENGED, this.authState.getState());
final Queue<AuthOption> options = this.authState.getAuthOptions(); final Queue<AuthScheme> options = this.authState.getAuthOptions();
Assert.assertNotNull(options); Assert.assertNotNull(options);
final AuthOption option1 = options.poll(); final AuthScheme authScheme1 = options.poll();
Assert.assertNotNull(option1); Assert.assertNotNull(authScheme1);
Assert.assertEquals("digest", option1.getAuthScheme().getSchemeName()); Assert.assertEquals("digest", authScheme1.getName());
Assert.assertNull(options.poll()); Assert.assertNull(options.poll());
} }
@ -333,21 +357,20 @@ public void testAuthenticationException() throws Exception {
Assert.assertEquals(AuthProtocolState.UNCHALLENGED, this.authState.getState()); Assert.assertEquals(AuthProtocolState.UNCHALLENGED, this.authState.getState());
Assert.assertNull(this.authState.getAuthScheme()); Assert.assertNull(this.authState.getAuthScheme());
Assert.assertNull(this.authState.getCredentials());
} }
@Test @Test
public void testAuthFailureState() throws Exception { public void testAuthFailureState() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.FAILURE); this.authState.setState(AuthProtocolState.FAILURE);
this.authState.update(this.authScheme, this.credentials); this.authState.update(this.authScheme);
this.httpAuthenticator.addAuthResponse(request, authState, context); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authState, context);
Assert.assertFalse(request.containsHeader(HttpHeaders.AUTHORIZATION)); Assert.assertFalse(request.containsHeader(HttpHeaders.AUTHORIZATION));
Mockito.verify(this.authScheme, Mockito.never()).authenticate( Mockito.verify(this.authScheme, Mockito.never()).generateAuthResponse(
Mockito.any(Credentials.class), Mockito.eq(defaultHost),
Mockito.any(HttpRequest.class), Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class)); Mockito.any(HttpContext.class));
} }
@ -356,42 +379,37 @@ public void testAuthFailureState() throws Exception {
public void testAuthChallengeStateNoOption() throws Exception { public void testAuthChallengeStateNoOption() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.setState(AuthProtocolState.CHALLENGED);
this.authState.update(this.authScheme, this.credentials); this.authState.update(this.authScheme);
Mockito.when(this.authScheme.authenticate( Mockito.when(this.authScheme.generateAuthResponse(
Mockito.any(Credentials.class), Mockito.eq(defaultHost),
Mockito.any(HttpRequest.class), Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(HttpHeaders.AUTHORIZATION, "stuff")); Mockito.any(HttpContext.class))).thenReturn("stuff");
this.httpAuthenticator.addAuthResponse(request, authState, context); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authState, context);
Assert.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION)); Assert.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION));
Mockito.verify(this.authScheme).authenticate(this.credentials, request, this.context);
} }
@Test @Test
public void testAuthChallengeStateOneOptions() throws Exception { public void testAuthChallengeStateOneOptions() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.setState(AuthProtocolState.CHALLENGED);
final LinkedList<AuthOption> authOptions = new LinkedList<>(); final LinkedList<AuthScheme> authOptions = new LinkedList<>();
authOptions.add(new AuthOption(this.authScheme, this.credentials)); authOptions.add(this.authScheme);
this.authState.update(authOptions); this.authState.update(authOptions);
Mockito.when(this.authScheme.authenticate( Mockito.when(this.authScheme.generateAuthResponse(
Mockito.any(Credentials.class), Mockito.eq(defaultHost),
Mockito.any(HttpRequest.class), Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(HttpHeaders.AUTHORIZATION, "stuff")); Mockito.any(HttpContext.class))).thenReturn("stuff");
this.httpAuthenticator.addAuthResponse(request, authState, context); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authState, context);
Assert.assertSame(this.authScheme, this.authState.getAuthScheme()); Assert.assertSame(this.authScheme, this.authState.getAuthScheme());
Assert.assertSame(this.credentials, this.authState.getCredentials());
Assert.assertNull(this.authState.getAuthOptions()); Assert.assertNull(this.authState.getAuthOptions());
Assert.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION)); Assert.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION));
Mockito.verify(this.authScheme).authenticate(this.credentials, request, this.context);
} }
@Test @Test
@ -399,74 +417,67 @@ public void testAuthChallengeStateMultipleOption() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.setState(AuthProtocolState.CHALLENGED);
final LinkedList<AuthOption> authOptions = new LinkedList<>(); final LinkedList<AuthScheme> authOptions = new LinkedList<>();
final AuthScheme authScheme1 = Mockito.mock(AuthScheme.class); final AuthScheme authScheme1 = Mockito.mock(AuthScheme.class);
Mockito.doThrow(new AuthenticationException()).when(authScheme1).authenticate( Mockito.doThrow(new AuthenticationException()).when(authScheme1).generateAuthResponse(
Mockito.any(Credentials.class), Mockito.eq(defaultHost),
Mockito.any(HttpRequest.class), Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class)); Mockito.any(HttpContext.class));
final AuthScheme authScheme2 = Mockito.mock(AuthScheme.class); final AuthScheme authScheme2 = Mockito.mock(AuthScheme.class);
Mockito.when(authScheme2.authenticate( Mockito.when(authScheme2.generateAuthResponse(
Mockito.any(Credentials.class), Mockito.eq(defaultHost),
Mockito.any(HttpRequest.class), Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(HttpHeaders.AUTHORIZATION, "stuff")); Mockito.any(HttpContext.class))).thenReturn("stuff");
authOptions.add(new AuthOption(authScheme1, this.credentials)); authOptions.add(authScheme1);
authOptions.add(new AuthOption(authScheme2, this.credentials)); authOptions.add(authScheme2);
this.authState.update(authOptions); this.authState.update(authOptions);
this.httpAuthenticator.addAuthResponse(request, authState, context); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authState, context);
Assert.assertSame(authScheme2, this.authState.getAuthScheme()); Assert.assertSame(authScheme2, this.authState.getAuthScheme());
Assert.assertSame(this.credentials, this.authState.getCredentials());
Assert.assertNull(this.authState.getAuthOptions()); Assert.assertNull(this.authState.getAuthOptions());
Assert.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION)); Assert.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION));
Mockito.verify(authScheme1, Mockito.times(1)).authenticate(this.credentials, request, this.context);
Mockito.verify(authScheme2, Mockito.times(1)).authenticate(this.credentials, request, this.context);
} }
@Test @Test
public void testAuthSuccess() throws Exception { public void testAuthSuccess() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.SUCCESS); this.authState.setState(AuthProtocolState.SUCCESS);
this.authState.update(this.authScheme, this.credentials); this.authState.update(this.authScheme);
Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.FALSE); Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.FALSE);
Mockito.when(this.authScheme.authenticate( Mockito.when(this.authScheme.generateAuthResponse(
Mockito.any(Credentials.class), Mockito.eq(defaultHost),
Mockito.any(HttpRequest.class), Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(HttpHeaders.AUTHORIZATION, "stuff")); Mockito.any(HttpContext.class))).thenReturn("stuff");
this.httpAuthenticator.addAuthResponse(request, authState, context); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authState, context);
Assert.assertSame(this.authScheme, this.authState.getAuthScheme()); Assert.assertSame(this.authScheme, this.authState.getAuthScheme());
Assert.assertSame(this.credentials, this.authState.getCredentials());
Assert.assertNull(this.authState.getAuthOptions()); Assert.assertNull(this.authState.getAuthOptions());
Assert.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION)); Assert.assertTrue(request.containsHeader(HttpHeaders.AUTHORIZATION));
Mockito.verify(this.authScheme).authenticate(this.credentials, request, this.context);
} }
@Test @Test
public void testAuthSuccessConnectionBased() throws Exception { public void testAuthSuccessConnectionBased() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpRequest request = new BasicHttpRequest("GET", "/");
this.authState.setState(AuthProtocolState.SUCCESS); this.authState.setState(AuthProtocolState.SUCCESS);
this.authState.update(this.authScheme, this.credentials); this.authState.update(this.authScheme);
Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.TRUE); Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.TRUE);
Mockito.when(this.authScheme.authenticate( Mockito.when(this.authScheme.generateAuthResponse(
Mockito.any(Credentials.class), Mockito.eq(defaultHost),
Mockito.any(HttpRequest.class), Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(HttpHeaders.AUTHORIZATION, "stuff")); Mockito.any(HttpContext.class))).thenReturn("stuff");
this.httpAuthenticator.addAuthResponse(request, authState, context); this.httpAuthenticator.addAuthResponse(defaultHost, ChallengeType.TARGET, request, authState, context);
Assert.assertFalse(request.containsHeader(HttpHeaders.AUTHORIZATION)); Assert.assertFalse(request.containsHeader(HttpHeaders.AUTHORIZATION));
Mockito.verify(this.authScheme, Mockito.never()).authenticate( Mockito.verify(this.authScheme, Mockito.never()).generateAuthResponse(
Mockito.any(Credentials.class), Mockito.eq(defaultHost),
Mockito.any(HttpRequest.class), Mockito.any(HttpRequest.class),
Mockito.any(HttpContext.class)); Mockito.any(HttpContext.class));
} }

View File

@ -1,87 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.auth;
import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.protocol.HttpContext;
import org.junit.Assert;
import org.junit.Test;
public class TestNonStandardHttpScheme {
static class TestAuthScheme extends NonStandardAuthScheme {
@Override
public void processChallenge(
final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException {
update(challengeType, authChallenge);
}
@Override
public Header authenticate(
final Credentials credentials,
final HttpRequest request,
final HttpContext context) throws AuthenticationException {
return null;
}
@Override
public String getSchemeName() {
return "test";
}
@Override
public boolean isComplete() {
return false;
}
@Override
public boolean isConnectionBased() {
return false;
}
}
@Test
public void testProcessChallenge() throws Exception {
final TestAuthScheme authscheme = new TestAuthScheme();
authscheme.processChallenge(ChallengeType.TARGET, new AuthChallenge("Test", "this_and_that", null));
Assert.assertEquals("test", authscheme.getSchemeName());
Assert.assertEquals("test(TARGET) this_and_that", authscheme.toString());
Assert.assertEquals("this_and_that", authscheme.getChallenge());
}
}

View File

@ -1,95 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* 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
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.auth;
import java.util.Arrays;
import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.apache.http.auth.AuthChallenge;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.junit.Assert;
import org.junit.Test;
public class TestStandardHttpScheme {
static class TestAuthScheme extends StandardAuthScheme {
private static final long serialVersionUID = 1L;
@Override
public void processChallenge(
final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException {
update(challengeType, authChallenge);
}
@Override
public Header authenticate(
final Credentials credentials,
final HttpRequest request,
final HttpContext context) throws AuthenticationException {
return null;
}
@Override
public String getSchemeName() {
return "test";
}
@Override
public boolean isComplete() {
return false;
}
@Override
public boolean isConnectionBased() {
return false;
}
}
@Test
public void testProcessChallenge() throws Exception {
final TestAuthScheme authscheme = new TestAuthScheme();
authscheme.processChallenge(ChallengeType.TARGET, new AuthChallenge("Test", null, Arrays.asList(
new BasicNameValuePair("realm", "realm1"), new BasicNameValuePair("this", "blah"))));
Assert.assertEquals("test", authscheme.getSchemeName());
Assert.assertEquals("test(TARGET) {realm=realm1, this=blah}", authscheme.toString());
Assert.assertEquals("realm1", authscheme.getParameter("realm"));
Assert.assertEquals(null, authscheme.getParameter("test"));
Assert.assertEquals("blah", authscheme.getParameter("this"));
}
}

View File

@ -588,14 +588,12 @@ public void testPreemptiveAuthentication() throws Exception {
final HttpHost target = start(); final HttpHost target = start();
final BasicScheme basicScheme = new BasicScheme();
basicScheme.initPreemptive(new UsernamePasswordCredentials("test", "test"));
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final AuthCache authCache = new BasicAuthCache(); final AuthCache authCache = new BasicAuthCache();
authCache.put(target, new BasicScheme()); authCache.put(target, basicScheme);
context.setAuthCache(authCache); context.setAuthCache(authCache);
final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("test", "test"));
context.setCredentialsProvider(credsProvider);
final HttpGet httpget = new HttpGet("/"); final HttpGet httpget = new HttpGet("/");
final HttpResponse response1 = this.httpclient.execute(target, httpget, context); final HttpResponse response1 = this.httpclient.execute(target, httpget, context);

View File

@ -153,7 +153,7 @@ public AuthScheme create(final HttpContext context) {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public String getSchemeName() { public String getName() {
return "MyBasic"; return "MyBasic";
} }

View File

@ -51,7 +51,6 @@
import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState; import org.apache.http.auth.AuthState;
import org.apache.http.auth.ChallengeType; import org.apache.http.auth.ChallengeType;
import org.apache.http.auth.NTCredentials;
import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.AuthenticationStrategy;
import org.apache.http.client.NonRepeatableRequestException; import org.apache.http.client.NonRepeatableRequestException;
@ -442,7 +441,7 @@ public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exceptio
final AuthState proxyAuthState = new AuthState(); final AuthState proxyAuthState = new AuthState();
proxyAuthState.setState(AuthProtocolState.SUCCESS); proxyAuthState.setState(AuthProtocolState.SUCCESS);
proxyAuthState.update(new NTLMScheme(), new NTCredentials("user:pass")); proxyAuthState.update(new NTLMScheme());
final HttpClientContext context = new HttpClientContext(); final HttpClientContext context = new HttpClientContext();
context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState); context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState);
@ -475,7 +474,6 @@ public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exceptio
Assert.assertNotNull(finalResponse); Assert.assertNotNull(finalResponse);
Assert.assertEquals(200, finalResponse.getStatusLine().getStatusCode()); Assert.assertEquals(200, finalResponse.getStatusLine().getStatusCode());
Assert.assertNull(proxyAuthState.getAuthScheme()); Assert.assertNull(proxyAuthState.getAuthScheme());
Assert.assertNull(proxyAuthState.getCredentials());
} }
@Test(expected = NonRepeatableRequestException.class) @Test(expected = NonRepeatableRequestException.class)

View File

@ -26,6 +26,10 @@
*/ */
package org.apache.http.impl.execchain; package org.apache.http.impl.execchain;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpException; import org.apache.http.HttpException;
@ -35,8 +39,6 @@
import org.apache.http.ProtocolException; import org.apache.http.ProtocolException;
import org.apache.http.auth.AuthProtocolState; import org.apache.http.auth.AuthProtocolState;
import org.apache.http.auth.AuthState; import org.apache.http.auth.AuthState;
import org.apache.http.auth.NTCredentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.RedirectException; import org.apache.http.client.RedirectException;
import org.apache.http.client.RedirectStrategy; import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig;
@ -60,10 +62,6 @@
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
@SuppressWarnings({"boxing","static-access"}) // test code @SuppressWarnings({"boxing","static-access"}) // test code
public class TestRedirectExec { public class TestRedirectExec {
@ -240,10 +238,10 @@ public void testCrossSiteRedirect() throws Exception {
final AuthState targetAuthState = new AuthState(); final AuthState targetAuthState = new AuthState();
targetAuthState.setState(AuthProtocolState.SUCCESS); targetAuthState.setState(AuthProtocolState.SUCCESS);
targetAuthState.update(new BasicScheme(), new UsernamePasswordCredentials("user:pass")); targetAuthState.update(new BasicScheme());
final AuthState proxyAuthState = new AuthState(); final AuthState proxyAuthState = new AuthState();
proxyAuthState.setState(AuthProtocolState.SUCCESS); proxyAuthState.setState(AuthProtocolState.SUCCESS);
proxyAuthState.update(new NTLMScheme(), new NTCredentials("user:pass")); proxyAuthState.update(new NTLMScheme());
context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, targetAuthState); context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, targetAuthState);
context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState); context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState);

View File

@ -546,7 +546,7 @@ HttpRoutePlanner routePlanner = new HttpRoutePlanner() {
HttpRequest request, HttpRequest request,
HttpContext context) throws HttpException { HttpContext context) throws HttpException {
return new HttpRoute(target, null, new HttpHost("someproxy", 8080), return new HttpRoute(target, null, new HttpHost("someproxy", 8080),
"https".equalsIgnoreCase(target.getSchemeName())); "https".equalsIgnoreCase(target.getName()));
} }
}; };