001/*
002 * The contents of this file are subject to the license and copyright
003 * detailed in the LICENSE and NOTICE files at the root of the source
004 * tree.
005 */
006package org.fcrepo.client;
007
008import static org.apache.commons.lang3.StringUtils.isBlank;
009import static org.slf4j.LoggerFactory.getLogger;
010
011import java.io.IOException;
012
013import org.apache.http.HttpException;
014import org.apache.http.HttpHost;
015import org.apache.http.HttpRequest;
016import org.apache.http.HttpRequestInterceptor;
017import org.apache.http.auth.AuthScope;
018import org.apache.http.auth.AuthState;
019import org.apache.http.auth.Credentials;
020import org.apache.http.auth.UsernamePasswordCredentials;
021import org.apache.http.client.CredentialsProvider;
022import org.apache.http.client.protocol.HttpClientContext;
023import org.apache.http.impl.auth.BasicScheme;
024import org.apache.http.impl.client.BasicCredentialsProvider;
025import org.apache.http.impl.client.CloseableHttpClient;
026import org.apache.http.impl.client.HttpClients;
027import org.apache.http.protocol.HttpContext;
028import org.apache.http.protocol.HttpCoreContext;
029import org.slf4j.Logger;
030
031/**
032 * A utility class for building an httpclient for interacting with a Fedora repository
033 *
034 * @author Aaron Coburn
035 * @since March 9, 2015
036 */
037public class FcrepoHttpClientBuilder {
038
039    private String username;
040
041    private String password;
042
043    private String host;
044
045    private static final Logger LOGGER = getLogger(FcrepoHttpClientBuilder.class);
046
047    /**
048     * Create a FcrepoHttpClientBuilder object with which it is possible to create
049     * an HttpClient object
050     *
051     * @param username an optional username for authentication
052     * @param password an optional password for authentication
053     * @param host an optional realm for authentication
054     */
055    public FcrepoHttpClientBuilder(final String username, final String password, final String host) {
056        this.username = username;
057        this.password = password;
058        this.host = host;
059    }
060
061    /**
062     *  Build an HttpClient
063     *
064     *  @return an HttpClient
065     */
066    public CloseableHttpClient build() {
067
068        if (isBlank(username) || isBlank(password)) {
069            return HttpClients.createSystem();
070        } else {
071            LOGGER.debug("Accessing fcrepo with user credentials");
072
073            final CredentialsProvider credsProvider = new BasicCredentialsProvider();
074            AuthScope scope = null;
075
076            if (isBlank(host)) {
077                scope = new AuthScope(AuthScope.ANY);
078            } else {
079                scope = new AuthScope(new HttpHost(host));
080            }
081            credsProvider.setCredentials(
082                    scope,
083                    new UsernamePasswordCredentials(username, password));
084            return HttpClients.custom()
085                    .setDefaultCredentialsProvider(credsProvider)
086                    .useSystemProperties()
087                    .addInterceptorFirst(new PreemptiveAuthInterceptor())
088                    .build();
089        }
090    }
091
092    static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
093
094        public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
095            final AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
096            // If no auth scheme available yet, try to initialize it
097            // preemptively
098            if (authState.getAuthScheme() == null) {
099                final CredentialsProvider credsProvider = (CredentialsProvider)
100                        context.getAttribute(HttpClientContext.CREDS_PROVIDER);
101                final HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
102                final AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
103                final Credentials creds = credsProvider.getCredentials(authScope);
104                if (creds == null) {
105                    LOGGER.debug("Cannot initiate preemtive authentication, Credentials not found!");
106                }
107                authState.update(new BasicScheme(), creds);
108            }
109        }
110    }
111}