
com.joyent.http.signature.apache.httpclient.HttpSignatureAuthenticationStrategy Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2016-2017, Joyent, Inc. All rights reserved.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.joyent.http.signature.apache.httpclient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthOption;
import org.apache.http.auth.AuthProtocolState;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.client.AuthCache;
import org.apache.http.client.AuthenticationStrategy;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Lookup;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.protocol.HttpContext;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
/**
* {@link AuthenticationStrategy} implementation that allows the Apache HTTP
* Client to authenticate via the HTTP Signature scheme.
*
* @author Elijah Zupancic
* @since 2.0.3
*/
public class HttpSignatureAuthenticationStrategy implements AuthenticationStrategy {
/**
* The static logger instance.
*/
private static final Log LOG = LogFactory.getLog(HttpSignatureAuthScheme.class);
/**
* Immutable list of challenges with a single dummy value because for our purposes
* all that matters is that this map is not empty.
*/
private final Map challenges = Collections.singletonMap(null, null);
/**
* AuthOption that always returns {@link HttpSignatureAuthScheme}.
*/
private final AuthOption authOption;
/**
* Create a new instance using a provider found via a {@link Lookup}.
*
* @param authSchemeProviderLookup Lookup that will return an {@link AuthScheme}
* when asked for "Signatures"
* @param credentials credentials containing the HTTP Signature username
*/
public HttpSignatureAuthenticationStrategy(
final Lookup authSchemeProviderLookup,
final Credentials credentials) {
this(authSchemeProviderLookup.lookup("Signatures").create(null), credentials);
}
/**
* Creates a new instance using the passed authentication scheme.
*
* @param authScheme authentication scheme to use to authenticate
* requests (expecting {@link HttpSignatureAuthScheme})
* @param credentials credentials containing the HTTP Signature username
*/
public HttpSignatureAuthenticationStrategy(final AuthScheme authScheme,
final Credentials credentials) {
this.authOption = new AuthOption(authScheme, credentials);
}
/**
* Determines if the given HTTP response response represents
* an authentication challenge that was sent back as a result
* of authentication failure.
*
* @param authHost authentication host.
* @param response HTTP response.
* @param context HTTP context.
* @return {@code true} if user authentication is required,
* {@code false} otherwise.
*/
@Override
public boolean isAuthenticationRequested(final HttpHost authHost,
final HttpResponse response,
final HttpContext context) {
final StatusLine line = response.getStatusLine();
final int code = line.getStatusCode();
final HttpClientContext clientContext = HttpClientContext.adapt(context);
final AuthState authState = clientContext.getTargetAuthState();
final AuthProtocolState authProtocolState = authState.getState();
if (code == HttpStatus.SC_UNAUTHORIZED) {
if (authProtocolState.equals(AuthProtocolState.CHALLENGED)) {
clientContext.getTargetAuthState().setState(AuthProtocolState.FAILURE);
authFailed(authHost, authState.getAuthScheme(), context);
}
return true;
}
return clientContext.getTargetAuthState() == null;
}
@Override
public Map getChallenges(final HttpHost authhost,
final HttpResponse response,
final HttpContext context)
throws MalformedChallengeException {
/* Unfortunately, we have to abuse the challenge functionality in
* because it won't enabled authentication unless at least a single
* challenge is available. The HTTP response for a HTTP Signatures
* API will never contain a challenge header, so effectively any
* headers that are added will never match. */
return this.challenges;
}
@Override
public Queue select(final Map challengeHeaders,
final HttpHost authhost,
final HttpResponse response,
final HttpContext context) {
final HttpClientContext httpClientContext = HttpClientContext.adapt(context);
final AuthState state = httpClientContext.getTargetAuthState();
final Queue queue = new ArrayDeque<>();
if (state == null || !state.getState().equals(AuthProtocolState.CHALLENGED)) {
queue.add(authOption);
} else {
System.out.println("does this happen?");
}
return queue;
}
@Override
public void authSucceeded(final HttpHost authhost,
final AuthScheme authScheme,
final HttpContext context) {
Objects.requireNonNull(authhost, "Authentication host must be present");
Objects.requireNonNull(authScheme, "Authentication scheme must be present");
Objects.requireNonNull(context, "HTTP context must be present");
LOG.debug("HTTP Signature authentication succeeded");
final HttpClientContext clientContext = HttpClientContext.adapt(context);
AuthCache authCache = clientContext.getAuthCache();
if (authCache == null) {
authCache = new BasicAuthCache();
clientContext.setAuthCache(authCache);
}
if (LOG.isDebugEnabled()) {
LOG.debug("Caching '" + authScheme.getSchemeName()
+ "' auth scheme for " + authhost);
}
authCache.put(authhost, authScheme);
}
@Override
public void authFailed(final HttpHost authhost,
final AuthScheme authScheme,
final HttpContext context) {
Objects.requireNonNull(authhost, "Authentication host must be present");
Objects.requireNonNull(context, "HTTP context must be present");
LOG.debug("HTTP Signature authentication failed");
final HttpClientContext clientContext = HttpClientContext.adapt(context);
final AuthCache authCache = clientContext.getAuthCache();
if (authCache != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Clearing cached auth scheme for " + authhost);
}
authCache.remove(authhost);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy