com.unboundid.scim.sdk.PreemptiveAuthInterceptor Maven / Gradle / Ivy
/*
* Copyright 2012-2019 Ping Identity Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPLv2 only)
* or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
package com.unboundid.scim.sdk;
import java.io.IOException;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthProtocolState;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
/**
* This class can be used to configure the Apache Http Client for preemptive
* authentication. In this mode, the client will send the basic authentication
* response even before the server gives an unauthorized response in certain
* situations. This reduces the overhead of making requests over authenticated
* connections.
*
* This behavior conforms to RFC2617: A client MAY preemptively send the
* corresponding Authorization header with requests for resources in that space
* without receipt of another challenge from the server. Similarly, when a
* client sends a request to a proxy, it may reuse a userid and password in the
* Proxy-Authorization header field without receiving another challenge from the
* proxy server.
*
* The Apache Http Client does not support preemptive authentication out of the
* box, because if misused or used incorrectly the preemptive authentication can
* lead to significant security issues, such as sending user credentials in
* clear text to an unauthorized third party.
*/
public class PreemptiveAuthInterceptor implements HttpRequestInterceptor
{
/**
* Constructs a new PreemptiveAuthInterceptor. It is important that this is
* added as the first request interceptor in the chain. You can do this
* by making sure the second parameter is zero when adding the interceptor:
*
*
* httpClient.addRequestInterceptor(new PreemptiveAuthInterceptor(), 0);
*
*/
public PreemptiveAuthInterceptor()
{
//No implementation necessary.
}
/**
* Constructs a new PreemptiveAuthInterceptor. It is important that this is
* added as the first request interceptor in the chain. You can do this
* by making sure the second parameter is zero when adding the interceptor:
*
*
* httpClient.addRequestInterceptor(
* new PreemptiveAuthInterceptor(new BasicScheme(), credentials), 0);
*
*
* NOTE: This constructor is deprecated and may be removed in a future
* release.
*
* @param authScheme The AuthScheme to use. This may not be null.
* @param credentials The Credentials to use. This may not be null.
*/
@Deprecated
public PreemptiveAuthInterceptor(final AuthScheme authScheme,
final Credentials credentials)
{
//No implementation necessary.
}
/**
* {@inheritDoc}
*/
@Override
public void process(final HttpRequest request, final HttpContext context)
throws HttpException, IOException
{
HttpHost target = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
if(target.getPort() < 0)
{
SchemeRegistry schemeRegistry = (SchemeRegistry) context.getAttribute(
ClientContext.SCHEME_REGISTRY);
Scheme scheme = schemeRegistry.getScheme(target);
target = new HttpHost(target.getHostName(),
scheme.resolvePort(target.getPort()), target.getSchemeName());
}
AuthCache authCache = (AuthCache) context.getAttribute(
ClientContext.AUTH_CACHE);
if(authCache == null)
{
authCache = new BasicAuthCache();
BasicScheme basicAuth = new BasicScheme();
authCache.put(target, basicAuth);
context.setAttribute(ClientContext.AUTH_CACHE, authCache);
return;
}
CredentialsProvider credsProvider =
(CredentialsProvider) context.getAttribute(ClientContext.CREDS_PROVIDER);
if(credsProvider == null)
{
return;
}
final AuthState targetState = (AuthState) context.getAttribute(
ClientContext.TARGET_AUTH_STATE);
if(targetState != null &&
targetState.getState() == AuthProtocolState.UNCHALLENGED)
{
final AuthScheme authScheme = authCache.get(target);
if(authScheme != null)
{
doPreemptiveAuth(target, authScheme, targetState, credsProvider);
}
}
final HttpHost proxy = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_PROXY_HOST);
final AuthState proxyState = (AuthState) context.getAttribute(
ClientContext.PROXY_AUTH_STATE);
if(proxy != null && proxyState != null &&
proxyState.getState() == AuthProtocolState.UNCHALLENGED)
{
final AuthScheme authScheme = authCache.get(proxy);
if(authScheme != null)
{
doPreemptiveAuth(proxy, authScheme, proxyState, credsProvider);
}
}
}
/**
* Method to update the AuthState in order to preemptively supply the
* credentials to the server.
*
* @param host the HttpHost which we're authenticating to
* @param authScheme the AuthScheme in use
* @param authState the AuthState object from the HttpContext
* @param credsProvider the CredentialsProvider which has the username and
* password
*/
private void doPreemptiveAuth(
final HttpHost host,
final AuthScheme authScheme,
final AuthState authState,
final CredentialsProvider credsProvider)
{
final String schemeName = authScheme.getSchemeName();
final AuthScope authScope = new AuthScope(
host, AuthScope.ANY_REALM, schemeName);
final Credentials creds = credsProvider.getCredentials(authScope);
if(creds != null)
{
if("BASIC".equalsIgnoreCase(schemeName))
{
authState.setState(AuthProtocolState.CHALLENGED);
}
else
{
authState.setState(AuthProtocolState.SUCCESS);
}
authState.update(authScheme, creds);
}
}
}