mirror of https://github.com/apache/lucene.git
SOLR-9520: Kerberos delegation support in SolrJ
This commit is contained in:
parent
e7ade287e3
commit
3a76ef1193
|
@ -115,6 +115,7 @@ New Features
|
|||
* SOLR-9205: Added method LukeResponse.getSchemaFlags() which returns field
|
||||
information as an EnumSet (Fengtan, Alan Woodward)
|
||||
|
||||
* SOLR-9520: Kerberos delegation support in SolrJ (Ishan Chattopadhyaya, noble)
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
|
|
@ -20,7 +20,10 @@ import junit.framework.Assert;
|
|||
import org.apache.hadoop.util.Time;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.client.solrj.impl.CloudSolrClient;
|
||||
import org.apache.solr.client.solrj.impl.HttpSolrClient;
|
||||
import org.apache.solr.client.solrj.impl.LBHttpSolrClient;
|
||||
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.request.CollectionAdminRequest;
|
||||
|
@ -171,30 +174,39 @@ public class TestSolrCloudWithDelegationTokens extends SolrTestCaseJ4 {
|
|||
|
||||
private int getStatusCode(String token, final String user, final String op, HttpSolrClient client)
|
||||
throws Exception {
|
||||
HttpSolrClient delegationTokenServer =
|
||||
new HttpSolrClient.Builder(client.getBaseURL().toString())
|
||||
.withDelegationToken(token)
|
||||
SolrClient delegationTokenClient;
|
||||
if (random().nextBoolean()) delegationTokenClient = new HttpSolrClient.Builder(client.getBaseURL().toString())
|
||||
.withKerberosDelegationToken(token)
|
||||
.withResponseParser(client.getParser())
|
||||
.build();
|
||||
else delegationTokenClient = new CloudSolrClient.Builder()
|
||||
.withZkHost((miniCluster.getZkServer().getZkAddress()))
|
||||
.withLBHttpSolrClientBuilder(new LBHttpSolrClient.Builder()
|
||||
.withResponseParser(client.getParser())
|
||||
.build();
|
||||
.withHttpSolrClientBuilder(
|
||||
new HttpSolrClient.Builder()
|
||||
.withKerberosDelegationToken(token)
|
||||
))
|
||||
.build();
|
||||
try {
|
||||
ModifiableSolrParams p = new ModifiableSolrParams();
|
||||
if (user != null) p.set(USER_PARAM, user);
|
||||
if (op != null) p.set("op", op);
|
||||
SolrRequest req = getAdminRequest(p);
|
||||
if (user != null || op != null) {
|
||||
Set<String> queryParams = new HashSet<String>();
|
||||
Set<String> queryParams = new HashSet<>();
|
||||
if (user != null) queryParams.add(USER_PARAM);
|
||||
if (op != null) queryParams.add("op");
|
||||
req.setQueryParams(queryParams);
|
||||
}
|
||||
try {
|
||||
delegationTokenServer.request(req, null, null);
|
||||
delegationTokenClient.request(req, null);
|
||||
return HttpStatus.SC_OK;
|
||||
} catch (HttpSolrClient.RemoteSolrException re) {
|
||||
return re.code();
|
||||
}
|
||||
} finally {
|
||||
delegationTokenServer.close();
|
||||
delegationTokenClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@ public class CloudSolrClient extends SolrClient {
|
|||
* the {@link HttpClient} instance to be used for all requests. The provided httpClient should use a
|
||||
* multi-threaded connection manager. If null, a default HttpClient will be used.
|
||||
* @param lbSolrClient
|
||||
* LBHttpSolrServer instance for requests. If null, a default HttpClient will be used.
|
||||
* LBHttpSolrClient instance for requests. If null, a default LBHttpSolrClient will be used.
|
||||
* @param updatesToLeaders
|
||||
* If true, sends updates to shard leaders.
|
||||
*
|
||||
|
@ -350,7 +350,7 @@ public class CloudSolrClient extends SolrClient {
|
|||
*/
|
||||
@Deprecated
|
||||
public CloudSolrClient(Collection<String> zkHosts, String chroot, HttpClient httpClient, LBHttpSolrClient lbSolrClient, boolean updatesToLeaders) {
|
||||
this(zkHosts, chroot, httpClient, lbSolrClient, updatesToLeaders, false);
|
||||
this(zkHosts, chroot, httpClient, lbSolrClient, null, updatesToLeaders, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -371,23 +371,31 @@ public class CloudSolrClient extends SolrClient {
|
|||
* the {@link HttpClient} instance to be used for all requests. The provided httpClient should use a
|
||||
* multi-threaded connection manager. If null, a default HttpClient will be used.
|
||||
* @param lbSolrClient
|
||||
* LBHttpSolrServer instance for requests. If null, a default HttpClient will be used.
|
||||
* LBHttpSolrClient instance for requests. If null, a default LBHttpSolrClient will be used.
|
||||
* @param lbHttpSolrClientBuilder
|
||||
* LBHttpSolrClient builder to construct the LBHttpSolrClient. If null, a default builder will be used.
|
||||
* @param updatesToLeaders
|
||||
* If true, sends updates to shard leaders.
|
||||
* @param directUpdatesToLeadersOnly
|
||||
* If true, sends direct updates to shard leaders only.
|
||||
*/
|
||||
private CloudSolrClient(Collection<String> zkHosts, String chroot, HttpClient httpClient, LBHttpSolrClient lbSolrClient,
|
||||
boolean updatesToLeaders, boolean directUpdatesToLeadersOnly) {
|
||||
private CloudSolrClient(Collection<String> zkHosts,
|
||||
String chroot,
|
||||
HttpClient httpClient,
|
||||
LBHttpSolrClient lbSolrClient,
|
||||
LBHttpSolrClient.Builder lbHttpSolrClientBuilder,
|
||||
boolean updatesToLeaders,
|
||||
boolean directUpdatesToLeadersOnly) {
|
||||
this.clientIsInternal = httpClient == null;
|
||||
this.shutdownLBHttpSolrServer = lbSolrClient == null;
|
||||
if(lbHttpSolrClientBuilder != null) lbSolrClient = lbHttpSolrClientBuilder.build();
|
||||
if(lbSolrClient != null) httpClient = lbSolrClient.getHttpClient();
|
||||
this.myClient = httpClient == null ? HttpClientUtil.createClient(null) : httpClient;
|
||||
if (lbSolrClient == null) lbSolrClient = createLBHttpSolrClient(myClient);
|
||||
this.lbClient = lbSolrClient;
|
||||
this.zkHost = buildZkHostString(zkHosts, chroot);
|
||||
this.updatesToLeaders = updatesToLeaders;
|
||||
this.directUpdatesToLeadersOnly = directUpdatesToLeadersOnly;
|
||||
|
||||
this.clientIsInternal = httpClient == null;
|
||||
this.myClient = httpClient == null ? HttpClientUtil.createClient(null) : httpClient;
|
||||
|
||||
this.shutdownLBHttpSolrServer = lbSolrClient == null;
|
||||
this.lbClient = lbSolrClient == null ? createLBHttpSolrClient(myClient) : lbSolrClient;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -475,7 +483,7 @@ public class CloudSolrClient extends SolrClient {
|
|||
public ResponseParser getParser() {
|
||||
return lbClient.getParser();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Note: This setter method is <b>not thread-safe</b>.
|
||||
*
|
||||
|
@ -1553,6 +1561,7 @@ public class CloudSolrClient extends SolrClient {
|
|||
private HttpClient httpClient;
|
||||
private String zkChroot;
|
||||
private LBHttpSolrClient loadBalancedSolrClient;
|
||||
private LBHttpSolrClient.Builder lbClientBuilder;
|
||||
private boolean shardLeadersOnly;
|
||||
private boolean directUpdatesToLeadersOnly;
|
||||
|
||||
|
@ -1575,6 +1584,14 @@ public class CloudSolrClient extends SolrClient {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link HttpClient} for the builder to use when creating clients.
|
||||
*/
|
||||
public Builder withLBHttpSolrClientBuilder(LBHttpSolrClient.Builder lbHttpSolrClientBuilder) {
|
||||
this.lbClientBuilder = lbHttpSolrClientBuilder;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link HttpClient} for the builder to use when creating clients.
|
||||
*/
|
||||
|
@ -1582,7 +1599,8 @@ public class CloudSolrClient extends SolrClient {
|
|||
this.httpClient = httpClient;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Provide a series of ZooKeeper client endpoints for the builder to use when creating clients.
|
||||
*
|
||||
|
@ -1652,7 +1670,7 @@ public class CloudSolrClient extends SolrClient {
|
|||
* Create a {@link CloudSolrClient} based on the provided configuration.
|
||||
*/
|
||||
public CloudSolrClient build() {
|
||||
return new CloudSolrClient(zkHosts, zkChroot, httpClient, loadBalancedSolrClient,
|
||||
return new CloudSolrClient(zkHosts, zkChroot, httpClient, loadBalancedSolrClient, lbClientBuilder,
|
||||
shardLeadersOnly, directUpdatesToLeadersOnly);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.lang.invoke.MethodHandles;
|
|||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
@ -31,7 +30,6 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
|
@ -746,43 +744,6 @@ public class HttpSolrClient extends SolrClient {
|
|||
}
|
||||
}
|
||||
|
||||
private static class DelegationTokenHttpSolrClient extends HttpSolrClient {
|
||||
private final String DELEGATION_TOKEN_PARAM = "delegation";
|
||||
private final String delegationToken;
|
||||
|
||||
public DelegationTokenHttpSolrClient(String baseURL,
|
||||
HttpClient client,
|
||||
ResponseParser parser,
|
||||
boolean allowCompression,
|
||||
String delegationToken) {
|
||||
super(baseURL, client, parser, allowCompression);
|
||||
if (delegationToken == null) {
|
||||
throw new IllegalArgumentException("Delegation token cannot be null");
|
||||
}
|
||||
this.delegationToken = delegationToken;
|
||||
setQueryParams(new TreeSet<String>(Arrays.asList(DELEGATION_TOKEN_PARAM)));
|
||||
invariantParams = new ModifiableSolrParams();
|
||||
invariantParams.set(DELEGATION_TOKEN_PARAM, delegationToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpRequestBase createMethod(final SolrRequest request, String collection) throws IOException, SolrServerException {
|
||||
SolrParams params = request.getParams();
|
||||
if (params.getParams(DELEGATION_TOKEN_PARAM) != null) {
|
||||
throw new IllegalArgumentException(DELEGATION_TOKEN_PARAM + " parameter not supported");
|
||||
}
|
||||
return super.createMethod(request, collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQueryParams(Set<String> queryParams) {
|
||||
if (queryParams == null || !queryParams.contains(DELEGATION_TOKEN_PARAM)) {
|
||||
throw new IllegalArgumentException("Query params must contain " + DELEGATION_TOKEN_PARAM);
|
||||
}
|
||||
super.setQueryParams(queryParams);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs {@link HttpSolrClient} instances from provided configuration.
|
||||
*/
|
||||
|
@ -792,7 +753,16 @@ public class HttpSolrClient extends SolrClient {
|
|||
private ResponseParser responseParser;
|
||||
private boolean compression;
|
||||
private String delegationToken;
|
||||
|
||||
|
||||
public Builder() {
|
||||
this.responseParser = new BinaryResponseParser();
|
||||
}
|
||||
|
||||
public Builder withBaseSolrUrl(String baseSolrUrl) {
|
||||
this.baseSolrUrl = baseSolrUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Builder object, based on the provided Solr URL.
|
||||
*
|
||||
|
@ -832,6 +802,15 @@ public class HttpSolrClient extends SolrClient {
|
|||
/**
|
||||
* Use a delegation token for authenticating via the KerberosPlugin
|
||||
*/
|
||||
public Builder withKerberosDelegationToken(String delegationToken) {
|
||||
this.delegationToken = delegationToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
/**
|
||||
* @deprecated use {@link withKerberosDelegationToken(String)} instead
|
||||
*/
|
||||
public Builder withDelegationToken(String delegationToken) {
|
||||
this.delegationToken = delegationToken;
|
||||
return this;
|
||||
|
|
|
@ -118,6 +118,7 @@ public class LBHttpSolrClient extends SolrClient {
|
|||
|
||||
private final HttpClient httpClient;
|
||||
private final boolean clientIsInternal;
|
||||
private HttpSolrClient.Builder httpSolrClientBuilder;
|
||||
private final AtomicInteger counter = new AtomicInteger(-1);
|
||||
|
||||
private static final SolrQuery solrQuery = new SolrQuery("*:*");
|
||||
|
@ -242,6 +243,26 @@ public class LBHttpSolrClient extends SolrClient {
|
|||
this(httpClient, new BinaryResponseParser(), solrServerUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* The provided httpClient should use a multi-threaded connection manager
|
||||
* @deprecated use {@link Builder} instead. This will soon be a protected
|
||||
* method and will only be available for use in implementing subclasses.
|
||||
*/
|
||||
public LBHttpSolrClient(HttpSolrClient.Builder httpSolrClientBuilder,
|
||||
HttpClient httpClient, String... solrServerUrl) {
|
||||
clientIsInternal = httpClient == null;
|
||||
this.httpSolrClientBuilder = httpSolrClientBuilder;
|
||||
httpClient = constructClient(null);
|
||||
this.httpClient = httpClient;
|
||||
if (solrServerUrl != null) {
|
||||
for (String s : solrServerUrl) {
|
||||
ServerWrapper wrapper = new ServerWrapper(makeSolrClient(s));
|
||||
aliveServers.put(wrapper.getKey(), wrapper);
|
||||
}
|
||||
}
|
||||
updateAliveList();
|
||||
}
|
||||
|
||||
/**
|
||||
* The provided httpClient should use a multi-threaded connection manager
|
||||
* @deprecated use {@link Builder} instead. This will soon be a protected
|
||||
|
@ -250,19 +271,7 @@ public class LBHttpSolrClient extends SolrClient {
|
|||
@Deprecated
|
||||
public LBHttpSolrClient(HttpClient httpClient, ResponseParser parser, String... solrServerUrl) {
|
||||
clientIsInternal = (httpClient == null);
|
||||
if (httpClient == null) {
|
||||
ModifiableSolrParams params = new ModifiableSolrParams();
|
||||
if (solrServerUrl.length > 1) {
|
||||
// we prefer retrying another server
|
||||
params.set(HttpClientUtil.PROP_USE_RETRY, false);
|
||||
} else {
|
||||
params.set(HttpClientUtil.PROP_USE_RETRY, true);
|
||||
}
|
||||
this.httpClient = HttpClientUtil.createClient(params);
|
||||
} else {
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
this.httpClient = httpClient == null ? constructClient(solrServerUrl) : httpClient;
|
||||
this.parser = parser;
|
||||
|
||||
for (String s : solrServerUrl) {
|
||||
|
@ -271,7 +280,18 @@ public class LBHttpSolrClient extends SolrClient {
|
|||
}
|
||||
updateAliveList();
|
||||
}
|
||||
|
||||
|
||||
private HttpClient constructClient(String[] solrServerUrl) {
|
||||
ModifiableSolrParams params = new ModifiableSolrParams();
|
||||
if (solrServerUrl != null && solrServerUrl.length > 1) {
|
||||
// we prefer retrying another server
|
||||
params.set(HttpClientUtil.PROP_USE_RETRY, false);
|
||||
} else {
|
||||
params.set(HttpClientUtil.PROP_USE_RETRY, true);
|
||||
}
|
||||
return HttpClientUtil.createClient(params);
|
||||
}
|
||||
|
||||
public Set<String> getQueryParams() {
|
||||
return queryParams;
|
||||
}
|
||||
|
@ -294,15 +314,19 @@ public class LBHttpSolrClient extends SolrClient {
|
|||
}
|
||||
|
||||
protected HttpSolrClient makeSolrClient(String server) {
|
||||
HttpSolrClient client = new HttpSolrClient.Builder(server)
|
||||
.withHttpClient(httpClient)
|
||||
.withResponseParser(parser)
|
||||
.build();
|
||||
if (connectionTimeout != null) {
|
||||
client.setConnectionTimeout(connectionTimeout);
|
||||
}
|
||||
if (soTimeout != null) {
|
||||
client.setSoTimeout(soTimeout);
|
||||
HttpSolrClient client;
|
||||
if (httpSolrClientBuilder != null) {
|
||||
synchronized (this) {
|
||||
client = httpSolrClientBuilder
|
||||
.withBaseSolrUrl(server)
|
||||
.withHttpClient(httpClient)
|
||||
.build();
|
||||
}
|
||||
} else {
|
||||
client = new HttpSolrClient.Builder(server)
|
||||
.withHttpClient(httpClient)
|
||||
.withResponseParser(parser)
|
||||
.build();
|
||||
}
|
||||
if (requestWriter != null) {
|
||||
client.setRequestWriter(requestWriter);
|
||||
|
@ -787,11 +811,16 @@ public class LBHttpSolrClient extends SolrClient {
|
|||
private final List<String> baseSolrUrls;
|
||||
private HttpClient httpClient;
|
||||
private ResponseParser responseParser;
|
||||
|
||||
private HttpSolrClient.Builder httpSolrClientBuilder;
|
||||
|
||||
public Builder() {
|
||||
this.baseSolrUrls = new ArrayList<String>();
|
||||
this.baseSolrUrls = new ArrayList<>();
|
||||
this.responseParser = new BinaryResponseParser();
|
||||
}
|
||||
|
||||
public HttpSolrClient.Builder getHttpSolrClientBuilder() {
|
||||
return httpSolrClientBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a Solr endpoint to be used when configuring {@link LBHttpSolrClient} instances.
|
||||
|
@ -831,13 +860,24 @@ public class LBHttpSolrClient extends SolrClient {
|
|||
this.responseParser = responseParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides a {@link HttpSolrClient.Builder} to be used for building the internally used clients.
|
||||
*/
|
||||
public Builder withHttpSolrClientBuilder(HttpSolrClient.Builder builder) {
|
||||
this.httpSolrClientBuilder = builder;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link HttpSolrClient} based on provided configuration.
|
||||
*/
|
||||
public LBHttpSolrClient build() {
|
||||
final String[] baseUrlArray = new String[baseSolrUrls.size()];
|
||||
return new LBHttpSolrClient(httpClient, responseParser, baseSolrUrls.toArray(baseUrlArray));
|
||||
String[] solrServerUrls = baseSolrUrls.toArray(baseUrlArray);
|
||||
return httpSolrClientBuilder != null ?
|
||||
new LBHttpSolrClient(httpSolrClientBuilder, httpClient, solrServerUrls) :
|
||||
new LBHttpSolrClient(httpClient, responseParser, solrServerUrls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue