This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new ddfe2f1b444 SOLR-14496: Add Basic Auth support to SolrCLI (#1954)
ddfe2f1b444 is described below
commit ddfe2f1b4444ed7d019360d5d675dff3edb3e1e7
Author: Eric Pugh <[email protected]>
AuthorDate: Tue Oct 17 06:43:55 2023 -0400
SOLR-14496: Add Basic Auth support to SolrCLI (#1954)
Introduce parameters to the various Solr CLI commands (bin/solr) that allow
them to interact with a Solr that is protected with Basic Authentication.
---
solr/CHANGES.txt | 2 +-
.../core/src/java/org/apache/solr/cli/ApiTool.java | 9 +-
.../src/java/org/apache/solr/cli/AssertTool.java | 63 ++++--
.../src/java/org/apache/solr/cli/AuthTool.java | 15 +-
.../src/java/org/apache/solr/cli/ConfigTool.java | 7 +-
.../src/java/org/apache/solr/cli/CreateTool.java | 21 +-
.../src/java/org/apache/solr/cli/DeleteTool.java | 14 +-
.../src/java/org/apache/solr/cli/ExportTool.java | 28 ++-
.../java/org/apache/solr/cli/HealthcheckTool.java | 9 +-
.../src/java/org/apache/solr/cli/PackageTool.java | 11 +-
.../src/java/org/apache/solr/cli/PostLogsTool.java | 11 +-
.../src/java/org/apache/solr/cli/PostTool.java | 18 +-
.../java/org/apache/solr/cli/RunExampleTool.java | 38 ++--
.../java/org/apache/solr/cli/SimplePostTool.java | 33 ++-
.../core/src/java/org/apache/solr/cli/SolrCLI.java | 72 +++++--
.../java/org/apache/solr/cli/SolrLogPostTool.java | 2 +-
.../src/java/org/apache/solr/cli/StatusTool.java | 36 ++--
.../src/test/org/apache/solr/cli/ApiToolTest.java | 7 +-
.../test/org/apache/solr/cli/CreateToolTest.java | 94 +++++++++
.../test/org/apache/solr/cli/DeleteToolTest.java | 132 ++++++++++++
.../PackageToolTest.java} | 228 +++++++++++++++++----
.../src/test/org/apache/solr/cli/PostToolTest.java | 69 ++++++-
.../src/test/org/apache/solr/cli/SolrCLITest.java | 3 +
.../test/org/apache/solr/cli/TestExportTool.java | 16 +-
.../org/apache/solr/cli/TestSolrCLIRunExample.java | 2 +-
.../apache/solr/cloud/SolrCloudExampleTest.java | 2 +-
solr/packaging/test/test_basic_auth.bats | 92 +++++++++
solr/packaging/test/test_bats.bats | 2 +-
.../configuration-guide/pages/package-manager.adoc | 2 +
.../pages/solr-control-script-reference.adoc | 83 +++++++-
.../modules/indexing-guide/pages/post-tool.adoc | 2 +-
.../query-guide/pages/document-transformers.adoc | 2 +-
.../modules/query-guide/pages/logs.adoc | 14 +-
.../solr/client/solrj/impl/Http2SolrClient.java | 20 ++
.../client/solrj/impl/Http2SolrClientTest.java | 65 ++++++
.../apache/solr/cloud/MiniSolrCloudCluster.java | 18 +-
36 files changed, 1025 insertions(+), 217 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index edbbfd0b9ec..5feaa5bf4e0 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -71,7 +71,7 @@ Other Changes
================== 9.5.0 ==================
New Features
---------------------
-(No changes)
+* SOLR-14496: Solr CLI commands now can interact with a Solr secured using
Basic Authentication. (Eric Pugh)
Improvements
---------------------
diff --git a/solr/core/src/java/org/apache/solr/cli/ApiTool.java
b/solr/core/src/java/org/apache/solr/cli/ApiTool.java
index 8ecfcd36a40..2b760cc7f76 100644
--- a/solr/core/src/java/org/apache/solr/cli/ApiTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ApiTool.java
@@ -57,7 +57,8 @@ public class ApiTool extends ToolBase {
.hasArg()
.required(true)
.desc("Send a GET request to a Solr API endpoint.")
- .build());
+ .build(),
+ SolrCLI.OPTION_CREDENTIALS);
}
@Override
@@ -65,7 +66,7 @@ public class ApiTool extends ToolBase {
String response = null;
String getUrl = cli.getOptionValue("get");
if (getUrl != null) {
- response = callGet(getUrl);
+ response = callGet(getUrl,
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()));
}
if (response != null) {
// pretty-print the response to stdout
@@ -73,11 +74,11 @@ public class ApiTool extends ToolBase {
}
}
- protected String callGet(String url) throws Exception {
+ protected String callGet(String url, String credentials) throws Exception {
URI uri = new URI(url.replace("+", "%20"));
String solrUrl = getSolrUrlFromUri(uri);
String path = uri.getPath();
- try (var solrClient = SolrCLI.getSolrClient(solrUrl)) {
+ try (var solrClient = SolrCLI.getSolrClient(solrUrl, credentials)) {
// For path parameter we need the path without the root so from the
second / char
// (because root can be configured)
// E.g URL is http://localhost:8983/solr/admin/info/system path is
diff --git a/solr/core/src/java/org/apache/solr/cli/AssertTool.java
b/solr/core/src/java/org/apache/solr/cli/AssertTool.java
index 44b4dce1250..8976212778f 100644
--- a/solr/core/src/java/org/apache/solr/cli/AssertTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/AssertTool.java
@@ -34,8 +34,10 @@ import org.apache.solr.common.util.NamedList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/** Supports assert command in the bin/solr script. */
-/** Asserts various conditions and exists with error code if fails, else
continues with no output */
+/**
+ * Supports assert command in the bin/solr script. Asserts various conditions
and exists with error
+ * code if there are failures, else continues with no output.
+ */
public class AssertTool extends ToolBase {
private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static String message = null;
@@ -123,6 +125,14 @@ public class AssertTool extends ToolBase {
Option.builder("e")
.desc("Return an exit code instead of printing error message on
assert fail.")
.longOpt("exitcode")
+ .build(),
+ // u was taken, can we change that instead?
+ Option.builder("credentials")
+ .argName("credentials")
+ .hasArg()
+ .required(false)
+ .desc(
+ "Credentials in the format username:password. Example:
--credentials solr:SolrRocks")
.build());
}
@@ -190,24 +200,34 @@ public class AssertTool extends ToolBase {
ret += sameUser(cli.getOptionValue("u"));
}
if (cli.hasOption("s")) {
- ret += assertSolrRunning(cli.getOptionValue("s"));
+ ret +=
+ assertSolrRunning(
+ cli.getOptionValue("s"),
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()));
}
if (cli.hasOption("S")) {
- ret += assertSolrNotRunning(cli.getOptionValue("S"));
+ ret +=
+ assertSolrNotRunning(
+ cli.getOptionValue("S"),
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()));
}
if (cli.hasOption("c")) {
- ret +=
assertSolrRunningInCloudMode(SolrCLI.normalizeSolrUrl(cli.getOptionValue("c")));
+ ret +=
+ assertSolrRunningInCloudMode(
+ SolrCLI.normalizeSolrUrl(cli.getOptionValue("c")),
+ cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()));
}
if (cli.hasOption("C")) {
- ret +=
assertSolrNotRunningInCloudMode(SolrCLI.normalizeSolrUrl(cli.getOptionValue("C")));
+ ret +=
+ assertSolrNotRunningInCloudMode(
+ SolrCLI.normalizeSolrUrl(cli.getOptionValue("C")),
+ cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()));
}
return ret;
}
- public static int assertSolrRunning(String url) throws Exception {
+ public static int assertSolrRunning(String url, String credentials) throws
Exception {
StatusTool status = new StatusTool();
try {
- status.waitToSeeSolrUp(url, timeoutMs, TimeUnit.MILLISECONDS);
+ status.waitToSeeSolrUp(url, credentials, timeoutMs,
TimeUnit.MILLISECONDS);
} catch (Exception se) {
if (SolrCLI.exceptionIsAuthRelated(se)) {
throw se;
@@ -222,11 +242,11 @@ public class AssertTool extends ToolBase {
return 0;
}
- public static int assertSolrNotRunning(String url) throws Exception {
+ public static int assertSolrNotRunning(String url, String credentials)
throws Exception {
StatusTool status = new StatusTool();
long timeout =
System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeoutMs,
TimeUnit.MILLISECONDS);
- try (SolrClient solrClient = SolrCLI.getSolrClient(url)) {
+ try (SolrClient solrClient = SolrCLI.getSolrClient(url, credentials)) {
NamedList<Object> response = solrClient.request(new
HealthCheckRequest());
Integer statusCode = (Integer) response.findRecursive("responseHeader",
"status");
SolrCLI.checkCodeForAuthError(statusCode);
@@ -236,7 +256,7 @@ public class AssertTool extends ToolBase {
}
while (System.nanoTime() < timeout) {
try {
- status.waitToSeeSolrUp(url, 1, TimeUnit.SECONDS);
+ status.waitToSeeSolrUp(url, credentials, 1, TimeUnit.SECONDS);
try {
log.debug("Solr still up. Waiting before trying again to see if it
was stopped");
Thread.sleep(1000L);
@@ -258,8 +278,8 @@ public class AssertTool extends ToolBase {
+ " seconds");
}
- public static int assertSolrRunningInCloudMode(String url) throws Exception {
- if (!isSolrRunningOn(url)) {
+ public static int assertSolrRunningInCloudMode(String url, String
credentials) throws Exception {
+ if (!isSolrRunningOn(url, credentials)) {
return exitOrException(
"Solr is not running on url "
+ url
@@ -268,14 +288,15 @@ public class AssertTool extends ToolBase {
+ " seconds");
}
- if (!runningSolrIsCloud(url)) {
+ if (!runningSolrIsCloud(url, credentials)) {
return exitOrException("Solr is not running in cloud mode on " + url);
}
return 0;
}
- public static int assertSolrNotRunningInCloudMode(String url) throws
Exception {
- if (!isSolrRunningOn(url)) {
+ public static int assertSolrNotRunningInCloudMode(String url, String
credentials)
+ throws Exception {
+ if (!isSolrRunningOn(url, credentials)) {
return exitOrException(
"Solr is not running on url "
+ url
@@ -284,7 +305,7 @@ public class AssertTool extends ToolBase {
+ " seconds");
}
- if (runningSolrIsCloud(url)) {
+ if (runningSolrIsCloud(url, credentials)) {
return exitOrException("Solr is not running in standalone mode on " +
url);
}
return 0;
@@ -352,10 +373,10 @@ public class AssertTool extends ToolBase {
}
}
- private static boolean isSolrRunningOn(String url) throws Exception {
+ private static boolean isSolrRunningOn(String url, String credentials)
throws Exception {
StatusTool status = new StatusTool();
try {
- status.waitToSeeSolrUp(url, timeoutMs, TimeUnit.MILLISECONDS);
+ status.waitToSeeSolrUp(url, credentials, timeoutMs,
TimeUnit.MILLISECONDS);
return true;
} catch (Exception se) {
if (SolrCLI.exceptionIsAuthRelated(se)) {
@@ -365,8 +386,8 @@ public class AssertTool extends ToolBase {
}
}
- private static boolean runningSolrIsCloud(String url) throws Exception {
- try (final SolrClient client = SolrCLI.getSolrClient(url)) {
+ private static boolean runningSolrIsCloud(String url, String credentials)
throws Exception {
+ try (final SolrClient client = SolrCLI.getSolrClient(url, credentials)) {
return SolrCLI.isCloudMode(client);
}
}
diff --git a/solr/core/src/java/org/apache/solr/cli/AuthTool.java
b/solr/core/src/java/org/apache/solr/cli/AuthTool.java
index 2afd71706b6..abaeb0a84f9 100644
--- a/solr/core/src/java/org/apache/solr/cli/AuthTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/AuthTool.java
@@ -74,12 +74,7 @@ public class AuthTool extends ToolBase {
.desc(
"The authentication mechanism to enable (basicAuth or
kerberos). Defaults to 'basicAuth'.")
.build(),
- Option.builder("credentials")
- .argName("credentials")
- .hasArg()
- .desc(
- "Credentials in the format username:password. Example:
-credentials solr:SolrRocks")
- .build(),
+ SolrCLI.OPTION_CREDENTIALS,
Option.builder("prompt")
.argName("prompt")
.hasArg()
@@ -292,8 +287,8 @@ public class AuthTool extends ToolBase {
.printHelp("bin/solr auth <enable|disable> [OPTIONS]",
SolrCLI.getToolOptions(this));
SolrCLI.exit(1);
} else if (!prompt
- && (cli.getOptionValue("credentials") == null
- || !cli.getOptionValue("credentials").contains(":"))) {
+ && (cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()) ==
null
+ ||
!cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()).contains(":"))) {
CLIO.out("Option -credentials is not in correct format.");
new HelpFormatter()
.printHelp("bin/solr auth <enable|disable> [OPTIONS]",
SolrCLI.getToolOptions(this));
@@ -337,8 +332,8 @@ public class AuthTool extends ToolBase {
}
String username, password;
- if (cli.hasOption("credentials")) {
- String credentials = cli.getOptionValue("credentials");
+ if (cli.hasOption(SolrCLI.OPTION_CREDENTIALS.getLongOpt())) {
+ String credentials =
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt());
username = credentials.split(":")[0];
password = credentials.split(":")[1];
} else {
diff --git a/solr/core/src/java/org/apache/solr/cli/ConfigTool.java
b/solr/core/src/java/org/apache/solr/cli/ConfigTool.java
index 5221a28325f..39ed60697c0 100644
--- a/solr/core/src/java/org/apache/solr/cli/ConfigTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ConfigTool.java
@@ -79,7 +79,8 @@ public class ConfigTool extends ToolBase {
.desc("Set the property to this value; accepts JSON objects and
strings.")
.build(),
SolrCLI.OPTION_SOLRURL,
- SolrCLI.OPTION_ZKHOST);
+ SolrCLI.OPTION_ZKHOST,
+ SolrCLI.OPTION_CREDENTIALS);
}
@Override
@@ -108,7 +109,9 @@ public class ConfigTool extends ToolBase {
echo("\nPOSTing request to Config API: " + solrUrl + updatePath);
echo(jsonBody);
- try (SolrClient solrClient = SolrCLI.getSolrClient(solrUrl)) {
+ try (SolrClient solrClient =
+ SolrCLI.getSolrClient(
+ solrUrl,
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()))) {
NamedList<Object> result = SolrCLI.postJsonToSolr(solrClient,
updatePath, jsonBody);
Integer statusCode = (Integer) result.findRecursive("responseHeader",
"status");
if (statusCode == 0) {
diff --git a/solr/core/src/java/org/apache/solr/cli/CreateTool.java
b/solr/core/src/java/org/apache/solr/cli/CreateTool.java
index e62c42d185a..5eca1c272ed 100644
--- a/solr/core/src/java/org/apache/solr/cli/CreateTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/CreateTool.java
@@ -112,15 +112,15 @@ public class CreateTool extends ToolBase {
.required(false)
.desc("Configuration name; default is the collection name.")
.build(),
+ SolrCLI.OPTION_CREDENTIALS,
SolrCLI.OPTION_VERBOSE);
}
@Override
public void runImpl(CommandLine cli) throws Exception {
SolrCLI.raiseLogLevelUnlessVerbose(cli);
- String solrUrl = SolrCLI.normalizeSolrUrl(cli);
- try (var solrClient = SolrCLI.getSolrClient(solrUrl)) {
+ try (var solrClient = SolrCLI.getSolrClient(cli)) {
if (SolrCLI.isCloudMode(solrClient)) {
createCollection(cli);
} else {
@@ -155,7 +155,8 @@ public class CreateTool extends ToolBase {
// convert raw JSON into user-friendly output
coreRootDirectory = (String) systemInfo.get("core_root");
- if (SolrCLI.safeCheckCoreExists(solrUrl, coreName)) {
+ if (SolrCLI.safeCheckCoreExists(
+ solrUrl, coreName,
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()))) {
throw new IllegalArgumentException(
"\nCore '"
+ coreName
@@ -197,13 +198,16 @@ public class CreateTool extends ToolBase {
}
protected void createCollection(CommandLine cli) throws Exception {
+ Http2SolrClient.Builder builder =
+ new Http2SolrClient.Builder()
+ .withIdleTimeout(30, TimeUnit.SECONDS)
+ .withConnectionTimeout(15, TimeUnit.SECONDS)
+ .withOptionalBasicAuthCredentials(
+ cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()));
String zkHost = SolrCLI.getZkHost(cli);
try (CloudSolrClient cloudSolrClient =
new CloudHttp2SolrClient.Builder(Collections.singletonList(zkHost),
Optional.empty())
- .withInternalClientBuilder(
- new Http2SolrClient.Builder()
- .withIdleTimeout(30, TimeUnit.SECONDS)
- .withConnectionTimeout(15, TimeUnit.SECONDS))
+ .withInternalClientBuilder(builder)
.build()) {
echoIfVerbose("Connecting to ZooKeeper at " + zkHost, cli);
cloudSolrClient.connect();
@@ -275,7 +279,8 @@ public class CreateTool extends ToolBase {
}
// since creating a collection is a heavy-weight operation, check for
existence first
- if (SolrCLI.safeCheckCollectionExists(solrUrl, collectionName)) {
+ if (SolrCLI.safeCheckCollectionExists(
+ solrUrl, collectionName,
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()))) {
throw new IllegalStateException(
"\nCollection '"
+ collectionName
diff --git a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
index bae371226ae..f709c9defa2 100644
--- a/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/DeleteTool.java
@@ -84,6 +84,7 @@ public class DeleteTool extends ToolBase {
"Skip safety checks when deleting the configuration directory
used by a collection.")
.build(),
SolrCLI.OPTION_ZKHOST,
+ SolrCLI.OPTION_CREDENTIALS,
SolrCLI.OPTION_VERBOSE);
}
@@ -92,7 +93,7 @@ public class DeleteTool extends ToolBase {
SolrCLI.raiseLogLevelUnlessVerbose(cli);
String solrUrl = SolrCLI.normalizeSolrUrl(cli);
- try (var solrClient = SolrCLI.getSolrClient(solrUrl)) {
+ try (var solrClient = SolrCLI.getSolrClient(cli)) {
if (SolrCLI.isCloudMode(solrClient)) {
deleteCollection(cli);
} else {
@@ -102,13 +103,16 @@ public class DeleteTool extends ToolBase {
}
protected void deleteCollection(CommandLine cli) throws Exception {
+ Http2SolrClient.Builder builder =
+ new Http2SolrClient.Builder()
+ .withIdleTimeout(30, TimeUnit.SECONDS)
+ .withConnectionTimeout(15, TimeUnit.SECONDS)
+
.withOptionalBasicAuthCredentials(cli.getOptionValue(("credentials")));
+
String zkHost = SolrCLI.getZkHost(cli);
try (CloudSolrClient cloudSolrClient =
new CloudHttp2SolrClient.Builder(Collections.singletonList(zkHost),
Optional.empty())
- .withInternalClientBuilder(
- new Http2SolrClient.Builder()
- .withIdleTimeout(30, TimeUnit.SECONDS)
- .withConnectionTimeout(15, TimeUnit.SECONDS))
+ .withInternalClientBuilder(builder)
.build()) {
echoIfVerbose("Connecting to ZooKeeper at " + zkHost, cli);
cloudSolrClient.connect();
diff --git a/solr/core/src/java/org/apache/solr/cli/ExportTool.java
b/solr/core/src/java/org/apache/solr/cli/ExportTool.java
index 9ea5435e071..e48d4ea5f91 100644
--- a/solr/core/src/java/org/apache/solr/cli/ExportTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/ExportTool.java
@@ -126,11 +126,13 @@ public class ExportTool extends ToolBase {
.hasArg()
.required(false)
.desc("Comma separated list of fields to export. By default all
fields are fetched.")
- .build());
+ .build(),
+ SolrCLI.OPTION_CREDENTIALS);
}
public abstract static class Info {
String baseurl;
+ String credentials;
String format;
boolean compress;
String query;
@@ -145,8 +147,9 @@ public class ExportTool extends ToolBase {
CloudSolrClient solrClient;
DocsSink sink;
- public Info(String url) {
+ public Info(String url, String credentials) {
setUrl(url);
+ setCredentials(credentials);
setOutFormat(null, "jsonl", false);
}
@@ -157,6 +160,10 @@ public class ExportTool extends ToolBase {
query = "*:*";
}
+ public void setCredentials(String credentials) {
+ this.credentials = credentials;
+ }
+
public void setLimit(String maxDocsStr) {
limit = Long.parseLong(maxDocsStr);
if (limit == -1) limit = Long.MAX_VALUE;
@@ -202,7 +209,13 @@ public class ExportTool extends ToolBase {
abstract void exportDocs() throws Exception;
void fetchUniqueKey() throws SolrServerException, IOException {
- solrClient = new
CloudHttp2SolrClient.Builder(Collections.singletonList(baseurl)).build();
+ Http2SolrClient.Builder builder =
+ new
Http2SolrClient.Builder().withOptionalBasicAuthCredentials(credentials);
+
+ solrClient =
+ new CloudHttp2SolrClient.Builder(Collections.singletonList(baseurl))
+ .withInternalClientBuilder(builder)
+ .build();
NamedList<Object> response =
solrClient.request(
new GenericSolrRequest(
@@ -234,7 +247,8 @@ public class ExportTool extends ToolBase {
@Override
public void runImpl(CommandLine cli) throws Exception {
String url = cli.getOptionValue("url");
- Info info = new MultiThreadedRunner(url);
+ String credentials =
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt());
+ Info info = new MultiThreadedRunner(url, credentials);
info.query = cli.getOptionValue("query", "*:*");
info.setOutFormat(
cli.getOptionValue("out"), cli.getOptionValue("format"),
cli.hasOption("compress"));
@@ -491,8 +505,8 @@ public class ExportTool extends ToolBase {
private final long startTime;
@SuppressForbidden(reason = "Need to print out time")
- public MultiThreadedRunner(String url) {
- super(url);
+ public MultiThreadedRunner(String url, String credentials) {
+ super(url, credentials);
startTime = System.currentTimeMillis();
}
@@ -604,7 +618,7 @@ public class ExportTool extends ToolBase {
boolean exportDocsFromCore() throws IOException, SolrServerException {
- try (SolrClient client = new Http2SolrClient.Builder(baseurl).build())
{
+ try (SolrClient client = SolrCLI.getSolrClient(baseurl, credentials)) {
expectedDocs = getDocCount(replica.getCoreName(), client, query);
GenericSolrRequest request;
ModifiableSolrParams params = new ModifiableSolrParams();
diff --git a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
index a657eca84bb..90b00239f48 100644
--- a/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/HealthcheckTool.java
@@ -170,10 +170,15 @@ public class HealthcheckTool extends ToolBase {
q = new SolrQuery("*:*");
q.setRows(0);
q.set(DISTRIB, "false");
- try (var solrClientForCollection = SolrCLI.getSolrClient(coreUrl)) {
+ try (var solrClientForCollection =
+ SolrCLI.getSolrClient(
+ coreUrl,
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()))) {
qr = solrClientForCollection.query(q);
numDocs = qr.getResults().getNumFound();
- try (var solrClient =
SolrCLI.getSolrClient(replicaCoreProps.getBaseUrl())) {
+ try (var solrClient =
+ SolrCLI.getSolrClient(
+ replicaCoreProps.getBaseUrl(),
+
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()))) {
NamedList<Object> systemInfo =
solrClient.request(
new GenericSolrRequest(
diff --git a/solr/core/src/java/org/apache/solr/cli/PackageTool.java
b/solr/core/src/java/org/apache/solr/cli/PackageTool.java
index 97ed80e7be3..a8bc4131ef4 100644
--- a/solr/core/src/java/org/apache/solr/cli/PackageTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/PackageTool.java
@@ -30,7 +30,6 @@ import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.lucene.util.SuppressForbidden;
import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.impl.Http2SolrClient;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.util.Pair;
@@ -85,7 +84,7 @@ public class PackageTool extends ToolBase {
log.info("ZK: {}", zkHost);
- try (SolrClient solrClient = new
Http2SolrClient.Builder(solrUrl).build()) {
+ try (SolrClient solrClient = SolrCLI.getSolrClient(cli, true)) {
packageManager = new PackageManager(solrClient, solrUrl, zkHost);
try {
repositoryManager = new RepositoryManager(solrClient,
packageManager);
@@ -342,6 +341,14 @@ public class PackageTool extends ToolBase {
.required(false)
.desc("Don't prompt for input; accept all default choices,
defaults to false.")
.longOpt("noprompt")
+ .build(),
+ // u was taken, can we change that instead?
+ Option.builder("credentials")
+ .argName("credentials")
+ .hasArg()
+ .required(false)
+ .desc(
+ "Credentials in the format username:password. Example:
--credentials solr:SolrRocks")
.build());
}
}
diff --git a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
index 91e1a4e6fbf..7a6c21649f2 100644
--- a/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/PostLogsTool.java
@@ -75,19 +75,22 @@ public class PostLogsTool extends ToolBase {
.hasArg()
.required(true)
.desc("All files found at or below the root directory will be
indexed.")
- .build());
+ .build(),
+ SolrCLI.OPTION_CREDENTIALS);
}
@Override
public void runImpl(CommandLine cli) throws Exception {
String url = cli.getOptionValue("url");
String rootDir = cli.getOptionValue("rootdir");
- runCommand(url, rootDir);
+ String credentials = cli.getOptionValue("credentials", null);
+ runCommand(url, rootDir, credentials);
}
- public void runCommand(String baseUrl, String root) throws IOException {
+ public void runCommand(String baseUrl, String root, String credentials)
throws IOException {
- Http2SolrClient.Builder builder = new Http2SolrClient.Builder(baseUrl);
+ Http2SolrClient.Builder builder =
+ new
Http2SolrClient.Builder(baseUrl).withOptionalBasicAuthCredentials(credentials);
try (SolrClient client = builder.build()) {
int rec = 0;
UpdateRequest request = new UpdateRequest();
diff --git a/solr/core/src/java/org/apache/solr/cli/PostTool.java
b/solr/core/src/java/org/apache/solr/cli/PostTool.java
index b44bfe397a6..4edc2a8f3b8 100644
--- a/solr/core/src/java/org/apache/solr/cli/PostTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/PostTool.java
@@ -95,7 +95,8 @@ public class PostTool extends ToolBase {
.required(false)
.desc(
"sends application/json content as Solr commands to /update
instead of /update/json/docs")
- .build());
+ .build(),
+ SolrCLI.OPTION_CREDENTIALS);
}
@Override
@@ -132,11 +133,24 @@ public class PostTool extends ToolBase {
boolean commit = cli.hasOption("commit");
boolean optimize = cli.hasOption("optimize");
+ String credentials =
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt());
+
String[] args = cli.getArgs();
SimplePostTool spt =
new SimplePostTool(
- mode, solrUrl, auto, type, format, recursive, delay, fileTypes,
out, commit, optimize,
+ mode,
+ solrUrl,
+ credentials,
+ auto,
+ type,
+ format,
+ recursive,
+ delay,
+ fileTypes,
+ out,
+ commit,
+ optimize,
args);
spt.execute();
diff --git a/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
b/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
index 4a6ad5d5351..f77ae4cb202 100644
--- a/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/RunExampleTool.java
@@ -267,7 +267,8 @@ public class RunExampleTool extends ToolBase {
// safe check if core / collection already exists
boolean alreadyExists = false;
if (nodeStatus.get("cloud") != null) {
- if (SolrCLI.safeCheckCollectionExists(solrUrl, collectionName)) {
+ if (SolrCLI.safeCheckCollectionExists(
+ solrUrl, collectionName,
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()))) {
alreadyExists = true;
echo(
"\nWARNING: Collection '"
@@ -276,7 +277,8 @@ public class RunExampleTool extends ToolBase {
}
} else {
String coreName = collectionName;
- if (SolrCLI.safeCheckCoreExists(solrUrl, coreName)) {
+ if (SolrCLI.safeCheckCoreExists(
+ solrUrl, coreName,
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()))) {
alreadyExists = true;
echo(
"\nWARNING: Core '"
@@ -536,7 +538,13 @@ public class RunExampleTool extends ToolBase {
waitToSeeLiveNodes(zkHost, numNodes);
// create the collection
- String collectionName = createCloudExampleCollection(numNodes, readInput,
prompt, solrUrl);
+ String collectionName =
+ createCloudExampleCollection(
+ numNodes,
+ readInput,
+ prompt,
+ solrUrl,
+ cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()));
echo("\n\nSolrCloud example running, please visit: " + solrUrl + " \n");
}
@@ -640,7 +648,9 @@ public class RunExampleTool extends ToolBase {
String.format(
Locale.ROOT, "%s://%s:%d/solr", urlScheme, (host != null ? host :
"localhost"), port);
- Map<String, Object> nodeStatus = checkPortConflict(solrUrl, solrHomeDir,
port);
+ String credentials = null; // for now we don't need it for example tool.
But we should.
+
+ Map<String, Object> nodeStatus = checkPortConflict(solrUrl, credentials,
solrHomeDir, port);
if (nodeStatus != null)
return nodeStatus; // the server they are trying to start is already
running
@@ -687,16 +697,18 @@ public class RunExampleTool extends ToolBase {
}
if (code != 0) throw new Exception("Failed to start Solr using command: "
+ startCmd);
- return getNodeStatus(solrUrl, maxWaitSecs);
+ return getNodeStatus(
+ solrUrl, cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()),
maxWaitSecs);
}
- protected Map<String, Object> checkPortConflict(String solrUrl, File
solrHomeDir, int port) {
+ protected Map<String, Object> checkPortConflict(
+ String solrUrl, String credentials, File solrHomeDir, int port) {
// quickly check if the port is in use
if (isPortAvailable(port)) return null; // not in use ... try to start
Map<String, Object> nodeStatus = null;
try {
- nodeStatus = (new StatusTool()).getStatus(solrUrl);
+ nodeStatus = (new StatusTool()).getStatus(solrUrl, credentials);
} catch (Exception ignore) {
/* just trying to determine if this example is already running. */
}
@@ -745,7 +757,8 @@ public class RunExampleTool extends ToolBase {
}
protected String createCloudExampleCollection(
- int numNodes, Scanner readInput, boolean prompt, String solrUrl) throws
Exception {
+ int numNodes, Scanner readInput, boolean prompt, String solrUrl, String
credentials)
+ throws Exception {
// yay! numNodes SolrCloud nodes running
int numShards = 2;
int replicationFactor = 2;
@@ -769,7 +782,7 @@ public class RunExampleTool extends ToolBase {
// Test for existence and then prompt to either create another
collection or skip the
// creation step
- if (SolrCLI.safeCheckCollectionExists(solrUrl, collectionName)) {
+ if (SolrCLI.safeCheckCollectionExists(solrUrl, credentials,
collectionName)) {
echo("\nCollection '" + collectionName + "' already exists!");
int oneOrTwo =
promptForInt(
@@ -825,7 +838,7 @@ public class RunExampleTool extends ToolBase {
}
} else {
// must verify if default collection exists
- if (SolrCLI.safeCheckCollectionExists(solrUrl, collectionName)) {
+ if (SolrCLI.safeCheckCollectionExists(solrUrl, collectionName,
credentials)) {
echo(
"\nCollection '"
+ collectionName
@@ -868,13 +881,14 @@ public class RunExampleTool extends ToolBase {
return configDir.isDirectory();
}
- protected Map<String, Object> getNodeStatus(String solrUrl, int maxWaitSecs)
throws Exception {
+ protected Map<String, Object> getNodeStatus(String solrUrl, String
credentials, int maxWaitSecs)
+ throws Exception {
StatusTool statusTool = new StatusTool();
if (verbose) echo("\nChecking status of Solr at " + solrUrl + " ...");
URL solrURL = new URL(solrUrl);
Map<String, Object> nodeStatus =
- statusTool.waitToSeeSolrUp(solrUrl, maxWaitSecs, TimeUnit.SECONDS);
+ statusTool.waitToSeeSolrUp(solrUrl, credentials, maxWaitSecs,
TimeUnit.SECONDS);
nodeStatus.put("baseUrl", solrUrl);
CharArr arr = new CharArr();
new JSONWriter(arr, 2).write(nodeStatus);
diff --git a/solr/core/src/java/org/apache/solr/cli/SimplePostTool.java
b/solr/core/src/java/org/apache/solr/cli/SimplePostTool.java
index 1c4ceaba9e8..35842eb1c38 100644
--- a/solr/core/src/java/org/apache/solr/cli/SimplePostTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/SimplePostTool.java
@@ -112,6 +112,7 @@ public class SimplePostTool {
int delay = 0;
String fileTypes;
URL solrUrl;
+ String credentials;
OutputStream out = null;
String type;
String format;
@@ -256,10 +257,12 @@ public class SimplePostTool {
urlStr = SimplePostTool.appendParam(urlStr, params);
URL url = new URL(urlStr);
String user = null;
+ String credentials = null;
if (url.getUserInfo() != null && url.getUserInfo().trim().length() > 0) {
user = url.getUserInfo().split(":")[0];
} else if (System.getProperty(BASIC_AUTH) != null) {
user = System.getProperty(BASIC_AUTH).trim().split(":")[0];
+ credentials = System.getProperty(BASIC_AUTH).trim();
}
if (user != null) {
info("Basic Authentication enabled, user=" + user);
@@ -290,7 +293,19 @@ public class SimplePostTool {
boolean optimize = isOn(System.getProperty("optimize",
DEFAULT_OPTIMIZE));
return new SimplePostTool(
- mode, url, auto, type, format, recursive, delay, fileTypes, out,
commit, optimize, args);
+ mode,
+ url,
+ credentials,
+ auto,
+ type,
+ format,
+ recursive,
+ delay,
+ fileTypes,
+ out,
+ commit,
+ optimize,
+ args);
} catch (MalformedURLException e) {
fatal("System Property 'url' is not a valid URL: " + urlStr);
return null;
@@ -316,6 +331,7 @@ public class SimplePostTool {
public SimplePostTool(
String mode,
URL url,
+ String credentials,
boolean auto,
String type,
String format,
@@ -328,6 +344,7 @@ public class SimplePostTool {
String[] args) {
this.mode = mode;
this.solrUrl = url;
+ this.credentials = credentials;
this.auto = auto;
this.type = type;
this.format = format;
@@ -1063,19 +1080,21 @@ public class SimplePostTool {
return success;
}
- private static void basicAuth(HttpURLConnection urlc) throws Exception {
+ private void basicAuth(HttpURLConnection urlc) throws Exception {
if (urlc.getURL().getUserInfo() != null) {
String encoding =
Base64.getEncoder().encodeToString(urlc.getURL().getUserInfo().getBytes(US_ASCII));
urlc.setRequestProperty("Authorization", "Basic " + encoding);
- } else if (System.getProperty(BASIC_AUTH) != null) {
- String basicauth = System.getProperty(BASIC_AUTH).trim();
- if (!basicauth.contains(":")) {
- throw new Exception("System property '" + BASIC_AUTH + "' must be of
format user:pass");
+ } else if (credentials != null) {
+ if (!credentials.contains(":")) {
+ throw new Exception("credentials '" + credentials + "' must be of
format user:pass");
}
+ // urlc.setRequestProperty(
+ // "Authorization",
+ // "Basic " +
Base64.getEncoder().encodeToString(credentials.getBytes(UTF_8)));
urlc.setRequestProperty(
"Authorization",
- "Basic " +
Base64.getEncoder().encodeToString(basicauth.getBytes(UTF_8)));
+ "Basic " +
Base64.getEncoder().encodeToString(credentials.getBytes(UTF_8)));
}
}
diff --git a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
index 3741672a952..14d9ab7f7c9 100755
--- a/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrCLI.java
@@ -112,6 +112,16 @@ public class SolrCLI implements CLIO {
.desc("Recurse (true|false), default is false.")
.build();
+ public static final Option OPTION_CREDENTIALS =
+ Option.builder("u")
+ .longOpt("credentials")
+ .argName("credentials")
+ .hasArg()
+ .required(false)
+ .desc(
+ "Credentials in the format username:password. Example:
--credentials solr:SolrRocks")
+ .build();
+
public static void exit(int exitStatus) {
try {
System.exit(exitStatus);
@@ -394,15 +404,44 @@ public class SolrCLI implements CLIO {
&& Arrays.asList(UNAUTHORIZED.code,
FORBIDDEN.code).contains(((SolrException) exc).code()));
}
- public static SolrClient getSolrClient(String solrUrl) {
+ public static SolrClient getSolrClient(String solrUrl, String credentials,
boolean barePath) {
// today we require all urls to end in /solr, however in the future we
will need to support the
- // /api url end point instead.
+ // /api url end point instead. Eventually we want to have this method
always
+ // return a bare url, and then individual calls decide if they are /solr
or /api
// The /solr/ check is because sometimes a full url is passed in, like
// http://localhost:8983/solr/films_shard1_replica_n1/.
- if (!solrUrl.endsWith("/solr") && !solrUrl.contains("/solr/")) {
+ if (!barePath && !solrUrl.endsWith("/solr") &&
!solrUrl.contains("/solr/")) {
solrUrl = solrUrl + "/solr";
}
- return new
Http2SolrClient.Builder(solrUrl).withMaxConnectionsPerHost(32).build();
+ Http2SolrClient.Builder builder =
+ new Http2SolrClient.Builder(solrUrl)
+ .withMaxConnectionsPerHost(32)
+ .withOptionalBasicAuthCredentials(credentials);
+
+ return builder.build();
+ }
+
+ /**
+ * Helper method for all the places where we assume a /solr on the url.
+ *
+ * @param solrUrl The solr url that you want the client for
+ * @param credentials The username:password for basic auth.
+ * @return The SolrClient
+ */
+ public static SolrClient getSolrClient(String solrUrl, String credentials) {
+ return getSolrClient(solrUrl, credentials, false);
+ }
+
+ public static SolrClient getSolrClient(CommandLine cli, boolean barePath)
throws Exception {
+ String solrUrl = SolrCLI.normalizeSolrUrl(cli);
+ String credentials =
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt());
+ return getSolrClient(solrUrl, credentials, barePath);
+ }
+
+ public static SolrClient getSolrClient(CommandLine cli) throws Exception {
+ String solrUrl = SolrCLI.normalizeSolrUrl(cli);
+ String credentials =
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt());
+ return getSolrClient(solrUrl, credentials, false);
}
private static final String JSON_CONTENT_TYPE = "application/json";
@@ -474,7 +513,7 @@ public class SolrCLI implements CLIO {
*/
public static String normalizeSolrUrl(String solrUrl) {
if (solrUrl != null) {
- if (solrUrl.indexOf("/solr") > -1) { //
+ if (solrUrl.contains("/solr")) { //
String newSolrUrl = solrUrl.substring(0, solrUrl.indexOf("/solr"));
CLIO.out(
"WARNING: URLs provided to this tool needn't include Solr's
context-root (e.g. \"/solr\"). Such URLs are deprecated and support for them
will be removed in a future release. Correcting from ["
@@ -531,23 +570,13 @@ public class SolrCLI implements CLIO {
* up from a running Solr instance based on the solrUrl option.
*/
public static String getZkHost(CommandLine cli) throws Exception {
+
String zkHost = cli.getOptionValue("zkHost");
if (zkHost != null && !zkHost.isBlank()) {
return zkHost;
}
- String solrUrl = cli.getOptionValue("solrUrl");
- if (solrUrl == null) {
- solrUrl = getDefaultSolrUrl();
- CLIO.getOutStream()
- .println(
- "Neither -zkHost or -solrUrl parameters provided so assuming
solrUrl is "
- + solrUrl
- + ".");
- }
- solrUrl = normalizeSolrUrl(solrUrl);
-
- try (var solrClient = getSolrClient(solrUrl)) {
+ try (SolrClient solrClient = getSolrClient(cli)) {
// hit Solr to get system info
NamedList<Object> systemInfo =
solrClient.request(
@@ -570,9 +599,10 @@ public class SolrCLI implements CLIO {
return zkHost;
}
- public static boolean safeCheckCollectionExists(String solrUrl, String
collection) {
+ public static boolean safeCheckCollectionExists(
+ String solrUrl, String collection, String credentials) {
boolean exists = false;
- try (var solrClient = getSolrClient(solrUrl)) {
+ try (var solrClient = getSolrClient(solrUrl, credentials)) {
NamedList<Object> existsCheckResult = solrClient.request(new
CollectionAdminRequest.List());
@SuppressWarnings("unchecked")
List<String> collections = (List<String>)
existsCheckResult.get("collections");
@@ -584,9 +614,9 @@ public class SolrCLI implements CLIO {
}
@SuppressWarnings("unchecked")
- public static boolean safeCheckCoreExists(String solrUrl, String coreName) {
+ public static boolean safeCheckCoreExists(String solrUrl, String coreName,
String credentials) {
boolean exists = false;
- try (var solrClient = getSolrClient(solrUrl)) {
+ try (var solrClient = getSolrClient(solrUrl, credentials)) {
boolean wait = false;
final long startWaitAt = System.nanoTime();
do {
diff --git a/solr/core/src/java/org/apache/solr/cli/SolrLogPostTool.java
b/solr/core/src/java/org/apache/solr/cli/SolrLogPostTool.java
index cf4fee63b35..b0d6bee2de0 100644
--- a/solr/core/src/java/org/apache/solr/cli/SolrLogPostTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/SolrLogPostTool.java
@@ -45,6 +45,6 @@ public class SolrLogPostTool {
String baseUrl = args[0];
String root = args[1];
PostLogsTool postLogsTool = new PostLogsTool();
- postLogsTool.runCommand(baseUrl, root);
+ postLogsTool.runCommand(baseUrl, root, null);
}
}
diff --git a/solr/core/src/java/org/apache/solr/cli/StatusTool.java
b/solr/core/src/java/org/apache/solr/cli/StatusTool.java
index 3758a60b3fe..a201fc42641 100644
--- a/solr/core/src/java/org/apache/solr/cli/StatusTool.java
+++ b/solr/core/src/java/org/apache/solr/cli/StatusTool.java
@@ -97,7 +97,11 @@ public class StatusTool extends ToolBase {
int solrPort = (new URL(solrUrl)).getPort();
echo("Waiting up to " + maxWaitSecs + " seconds to see Solr running on
port " + solrPort);
try {
- waitToSeeSolrUp(solrUrl, maxWaitSecs, TimeUnit.SECONDS);
+ waitToSeeSolrUp(
+ solrUrl,
+ cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt()),
+ maxWaitSecs,
+ TimeUnit.SECONDS);
echo("Started Solr server on port " + solrPort + ". Happy searching!");
} catch (TimeoutException timeout) {
throw new Exception(
@@ -106,7 +110,8 @@ public class StatusTool extends ToolBase {
} else {
try {
CharArr arr = new CharArr();
- new JSONWriter(arr, 2).write(getStatus(solrUrl));
+ new JSONWriter(arr, 2)
+ .write(getStatus(solrUrl,
cli.getOptionValue(SolrCLI.OPTION_CREDENTIALS.getLongOpt())));
echo(arr.toString());
} catch (Exception exc) {
if (SolrCLI.exceptionIsAuthRelated(exc)) {
@@ -123,12 +128,13 @@ public class StatusTool extends ToolBase {
}
}
- public Map<String, Object> waitToSeeSolrUp(String solrUrl, long maxWait,
TimeUnit unit)
- throws Exception {
+ public Map<String, Object> waitToSeeSolrUp(
+ String solrUrl, String credentials, long maxWait, TimeUnit unit) throws
Exception {
long timeout = System.nanoTime() + TimeUnit.NANOSECONDS.convert(maxWait,
unit);
while (System.nanoTime() < timeout) {
+
try {
- return getStatus(solrUrl);
+ return getStatus(solrUrl, credentials);
} catch (Exception exc) {
if (SolrCLI.exceptionIsAuthRelated(exc)) {
throw exc;
@@ -148,16 +154,20 @@ public class StatusTool extends ToolBase {
+ " seconds!");
}
- public Map<String, Object> getStatus(String solrUrl) throws Exception {
+ public Map<String, Object> getStatus(String solrUrl, String credentials)
throws Exception {
+ try (var solrClient = SolrCLI.getSolrClient(solrUrl, credentials)) {
+ return getStatus(solrClient);
+ }
+ }
+
+ public Map<String, Object> getStatus(SolrClient solrClient) throws Exception
{
Map<String, Object> status;
- try (var solrClient = SolrCLI.getSolrClient(solrUrl)) {
- NamedList<Object> systemInfo =
- solrClient.request(
- new GenericSolrRequest(SolrRequest.METHOD.GET,
CommonParams.SYSTEM_INFO_PATH));
- // convert raw JSON into user-friendly output
- status = reportStatus(systemInfo, solrClient);
- }
+ NamedList<Object> systemInfo =
+ solrClient.request(
+ new GenericSolrRequest(SolrRequest.METHOD.GET,
CommonParams.SYSTEM_INFO_PATH));
+ // convert raw JSON into user-friendly output
+ status = reportStatus(systemInfo, solrClient);
return status;
}
diff --git a/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java
b/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java
index d817845854a..db04f60f0ef 100644
--- a/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java
+++ b/solr/core/src/test/org/apache/solr/cli/ApiToolTest.java
@@ -16,7 +16,6 @@
*/
package org.apache.solr.cli;
-import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
@@ -66,9 +65,6 @@ public class ApiToolTest extends SolrCloudTestCase {
.process(cluster.getSolrClient());
cluster.waitForActiveCollection(COLLECTION_NAME, 2, 2);
- String tmpFileLoc =
- new File(cluster.getBaseDir().toFile().getAbsolutePath() +
File.separator).getPath();
-
UpdateRequest ur = new UpdateRequest();
ur.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
@@ -90,7 +86,8 @@ public class ApiToolTest extends SolrCloudTestCase {
cluster.getJettySolrRunner(0).getBaseUrl()
+ "/"
+ COLLECTION_NAME
- + "/select?q=*:*&rows=1&fl=id&sort=id+asc");
+ + "/select?q=*:*&rows=1&fl=id&sort=id+asc",
+ null);
// Fields that could be missed because of serialization
assertFindInJson(response, "\"numFound\":1000,");
// Correct formatting
diff --git a/solr/core/src/test/org/apache/solr/cli/CreateToolTest.java
b/solr/core/src/test/org/apache/solr/cli/CreateToolTest.java
new file mode 100644
index 00000000000..a4801c99fb0
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/CreateToolTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.cli;
+
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static org.apache.solr.cli.SolrCLI.findTool;
+import static org.apache.solr.cli.SolrCLI.parseCmdLine;
+import static
org.apache.solr.security.Sha256AuthenticationProvider.getSaltedHashedValue;
+
+import java.util.Map;
+import org.apache.commons.cli.CommandLine;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.security.BasicAuthPlugin;
+import org.apache.solr.security.RuleBasedAuthorizationPlugin;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class CreateToolTest extends SolrCloudTestCase {
+
+ private static final String USER = "solr";
+ private static final String PASS = "SolrRocksAgain";
+ private static final String collectionName =
"testCreateCollectionWithBasicAuth";
+
+ @BeforeClass
+ public static void setupClusterWithSecurityEnabled() throws Exception {
+ final String SECURITY_JSON =
+ Utils.toJSONString(
+ Map.of(
+ "authorization",
+ Map.of(
+ "class",
+ RuleBasedAuthorizationPlugin.class.getName(),
+ "user-role",
+ singletonMap(USER, "admin"),
+ "permissions",
+ singletonList(Map.of("name", "all", "role", "admin"))),
+ "authentication",
+ Map.of(
+ "class",
+ BasicAuthPlugin.class.getName(),
+ "blockUnknown",
+ true,
+ "credentials",
+ singletonMap(USER, getSaltedHashedValue(PASS)))));
+
+ configureCluster(2)
+ .addConfig("conf", configset("cloud-minimal"))
+ .withSecurityJson(SECURITY_JSON)
+ .configure();
+ }
+
+ @Test
+ public void testCreateCollectionWithBasicAuth() throws Exception {
+
+ String[] args = {
+ "create",
+ "-c",
+ collectionName,
+ "-n",
+ "cloud-minimal",
+ "-zkHost",
+ cluster.getZkClient().getZkServerAddress(),
+ "-credentials",
+ USER + ":" + PASS,
+ "-verbose"
+ };
+
+ assertEquals(0, runTool(args));
+ }
+
+ private int runTool(String[] args) throws Exception {
+ Tool tool = findTool(args);
+ assertTrue(tool instanceof CreateTool);
+ CommandLine cli = parseCmdLine(tool.getName(), args, tool.getOptions());
+ return tool.runTool(cli);
+ }
+}
diff --git a/solr/core/src/test/org/apache/solr/cli/DeleteToolTest.java
b/solr/core/src/test/org/apache/solr/cli/DeleteToolTest.java
new file mode 100644
index 00000000000..808f6171971
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cli/DeleteToolTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.cli;
+
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static org.apache.solr.cli.SolrCLI.findTool;
+import static org.apache.solr.cli.SolrCLI.parseCmdLine;
+import static
org.apache.solr.security.Sha256AuthenticationProvider.getSaltedHashedValue;
+
+import java.util.Map;
+import org.apache.commons.cli.CommandLine;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.security.BasicAuthPlugin;
+import org.apache.solr.security.RuleBasedAuthorizationPlugin;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class DeleteToolTest extends SolrCloudTestCase {
+
+ private static final String USER = "solr";
+ private static final String PASS = "SolrRocksAgain";
+
+ @BeforeClass
+ public static void setupClusterWithSecurityEnabled() throws Exception {
+ final String SECURITY_JSON =
+ Utils.toJSONString(
+ Map.of(
+ "authorization",
+ Map.of(
+ "class",
+ RuleBasedAuthorizationPlugin.class.getName(),
+ "user-role",
+ singletonMap(USER, "admin"),
+ "permissions",
+ singletonList(Map.of("name", "all", "role", "admin"))),
+ "authentication",
+ Map.of(
+ "class",
+ BasicAuthPlugin.class.getName(),
+ "blockUnknown",
+ true,
+ "credentials",
+ singletonMap(USER, getSaltedHashedValue(PASS)))));
+
+ configureCluster(2)
+ .addConfig("conf", configset("cloud-minimal"))
+ .withSecurityJson(SECURITY_JSON)
+ .configure();
+ }
+
+ private <T extends SolrRequest<? extends SolrResponse>> T withBasicAuth(T
req) {
+ req.setBasicAuthCredentials(USER, PASS);
+ return req;
+ }
+
+ @Test
+ public void testDeleteCollectionWithBasicAuth() throws Exception {
+
+ withBasicAuth(
+ CollectionAdminRequest.createCollection(
+ "testDeleteCollectionWithBasicAuth", "conf", 1, 1))
+ .processAndWait(cluster.getSolrClient(), 10);
+ waitForState(
+ "Expected collection to be created with 1 shard and 1 replicas",
+ "testDeleteCollectionWithBasicAuth",
+ clusterShape(1, 1));
+
+ String[] args = {
+ "delete",
+ "-c",
+ "testDeleteCollectionWithBasicAuth",
+ "-deleteConfig",
+ "false",
+ "-zkHost",
+ cluster.getZkClient().getZkServerAddress(),
+ "-credentials",
+ USER + ":" + PASS,
+ "-verbose"
+ };
+ assertEquals(0, runTool(args));
+ }
+
+ @Test
+ public void testFailsToDeleteProtectedCollection() throws Exception {
+
+ withBasicAuth(
+ CollectionAdminRequest.createCollection(
+ "testFailsToDeleteProtectedCollection", "conf", 1, 1))
+ .processAndWait(cluster.getSolrClient(), 10);
+ waitForState(
+ "Expected collection to be created with 1 shard and 1 replicas",
+ "testFailsToDeleteProtectedCollection",
+ clusterShape(1, 1));
+
+ String[] args = {
+ "delete",
+ "-c",
+ "testFailsToDeleteProtectedCollection",
+ "-zkHost",
+ cluster.getZkClient().getZkServerAddress(),
+ "-verbose"
+ };
+ assertEquals(1, runTool(args));
+ }
+
+ private int runTool(String[] args) throws Exception {
+ Tool tool = findTool(args);
+ assertTrue(tool instanceof DeleteTool);
+ CommandLine cli = parseCmdLine(tool.getName(), args, tool.getOptions());
+ return tool.runTool(cli);
+ }
+}
diff --git
a/solr/core/src/test/org/apache/solr/cloud/PackageManagerCLITest.java
b/solr/core/src/test/org/apache/solr/cli/PackageToolTest.java
similarity index 51%
rename from solr/core/src/test/org/apache/solr/cloud/PackageManagerCLITest.java
rename to solr/core/src/test/org/apache/solr/cli/PackageToolTest.java
index b1e11c45a3c..dea89655772 100644
--- a/solr/core/src/test/org/apache/solr/cloud/PackageManagerCLITest.java
+++ b/solr/core/src/test/org/apache/solr/cli/PackageToolTest.java
@@ -15,14 +15,27 @@
* limitations under the License.
*/
-package org.apache.solr.cloud;
+package org.apache.solr.cli;
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static
org.apache.solr.security.Sha256AuthenticationProvider.getSaltedHashedValue;
+
+import java.io.StringReader;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
-import org.apache.solr.cli.PackageTool;
-import org.apache.solr.cli.SolrCLI;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrResponse;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
-import org.apache.solr.core.TestSolrConfigHandler;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.LinkedHashMapWriter;
+import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.security.BasicAuthPlugin;
+import org.apache.solr.security.RuleBasedAuthorizationPlugin;
import org.apache.solr.util.LogLevel;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
@@ -33,11 +46,14 @@ import org.eclipse.jetty.server.handler.ResourceHandler;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.noggit.JSONParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@LogLevel("org.apache=INFO")
-public class PackageManagerCLITest extends SolrCloudTestCase {
+public class PackageToolTest extends SolrCloudTestCase {
+ private static final String USER = "solr";
+ private static final String PASS = "SolrRocksAgain";
// Note for those who want to modify the jar files used in the packages used
in this test:
// You need to re-sign the jars for install step, as follows:
@@ -52,14 +68,35 @@ public class PackageManagerCLITest extends
SolrCloudTestCase {
private static LocalWebServer repositoryServer;
@BeforeClass
- public static void setupCluster() throws Exception {
+ public static void setupClusterWithSecurityEnabled() throws Exception {
System.setProperty("enable.packages", "true");
- configureCluster(1)
+ final String SECURITY_JSON =
+ Utils.toJSONString(
+ Map.of(
+ "authorization",
+ Map.of(
+ "class",
+ RuleBasedAuthorizationPlugin.class.getName(),
+ "user-role",
+ singletonMap(USER, "admin"),
+ "permissions",
+ singletonList(Map.of("name", "all", "role", "admin"))),
+ "authentication",
+ Map.of(
+ "class",
+ BasicAuthPlugin.class.getName(),
+ "blockUnknown",
+ true,
+ "credentials",
+ singletonMap(USER, getSaltedHashedValue(PASS)))));
+
+ configureCluster(2)
.addConfig(
"conf1",
TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
.addConfig(
"conf3",
TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+ .withSecurityJson(SECURITY_JSON)
.configure();
repositoryServer =
@@ -78,36 +115,61 @@ public class PackageManagerCLITest extends
SolrCloudTestCase {
}
}
+ private <T extends SolrRequest<? extends SolrResponse>> T withBasicAuth(T
req) {
+ req.setBasicAuthCredentials(USER, PASS);
+ return req;
+ }
+
@Test
- public void testPackageManager() throws Exception {
+ public void testPackageTool() throws Exception {
PackageTool tool = new PackageTool();
String solrUrl = cluster.getJettySolrRunner(0).getBaseUrl().toString();
- run(tool, new String[] {"-solrUrl", solrUrl, "list-installed"});
+ run(
+ tool,
+ new String[] {"-solrUrl", solrUrl, "list-installed", "-credentials",
USER + ":" + PASS});
run(
tool,
new String[] {
"-solrUrl",
solrUrl,
+ "-credentials",
+ USER + ":" + PASS,
"add-repo",
"fullstory",
- "http://localhost:" + repositoryServer.getPort()
+ "http://localhost:" + repositoryServer.getPort(),
+ "-credentials",
+ USER + ":" + PASS
});
- run(tool, new String[] {"-solrUrl", solrUrl, "list-available"});
+ run(
+ tool,
+ new String[] {"-solrUrl", solrUrl, "list-available", "-credentials",
USER + ":" + PASS});
- run(tool, new String[] {"-solrUrl", solrUrl, "install",
"question-answer:1.0.0"});
+ run(
+ tool,
+ new String[] {
+ "-solrUrl", solrUrl, "install", "question-answer:1.0.0",
"-credentials", USER + ":" + PASS
+ });
- run(tool, new String[] {"-solrUrl", solrUrl, "list-installed"});
+ run(
+ tool,
+ new String[] {"-solrUrl", solrUrl, "list-installed", "-credentials",
USER + ":" + PASS});
- CollectionAdminRequest.createCollection("abc", "conf1", 1,
1).process(cluster.getSolrClient());
- CollectionAdminRequest.createCollection("def", "conf3", 1,
1).process(cluster.getSolrClient());
+ withBasicAuth(CollectionAdminRequest.createCollection("abc", "conf1", 1,
1))
+ .processAndWait(cluster.getSolrClient(), 10);
+ withBasicAuth(CollectionAdminRequest.createCollection("def", "conf3", 1,
1))
+ .processAndWait(cluster.getSolrClient(), 10);
String rhPath = "/mypath2";
- run(tool, new String[] {"-solrUrl", solrUrl, "list-deployed",
"question-answer"});
+ run(
+ tool,
+ new String[] {
+ "-solrUrl", solrUrl, "list-deployed", "question-answer",
"-credentials", USER + ":" + PASS
+ });
run(
tool,
@@ -120,13 +182,23 @@ public class PackageManagerCLITest extends
SolrCloudTestCase {
"-collections",
"abc",
"-p",
- "RH-HANDLER-PATH=" + rhPath
+ "RH-HANDLER-PATH=" + rhPath,
+ "-credentials",
+ USER + ":" + PASS
});
- assertPackageVersion("abc", "question-answer", "1.0.0", rhPath, "1.0.0");
+ assertPackageVersion("abc", "question-answer", "1.0.0", rhPath, "1.0.0",
USER + ":" + PASS);
- run(tool, new String[] {"-solrUrl", solrUrl, "list-deployed",
"question-answer"});
+ run(
+ tool,
+ new String[] {
+ "-solrUrl", solrUrl, "list-deployed", "question-answer",
"-credentials", USER + ":" + PASS
+ });
- run(tool, new String[] {"-solrUrl", solrUrl, "list-deployed", "-c",
"abc"});
+ run(
+ tool,
+ new String[] {
+ "-solrUrl", solrUrl, "list-deployed", "-c", "abc", "-credentials",
USER + ":" + PASS
+ });
// Should we test the "auto-update to latest" functionality or the default
explicit deploy
// functionality
@@ -139,17 +211,33 @@ public class PackageManagerCLITest extends
SolrCloudTestCase {
run(
tool,
new String[] {
- "-solrUrl", solrUrl, "deploy", "question-answer:latest", "-y",
"-collections", "abc"
+ "-solrUrl",
+ solrUrl,
+ "deploy",
+ "question-answer:latest",
+ "-y",
+ "-collections",
+ "abc",
+ "-credentials",
+ USER + ":" + PASS
});
- assertPackageVersion("abc", "question-answer", "$LATEST", rhPath,
"1.0.0");
+ assertPackageVersion("abc", "question-answer", "$LATEST", rhPath,
"1.0.0", USER + ":" + PASS);
- run(tool, new String[] {"-solrUrl", solrUrl, "install",
"question-answer"});
- assertPackageVersion("abc", "question-answer", "$LATEST", rhPath,
"1.1.0");
+ run(
+ tool,
+ new String[] {
+ "-solrUrl", solrUrl, "install", "question-answer", "-credentials",
USER + ":" + PASS
+ });
+ assertPackageVersion("abc", "question-answer", "$LATEST", rhPath,
"1.1.0", USER + ":" + PASS);
} else {
log.info("Testing explicit deployment to a different/newer version");
- run(tool, new String[] {"-solrUrl", solrUrl, "install",
"question-answer"});
- assertPackageVersion("abc", "question-answer", "1.0.0", rhPath, "1.0.0");
+ run(
+ tool,
+ new String[] {
+ "-solrUrl", solrUrl, "install", "question-answer", "-credentials",
USER + ":" + PASS
+ });
+ assertPackageVersion("abc", "question-answer", "1.0.0", rhPath, "1.0.0",
USER + ":" + PASS);
// even if parameters are not passed in, they should be picked up from
previous deployment
if (random().nextBoolean()) {
@@ -165,7 +253,9 @@ public class PackageManagerCLITest extends
SolrCloudTestCase {
"-collections",
"abc",
"-p",
- "RH-HANDLER-PATH=" + rhPath
+ "RH-HANDLER-PATH=" + rhPath,
+ "-credentials",
+ USER + ":" + PASS
});
} else {
run(
@@ -178,40 +268,92 @@ public class PackageManagerCLITest extends
SolrCloudTestCase {
"-y",
"question-answer",
"-collections",
- "abc"
+ "abc",
+ "-credentials",
+ USER + ":" + PASS
});
}
- assertPackageVersion("abc", "question-answer", "1.1.0", rhPath, "1.1.0");
+ assertPackageVersion("abc", "question-answer", "1.1.0", rhPath, "1.1.0",
USER + ":" + PASS);
}
log.info("Running undeploy...");
run(
tool,
- new String[] {"-solrUrl", solrUrl, "undeploy", "question-answer",
"-collections", "abc"});
+ new String[] {
+ "-solrUrl",
+ solrUrl,
+ "undeploy",
+ "question-answer",
+ "-collections",
+ "abc",
+ "-credentials",
+ USER + ":" + PASS
+ });
- run(tool, new String[] {"-solrUrl", solrUrl, "list-deployed",
"question-answer"});
+ run(
+ tool,
+ new String[] {
+ "-solrUrl", solrUrl, "list-deployed", "question-answer",
"-credentials", USER + ":" + PASS
+ });
}
void assertPackageVersion(
- String collection, String pkg, String version, String component, String
componentVersion)
+ String collection,
+ String pkg,
+ String version,
+ String component,
+ String componentVersion,
+ String credentials)
throws Exception {
- TestSolrConfigHandler.testForResponseElement(
- null,
+
+ testForResponseElement(
cluster.getJettySolrRunner(0).getBaseUrl().toString() + "/" +
collection,
"/config/params?meta=true",
- cluster.getSolrClient(),
+ credentials,
Arrays.asList("response", "params", "PKG_VERSIONS", pkg),
- version,
- 10);
+ version);
- TestSolrConfigHandler.testForResponseElement(
- null,
+ testForResponseElement(
cluster.getJettySolrRunner(0).getBaseUrl().toString() + "/" +
collection,
"/config/requestHandler?componentName=" + component + "&meta=true",
- cluster.getSolrClient(),
+ credentials,
Arrays.asList("config", "requestHandler", component, "_packageinfo_",
"version"),
- componentVersion,
- 10);
+ componentVersion);
+ }
+
+ @SuppressWarnings({"rawtypes"})
+ public static void testForResponseElement(
+ String testServerBaseUrl,
+ String uri,
+ String credentials,
+ List<String> jsonPath,
+ Object expected)
+ throws Exception {
+
+ // Copied method from TestSolrConfigHandler.java
+ // and then tweaked it to handle basic auth. We need a nice pattern for
doing
+ // http gets/puts to various end points that can be used across all tests.
+ // using the ApiTool makes me sad ;-)
+
+ boolean success = false;
+
+ ApiTool apiTool = new ApiTool();
+ String response = apiTool.callGet(testServerBaseUrl + uri, credentials);
+
+ LinkedHashMapWriter m =
+ (LinkedHashMapWriter)
+ Utils.MAPWRITEROBJBUILDER.apply(new JSONParser(new
StringReader(response))).getVal();
+ Object actual = Utils.getObjectByPath(m, false, jsonPath);
+
+ if (Objects.equals(expected, actual)) {
+ success = true;
+ }
+
+ assertTrue(
+ StrUtils.formatString(
+ "Could not get expected value ''{0}'' for path ''{1}'' full
output: {2}, from server: {3}",
+ expected, StrUtils.join(jsonPath, '/'), m.toString(),
testServerBaseUrl),
+ success);
}
private void run(PackageTool tool, String[] args) throws Exception {
@@ -220,7 +362,7 @@ public class PackageManagerCLITest extends
SolrCloudTestCase {
}
static class LocalWebServer {
- private int port = 0;
+ private final int port = 0;
private final String resourceDir;
Server server;
ServerConnector connector;
diff --git a/solr/core/src/test/org/apache/solr/cli/PostToolTest.java
b/solr/core/src/test/org/apache/solr/cli/PostToolTest.java
index 681d788138c..c45bd7095f8 100644
--- a/solr/core/src/test/org/apache/solr/cli/PostToolTest.java
+++ b/solr/core/src/test/org/apache/solr/cli/PostToolTest.java
@@ -17,32 +17,87 @@
package org.apache.solr.cli;
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
import static org.apache.solr.cli.SolrCLI.findTool;
import static org.apache.solr.cli.SolrCLI.parseCmdLine;
+import static
org.apache.solr.security.Sha256AuthenticationProvider.getSaltedHashedValue;
+import java.io.File;
+import java.io.FileWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
import org.apache.commons.cli.CommandLine;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrResponse;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.security.BasicAuthPlugin;
+import org.apache.solr.security.RuleBasedAuthorizationPlugin;
import org.junit.BeforeClass;
import org.junit.Test;
[email protected]
public class PostToolTest extends SolrCloudTestCase {
+ private static final String USER = "solr";
+ private static final String PASS = "SolrRocksAgain";
+
@BeforeClass
- public static void setupCluster() throws Exception {
- configureCluster(1)
- .addConfig(
- "config",
TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+ public static void setupClusterWithSecurityEnabled() throws Exception {
+ final String SECURITY_JSON =
+ Utils.toJSONString(
+ Map.of(
+ "authorization",
+ Map.of(
+ "class",
+ RuleBasedAuthorizationPlugin.class.getName(),
+ "user-role",
+ singletonMap(USER, "admin"),
+ "permissions",
+ singletonList(Map.of("name", "all", "role", "admin"))),
+ "authentication",
+ Map.of(
+ "class",
+ BasicAuthPlugin.class.getName(),
+ "blockUnknown",
+ true,
+ "credentials",
+ singletonMap(USER, getSaltedHashedValue(PASS)))));
+
+ configureCluster(2)
+ .addConfig("conf", configset("cloud-minimal"))
+ .withSecurityJson(SECURITY_JSON)
.configure();
}
+ private <T extends SolrRequest<? extends SolrResponse>> T withBasicAuth(T
req) {
+ req.setBasicAuthCredentials(USER, PASS);
+ return req;
+ }
+
@Test
public void testBasicRun() throws Exception {
final String collection = "aliasedCollection";
- CollectionAdminRequest.createCollection(collection, "config", 1, 1)
- .process(cluster.getSolrClient());
- String[] args = {"post", "-url",
"http://localhost:8983/solr/aliasedCollection", "blah.json"};
+ withBasicAuth(CollectionAdminRequest.createCollection(collection,
"config", 1, 1, 0, 0))
+ .processAndWait(cluster.getSolrClient(), 10);
+
+ File jsonDoc = File.createTempFile("temp", "json");
+
+ FileWriter fw = new FileWriter(jsonDoc, StandardCharsets.UTF_8);
+ Utils.writeJson(Utils.toJSONString(Map.of("id", "1", "title", "mytitle")),
fw, true);
+
+ String[] args = {
+ "post",
+ "-url",
+ cluster.getJettySolrRunner(0).getBaseUrl() + "/" + collection,
+ "-credentials",
+ USER + ":" + PASS,
+ jsonDoc.getAbsolutePath()
+ };
assertEquals(0, runTool(args));
}
diff --git a/solr/core/src/test/org/apache/solr/cli/SolrCLITest.java
b/solr/core/src/test/org/apache/solr/cli/SolrCLITest.java
index 03f35246211..091fb4daa3a 100644
--- a/solr/core/src/test/org/apache/solr/cli/SolrCLITest.java
+++ b/solr/core/src/test/org/apache/solr/cli/SolrCLITest.java
@@ -43,4 +43,7 @@ public class SolrCLITest extends SolrTestCase {
assertEquals(
"106751991167 days, 7 hours, 12 minutes, 56 seconds",
SolrCLI.uptime(Long.MAX_VALUE));
}
+
+ @Test
+ public void testGetCredentials() {}
}
diff --git a/solr/core/src/test/org/apache/solr/cli/TestExportTool.java
b/solr/core/src/test/org/apache/solr/cli/TestExportTool.java
index a59dbafe7fb..0aa9bfb9b76 100644
--- a/solr/core/src/test/org/apache/solr/cli/TestExportTool.java
+++ b/solr/core/src/test/org/apache/solr/cli/TestExportTool.java
@@ -80,7 +80,7 @@ public class TestExportTool extends SolrCloudTestCase {
String url = cluster.getRandomJetty(random()).getBaseUrl() + "/" +
COLLECTION_NAME;
- ExportTool.Info info = new ExportTool.MultiThreadedRunner(url);
+ ExportTool.Info info = new ExportTool.MultiThreadedRunner(url, null);
String absolutePath = tmpFileLoc + COLLECTION_NAME +
random().nextInt(100000) + ".jsonl";
info.setOutFormat(absolutePath, "jsonl", false);
info.setLimit("200");
@@ -89,7 +89,7 @@ public class TestExportTool extends SolrCloudTestCase {
assertJsonDocsCount(info, 200, record ->
"2019-09-30T05:58:03Z".equals(record.get("a_dt")));
- info = new ExportTool.MultiThreadedRunner(url);
+ info = new ExportTool.MultiThreadedRunner(url, null);
absolutePath = tmpFileLoc + COLLECTION_NAME + random().nextInt(100000) +
".jsonl";
info.setOutFormat(absolutePath, "jsonl", false);
info.setLimit("-1");
@@ -98,7 +98,7 @@ public class TestExportTool extends SolrCloudTestCase {
assertJsonDocsCount(info, 1000, null);
- info = new ExportTool.MultiThreadedRunner(url);
+ info = new ExportTool.MultiThreadedRunner(url, null);
absolutePath = tmpFileLoc + COLLECTION_NAME + random().nextInt(100000) +
".javabin";
info.setOutFormat(absolutePath, "javabin", false);
info.setLimit("200");
@@ -107,7 +107,7 @@ public class TestExportTool extends SolrCloudTestCase {
assertJavabinDocsCount(info, 200);
- info = new ExportTool.MultiThreadedRunner(url);
+ info = new ExportTool.MultiThreadedRunner(url, null);
absolutePath = tmpFileLoc + COLLECTION_NAME + random().nextInt(100000) +
".javabin";
info.setOutFormat(absolutePath, "javabin", false);
info.setLimit("-1");
@@ -115,7 +115,7 @@ public class TestExportTool extends SolrCloudTestCase {
info.exportDocs();
assertJavabinDocsCount(info, 1000);
- info = new ExportTool.MultiThreadedRunner(url);
+ info = new ExportTool.MultiThreadedRunner(url, null);
absolutePath = tmpFileLoc + COLLECTION_NAME + random().nextInt(100000) +
".json";
info.setOutFormat(absolutePath, "json", false);
info.setLimit("200");
@@ -124,7 +124,7 @@ public class TestExportTool extends SolrCloudTestCase {
assertJsonDocsCount2(info, 200);
- info = new ExportTool.MultiThreadedRunner(url);
+ info = new ExportTool.MultiThreadedRunner(url, null);
absolutePath = tmpFileLoc + COLLECTION_NAME + random().nextInt(100000) +
".json";
info.setOutFormat(absolutePath, "json", false);
info.setLimit("-1");
@@ -190,7 +190,7 @@ public class TestExportTool extends SolrCloudTestCase {
ExportTool.MultiThreadedRunner info;
String absolutePath;
- info = new ExportTool.MultiThreadedRunner(url);
+ info = new ExportTool.MultiThreadedRunner(url, null);
info.output = System.out;
absolutePath = tmpFileLoc + COLLECTION_NAME + random().nextInt(100000) +
".javabin";
info.setOutFormat(absolutePath, "javabin", false);
@@ -201,7 +201,7 @@ public class TestExportTool extends SolrCloudTestCase {
assertEquals(
e.getValue().longValue(),
info.corehandlers.get(e.getKey()).receivedDocs.get());
}
- info = new ExportTool.MultiThreadedRunner(url);
+ info = new ExportTool.MultiThreadedRunner(url, null);
info.output = System.out;
absolutePath = tmpFileLoc + COLLECTION_NAME + random().nextInt(100000) +
".jsonl";
info.setOutFormat(absolutePath, "jsonl", false);
diff --git a/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java
b/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java
index d6f7da99eee..47577082428 100644
--- a/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java
+++ b/solr/core/src/test/org/apache/solr/cli/TestSolrCLIRunExample.java
@@ -503,7 +503,7 @@ public class TestSolrCLIRunExample extends SolrTestCaseJ4 {
// verify Solr is running on the expected port and verify the collection
exists
String solrUrl = "http://localhost:" + bindPort + "/solr";
- if (!SolrCLI.safeCheckCollectionExists(solrUrl, collectionName)) {
+ if (!SolrCLI.safeCheckCollectionExists(solrUrl, collectionName, null)) {
fail(
"After running Solr cloud example, test collection '"
+ collectionName
diff --git a/solr/core/src/test/org/apache/solr/cloud/SolrCloudExampleTest.java
b/solr/core/src/test/org/apache/solr/cloud/SolrCloudExampleTest.java
index 03ccf37751b..5ba4f287b88 100644
--- a/solr/core/src/test/org/apache/solr/cloud/SolrCloudExampleTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/SolrCloudExampleTest.java
@@ -196,6 +196,6 @@ public class SolrCloudExampleTest extends
AbstractFullDistribZkTestBase {
assertEquals("Delete action failed!", 0, tool.runTool(cli));
assertFalse(
SolrCLI.safeCheckCollectionExists(
- solrUrl, testCollectionName)); // it should not exist anymore
+ solrUrl, testCollectionName, null)); // it should not exist anymore
}
}
diff --git a/solr/packaging/test/test_basic_auth.bats
b/solr/packaging/test/test_basic_auth.bats
new file mode 100644
index 00000000000..28a9efa0335
--- /dev/null
+++ b/solr/packaging/test/test_basic_auth.bats
@@ -0,0 +1,92 @@
+#!/usr/bin/env bats
+
+# 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.
+
+load bats_helper
+
+setup() {
+ common_clean_setup
+
+ echo "Starting Solr"
+ solr start -c -Denable.packages=true
+
+ # The auth command exports some system variables that are injected as basic
auth username and password,
+ # however that defeats our test so fake that out via -solrIncludeFile param
specifing a bogus path.
+ solr auth enable -type basicAuth -credentials name:password -solrIncludeFile
/force/credentials/to/be/supplied
+
+ solr assert -credentials name:password --cloud http://localhost:${SOLR_PORT}
--timeout 5000
+}
+
+teardown() {
+ # save a snapshot of SOLR_HOME for failed tests
+ save_home_on_failure
+
+ run solr auth disable -z localhost:${ZK_PORT}
+ solr stop -all >/dev/null 2>&1
+}
+
+# Remaining commands that should support basic auth:
+# package, export
+
+
+@test "create, config, api, and delete with basic auth" {
+
+ # Test create
+ run solr create -u name:password -c COLL_NAME
+ assert_output --partial "Created collection 'COLL_NAME'"
+
+ # Test config
+ run solr config -u name:password -c COLL_NAME -action set-property -property
updateHandler.autoCommit.maxDocs -value 100 -solrUrl
http://localhost:${SOLR_PORT}/solr
+ assert_output --partial "Successfully set-property
updateHandler.autoCommit.maxDocs to 100"
+
+ # Test api
+ run solr api -u name:password -get
"http://localhost:${SOLR_PORT}/solr/COLL_NAME/select?q=*:*" -verbose
+ assert_output --partial '"numFound":0'
+
+ # Test delete
+ run solr delete --credentials name:password -c COLL_NAME -zkHost
localhost:${ZK_PORT} -verbose
+ assert_output --partial "Deleted collection 'COLL_NAME'"
+ refute collection_exists "COLL_NAME"
+
+}
+
+@test "post, postlogs and export with basic auth" {
+run solr create -c COLL_NAME
+ run solr create -u name:password -c COLL_NAME
+ assert_output --partial "Created collection 'COLL_NAME'"
+
+ # Test post
+ run solr post -u name:password -type application/xml -url
http://localhost:${SOLR_PORT}/solr/monitors/update
${SOLR_TIP}/example/exampledocs/monitor.xml
+ assert_output --partial '1 files indexed.'
+
+ # Test postlogs
+ run solr postlogs -u name:password -url
http://localhost:${SOLR_PORT}/solr/COLL_NAME -rootdir ${SOLR_LOGS_DIR}/solr.log
+ assert_output --partial 'Committed'
+
+ # Test export
+ #run solr export -u name:password -url
"http://localhost:${SOLR_PORT}/solr/COLL_NAME" -query "*:*" -out
"${BATS_TEST_TMPDIR}/output"
+ #assert_output --partial 'Export complete'
+
+}
+
+@test "package with basic auth" {
+
+ run solr package deploy PACKAGE_NAME -credentials name:password -collections
foo-1.2
+ # verify that package tool is communicating with Solr via basic auth
+ assert_output --partial "Collection(s) doesn't exist: [foo-1.2]"
+ #assert_output --partial "Deployment successful"
+ #refute_output --partial "Invalid collection"
+}
diff --git a/solr/packaging/test/test_bats.bats
b/solr/packaging/test/test_bats.bats
index 9d269a62d3f..7fbe19c74b4 100644
--- a/solr/packaging/test/test_bats.bats
+++ b/solr/packaging/test/test_bats.bats
@@ -33,7 +33,7 @@ teardown_file() {
sleep 3
# Conversely, on shutdown, we do need this to execute strictly
- # because using "run" will eat filing test exit codes
+ # because using "run" will eat failing test exit codes
solr stop -all
# DEBUG : (echo -n "# " ; solr stop -V -all) >&3
}
diff --git
a/solr/solr-ref-guide/modules/configuration-guide/pages/package-manager.adoc
b/solr/solr-ref-guide/modules/configuration-guide/pages/package-manager.adoc
index 2456ef7f244..4c09666a2ec 100644
--- a/solr/solr-ref-guide/modules/configuration-guide/pages/package-manager.adoc
+++ b/solr/solr-ref-guide/modules/configuration-guide/pages/package-manager.adoc
@@ -216,3 +216,5 @@ Also, it is critical to protect ZooKeeper from unauthorized
write access.
Also, keep in mind, that it is possible to install *any* package from a
repository once it has been added.
If you want to use some packages in production, a best practice is to setup
your own repository and add that to Solr instead of adding a generic
third-party repository that is beyond your administrative control.
You might want to re-sign packages from a third-party repository using your
own private keys and host them at your own repository.
+
+To use the package tool with Basic Auth protected Solr installation pass in
the credentials via `-credentials username:password`.
diff --git
a/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc
b/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc
index 54143627189..d292ed27e2c 100644
---
a/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc
+++
b/solr/solr-ref-guide/modules/deployment-guide/pages/solr-control-script-reference.adoc
@@ -51,7 +51,7 @@ If no nodes are already running, restart will skip the step
to stop and proceed
The `bin/solr` script provides many options to allow you to customize the
server in common ways, such as changing the listening port.
However, most of the defaults are adequate for most Solr installations,
especially when just getting started.
-`-a "<jvmParams>"`::
+`-a <jvmParams>`::
+
[%autowidth,frame=none]
|===
@@ -69,7 +69,7 @@ If you are passing JVM parameters that begin with `-D`, you
can omit the `-a` op
[source,bash]
bin/solr start -a "-Xdebug -Xrunjdwp:transport=dt_socket,
server=y,suspend=n,address=1044"
-`-j "<jettyParams>"`::
+`-j <jettyParams>`::
+
[%autowidth,frame=none]
|===
@@ -484,7 +484,7 @@ Solr process 39827 running on port 8865
=== Assert
-The `assert` command sanity checks common issues with Solr installations.
+The `assert` command checks common issues with Solr installations.
These include checking the ownership/existence of particular directories, and
ensuring Solr is available on the expected URL.
The command can either output a specified error message, or change its exit
code to indicate errors.
@@ -530,6 +530,7 @@ usage: bin/solr assert [-m <message>] [-e] [-rR] [-s <url>]
[-S <url>] [-c
-x,--exists <directory> Asserts that directory <directory> exists.
-X,--not-exists <directory> Asserts that directory <directory> does NOT
exist.
+ --credentials <user:pass> Username and password for Basic authentication.
----
=== Healthcheck
@@ -577,6 +578,16 @@ This parameter is unnecessary if `ZK_HOST` is defined in
`solr.in.sh` or `solr.i
+
*Example*: `bin/solr healthcheck -z localhost:2181`
+`-u <user:pass>` or `--credentials <user:pass>`::
++
+[%autowidth,frame=none]
+|===
+|Optional | Default: none
+|===
++
+Basic authentication username and password separated by a `:` character.
+This parameter is unnecessary if `SOLR_AUTH_TYPE` is defined in `solr.in.sh`
or `solr.in.cmd`.
+
Below is an example healthcheck request and response using a non-standard
ZooKeeper connect string, with 2 nodes running:
`$ bin/solr healthcheck -c gettingstarted -z localhost:9865`
@@ -734,6 +745,16 @@ Unnecessary if `ZK_HOST` is defined in `solr.in.sh` or
`solr.in.cmd`.
+
Base Solr URL, which can be used in SolrCloud mode to determine the ZooKeeper
connection string if that's not known.
+`-u <user:pass>` or `--credentials <user:pass>`::
++
+[%autowidth,frame=none]
+|===
+|Optional | Default: none
+|===
++
+Basic authentication username and password separated by a `:` character.
+This parameter is unnecessary if `SOLR_AUTH_TYPE` is defined in `solr.in.sh`
or `solr.in.cmd`.
+
==== Configuration Directories and SolrCloud
Before creating a collection in SolrCloud, the configuration directory used by
the collection must be uploaded to ZooKeeper.
@@ -843,6 +864,16 @@ Unnecessary if `ZK_HOST` is defined in `solr.in.sh` or
`solr.in.cmd`.
+
Base Solr URL, which can be used in SolrCloud mode to determine the ZooKeeper
connection string if that's not known.
+`-u <user:pass>` or `--credentials <user:pass>`::
++
+[%autowidth,frame=none]
+|===
+|Optional | Default: none
+|===
++
+Basic authentication username and password separated by a `:` character.
+This parameter is unnecessary if `SOLR_AUTH_TYPE` is defined in `solr.in.sh`
or `solr.in.cmd`.
+
== Authentication
The `bin/solr` script allows enabling or disabling Authentication, allowing
you to configure authentication from the command line.
@@ -880,7 +911,7 @@ Here are some example usages:
----
Usage: solr auth enable [-type basicAuth] -credentials user:pass
[-blockUnknown <true|false>] [-updateIncludeFileOnly <true|false>] [-V]
solr auth enable [-type basicAuth] -prompt <true|false> [-blockUnknown
<true|false>] [-updateIncludeFileOnly <true|false>] [-V]
- solr auth enable -type kerberos -config "<kerberos configs>"
[-updateIncludeFileOnly <true|false>] [-V]
+ solr auth enable -type kerberos -config <kerberos configs>
[-updateIncludeFileOnly <true|false>] [-V]
solr auth disable [-updateIncludeFileOnly <true|false>] [-V]
----
@@ -888,7 +919,7 @@ Usage: solr auth enable [-type basicAuth] -credentials
user:pass [-blockUnknown
The command takes the following parameters:
-`-credentials <user:pass>`::
+`-u <user:pass>` or `--credentials <user:pass>`::
+
[%autowidth,frame=none]
|===
@@ -1088,6 +1119,16 @@ Base Solr URL, which can be used in SolrCloud mode to
determine the ZooKeeper co
+
The scheme for accessing Solr. Accepted values: http or https. Default is
'http'
+`-u <user:pass>` or `--credentials <user:pass>`::
++
+[%autowidth,frame=none]
+|===
+|Optional | Default: none
+|===
++
+Basic authentication username and password separated by a `:` character.
+This parameter is unnecessary if `SOLR_AUTH_TYPE` is defined in `solr.in.sh`
or `solr.in.cmd`.
+
== ZooKeeper Operations
@@ -1614,6 +1655,16 @@ By default all fields are fetched.
Maximum number of docs to download.
The value `-1` will export all documents.
+`-u <user:pass>` or `--credentials <user:pass>`::
++
+[%autowidth,frame=none]
+|===
+|Optional | Default: none
+|===
++
+Basic authentication username and password separated by a `:` character.
+This parameter is unnecessary if `SOLR_AUTH_TYPE` is defined in `solr.in.sh`
or `solr.in.cmd`.
+
*Examples*
Export all documents from a collection `gettingstarted`:
@@ -1671,6 +1722,7 @@ $ curl -X POST --header "Content-Type:
application/javabin" --data-binary @getti
== Interacting with API
The `api` command will allow you to send an arbitrary HTTP request to a Solr
API endpoint.
+If you have configured basicAuth or TLS with your Solr you may find this
easier than using a separate tool like `curl`.
`bin/solr api -get
http://localhost:8983/solr/COLL_NAME/sql?stmt=select+id+from+COLL_NAME+limit+10`
@@ -1689,18 +1741,27 @@ Send a GET request to a Solr API endpoint.
+
*Example*: `bin/solr api -get
http://localhost:8983/solr/COLL_NAME/sql?stmt=select+id+from+COLL_NAME+limit+10`
-=== API
+`-u <user:pass>` or `--credentials <username:user:pass>`::
++
+[%autowidth,frame=none]
+|===
+|Optional | Default: none
+|===
++
+Basic authentication username and password separated by a `:` character.
+This parameter is unnecessary if `SOLR_AUTH_TYPE` is defined in `solr.in.sh`
or `solr.in.cmd`.
+
+*Examples*
-The `api` command will allow you to send an arbitrary HTTP request to a Solr
API endpoint.
-If you have configured basicAuth or TLS with your Solr you may find this
easier than using a separate tool like `curl`.
+Simple search passing in Basic authentication credentials:
[source,bash]
-$ bin/solr api api -get http://localhost:8983/solr/techproducts/select?q=*:*
+$ bin/solr api -get http://localhost:8983/solr/techproducts/select?q=*:* -u
user:password
-Here is an example of sending a SQL query to the techproducts /sql end point
(assumes you started Solr in Cloud mode with the SQL module enabled):
+Here is an example of sending a SQL query to the techproducts `/sql` end point
(assumes you started Solr in Cloud mode with the SQL module enabled):
[source,bash]
-$ bin/solr api api -get
http://localhost:8983/solr/techproducts/sql?stmt=select+id+from+techproducts+limit+10
+$ bin/solr api -get
http://localhost:8983/solr/techproducts/sql?stmt=select+id+from+techproducts+limit+10
Results are streamed to the terminal.
diff --git a/solr/solr-ref-guide/modules/indexing-guide/pages/post-tool.adoc
b/solr/solr-ref-guide/modules/indexing-guide/pages/post-tool.adoc
index 28a47d28bbc..f285ac6486d 100644
--- a/solr/solr-ref-guide/modules/indexing-guide/pages/post-tool.adoc
+++ b/solr/solr-ref-guide/modules/indexing-guide/pages/post-tool.adoc
@@ -53,7 +53,7 @@ OPTIONS
Solr options:
-url <base Solr update URL>
-commit issue a commit
- -u or -user <user:pass> (sets BasicAuth credentials)
+ -u or -credentials <user:pass> (sets BasicAuth credentials)
Web crawl options:
-recursive <depth> (default: 1)
diff --git
a/solr/solr-ref-guide/modules/query-guide/pages/document-transformers.adoc
b/solr/solr-ref-guide/modules/query-guide/pages/document-transformers.adoc
index 3821f772a94..addbefedb9d 100644
--- a/solr/solr-ref-guide/modules/query-guide/pages/document-transformers.adoc
+++ b/solr/solr-ref-guide/modules/query-guide/pages/document-transformers.adoc
@@ -187,7 +187,7 @@ It is likely to be removed in a future Solr release, so _if
you find it has some
When a `\_nest_path_` field is defined, the `childFilter` option supports an
experimental syntax to combine a "path syntax" restriction with a more
traditional filtering query.
-*This syntax is triggered by including a `/` seperated path structure prior to
a query that includes a `:` character.*
+*This syntax is triggered by including a `/` separated path structure prior to
a query that includes a `:` character.*
When the "path" begins with a `/` character, it restricts matches to documents
that have that exist "path" of nested pseudo-field documents, starting at the
Root document of the block (even if the document being transformed is not a
Root level document)
diff --git a/solr/solr-ref-guide/modules/query-guide/pages/logs.adoc
b/solr/solr-ref-guide/modules/query-guide/pages/logs.adoc
index 7ac02556335..6246008ac7a 100644
--- a/solr/solr-ref-guide/modules/query-guide/pages/logs.adoc
+++ b/solr/solr-ref-guide/modules/query-guide/pages/logs.adoc
@@ -36,7 +36,7 @@ The `postlogs` command reads in Solr's log format and indexes
it in a Solr colle
==== Healthcheck Parameters
-`-url <ADDRESS>`::
+`-url <address>`::
+
[%autowidth,frame=none]
|===
@@ -46,7 +46,7 @@ The `postlogs` command reads in Solr's log format and indexes
it in a Solr colle
Address of the collection, example http://localhost:8983/solr/collection1/.
+
-`-rootdir <DIRECTORY>`::
+`-rootdir <directory>`::
+
[%autowidth,frame=none]
|===
@@ -56,6 +56,16 @@ Address of the collection, example
http://localhost:8983/solr/collection1/.
File path to root of the logs directory: All files found under this directory
(including sub-directories) will be indexed.
If the path points to a single log file only that log file will be loaded.
+`-u <user:pass>` or `--credentials <user:pass>`::
++
+[%autowidth,frame=none]
+|===
+|Optional | Default: none
+|===
++
+Basic authentication username and password separated by a `:` character.
+This parameter is unnecessary if `SOLR_AUTH_TYPE` is defined in `solr.in.sh`
or `solr.in.cmd`.
+
+
*Example*: `bin/solr postlogs --url http://localhost:8983/solr/logs --rootdir
/var/logs/solrlogs`
diff --git
a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
index f6d8a08df98..291c91df074 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/Http2SolrClient.java
@@ -1313,6 +1313,26 @@ public class Http2SolrClient extends SolrClient {
this.proxyIsSecure = isSecure;
return this;
}
+
+ /**
+ * Setup basic authentication from a string formatted as
username:password. If the string is
+ * Null then it doesn't do anything.
+ *
+ * @param credentials The username and password formatted as
username:password
+ * @return this Builder
+ */
+ public Builder withOptionalBasicAuthCredentials(String credentials) {
+ if (credentials != null) {
+ if (credentials.indexOf(':') == -1) {
+ throw new IllegalStateException(
+ "Invalid Authentication credential formatting. Provide username
and password in the 'username:password' format.");
+ }
+ String username = credentials.substring(0, credentials.indexOf(':'));
+ String password = credentials.substring(credentials.indexOf(':') + 1,
credentials.length());
+ withBasicAuthCredentials(username, password);
+ }
+ return this;
+ }
}
public Set<String> getUrlParamNames() {
diff --git
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
index dd9bf0ea927..20b96a782e7 100644
---
a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
+++
b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/Http2SolrClientTest.java
@@ -929,6 +929,71 @@ public class Http2SolrClientTest extends SolrJettyTestBase
{
}
}
+ @Test
+ public void testUseOptionalCredentials() {
+ // username foo, password with embedded colon separator is "expli:cit".
+ try (Http2SolrClient client =
+ new Http2SolrClient.Builder(getBaseUrl() + "/debug/foo")
+ .withOptionalBasicAuthCredentials("foo:expli:cit")
+ .build(); ) {
+ QueryRequest r = new QueryRequest(new SolrQuery("quick brown fox"));
+ try {
+ ignoreException("Error from server");
+ client.request(r);
+ } catch (Exception e) {
+ // expected
+ }
+ unIgnoreException("Error from server");
+ assertTrue(DebugServlet.headers.size() > 0);
+ String authorizationHeader = DebugServlet.headers.get("authorization");
+ assertNotNull(
+ "No authorization information in headers found. Headers: " +
DebugServlet.headers,
+ authorizationHeader);
+ assertEquals(
+ "Basic "
+ + Base64.getEncoder()
+
.encodeToString("foo:expli:cit".getBytes(StandardCharsets.UTF_8)),
+ authorizationHeader);
+ }
+ }
+
+ @Test
+ public void testUseOptionalCredentialsWithNull() {
+ // username foo, password with embedded colon separator is "expli:cit".
+ try (Http2SolrClient client =
+ new Http2SolrClient.Builder(getBaseUrl() + "/debug/foo")
+ .withOptionalBasicAuthCredentials(null)
+ .build(); ) {
+ QueryRequest r = new QueryRequest(new SolrQuery("quick brown fox"));
+ try {
+ ignoreException("Error from server");
+ client.request(r);
+ } catch (Exception e) {
+ // expected
+ }
+ unIgnoreException("Error from server");
+ assertTrue(DebugServlet.headers.size() > 0);
+ String authorizationHeader = DebugServlet.headers.get("authorization");
+ assertNull(
+ "No authorization headers expected. Headers: " +
DebugServlet.headers,
+ authorizationHeader);
+ }
+ }
+
+ @Test
+ public void testMalformedOptionalCredentials() {
+
+ expectThrowsAndMessage(
+ IllegalStateException.class,
+ () -> new
Http2SolrClient.Builder().withOptionalBasicAuthCredentials("usernamepassword"),
+ "Invalid Authentication credential formatting. Provide username and
password in the 'username:password' format.");
+
+ expectThrowsAndMessage(
+ IllegalStateException.class,
+ () -> new
Http2SolrClient.Builder().withOptionalBasicAuthCredentials("username password"),
+ "Invalid Authentication credential formatting. Provide username and
password in the 'username:password' format.");
+ }
+
@Test
public void testBadHttpFactory() {
System.setProperty(HttpClientUtil.SYS_PROP_HTTP_CLIENT_BUILDER_FACTORY,
"FakeClassName");
diff --git
a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
index 5edb9ed909d..6f78c06c018 100644
---
a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
+++
b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
@@ -343,7 +343,7 @@ public class MiniSolrCloudCluster {
}
private void waitForAllNodes(int numServers, int timeoutSeconds)
- throws IOException, InterruptedException, TimeoutException {
+ throws InterruptedException, TimeoutException {
log.info("waitForAllNodes: numServers={}", numServers);
int numRunning;
@@ -667,7 +667,7 @@ public class MiniSolrCloudCluster {
IOUtils.closeQuietly(c);
});
solrClientByCollection.clear();
- ;
+
List<Callable<JettySolrRunner>> shutdowns = new
ArrayList<>(jettys.size());
for (final JettySolrRunner jetty : jettys) {
shutdowns.add(() -> stopJettySolrRunner(jetty));
@@ -767,13 +767,6 @@ public class MiniSolrCloudCluster {
.withConnectionTimeout(15000);
}
- private static String getHostContextSuitableForServletContext(String ctx) {
- if (ctx == null || ctx.isEmpty()) ctx = "/solr";
- if (ctx.endsWith("/")) ctx = ctx.substring(0, ctx.length() - 1);
- if (!ctx.startsWith("/")) ctx = "/" + ctx;
- return ctx;
- }
-
private Exception checkForExceptions(String message,
Collection<Future<JettySolrRunner>> futures)
throws InterruptedException {
Exception parsed = new Exception(message);
@@ -814,6 +807,7 @@ public class MiniSolrCloudCluster {
}
}
+ // Currently not used ;-(
public synchronized void injectChaos(Random random) throws Exception {
// sometimes we restart one of the jetty nodes
@@ -943,11 +937,7 @@ public class MiniSolrCloudCluster {
}
}
}
- if (activeReplicas == expectedReplicas) {
- return true;
- }
-
- return false;
+ return activeReplicas == expectedReplicas;
};
}