package com.lucidworks.storm.fusion;
import com.google.common.collect.Sets;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HttpContext;
import org.apache.solr.client.solrj.impl.HttpClientConfigurer;
import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.common.params.SolrParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.IOException;
import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class FusionKrb5HttpClientConfigurer extends HttpClientConfigurer {
private static final Logger logger = LoggerFactory.getLogger(FusionKrb5HttpClientConfigurer.class);
private String fusionPrincipal = null;
public static final String LOGIN_CONFIG_PROP = "java.security.auth.login.config";
public static final String LOGIN_APP_NAME = "fusion.jaas.appname";
private Configuration jaasConfig = null;
public Configuration getJaasConfig() {
return jaasConfig;
}
public static synchronized CloseableHttpClient createClient(String fusionPrincipal) {
if (logger.isDebugEnabled()) {
System.setProperty("sun.security.krb5.debug", "true");
}
if (fusionPrincipal == null) {
logger.error("fusion.user (principal) must be set in order to use kerberos!");
}
HttpClientUtil.setConfigurer(new FusionKrb5HttpClientConfigurer(fusionPrincipal));
CloseableHttpClient httpClient = HttpClientUtil.createClient(null);
HttpClientUtil.setMaxConnections(httpClient, 500);
HttpClientUtil.setMaxConnectionsPerHost(httpClient, 100);
return httpClient;
}
private HttpRequestInterceptor bufferedEntityInterceptor = new HttpRequestInterceptor() {
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
if (request instanceof HttpEntityEnclosingRequest) {
HttpEntityEnclosingRequest enclosingRequest = (HttpEntityEnclosingRequest) request;
HttpEntity requestEntity = enclosingRequest.getEntity();
enclosingRequest.setEntity(new BufferedHttpEntity(requestEntity));
}
}
};
public FusionKrb5HttpClientConfigurer(String fusionPrincipal) {
this.fusionPrincipal = fusionPrincipal;
jaasConfig = new FusionKrb5HttpClientConfigurer.FusionJaasConfiguration(fusionPrincipal);
}
public void configure(DefaultHttpClient httpClient, SolrParams config) {
super.configure(httpClient, config);
if (System.getProperty(LOGIN_CONFIG_PROP) != null) {
String configValue = System.getProperty(LOGIN_CONFIG_PROP);
if (configValue != null) {
logger.debug("Setting up kerberos auth with config: " + configValue);
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
if (fusionPrincipal != null) {
Subject subject = new Subject(false, Sets.newHashSet(new KerberosPrincipal(fusionPrincipal)),
Collections.emptySet(), Collections.emptySet());
LoginContext loginContext;
try {
loginContext = new LoginContext("", subject, null, jaasConfig);
loginContext.login();
logger.debug("Successful Fusion Login with principal: " + fusionPrincipal);
} catch (LoginException e) {
String errorMessage = "Unsuccessful Fusion Login with principal: " + fusionPrincipal;
logger.error(errorMessage, e);
throw new RuntimeException(errorMessage, e);
}
}
Configuration.setConfiguration(jaasConfig);
httpClient.getAuthSchemes().register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true, false));
Credentials useJaasCreds = new Credentials() {
public String getPassword() {
return null;
}
public Principal getUserPrincipal() {
return null;
}
};
httpClient.getCredentialsProvider().setCredentials(AuthScope.ANY, useJaasCreds);
httpClient.addRequestInterceptor(this.bufferedEntityInterceptor);
} else {
httpClient.getCredentialsProvider().clear();
}
}
}
private static class FusionJaasConfiguration extends Configuration {
private Configuration baseConfig;
private String fusionPrincipal;
private AppConfigurationEntry[] globalAppConfigurationEntry;
public FusionJaasConfiguration(String fusionPrincipal) {
this.fusionPrincipal = fusionPrincipal;
try {
this.baseConfig = Configuration.getConfiguration();
} catch (SecurityException var2) {
this.baseConfig = null;
}
if (this.baseConfig != null) {
String clientAppName = System.getProperty(LOGIN_APP_NAME, "Client");
this.globalAppConfigurationEntry = this.baseConfig.getAppConfigurationEntry(clientAppName);
}
}
private AppConfigurationEntry overwriteOptions(AppConfigurationEntry app) {
Map<String, ?> options = app.getOptions();
AppConfigurationEntry.LoginModuleControlFlag flag = app.getControlFlag();
String loginModule = app.getLoginModuleName();
// Get the current principal for debug
Object principal = options.get("principal");
if (principal != null) {
FusionKrb5HttpClientConfigurer.logger.debug(principal.getClass() + "not using" + principal);
}
// Overwriting options
Map<String, Object> overwriteOptions = new HashMap<String, Object>(options);
overwriteOptions.put("principal", fusionPrincipal);
overwriteOptions.put("doNotPrompt", "true");
return new AppConfigurationEntry(loginModule, flag, overwriteOptions);
}
public AppConfigurationEntry[] getAppConfigurationEntry(String appName) {
if (this.baseConfig == null) {
return null;
} else {
FusionKrb5HttpClientConfigurer.logger.debug("Login prop: " + System.getProperty(LOGIN_CONFIG_PROP));
if (fusionPrincipal == null) {
FusionKrb5HttpClientConfigurer.logger.debug("fusionPrincipal is null using principal from JAAS file.");
return globalAppConfigurationEntry;
}
if (globalAppConfigurationEntry == null) {
return null;
}
// Must be only one Entry, if more use the first one.
return new AppConfigurationEntry[]{overwriteOptions(globalAppConfigurationEntry[0])};
}
}
}
}