You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by gc...@apache.org on 2016/08/02 23:42:36 UTC
lucene-solr:master: SOLR-9324: Support Secure Impersonation / Proxy
User for solr authentication
Repository: lucene-solr
Updated Branches:
refs/heads/master b3505298a -> e50858c31
SOLR-9324: Support Secure Impersonation / Proxy User for solr authentication
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/e50858c3
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/e50858c3
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/e50858c3
Branch: refs/heads/master
Commit: e50858c314a138e2c2ced50bee9a5c2754929f8b
Parents: b350529
Author: Gregory Chanan <gc...@cloudera.com>
Authored: Mon Jul 25 14:15:48 2016 -0400
Committer: Gregory Chanan <gc...@cloudera.com>
Committed: Tue Aug 2 19:42:10 2016 -0400
----------------------------------------------------------------------
solr/CHANGES.txt | 3 +
.../security/DelegationTokenKerberosFilter.java | 46 ++-
.../apache/solr/security/KerberosPlugin.java | 205 ++++++-----
.../TestSolrCloudWithDelegationTokens.java | 9 +-
.../TestSolrCloudWithSecureImpersonation.java | 340 +++++++++++++++++++
...ramDelegationTokenAuthenticationHandler.java | 109 ------
.../HttpParamDelegationTokenPlugin.java | 272 +++++++++++++++
7 files changed, 776 insertions(+), 208 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e50858c3/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index c2a69fd..538edce 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -111,6 +111,9 @@ New Features
* SOLR-9279: New boolean comparison function queries comparing numeric arguments: gt, gte, lt, lte, eq
(Doug Turnbull, David Smiley)
+* SOLR-9346: Support Secure Impersonation / Proxy User for solr authentication
+ (Gregory Chanan)
+
Bug Fixes
----------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e50858c3/solr/core/src/java/org/apache/solr/security/DelegationTokenKerberosFilter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/security/DelegationTokenKerberosFilter.java b/solr/core/src/java/org/apache/solr/security/DelegationTokenKerberosFilter.java
index 7dbb1ad..ca27861 100644
--- a/solr/core/src/java/org/apache/solr/security/DelegationTokenKerberosFilter.java
+++ b/solr/core/src/java/org/apache/solr/security/DelegationTokenKerberosFilter.java
@@ -18,6 +18,7 @@ package org.apache.solr.security;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
+import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
@@ -36,8 +37,11 @@ import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.ACLProvider;
import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authentication.server.AuthenticationHandler;
import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticationFilter;
+import org.apache.hadoop.security.token.delegation.web.HttpUserGroupInformation;
import org.apache.solr.common.cloud.SecurityAwareZkACLProvider;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkACLProvider;
@@ -62,6 +66,27 @@ public class DelegationTokenKerberosFilter extends DelegationTokenAuthentication
super.init(conf);
}
+ /**
+ * Return the ProxyUser Configuration. FilterConfig properties beginning with
+ * "solr.impersonator.user.name" will be added to the configuration.
+ */
+ @Override
+ protected Configuration getProxyuserConfiguration(FilterConfig filterConf)
+ throws ServletException {
+ Configuration conf = new Configuration(false);
+
+ Enumeration<?> names = filterConf.getInitParameterNames();
+ while (names.hasMoreElements()) {
+ String name = (String) names.nextElement();
+ if (name.startsWith(KerberosPlugin.IMPERSONATOR_PREFIX)) {
+ String value = filterConf.getInitParameter(name);
+ conf.set(PROXYUSER_PREFIX + "." + name.substring(KerberosPlugin.IMPERSONATOR_PREFIX.length()), value);
+ conf.set(name, value);
+ }
+ }
+ return conf;
+ }
+
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
@@ -76,7 +101,26 @@ public class DelegationTokenKerberosFilter extends DelegationTokenAuthentication
return nonNullQueryString;
}
};
- super.doFilter(requestNonNullQueryString, response, filterChain);
+
+ // include Impersonator User Name in case someone (e.g. logger) wants it
+ FilterChain filterChainWrapper = new FilterChain() {
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws IOException, ServletException {
+ HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
+
+ UserGroupInformation ugi = HttpUserGroupInformation.get();
+ if (ugi != null && ugi.getAuthenticationMethod() == UserGroupInformation.AuthenticationMethod.PROXY) {
+ UserGroupInformation realUserUgi = ugi.getRealUser();
+ if (realUserUgi != null) {
+ httpRequest.setAttribute(KerberosPlugin.IMPERSONATOR_USER_NAME, realUserUgi.getShortUserName());
+ }
+ }
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ };
+
+ super.doFilter(requestNonNullQueryString, response, filterChainWrapper);
}
@Override
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e50858c3/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java b/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java
index 1cd476f..d4a2823 100644
--- a/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java
+++ b/solr/core/src/java/org/apache/solr/security/KerberosPlugin.java
@@ -69,7 +69,7 @@ public class KerberosPlugin extends AuthenticationPlugin implements HttpClientBu
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Krb5HttpClientBuilder kerberosBuilder = new Krb5HttpClientBuilder();
- Filter kerberosFilter;
+ private Filter kerberosFilter;
public static final String NAME_RULES_PARAM = "solr.kerberos.name.rules";
public static final String COOKIE_DOMAIN_PARAM = "solr.kerberos.cookie.domain";
@@ -78,6 +78,7 @@ public class KerberosPlugin extends AuthenticationPlugin implements HttpClientBu
public static final String KEYTAB_PARAM = "solr.kerberos.keytab";
public static final String TOKEN_VALID_PARAM = "solr.kerberos.token.valid";
public static final String COOKIE_PORT_AWARE_PARAM = "solr.kerberos.cookie.portaware";
+ public static final String IMPERSONATOR_PREFIX = "solr.kerberos.impersonator.user.";
public static final String DELEGATION_TOKEN_ENABLED = "solr.kerberos.delegation.token.enabled";
public static final String DELEGATION_TOKEN_KIND = "solr.kerberos.delegation.token.kind";
public static final String DELEGATION_TOKEN_VALIDITY = "solr.kerberos.delegation.token.validity";
@@ -86,18 +87,17 @@ public class KerberosPlugin extends AuthenticationPlugin implements HttpClientBu
"solr.kerberos.delegation.token.signer.secret.provider.zookeper.path";
public static final String DELEGATION_TOKEN_SECRET_MANAGER_ZNODE_WORKING_PATH =
"solr.kerberos.delegation.token.secret.manager.znode.working.path";
+
public static final String DELEGATION_TOKEN_TYPE_DEFAULT = "solr-dt";
-
+ public static final String IMPERSONATOR_DO_AS_HTTP_PARAM = "doAs";
+ public static final String IMPERSONATOR_USER_NAME = "solr.impersonator.user.name";
+
// filled in by Plugin/Filter
static final String REQUEST_CONTINUES_ATTR =
"org.apache.solr.security.kerberosplugin.requestcontinues";
static final String DELEGATION_TOKEN_ZK_CLIENT =
"solr.kerberos.delegation.token.zk.client";
- // allows test to specify an alternate auth handler
- @VisibleForTesting
- public static final String AUTH_HANDLER_PARAM = "solr.kerberos.auth.handler";
-
private final CoreContainer coreContainer;
public KerberosPlugin(CoreContainer coreContainer) {
@@ -107,105 +107,121 @@ public class KerberosPlugin extends AuthenticationPlugin implements HttpClientBu
@Override
public void init(Map<String, Object> pluginConfig) {
try {
- Map<String, String> params = new HashMap();
- putParam(params, "type", AUTH_HANDLER_PARAM, "kerberos");
- putParam(params, "kerberos.name.rules", NAME_RULES_PARAM, "DEFAULT");
- putParam(params, "token.valid", TOKEN_VALID_PARAM, "30");
- putParam(params, "cookie.path", COOKIE_PATH_PARAM, "/");
- if ("kerberos".equals(params.get("type"))) {
- putParam(params, "kerberos.principal", PRINCIPAL_PARAM, null);
- putParam(params, "kerberos.keytab", KEYTAB_PARAM, null);
- } else {
- // allow tests which specify AUTH_HANDLER_PARAM to avoid specifying kerberos principal/keytab
- putParamOptional(params, "kerberos.principal", PRINCIPAL_PARAM);
- putParamOptional(params, "kerberos.keytab", KEYTAB_PARAM);
- }
+ FilterConfig conf = getInitFilterConfig(pluginConfig, false);
+ kerberosFilter.init(conf);
+ } catch (ServletException e) {
+ throw new SolrException(ErrorCode.SERVER_ERROR, "Error initializing kerberos authentication plugin: "+e);
+ }
+ }
- String delegationTokenStr = System.getProperty(DELEGATION_TOKEN_ENABLED, null);
- boolean delegationTokenEnabled =
- (delegationTokenStr == null) ? false : Boolean.parseBoolean(delegationTokenStr);
- ZkController controller = coreContainer.getZkController();
-
- if (delegationTokenEnabled) {
- putParam(params, "delegation-token.token-kind", DELEGATION_TOKEN_KIND, DELEGATION_TOKEN_TYPE_DEFAULT);
- if (coreContainer.isZooKeeperAware()) {
- putParam(params, "signer.secret.provider", DELEGATION_TOKEN_SECRET_PROVIDER, "zookeeper");
- if ("zookeeper".equals(params.get("signer.secret.provider"))) {
- String zkHost = controller.getZkServerAddress();
- putParam(params, "token.validity", DELEGATION_TOKEN_VALIDITY, "36000");
- params.put("zk-dt-secret-manager.enable", "true");
- // Note - Curator complains if the znodeWorkingPath starts with /
- String chrootPath = zkHost.substring(zkHost.indexOf("/"));
- String relativePath = chrootPath.startsWith("/") ? chrootPath.substring(1) : chrootPath;
- putParam(params, "zk-dt-secret-manager.znodeWorkingPath",
- DELEGATION_TOKEN_SECRET_MANAGER_ZNODE_WORKING_PATH,
- relativePath + SecurityAwareZkACLProvider.SECURITY_ZNODE_PATH + "/zkdtsm");
- putParam(params, "signer.secret.provider.zookeeper.path",
- DELEGATION_TOKEN_SECRET_PROVIDER_ZK_PATH, "/token");
- // ensure krb5 is setup properly before running curator
- getHttpClientBuilder(SolrHttpClientBuilder.create());
- }
- } else {
- log.info("CoreContainer is not ZooKeeperAware, not setting ZK-related delegation token properties");
+ @VisibleForTesting
+ protected FilterConfig getInitFilterConfig(Map<String, Object> pluginConfig, boolean skipKerberosChecking) {
+ Map<String, String> params = new HashMap();
+ params.put("type", "kerberos");
+ putParam(params, "kerberos.name.rules", NAME_RULES_PARAM, "DEFAULT");
+ putParam(params, "token.valid", TOKEN_VALID_PARAM, "30");
+ putParam(params, "cookie.path", COOKIE_PATH_PARAM, "/");
+ if (!skipKerberosChecking) {
+ putParam(params, "kerberos.principal", PRINCIPAL_PARAM, null);
+ putParam(params, "kerberos.keytab", KEYTAB_PARAM, null);
+ } else {
+ putParamOptional(params, "kerberos.principal", PRINCIPAL_PARAM);
+ putParamOptional(params, "kerberos.keytab", KEYTAB_PARAM);
+ }
+
+ String delegationTokenStr = System.getProperty(DELEGATION_TOKEN_ENABLED, null);
+ boolean delegationTokenEnabled =
+ (delegationTokenStr == null) ? false : Boolean.parseBoolean(delegationTokenStr);
+ ZkController controller = coreContainer.getZkController();
+
+ if (delegationTokenEnabled) {
+ putParam(params, "delegation-token.token-kind", DELEGATION_TOKEN_KIND, DELEGATION_TOKEN_TYPE_DEFAULT);
+ if (coreContainer.isZooKeeperAware()) {
+ putParam(params, "signer.secret.provider", DELEGATION_TOKEN_SECRET_PROVIDER, "zookeeper");
+ if ("zookeeper".equals(params.get("signer.secret.provider"))) {
+ String zkHost = controller.getZkServerAddress();
+ putParam(params, "token.validity", DELEGATION_TOKEN_VALIDITY, "36000");
+ params.put("zk-dt-secret-manager.enable", "true");
+ // Note - Curator complains if the znodeWorkingPath starts with /
+ String chrootPath = zkHost.substring(zkHost.indexOf("/"));
+ String relativePath = chrootPath.startsWith("/") ? chrootPath.substring(1) : chrootPath;
+ putParam(params, "zk-dt-secret-manager.znodeWorkingPath",
+ DELEGATION_TOKEN_SECRET_MANAGER_ZNODE_WORKING_PATH,
+ relativePath + SecurityAwareZkACLProvider.SECURITY_ZNODE_PATH + "/zkdtsm");
+ putParam(params, "signer.secret.provider.zookeeper.path",
+ DELEGATION_TOKEN_SECRET_PROVIDER_ZK_PATH, "/token");
+ // ensure krb5 is setup properly before running curator
+ getHttpClientBuilder(SolrHttpClientBuilder.create());
}
+ } else {
+ log.info("CoreContainer is not ZooKeeperAware, not setting ZK-related delegation token properties");
}
+ }
- // Special handling for the "cookie.domain" based on whether port should be
- // appended to the domain. Useful for situations where multiple solr nodes are
- // on the same host.
- String usePortStr = System.getProperty(COOKIE_PORT_AWARE_PARAM, null);
- boolean needPortAwareCookies = (usePortStr == null) ? false: Boolean.parseBoolean(usePortStr);
-
- if (!needPortAwareCookies || !coreContainer.isZooKeeperAware()) {
- putParam(params, "cookie.domain", COOKIE_DOMAIN_PARAM, null);
- } else { // we need port aware cookies and we are in SolrCloud mode.
- String host = System.getProperty(COOKIE_DOMAIN_PARAM, null);
- if (host==null) {
- throw new SolrException(ErrorCode.SERVER_ERROR, "Missing required parameter '"+COOKIE_DOMAIN_PARAM+"'.");
- }
- int port = controller.getHostPort();
- params.put("cookie.domain", host + ":" + port);
+ // Special handling for the "cookie.domain" based on whether port should be
+ // appended to the domain. Useful for situations where multiple solr nodes are
+ // on the same host.
+ String usePortStr = System.getProperty(COOKIE_PORT_AWARE_PARAM, null);
+ boolean needPortAwareCookies = (usePortStr == null) ? false: Boolean.parseBoolean(usePortStr);
+
+ if (!needPortAwareCookies || !coreContainer.isZooKeeperAware()) {
+ putParam(params, "cookie.domain", COOKIE_DOMAIN_PARAM, null);
+ } else { // we need port aware cookies and we are in SolrCloud mode.
+ String host = System.getProperty(COOKIE_DOMAIN_PARAM, null);
+ if (host==null) {
+ throw new SolrException(ErrorCode.SERVER_ERROR, "Missing required parameter '"+COOKIE_DOMAIN_PARAM+"'.");
}
+ int port = controller.getHostPort();
+ params.put("cookie.domain", host + ":" + port);
+ }
- final ServletContext servletContext = new AttributeOnlyServletContext();
- if (delegationTokenEnabled) {
- kerberosFilter = new DelegationTokenKerberosFilter();
- // pass an attribute-enabled context in order to pass the zkClient
- // and because the filter may pass a curator instance.
- if (controller != null) {
- servletContext.setAttribute(DELEGATION_TOKEN_ZK_CLIENT, controller.getZkClient());
+ // check impersonator config
+ for (Enumeration e = System.getProperties().propertyNames(); e.hasMoreElements();) {
+ String key = e.nextElement().toString();
+ if (key.startsWith(IMPERSONATOR_PREFIX)) {
+ if (!delegationTokenEnabled) {
+ throw new SolrException(ErrorCode.SERVER_ERROR,
+ "Impersonator configuration requires delegation tokens to be enabled: " + key);
}
- } else {
- kerberosFilter = new KerberosFilter();
+ params.put(key, System.getProperty(key));
}
- log.info("Params: "+params);
+ }
+ final ServletContext servletContext = new AttributeOnlyServletContext();
+ if (controller != null) {
+ servletContext.setAttribute(DELEGATION_TOKEN_ZK_CLIENT, controller.getZkClient());
+ }
+ if (delegationTokenEnabled) {
+ kerberosFilter = new DelegationTokenKerberosFilter();
+ // pass an attribute-enabled context in order to pass the zkClient
+ // and because the filter may pass a curator instance.
+ } else {
+ kerberosFilter = new KerberosFilter();
+ }
+ log.info("Params: "+params);
- FilterConfig conf = new FilterConfig() {
- @Override
- public ServletContext getServletContext() {
- return servletContext;
- }
+ FilterConfig conf = new FilterConfig() {
+ @Override
+ public ServletContext getServletContext() {
+ return servletContext;
+ }
- @Override
- public Enumeration<String> getInitParameterNames() {
- return new IteratorEnumeration(params.keySet().iterator());
- }
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return new IteratorEnumeration(params.keySet().iterator());
+ }
- @Override
- public String getInitParameter(String param) {
- return params.get(param);
- }
+ @Override
+ public String getInitParameter(String param) {
+ return params.get(param);
+ }
- @Override
- public String getFilterName() {
- return "KerberosFilter";
- }
- };
+ @Override
+ public String getFilterName() {
+ return "KerberosFilter";
+ }
+ };
- kerberosFilter.init(conf);
- } catch (ServletException e) {
- throw new SolrException(ErrorCode.SERVER_ERROR, "Error initializing kerberos authentication plugin: "+e);
- }
+ return conf;
}
private void putParam(Map<String, String> params, String internalParamName, String externalParamName, String defaultValue) {
@@ -260,11 +276,16 @@ public class KerberosPlugin extends AuthenticationPlugin implements HttpClientBu
return kerberosBuilder.getBuilder(builder);
}
+ @Override
public void close() {
kerberosFilter.destroy();
kerberosBuilder.close();
}
+ protected Filter getKerberosFilter() { return kerberosFilter; }
+
+ protected void setKerberosFilter(Filter kerberosFilter) { this.kerberosFilter = kerberosFilter; }
+
protected static class AttributeOnlyServletContext implements ServletContext {
private Map<String, Object> attributes = new HashMap<String, Object>();
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e50858c3/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithDelegationTokens.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithDelegationTokens.java b/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithDelegationTokens.java
index ae1c439..a58ec8c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithDelegationTokens.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithDelegationTokens.java
@@ -30,10 +30,10 @@ import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.params.ModifiableSolrParams;
-import static org.apache.solr.security.HttpParamDelegationTokenAuthenticationHandler.USER_PARAM;
+import static org.apache.solr.security.HttpParamDelegationTokenPlugin.USER_PARAM;
import org.apache.http.HttpStatus;
-import org.apache.solr.security.HttpParamDelegationTokenAuthenticationHandler;
+import org.apache.solr.security.HttpParamDelegationTokenPlugin;
import org.apache.solr.security.KerberosPlugin;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -59,10 +59,8 @@ public class TestSolrCloudWithDelegationTokens extends SolrTestCaseJ4 {
@BeforeClass
public static void startup() throws Exception {
- System.setProperty("authenticationPlugin", KerberosPlugin.class.getName());
+ System.setProperty("authenticationPlugin", HttpParamDelegationTokenPlugin.class.getName());
System.setProperty(KerberosPlugin.DELEGATION_TOKEN_ENABLED, "true");
- System.setProperty(KerberosPlugin.AUTH_HANDLER_PARAM,
- HttpParamDelegationTokenAuthenticationHandler.class.getName());
System.setProperty("solr.kerberos.cookie.domain", "127.0.0.1");
miniCluster = new MiniSolrCloudCluster(NUM_SERVERS, createTempDir(), buildJettyConfig("/solr"));
@@ -88,7 +86,6 @@ public class TestSolrCloudWithDelegationTokens extends SolrTestCaseJ4 {
solrClientSecondary = null;
System.clearProperty("authenticationPlugin");
System.clearProperty(KerberosPlugin.DELEGATION_TOKEN_ENABLED);
- System.clearProperty(KerberosPlugin.AUTH_HANDLER_PARAM);
System.clearProperty("solr.kerberos.cookie.domain");
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e50858c3/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithSecureImpersonation.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithSecureImpersonation.java b/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithSecureImpersonation.java
new file mode 100644
index 0000000..1839d32
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/TestSolrCloudWithSecureImpersonation.java
@@ -0,0 +1,340 @@
+/*
+ * 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.
+ */
+package org.apache.solr.cloud;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.embedded.JettySolrRunner;
+import org.apache.solr.client.solrj.impl.HttpSolrClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.handler.admin.CollectionsHandler;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.HttpParamDelegationTokenPlugin;
+import org.apache.solr.security.KerberosPlugin;
+import org.apache.solr.servlet.SolrRequestParsers;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.apache.solr.security.HttpParamDelegationTokenPlugin.USER_PARAM;
+import static org.apache.solr.security.HttpParamDelegationTokenPlugin.REMOTE_HOST_PARAM;
+import static org.apache.solr.security.HttpParamDelegationTokenPlugin.REMOTE_ADDRESS_PARAM;
+
+public class TestSolrCloudWithSecureImpersonation extends SolrTestCaseJ4 {
+ private static final int NUM_SERVERS = 2;
+ private static MiniSolrCloudCluster miniCluster;
+ private static SolrClient solrClient;
+
+ private static String getUsersFirstGroup() throws Exception {
+ org.apache.hadoop.security.Groups hGroups =
+ new org.apache.hadoop.security.Groups(new Configuration());
+ List<String> g = hGroups.getGroups(System.getProperty("user.name"));
+ return g.get(0);
+ }
+
+ private static Map<String, String> getImpersonatorSettings() throws Exception {
+ Map<String, String> filterProps = new TreeMap<String, String>();
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "noGroups.hosts", "*");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "anyHostAnyUser.groups", "*");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "anyHostAnyUser.hosts", "*");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "wrongHost.hosts", "1.1.1.1.1.1");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "wrongHost.groups", "*");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "noHosts.groups", "*");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "localHostAnyGroup.groups", "*");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "localHostAnyGroup.hosts", "127.0.0.1");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "anyHostUsersGroup.groups", getUsersFirstGroup());
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "anyHostUsersGroup.hosts", "*");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "bogusGroup.groups", "__some_bogus_group");
+ filterProps.put(KerberosPlugin.IMPERSONATOR_PREFIX + "bogusGroup.hosts", "*");
+ return filterProps;
+ }
+
+ @BeforeClass
+ public static void startup() throws Exception {
+ System.setProperty("authenticationPlugin", HttpParamDelegationTokenPlugin.class.getName());
+ System.setProperty(KerberosPlugin.DELEGATION_TOKEN_ENABLED, "true");
+
+ System.setProperty("solr.kerberos.cookie.domain", "127.0.0.1");
+ Map<String, String> impSettings = getImpersonatorSettings();
+ for (Map.Entry<String, String> entry : impSettings.entrySet()) {
+ System.setProperty(entry.getKey(), entry.getValue());
+ }
+ System.setProperty("solr.test.sys.prop1", "propone");
+ System.setProperty("solr.test.sys.prop2", "proptwo");
+
+ SolrRequestParsers.DEFAULT.setAddRequestHeadersToContext(true);
+ String solrXml = MiniSolrCloudCluster.DEFAULT_CLOUD_SOLR_XML.replace("</solr>",
+ " <str name=\"collectionsHandler\">" + ImpersonatorCollectionsHandler.class.getName() + "</str>\n" +
+ "</solr>");
+
+ miniCluster = new MiniSolrCloudCluster(NUM_SERVERS, createTempDir(), solrXml, buildJettyConfig("/solr"));
+ JettySolrRunner runner = miniCluster.getJettySolrRunners().get(0);
+ solrClient = new HttpSolrClient.Builder(runner.getBaseUrl().toString()).build();
+ }
+
+ /**
+ * Verify that impersonator info is preserved in the request
+ */
+ public static class ImpersonatorCollectionsHandler extends CollectionsHandler {
+ public static AtomicBoolean called = new AtomicBoolean(false);
+
+ public ImpersonatorCollectionsHandler() { super(); }
+
+ public ImpersonatorCollectionsHandler(final CoreContainer coreContainer) {
+ super(coreContainer);
+ }
+
+ @Override
+ public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+ called.set(true);
+ super.handleRequestBody(req, rsp);
+ String doAs = req.getParams().get(KerberosPlugin.IMPERSONATOR_DO_AS_HTTP_PARAM);
+ if (doAs != null) {
+ HttpServletRequest httpRequest = (HttpServletRequest)req.getContext().get("httpRequest");
+ assertNotNull(httpRequest);
+ String user = (String)httpRequest.getAttribute(USER_PARAM);
+ assertNotNull(user);
+ assertEquals(user, httpRequest.getAttribute(KerberosPlugin.IMPERSONATOR_USER_NAME));
+ }
+ }
+ }
+
+ @AfterClass
+ public static void shutdown() throws Exception {
+ if (miniCluster != null) {
+ miniCluster.shutdown();
+ }
+ miniCluster = null;
+ solrClient.close();
+ solrClient = null;
+ System.clearProperty("authenticationPlugin");
+ System.clearProperty(KerberosPlugin.DELEGATION_TOKEN_ENABLED);
+ System.clearProperty("solr.kerberos.cookie.domain");
+ Map<String, String> impSettings = getImpersonatorSettings();
+ for (Map.Entry<String, String> entry : impSettings.entrySet()) {
+ System.clearProperty(entry.getKey());
+ }
+ System.clearProperty("solr.test.sys.prop1");
+ System.clearProperty("solr.test.sys.prop2");
+ SolrRequestParsers.DEFAULT.setAddRequestHeadersToContext(false);
+ }
+
+ private void create1ShardCollection(String name, String config, MiniSolrCloudCluster solrCluster) throws Exception {
+ CollectionAdminResponse response;
+ CollectionAdminRequest.Create create = new CollectionAdminRequest.Create() {
+ @Override
+ public SolrParams getParams() {
+ ModifiableSolrParams msp = new ModifiableSolrParams(super.getParams());
+ msp.set(USER_PARAM, "user");
+ return msp;
+ }
+ };
+ create.setConfigName(config);
+ create.setCollectionName(name);
+ create.setNumShards(1);
+ create.setReplicationFactor(1);
+ create.setMaxShardsPerNode(1);
+ response = create.process(solrCluster.getSolrClient());
+
+ if (response.getStatus() != 0 || response.getErrorMessages() != null) {
+ fail("Could not create collection. Response" + response.toString());
+ }
+ ZkStateReader zkStateReader = solrCluster.getSolrClient().getZkStateReader();
+ AbstractDistribZkTestBase.waitForRecoveriesToFinish(name, zkStateReader, false, true, 100);
+ }
+
+ private SolrRequest getProxyRequest(String user, String doAs) {
+ return getProxyRequest(user, doAs, null);
+ }
+
+ private SolrRequest getProxyRequest(String user, String doAs, String remoteHost) {
+ return getProxyRequest(user, doAs, remoteHost, null);
+ }
+
+ private SolrRequest getProxyRequest(String user, String doAs, String remoteHost, String remoteAddress) {
+ return new CollectionAdminRequest.List() {
+ @Override
+ public SolrParams getParams() {
+ ModifiableSolrParams params = new ModifiableSolrParams(super.getParams());
+ params.set(USER_PARAM, user);
+ params.set(KerberosPlugin.IMPERSONATOR_DO_AS_HTTP_PARAM, doAs);
+ if (remoteHost != null) params.set(REMOTE_HOST_PARAM, remoteHost);
+ if (remoteAddress != null) params.set(REMOTE_ADDRESS_PARAM, remoteAddress);
+ return params;
+ }
+ };
+ }
+
+ private String getExpectedGroupExMsg(String user, String doAs) {
+ return "User: " + user + " is not allowed to impersonate " + doAs;
+ }
+
+ private String getExpectedHostExMsg(String user) {
+ return "Unauthorized connection for super-user: " + user;
+ }
+
+ @Test
+ public void testProxyNoConfigGroups() throws Exception {
+ try {
+ solrClient.request(getProxyRequest("noGroups","bar"));
+ fail("Expected RemoteSolrException");
+ }
+ catch (HttpSolrClient.RemoteSolrException ex) {
+ assertTrue(ex.getMessage().contains(getExpectedGroupExMsg("noGroups", "bar")));
+ }
+ }
+
+ @Test
+ public void testProxyWrongHost() throws Exception {
+ try {
+ solrClient.request(getProxyRequest("wrongHost","bar"));
+ fail("Expected RemoteSolrException");
+ }
+ catch (HttpSolrClient.RemoteSolrException ex) {
+ assertTrue(ex.getMessage().contains(getExpectedHostExMsg("wrongHost")));
+ }
+ }
+
+ @Test
+ public void testProxyNoConfigHosts() throws Exception {
+ try {
+ solrClient.request(getProxyRequest("noHosts","bar"));
+ fail("Expected RemoteSolrException");
+ }
+ catch (HttpSolrClient.RemoteSolrException ex) {
+ // FixMe: this should return an exception about the host being invalid,
+ // but a bug (HADOOP-11077) causes an NPE instead.
+ //assertTrue(ex.getMessage().contains(getExpectedHostExMsg("noHosts")));
+ }
+ }
+
+ @Test
+ public void testProxyValidateAnyHostAnyUser() throws Exception {
+ solrClient.request(getProxyRequest("anyHostAnyUser", "bar", null));
+ assertTrue(ImpersonatorCollectionsHandler.called.get());
+ }
+
+ @Test
+ public void testProxyInvalidProxyUser() throws Exception {
+ try {
+ // wrong direction, should fail
+ solrClient.request(getProxyRequest("bar","anyHostAnyUser"));
+ fail("Expected RemoteSolrException");
+ }
+ catch (HttpSolrClient.RemoteSolrException ex) {
+ assertTrue(ex.getMessage().contains(getExpectedGroupExMsg("bar", "anyHostAnyUser")));
+ }
+ }
+
+ @Test
+ public void testProxyValidateHost() throws Exception {
+ solrClient.request(getProxyRequest("localHostAnyGroup", "bar"));
+ assertTrue(ImpersonatorCollectionsHandler.called.get());
+ }
+
+
+
+ @Test
+ public void testProxyValidateGroup() throws Exception {
+ solrClient.request(getProxyRequest("anyHostUsersGroup", System.getProperty("user.name"), null));
+ assertTrue(ImpersonatorCollectionsHandler.called.get());
+ }
+
+ @Test
+ public void testProxyUnknownRemote() throws Exception {
+ try {
+ // Use a reserved ip address
+ String nonProxyUserConfiguredIpAddress = "255.255.255.255";
+ solrClient.request(getProxyRequest("localHostAnyGroup", "bar", "unknownhost.bar.foo", nonProxyUserConfiguredIpAddress));
+ fail("Expected RemoteSolrException");
+ }
+ catch (HttpSolrClient.RemoteSolrException ex) {
+ assertTrue(ex.getMessage().contains(getExpectedHostExMsg("localHostAnyGroup")));
+ }
+ }
+
+ @Test
+ public void testProxyInvalidRemote() throws Exception {
+ try {
+ String invalidIpAddress = "-127.-128";
+ solrClient.request(getProxyRequest("localHostAnyGroup","bar", "[ff01::114]", invalidIpAddress));
+ fail("Expected RemoteSolrException");
+ }
+ catch (HttpSolrClient.RemoteSolrException ex) {
+ assertTrue(ex.getMessage().contains(getExpectedHostExMsg("localHostAnyGroup")));
+ }
+ }
+
+ @Test
+ public void testProxyInvalidGroup() throws Exception {
+ try {
+ solrClient.request(getProxyRequest("bogusGroup","bar", null));
+ fail("Expected RemoteSolrException");
+ }
+ catch (HttpSolrClient.RemoteSolrException ex) {
+ assertTrue(ex.getMessage().contains(getExpectedGroupExMsg("bogusGroup", "bar")));
+ }
+ }
+
+ @Test
+ public void testProxyNullProxyUser() throws Exception {
+ try {
+ solrClient.request(getProxyRequest("","bar"));
+ fail("Expected RemoteSolrException");
+ }
+ catch (HttpSolrClient.RemoteSolrException ex) {
+ // this exception is specific to our implementation, don't check a specific message.
+ }
+ }
+
+ @Test
+ public void testForwarding() throws Exception {
+ String collectionName = "forwardingCollection";
+ File configDir = getFile("solr").toPath().resolve("collection1/conf").toFile();
+ miniCluster.uploadConfigDir(configDir, "conf1");
+ create1ShardCollection(collectionName, "conf1", miniCluster);
+
+ // try a command to each node, one of them must be forwarded
+ for (JettySolrRunner jetty : miniCluster.getJettySolrRunners()) {
+ HttpSolrClient client =
+ new HttpSolrClient.Builder(jetty.getBaseUrl().toString() + "/" + collectionName).build();
+ try {
+ ModifiableSolrParams params = new ModifiableSolrParams();
+ params.set("q", "*:*");
+ params.set(USER_PARAM, "user");
+ client.query(params);
+ } finally {
+ client.close();
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e50858c3/solr/core/src/test/org/apache/solr/security/HttpParamDelegationTokenAuthenticationHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/security/HttpParamDelegationTokenAuthenticationHandler.java b/solr/core/src/test/org/apache/solr/security/HttpParamDelegationTokenAuthenticationHandler.java
deleted file mode 100644
index 7c5c94a..0000000
--- a/solr/core/src/test/org/apache/solr/security/HttpParamDelegationTokenAuthenticationHandler.java
+++ /dev/null
@@ -1,109 +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.
- */
-package org.apache.solr.security;
-
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.hadoop.security.authentication.client.AuthenticationException;
-import org.apache.hadoop.security.authentication.server.AuthenticationHandler;
-import org.apache.hadoop.security.authentication.server.AuthenticationToken;
-import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticationHandler;
-
-import org.apache.http.NameValuePair;
-import org.apache.http.client.utils.URLEncodedUtils;
-
-/**
- * AuthenticationHandler that supports delegation tokens and simple
- * authentication via the "user" http parameter
- */
-public class HttpParamDelegationTokenAuthenticationHandler extends
- DelegationTokenAuthenticationHandler {
-
- public static final String USER_PARAM = "user";
-
- public HttpParamDelegationTokenAuthenticationHandler() {
- super(new HttpParamAuthenticationHandler());
- }
-
- @Override
- public void init(Properties config) throws ServletException {
- Properties conf = new Properties();
- for (Map.Entry entry : config.entrySet()) {
- conf.setProperty((String) entry.getKey(), (String) entry.getValue());
- }
- conf.setProperty(TOKEN_KIND, KerberosPlugin.DELEGATION_TOKEN_TYPE_DEFAULT);
- super.init(conf);
- }
-
- private static String getHttpParam(HttpServletRequest request, String param) {
- List<NameValuePair> pairs =
- URLEncodedUtils.parse(request.getQueryString(), Charset.forName("UTF-8"));
- for (NameValuePair nvp : pairs) {
- if(param.equals(nvp.getName())) {
- return nvp.getValue();
- }
- }
- return null;
- }
-
- private static class HttpParamAuthenticationHandler
- implements AuthenticationHandler {
-
- @Override
- public String getType() {
- return "dummy";
- }
-
- @Override
- public void init(Properties config) throws ServletException {
- }
-
- @Override
- public void destroy() {
- }
-
- @Override
- public boolean managementOperation(AuthenticationToken token,
- HttpServletRequest request, HttpServletResponse response)
- throws IOException, AuthenticationException {
- return false;
- }
-
- @Override
- public AuthenticationToken authenticate(HttpServletRequest request,
- HttpServletResponse response)
- throws IOException, AuthenticationException {
- AuthenticationToken token = null;
- String userName = getHttpParam(request, USER_PARAM);
- if (userName != null) {
- return new AuthenticationToken(userName, userName, "test");
- } else {
- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- response.setHeader("WWW-Authenticate", "dummy");
- }
- return token;
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e50858c3/solr/core/src/test/org/apache/solr/security/HttpParamDelegationTokenPlugin.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/security/HttpParamDelegationTokenPlugin.java b/solr/core/src/test/org/apache/solr/security/HttpParamDelegationTokenPlugin.java
new file mode 100644
index 0000000..7a4f69f
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/security/HttpParamDelegationTokenPlugin.java
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ */
+package org.apache.solr.security;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.hadoop.security.authentication.client.AuthenticationException;
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
+import org.apache.hadoop.security.authentication.server.AuthenticationHandler;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticationHandler;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.protocol.HttpContext;
+import org.apache.solr.client.solrj.impl.HttpClientUtil;
+import org.apache.solr.client.solrj.impl.SolrHttpClientBuilder;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.ExecutorUtil;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.request.SolrRequestInfo;
+
+/**
+ * AuthenticationHandler that supports delegation tokens and simple
+ * authentication via the "user" http parameter
+ */
+public class HttpParamDelegationTokenPlugin extends KerberosPlugin {
+ public static final String USER_PARAM = "user"; // http parameter for user authentication
+ public static final String REMOTE_HOST_PARAM = "remoteHost"; // http parameter for indicating remote host
+ public static final String REMOTE_ADDRESS_PARAM = "remoteAddress"; // http parameter for indicating remote address
+ public static final String INTERNAL_REQUEST_HEADER = "internalRequest"; // http header for indicating internal request
+
+ boolean isSolrThread() {
+ return ExecutorUtil.isSolrServerThread();
+ }
+
+ private final HttpRequestInterceptor interceptor = new HttpRequestInterceptor() {
+ @Override
+ public void process(HttpRequest httpRequest, HttpContext httpContext) throws HttpException, IOException {
+ SolrRequestInfo reqInfo = SolrRequestInfo.getRequestInfo();
+ String usr;
+ if (reqInfo != null) {
+ Principal principal = reqInfo.getReq().getUserPrincipal();
+ if (principal == null) {
+ //this had a request but not authenticated
+ //so we don't not need to set a principal
+ return;
+ } else {
+ usr = principal.getName();
+ }
+ } else {
+ if (!isSolrThread()) {
+ //if this is not running inside a Solr threadpool (as in testcases)
+ // then no need to add any header
+ return;
+ }
+ //this request seems to be originated from Solr itself
+ usr = "$"; //special name to denote the user is the node itself
+ }
+ httpRequest.setHeader(INTERNAL_REQUEST_HEADER, usr);
+ }
+ };
+
+ public HttpParamDelegationTokenPlugin(CoreContainer coreContainer) {
+ super(coreContainer);
+ }
+
+ @Override
+ public void init(Map<String, Object> pluginConfig) {
+ try {
+ final FilterConfig initConf = getInitFilterConfig(pluginConfig, true);
+
+ FilterConfig conf = new FilterConfig() {
+ @Override
+ public ServletContext getServletContext() {
+ return initConf.getServletContext();
+ }
+
+ @Override
+ public Enumeration<String> getInitParameterNames() {
+ return initConf.getInitParameterNames();
+ }
+
+ @Override
+ public String getInitParameter(String param) {
+ if (AuthenticationFilter.AUTH_TYPE.equals(param)) {
+ return HttpParamDelegationTokenAuthenticationHandler.class.getName();
+ }
+ return initConf.getInitParameter(param);
+ }
+
+ @Override
+ public String getFilterName() {
+ return "HttpParamFilter";
+ }
+ };
+ Filter kerberosFilter = new HttpParamToRequestFilter();
+ kerberosFilter.init(conf);
+ setKerberosFilter(kerberosFilter);
+ } catch (ServletException e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+ "Error initializing kerberos authentication plugin: "+e);
+ }
+ }
+
+ @Override
+ public SolrHttpClientBuilder getHttpClientBuilder(SolrHttpClientBuilder builder) {
+ HttpClientUtil.addRequestInterceptor(interceptor);
+ return super.getHttpClientBuilder(builder);
+ }
+
+ @Override
+ public void close() {
+ HttpClientUtil.removeRequestInterceptor(interceptor);
+ super.close();
+ }
+
+ private static String getHttpParam(HttpServletRequest request, String param) {
+ List<NameValuePair> pairs = URLEncodedUtils.parse(request.getQueryString(), Charset.forName("UTF-8"));
+ for (NameValuePair nvp : pairs) {
+ if (param.equals(nvp.getName())) {
+ return nvp.getValue();
+ }
+ }
+ return null;
+ }
+
+ public static class HttpParamDelegationTokenAuthenticationHandler extends
+ DelegationTokenAuthenticationHandler {
+
+ public HttpParamDelegationTokenAuthenticationHandler() {
+ super(new HttpParamAuthenticationHandler());
+ }
+
+ @Override
+ public void init(Properties config) throws ServletException {
+ Properties conf = new Properties();
+ for (Map.Entry entry : config.entrySet()) {
+ conf.setProperty((String) entry.getKey(), (String) entry.getValue());
+ }
+ conf.setProperty(TOKEN_KIND, KerberosPlugin.DELEGATION_TOKEN_TYPE_DEFAULT);
+ super.init(conf);
+ }
+
+ private static class HttpParamAuthenticationHandler implements AuthenticationHandler {
+ @Override
+ public String getType() {
+ return "dummy";
+ }
+
+ @Override
+ public void init(Properties config) throws ServletException {
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ @Override
+ public boolean managementOperation(AuthenticationToken token,
+ HttpServletRequest request, HttpServletResponse response)
+ throws IOException, AuthenticationException {
+ return false;
+ }
+
+ @Override
+ public AuthenticationToken authenticate(HttpServletRequest request,
+ HttpServletResponse response)
+ throws IOException, AuthenticationException {
+ AuthenticationToken token = null;
+ String userName = getHttpParam(request, USER_PARAM);
+ if (userName == null) {
+ //check if this is an internal request
+ userName = request.getHeader(INTERNAL_REQUEST_HEADER);
+ }
+ if (userName != null) {
+ return new AuthenticationToken(userName, userName, "test");
+ } else {
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ response.setHeader("WWW-Authenticate", "dummy");
+ }
+ return token;
+ }
+ }
+ }
+
+ /**
+ * Filter that converts http params to HttpServletRequest params
+ */
+ private static class HttpParamToRequestFilter extends DelegationTokenKerberosFilter {
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ final HttpServletRequest httpRequest = (HttpServletRequest) request;
+ final HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
+ @Override
+ public String getRemoteHost() {
+ String param = getHttpParam(httpRequest, REMOTE_HOST_PARAM);
+ return param != null ? param : httpRequest.getRemoteHost();
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ String param = getHttpParam(httpRequest, REMOTE_ADDRESS_PARAM);
+ return param != null ? param : httpRequest.getRemoteAddr();
+ }
+ };
+
+ super.doFilter(requestWrapper, response, chain);
+ }
+
+ @Override
+ protected void doFilter(FilterChain filterChain, HttpServletRequest request,
+ HttpServletResponse response) throws IOException, ServletException {
+ // remove the filter-specific authentication information, so it doesn't get accidentally forwarded.
+ List<NameValuePair> newPairs = new LinkedList<NameValuePair>();
+ List<NameValuePair> pairs = URLEncodedUtils.parse(request.getQueryString(), Charset.forName("UTF-8"));
+ for (NameValuePair nvp : pairs) {
+ if (!USER_PARAM.equals(nvp.getName())) {
+ newPairs.add(nvp);
+ }
+ else {
+ request.setAttribute(USER_PARAM, nvp.getValue());
+ }
+ }
+ final String queryStringNoUser = URLEncodedUtils.format(newPairs, StandardCharsets.UTF_8);
+ HttpServletRequest requestWrapper = new HttpServletRequestWrapper(request) {
+ @Override
+ public String getQueryString() {
+ return queryStringNoUser;
+ }
+ };
+ super.doFilter(filterChain, requestWrapper, response);
+ }
+ }
+}