blob: b230aad20210a47c8e9d3d546c26d9705969515f [file] [log] [blame]
/*
* 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.handler;
import static org.apache.solr.common.params.CommonParams.JAVABIN;
import static org.apache.solr.handler.ReplicationHandler.CMD_DISABLE_POLL;
import static org.apache.solr.handler.ReplicationHandler.CMD_FETCH_INDEX;
import static org.apache.solr.handler.ReplicationHandler.COMMAND;
import static org.apache.solr.handler.ReplicationTestHelper.createAndStartJetty;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.SolrTestCaseJ4.SuppressSSL;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrResponse;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.request.HealthCheckRequest;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.embedded.JettySolrRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@SuppressSSL
public class TestUserManagedReplicationWithAuth extends SolrTestCaseJ4 {
JettySolrRunner leaderJetty, followerJetty, followerJettyWithAuth;
SolrClient leaderClient, followerClient, followerClientWithAuth;
ReplicationTestHelper.SolrInstance leader = null, follower = null, followerWithAuth = null;
private static String user = "solr";
private static String pass = "SolrRocks";
private static String securityJson =
"{\n"
+ "\"authentication\":{ \n"
+ " \"blockUnknown\": true, \n"
+ " \"class\":\"solr.BasicAuthPlugin\",\n"
+ " \"credentials\":{\"solr\":\"IV0EHq1OnNrj6gvRCwvFwTrZ1+z1oBbnQdiVC3otuq0= Ndd7LKvVBAaZIF0QAVi1ekCfAJXr1GGfLtRUXhgrF8c=\"}, \n"
+ " \"realm\":\"My Solr users\", \n"
+ " \"forwardCredentials\": false \n"
+ "},\n"
+ "\"authorization\":{\n"
+ " \"class\":\"solr.RuleBasedAuthorizationPlugin\",\n"
+ " \"permissions\":[{\"name\":\"security-edit\",\n"
+ " \"role\":\"admin\"}],\n"
+ " \"user-role\":{\"solr\":\"admin\"}\n"
+ "}}";
@Override
@Before
public void setUp() throws Exception {
super.setUp();
systemSetPropertySolrDisableUrlAllowList("true");
// leader with Basic auth enabled via security.json
leader =
new ReplicationTestHelper.SolrInstance(
createTempDir("solr-instance").toFile(), "leader", null);
leader.setUp();
// Configuring basic auth for Leader
Path solrLeaderHome = Path.of(leader.getHomeDir());
Files.write(
solrLeaderHome.resolve("security.json"), securityJson.getBytes(StandardCharsets.UTF_8));
leaderJetty = ReplicationTestHelper.createAndStartJetty(leader);
leaderClient =
ReplicationTestHelper.createNewSolrClient(
buildUrl(leaderJetty.getLocalPort()), DEFAULT_TEST_CORENAME);
// follower with no basic auth credentials for leader configured.
follower =
new ReplicationTestHelper.SolrInstance(
createTempDir("solr-instance").toFile(), "follower", leaderJetty.getLocalPort());
follower.setUp();
followerJetty = createAndStartJetty(follower);
followerClient =
ReplicationTestHelper.createNewSolrClient(
buildUrl(followerJetty.getLocalPort()), DEFAULT_TEST_CORENAME);
// follower with basic auth credentials for leader configured in solrconfig.xml.
followerWithAuth =
new ReplicationTestHelper.SolrInstance(
createTempDir("solr-instance").toFile(), "follower-auth", leaderJetty.getLocalPort());
followerWithAuth.setUp();
followerJettyWithAuth = createAndStartJetty(followerWithAuth);
followerClientWithAuth =
ReplicationTestHelper.createNewSolrClient(
buildUrl(followerJettyWithAuth.getLocalPort()), DEFAULT_TEST_CORENAME);
}
@Override
@After
public void tearDown() throws Exception {
super.tearDown();
if (null != leaderJetty) {
leaderJetty.stop();
leaderJetty = null;
}
if (null != followerJetty) {
followerJetty.stop();
followerJetty = null;
}
if (null != followerJettyWithAuth) {
followerJettyWithAuth.stop();
followerJettyWithAuth = null;
}
if (null != leaderClient) {
leaderClient.close();
leaderClient = null;
}
if (null != followerClient) {
followerClient.close();
followerClient = null;
}
if (null != followerClientWithAuth) {
followerClientWithAuth.close();
followerClientWithAuth = null;
}
}
private <T extends SolrRequest<? extends SolrResponse>> T withBasicAuth(T req) {
req.setBasicAuthCredentials(user, pass);
return req;
}
@Test
public void doTestManualFetchIndexWithAuthEnabled() throws Exception {
disablePoll(followerJetty, followerClient);
int nDocs = 500;
int docsAdded = 0;
UpdateRequest commitReq = new UpdateRequest();
withBasicAuth(commitReq);
for (int i = 0; docsAdded < nDocs / 2; i++, docsAdded++) {
SolrInputDocument doc = new SolrInputDocument();
String[] fields = {"id", i + "", "name", "name = " + i};
for (int j = 0; j < fields.length; j += 2) {
doc.addField(fields[j], fields[j + 1]);
}
UpdateRequest req = new UpdateRequest();
withBasicAuth(req).add(doc);
req.process(leaderClient, DEFAULT_TEST_CORENAME);
if (i % 10 == 0) {
commitReq.commit(leaderClient, DEFAULT_TEST_CORENAME);
}
}
commitReq.commit(leaderClient, DEFAULT_TEST_CORENAME);
assertEquals(
docsAdded,
queryWithBasicAuth(leaderClient, new SolrQuery("*:*")).getResults().getNumFound());
// Without Auth credentials fetchIndex will fail
pullIndexFromTo(leaderJetty, followerJetty, false);
assertNotEquals(
docsAdded,
queryWithBasicAuth(followerClient, new SolrQuery("*:*")).getResults().getNumFound());
// With Auth credentials
pullIndexFromTo(leaderJetty, followerJetty, true);
assertEquals(
docsAdded,
queryWithBasicAuth(followerClient, new SolrQuery("*:*")).getResults().getNumFound());
}
@Test
public void doTestAutoReplicationWithAuthEnabled() throws Exception {
int nDocs = 250;
UpdateRequest commitReq = new UpdateRequest();
withBasicAuth(commitReq);
for (int i = 0; i < nDocs; i++) {
SolrInputDocument doc = new SolrInputDocument();
String[] fields = {"id", i + "", "name", "name = " + i};
for (int j = 0; j < fields.length; j += 2) {
doc.addField(fields[j], fields[j + 1]);
}
UpdateRequest req = new UpdateRequest();
withBasicAuth(req).add(doc);
req.process(leaderClient, DEFAULT_TEST_CORENAME);
if (i % 10 == 0) {
commitReq.commit(leaderClient, DEFAULT_TEST_CORENAME);
}
}
commitReq.commit(leaderClient, DEFAULT_TEST_CORENAME);
// wait for followers to fetchIndex
Thread.sleep(5000);
// follower with auth should be healthy
HealthCheckRequest healthCheckRequestFollower = new HealthCheckRequest();
healthCheckRequestFollower.setMaxGenerationLag(2);
assertEquals(
CommonParams.OK,
healthCheckRequestFollower
.process(followerClientWithAuth)
.getResponse()
.get(CommonParams.STATUS));
// follower with auth should be unhealthy
healthCheckRequestFollower = new HealthCheckRequest();
healthCheckRequestFollower.setMaxGenerationLag(2);
assertEquals(
CommonParams.FAILURE,
healthCheckRequestFollower.process(followerClient).getResponse().get(CommonParams.STATUS));
}
private QueryResponse queryWithBasicAuth(SolrClient client, SolrQuery q)
throws IOException, SolrServerException {
return withBasicAuth(new QueryRequest(q)).process(client);
}
private void disablePoll(JettySolrRunner Jetty, SolrClient solrClient)
throws SolrServerException, IOException {
ModifiableSolrParams disablePollParams = new ModifiableSolrParams();
disablePollParams.set(COMMAND, CMD_DISABLE_POLL);
disablePollParams.set(CommonParams.WT, JAVABIN);
disablePollParams.set(CommonParams.QT, ReplicationHandler.PATH);
QueryRequest req = new QueryRequest(disablePollParams);
withBasicAuth(req);
req.setBasePath(buildUrl(Jetty.getLocalPort()));
solrClient.request(req, DEFAULT_TEST_CORENAME);
}
private void pullIndexFromTo(
JettySolrRunner srcSolr, JettySolrRunner destSolr, boolean authEnabled)
throws SolrServerException, IOException {
String srcUrl = buildUrl(srcSolr.getLocalPort()) + "/" + DEFAULT_TEST_CORENAME;
String destUrl = buildUrl(destSolr.getLocalPort()) + "/" + DEFAULT_TEST_CORENAME;
QueryRequest req = getQueryRequestForFetchIndex(authEnabled, srcUrl);
req.setBasePath(buildUrl(destSolr.getLocalPort()));
followerClient.request(req, DEFAULT_TEST_CORENAME);
}
private QueryRequest getQueryRequestForFetchIndex(boolean authEnabled, String srcUrl) {
ModifiableSolrParams solrParams = new ModifiableSolrParams();
solrParams.set(COMMAND, CMD_FETCH_INDEX);
solrParams.set(CommonParams.WT, JAVABIN);
solrParams.set(CommonParams.QT, ReplicationHandler.PATH);
solrParams.set("leaderUrl", srcUrl);
solrParams.set("wait", "true");
if (authEnabled) {
solrParams.set("httpBasicAuthUser", user);
solrParams.set("httpBasicAuthPassword", pass);
}
QueryRequest req = new QueryRequest(solrParams);
return req;
}
}