You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ja...@apache.org on 2021/11/18 12:44:46 UTC
[solr] branch main updated: SOLR-11623 Every request handler in Solr implement PermissionNameProvider (#372)
This is an automated email from the ASF dual-hosted git repository.
janhoy 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 e90f50f SOLR-11623 Every request handler in Solr implement PermissionNameProvider (#372)
e90f50f is described below
commit e90f50fb070fc87e8929ba9a67a1499b7d3a3da0
Author: Jan Høydahl <ja...@users.noreply.github.com>
AuthorDate: Thu Nov 18 13:24:02 2021 +0100
SOLR-11623 Every request handler in Solr implement PermissionNameProvider (#372)
* Make HEALTH_PERM apply to both node and collection-level
* Reqire CONFIG_EDIT_PERM when changing logging level
* Different access level to /security.json
* Require config-read for dump handler only when remote streaming is enabled
* Info-handler needs to delegate permissionName to sub handler
* Metrics permission both on system and collection level
* Add new health handler to UI list
* Admin UI display error on HTTP 403
* Add documentation for health permission, as well as new endpoints protected by metrics-read permission
---
solr/CHANGES.txt | 3 +
.../handler/DocumentAnalysisRequestHandler.java | 6 ++
.../apache/solr/handler/DumpRequestHandler.java | 25 +++++-
.../solr/handler/FieldAnalysisRequestHandler.java | 6 ++
.../apache/solr/handler/MoreLikeThisHandler.java | 8 +-
.../solr/handler/NotFoundRequestHandler.java | 6 ++
.../apache/solr/handler/PingRequestHandler.java | 18 +++++
.../apache/solr/handler/ReplicationHandler.java | 6 ++
.../apache/solr/handler/RequestHandlerBase.java | 10 +--
.../solr/handler/admin/HealthCheckHandler.java | 6 ++
.../org/apache/solr/handler/admin/InfoHandler.java | 14 ++++
.../apache/solr/handler/admin/LoggingHandler.java | 9 +++
.../solr/handler/admin/LukeRequestHandler.java | 6 ++
.../handler/admin/MetricsCollectorHandler.java | 6 ++
.../solr/handler/admin/PluginInfoHandler.java | 6 ++
.../handler/admin/PropertiesRequestHandler.java | 6 ++
.../handler/admin/SegmentsInfoRequestHandler.java | 6 ++
.../solr/handler/admin/SolrInfoMBeanHandler.java | 6 ++
.../solr/handler/admin/SystemInfoHandler.java | 7 +-
.../solr/handler/admin/ThreadDumpHandler.java | 6 ++
.../solr/handler/admin/ZookeeperInfoHandler.java | 23 +++++-
.../solr/handler/admin/ZookeeperReadAPI.java | 89 ++++++++++++++++------
.../solr/handler/admin/ZookeeperStatusHandler.java | 6 ++
.../solr/handler/tagger/TaggerRequestHandler.java | 6 ++
.../solr/search/function/FileFloatSource.java | 6 ++
.../solr/security/PermissionNameProvider.java | 7 +-
.../org/apache/solr/security/PublicKeyHandler.java | 5 ++
.../org/apache/solr/BasicFunctionalityTest.java | 8 +-
.../core/MockQuerySenderListenerReqHandler.java | 6 ++
.../test/org/apache/solr/core/SolrCoreTest.java | 6 ++
.../handler/ThrowErrorOnInitRequestHandler.java | 6 ++
.../solr/handler/admin/MetricsHandlerTest.java | 6 ++
.../src/test/org/apache/solr/pkg/TestPackages.java | 6 ++
.../src/major-changes-in-solr-9.adoc | 2 +
.../src/rule-based-authorization-plugin.adoc | 3 +-
solr/webapp/web/index.html | 7 ++
solr/webapp/web/js/angular/app.js | 4 +
solr/webapp/web/js/angular/controllers/security.js | 3 +-
38 files changed, 325 insertions(+), 40 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index c96a00f..6e00301 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -162,6 +162,9 @@ when told to. The admin UI now tells it to. (Nazerke Seidan, David Smiley)
* SOLR-15278: Add V2 equivalent to allow deleting async collection list of statuses. (Eric Pugh)
+* SOLR-11623: Every request handler in Solr now implements PermissionNameProvider to explicitly decide on what security
+ permissions are required to access the handler (janhoy, Hrishikesh Gadre, David Smiley)
+
* SOLR-15608: Remove deprecated methods, classes and constructors from solrj clients (janhoy)
* SOLR-15705: A delete-by-id command is forwarded to all shards when using the CompositeId router with a router field
diff --git a/solr/core/src/java/org/apache/solr/handler/DocumentAnalysisRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/DocumentAnalysisRequestHandler.java
index d461d2f..74a00dd 100644
--- a/solr/core/src/java/org/apache/solr/handler/DocumentAnalysisRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/DocumentAnalysisRequestHandler.java
@@ -46,6 +46,7 @@ import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.common.EmptyEntityResolver;
+import org.apache.solr.security.AuthorizationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -345,4 +346,9 @@ public class DocumentAnalysisRequestHandler extends AnalysisRequestHandlerBase {
}
return stream;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.ALL;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java
index 87dacef..1f2ee41 100644
--- a/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java
@@ -18,6 +18,7 @@ package org.apache.solr.handler;
import java.io.IOException;
import java.io.Reader;
+import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -28,14 +29,21 @@ import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.core.PluginInfo;
+import org.apache.solr.core.SolrCore;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.util.plugin.SolrCoreAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CommonParams.NAME;
-public class DumpRequestHandler extends RequestHandlerBase
+public class DumpRequestHandler extends RequestHandlerBase implements SolrCoreAware
{
+ private SolrCore solrCore;
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Override
@SuppressWarnings({"unchecked"})
@@ -127,4 +135,19 @@ public class DumpRequestHandler extends RequestHandlerBase
if (nl!=null) subpaths = nl.getAll("subpath");
}
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ if (solrCore != null && solrCore.getSolrConfig().isEnableRemoteStreams()) {
+ log.warn("Dump request handler requires config-read permission when remote streams are enabled");
+ return Name.CONFIG_READ_PERM;
+ } else {
+ return Name.ALL;
+ }
+ }
+
+ @Override
+ public void inform(SolrCore core) {
+ this.solrCore = core;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/FieldAnalysisRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/FieldAnalysisRequestHandler.java
index 8e1ce26..df48a0f 100644
--- a/solr/core/src/java/org/apache/solr/handler/FieldAnalysisRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/FieldAnalysisRequestHandler.java
@@ -29,6 +29,7 @@ import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.IndexSchema;
import org.apache.commons.io.IOUtils;
+import org.apache.solr.security.AuthorizationContext;
import java.io.Reader;
import java.io.IOException;
@@ -232,4 +233,9 @@ public class FieldAnalysisRequestHandler extends AnalysisRequestHandlerBase {
return analyzeResults;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.READ_PERM;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java b/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java
index 217066b..82d6179 100644
--- a/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java
@@ -66,6 +66,7 @@ import org.apache.solr.search.SolrQueryTimeoutImpl;
import org.apache.solr.search.SolrReturnFields;
import org.apache.solr.search.SortSpec;
import org.apache.solr.search.SyntaxError;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.util.SolrPluginUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -274,7 +275,12 @@ public class MoreLikeThisHandler extends RequestHandlerBase
SolrQueryTimeoutImpl.reset();
}
}
-
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.READ_PERM;
+ }
+
public static class InterestingTerm
{
public Term term;
diff --git a/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java
index 511edbe..1165083 100644
--- a/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/NotFoundRequestHandler.java
@@ -19,6 +19,7 @@ package org.apache.solr.handler;
import org.apache.solr.common.SolrException;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import static org.apache.solr.common.params.CommonParams.PATH;
@@ -35,4 +36,9 @@ public class NotFoundRequestHandler extends RequestHandlerBase{
public String getDescription() {
return "No Operation";
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.ALL;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/PingRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/PingRequestHandler.java
index c3a2021..d8cb84e 100644
--- a/solr/core/src/java/org/apache/solr/handler/PingRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/PingRequestHandler.java
@@ -34,11 +34,15 @@ import org.apache.solr.core.SolrCore;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.util.plugin.SolrCoreAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.common.params.CommonParams.DISTRIB;
+import static org.apache.solr.common.params.CommonParams.ENABLE;
+import static org.apache.solr.common.params.CommonParams.DISABLE;
+import static org.apache.solr.common.params.CommonParams.ACTION;
/**
* Ping Request Handler for reporting SolrCore health to a Load Balancer.
@@ -133,6 +137,20 @@ public class PingRequestHandler extends RequestHandlerBase implements SolrCoreAw
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static final String HEALTHCHECK_FILE_PARAM = "healthcheckFile";
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ String action = request.getParams().get(ACTION, "").strip().toLowerCase(Locale.ROOT);
+ // Modifying the health check file requires more permission than just doing a ping
+ switch (action) {
+ case ENABLE:
+ case DISABLE:
+ return Name.CONFIG_EDIT_PERM;
+ default:
+ return Name.HEALTH_PERM;
+ }
+ }
+
protected enum ACTIONS {STATUS, ENABLE, DISABLE, PING};
private String healthFileName = null;
diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java
index 71fe0f4..f47681c 100644
--- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java
@@ -96,6 +96,7 @@ import org.apache.solr.metrics.SolrMetricsContext;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.update.SolrIndexWriter;
import org.apache.solr.update.VersionInfo;
import org.apache.solr.common.util.SolrNamedThreadFactory;
@@ -133,6 +134,11 @@ public class ReplicationHandler extends RequestHandlerBase implements SolrCoreAw
private volatile boolean closed = false;
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.READ_PERM;
+ }
+
private static final class CommitVersionInfo {
public final long version;
public final long generation;
diff --git a/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java b/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java
index d761c55..8156c19 100644
--- a/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java
+++ b/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java
@@ -43,6 +43,7 @@ import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.SyntaxError;
+import org.apache.solr.security.PermissionNameProvider;
import org.apache.solr.util.SolrPluginUtils;
import org.apache.solr.util.TestInjection;
import org.slf4j.Logger;
@@ -51,9 +52,10 @@ import org.slf4j.LoggerFactory;
import static org.apache.solr.core.RequestParams.USEPARAM;
/**
- *
+ * Base class for all request handlers.
*/
-public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfoBean, NestedRequestHandler, ApiSupport {
+public abstract class RequestHandlerBase implements
+ SolrRequestHandler, SolrInfoBean, NestedRequestHandler, ApiSupport, PermissionNameProvider {
protected NamedList<?> initArgs = null;
protected SolrParams defaults;
@@ -332,6 +334,4 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo
public Collection<Api> getApis() {
return ImmutableList.of(new ApiBag.ReqHandlerToApi(this, ApiBag.constructSpec(pluginInfo)));
}
-}
-
-
+}
\ No newline at end of file
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/HealthCheckHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/HealthCheckHandler.java
index 147ad8a..d5f620b 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/HealthCheckHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/HealthCheckHandler.java
@@ -40,6 +40,7 @@ import org.apache.solr.handler.ReplicationHandler;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -275,4 +276,9 @@ public class HealthCheckHandler extends RequestHandlerBase {
public Boolean registerV2() {
return Boolean.TRUE;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.HEALTH_PERM;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
index 29032da..324c06c 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java
@@ -30,6 +30,7 @@ import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.api.Api;
+import org.apache.solr.security.AuthorizationContext;
import static java.util.Collections.singletonList;
import static org.apache.solr.common.util.Utils.getSpec;
@@ -157,4 +158,17 @@ public class InfoHandler extends RequestHandlerBase {
public Boolean registerV2() {
return Boolean.TRUE;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ // Delegate permission to the actual handler
+ String path = request.getResource();
+ String lastPath = path.substring(path.lastIndexOf("/") +1 );
+ RequestHandlerBase handler = handlers.get(lastPath.toLowerCase(Locale.ROOT));
+ if (handler != null) {
+ return handler.getPermissionName(request);
+ } else {
+ return null;
+ }
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java
index 3d2d764..7deb766 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java
@@ -34,6 +34,7 @@ import org.apache.solr.logging.LogWatcher;
import org.apache.solr.logging.LoggerInfo;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.util.plugin.SolrCoreAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -168,4 +169,12 @@ public class LoggingHandler extends RequestHandlerBase implements SolrCoreAware
return Category.ADMIN;
}
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ if (request.getParams().get("set") != null) {
+ return Name.CONFIG_EDIT_PERM; // Change log level
+ } else {
+ return Name.CONFIG_READ_PERM;
+ }
+ }
}
\ No newline at end of file
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java
index 7f6f8cb..ae76eaa 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java
@@ -79,6 +79,7 @@ import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.update.SolrIndexWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -105,6 +106,11 @@ public class LukeRequestHandler extends RequestHandlerBase
static final int HIST_ARRAY_SIZE = 33;
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.READ_PERM;
+ }
+
private static enum ShowStyle {
ALL,
DOC,
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/MetricsCollectorHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/MetricsCollectorHandler.java
index a445c0d..8308820 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/MetricsCollectorHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/MetricsCollectorHandler.java
@@ -40,6 +40,7 @@ import org.apache.solr.metrics.SolrMetricManager;
import org.apache.solr.metrics.reporters.solr.SolrReporter;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.update.AddUpdateCommand;
import org.apache.solr.update.CommitUpdateCommand;
import org.apache.solr.update.DeleteUpdateCommand;
@@ -137,6 +138,11 @@ public class MetricsCollectorHandler extends RequestHandlerBase {
return "Handler for collecting and aggregating SolrCloud metric reports.";
}
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.METRICS_READ_PERM;
+ }
+
private static class MetricUpdateProcessor extends UpdateRequestProcessor {
private final SolrMetricManager metricManager;
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/PluginInfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/PluginInfoHandler.java
index 7d9c3d7..d5ede4a 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/PluginInfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/PluginInfoHandler.java
@@ -25,6 +25,7 @@ import org.apache.solr.core.SolrInfoBean;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import static org.apache.solr.common.params.CommonParams.NAME;
@@ -82,4 +83,9 @@ public class PluginInfoHandler extends RequestHandlerBase
public Category getCategory() {
return Category.ADMIN;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.METRICS_READ_PERM;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java
index 57a7492..4aa74e1 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/PropertiesRequestHandler.java
@@ -24,6 +24,7 @@ import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.util.RedactionUtils;
import static org.apache.solr.common.params.CommonParams.NAME;
@@ -75,4 +76,9 @@ public class PropertiesRequestHandler extends RequestHandlerBase
public Category getCategory() {
return Category.ADMIN;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.CONFIG_READ_PERM;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SegmentsInfoRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SegmentsInfoRequestHandler.java
index a5da9f1..05d4d75 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/SegmentsInfoRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/SegmentsInfoRequestHandler.java
@@ -32,6 +32,7 @@ import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.update.SolrIndexWriter;
import org.apache.solr.util.RefCounted;
import org.slf4j.Logger;
@@ -434,4 +435,9 @@ public class SegmentsInfoRequestHandler extends RequestHandlerBase {
public Category getCategory() {
return Category.ADMIN;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.METRICS_READ_PERM;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java
index bbf3c30..80502c6 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/SolrInfoMBeanHandler.java
@@ -28,6 +28,7 @@ import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.response.BinaryResponseWriter;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import java.io.StringReader;
import java.text.NumberFormat;
@@ -295,4 +296,9 @@ public class SolrInfoMBeanHandler extends RequestHandlerBase {
public Category getCategory() {
return Category.ADMIN;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.METRICS_READ_PERM;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
index 5a3c7b9..f5cd500 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java
@@ -41,6 +41,7 @@ import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.AuthorizationPlugin;
import org.apache.solr.security.RuleBasedAuthorizationPluginBase;
import org.apache.solr.util.RTimer;
@@ -420,7 +421,11 @@ public class SystemInfoHandler extends RequestHandlerBase
}
return list;
}
-
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.CONFIG_READ_PERM;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ThreadDumpHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/ThreadDumpHandler.java
index e13a0a0..ad37344 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ThreadDumpHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ThreadDumpHandler.java
@@ -30,6 +30,7 @@ import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import static org.apache.solr.common.params.CommonParams.ID;
import static org.apache.solr.common.params.CommonParams.NAME;
@@ -172,4 +173,9 @@ public class ThreadDumpHandler extends RequestHandlerBase
public Category getCategory() {
return Category.ADMIN;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.METRICS_READ_PERM;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperInfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperInfoHandler.java
index 1c54f10..41fd300 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperInfoHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperInfoHandler.java
@@ -46,6 +46,7 @@ import org.apache.solr.common.cloud.ClusterState;
import org.apache.solr.common.cloud.DocCollection;
import org.apache.solr.common.cloud.OnReconnect;
import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.MapSolrParams;
@@ -58,6 +59,7 @@ import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.JSONResponseWriter;
import org.apache.solr.response.RawResponseWriter;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.util.SimplePostTool.BAOS;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
@@ -80,6 +82,7 @@ import static org.apache.solr.common.params.CommonParams.WT;
* @since solr 4.0
*/
public final class ZookeeperInfoHandler extends RequestHandlerBase {
+ private static final String PARAM_DETAIL = "detail";
private final CoreContainer cores;
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -103,6 +106,18 @@ public final class ZookeeperInfoHandler extends RequestHandlerBase {
return Category.ADMIN;
}
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ var params = request.getParams();
+ String path = params.get(PATH, "");
+ String detail = params.get(PARAM_DETAIL, "false");
+ if ("/security.json".equalsIgnoreCase(path) && "true".equalsIgnoreCase(detail)) {
+ return Name.SECURITY_READ_PERM;
+ } else {
+ return Name.ZK_READ_PERM;
+ }
+ }
+
/**
* Enumeration of ways to filter collections on the graph panel.
*/
@@ -184,15 +199,15 @@ public final class ZookeeperInfoHandler extends RequestHandlerBase {
boolean hasDownedShard = false; // means one or more shards is down
boolean replicaInRecovery = false;
- Map<String, Object> shards = (Map<String, Object>) collectionState.get("shards");
+ Map<String, Object> shards = (Map<String, Object>) collectionState.get(DocCollection.SHARDS);
for (Object o : shards.values()) {
boolean hasActive = false;
Map<String, Object> shard = (Map<String, Object>) o;
- Map<String, Object> replicas = (Map<String, Object>) shard.get("replicas");
+ Map<String, Object> replicas = (Map<String, Object>) shard.get(Slice.REPLICAS);
for (Object value : replicas.values()) {
Map<String, Object> replicaState = (Map<String, Object>) value;
Replica.State coreState = Replica.State.getState((String) replicaState.get(ZkStateReader.STATE_PROP));
- String nodeName = (String) replicaState.get("node_name");
+ String nodeName = (String) replicaState.get(ZkStateReader.NODE_NAME_PROP);
// state can lie to you if the node is offline, so need to reconcile with live_nodes too
if (!liveNodes.contains(nodeName))
@@ -377,7 +392,7 @@ public final class ZookeeperInfoHandler extends RequestHandlerBase {
throw new SolrException(ErrorCode.BAD_REQUEST, "Illegal parameter \"addr\"");
}
- String detailS = params.get("detail");
+ String detailS = params.get(PARAM_DETAIL);
boolean detail = detailS != null && detailS.equals("true");
String dumpS = params.get("dump");
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperReadAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperReadAPI.java
index 0220ca5..3b3d51f 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperReadAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperReadAPI.java
@@ -45,6 +45,7 @@ import org.apache.zookeeper.data.Stat;
import static org.apache.solr.common.params.CommonParams.OMIT_HEADER;
import static org.apache.solr.common.params.CommonParams.WT;
import static org.apache.solr.response.RawResponseWriter.CONTENT;
+import static org.apache.solr.security.PermissionNameProvider.Name.SECURITY_READ_PERM;
import static org.apache.solr.security.PermissionNameProvider.Name.ZK_READ_PERM;
/**
@@ -58,41 +59,42 @@ import static org.apache.solr.security.PermissionNameProvider.Name.ZK_READ_PERM;
public class ZookeeperReadAPI {
private final CoreContainer coreContainer;
+ private final SolrParams rawWtParams;
public ZookeeperReadAPI(CoreContainer coreContainer) {
this.coreContainer = coreContainer;
+ Map<String, String> map = new HashMap<>(1);
+ map.put(WT, "raw");
+ map.put(OMIT_HEADER, "true");
+ rawWtParams = new MapSolrParams(map);
}
+
+ /**
+ * Request contents of a znode, except security.json
+ */
@EndPoint(path = "/cluster/zk/data/*",
method = SolrRequest.METHOD.GET,
permission = ZK_READ_PERM)
public void readNode(SolrQueryRequest req, SolrQueryResponse rsp) {
String path = req.getPathTemplateValues().get("*");
if (path == null || path.isEmpty()) path = "/";
- byte[] d = null;
- try {
- d = coreContainer.getZkController().getZkClient().getData(path, null, null, false);
- } catch (KeeperException.NoNodeException e) {
- throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "No such node: " + path);
- } catch (Exception e) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unexpected error", e);
- }
- if (d == null || d.length == 0) {
- rsp.add(path, null);
- return;
- }
-
- Map<String, String> map = new HashMap<>(1);
- map.put(WT, "raw");
- map.put(OMIT_HEADER, "true");
- req.setParams(SolrParams.wrapDefaults(new MapSolrParams(map), req.getParams()));
-
- String mime = BinaryResponseParser.BINARY_CONTENT_TYPE;
+ readNodeAndAddToResponse(path, req, rsp);
+ }
- if (d[0] == '{') mime = CommonParams.JSON_MIME;
- if (d[0] == '<' || d[1] == '?') mime = XMLResponseParser.XML_CONTENT_TYPE;
- rsp.add(CONTENT, new ContentStreamBase.ByteArrayStream(d, null, mime));
+ /**
+ * Request contents of the security.json node
+ */
+ @EndPoint(path = "/cluster/zk/data/security.json",
+ method = SolrRequest.METHOD.GET,
+ permission = SECURITY_READ_PERM)
+ public void readSecurityJsonNode(SolrQueryRequest req, SolrQueryResponse rsp) {
+ String path = "/security.json";
+ readNodeAndAddToResponse(path, req, rsp);
}
+ /**
+ * List the children of a certain zookeeper znode
+ */
@EndPoint(path = "/cluster/zk/ls/*",
method = SolrRequest.METHOD.GET,
permission = ZK_READ_PERM)
@@ -130,6 +132,49 @@ public class ZookeeperReadAPI {
}
}
+ /**
+ * Simple mime type guessing based on first character of the response
+ */
+ private String guessMime(byte firstByte) {
+ switch(firstByte) {
+ case '{':
+ return CommonParams.JSON_MIME;
+ case '<':
+ case '?':
+ return XMLResponseParser.XML_CONTENT_TYPE;
+ default:
+ return BinaryResponseParser.BINARY_CONTENT_TYPE;
+ }
+ }
+
+ /**
+ * Reads content of a znode and adds it to the response
+ */
+ private void readNodeAndAddToResponse(String zkPath, SolrQueryRequest req, SolrQueryResponse rsp) {
+ byte[] d = readPathFromZookeeper(zkPath);
+ if (d == null || d.length == 0) {
+ rsp.add(zkPath, null);
+ return;
+ }
+ req.setParams(SolrParams.wrapDefaults(rawWtParams, req.getParams()));
+ rsp.add(CONTENT, new ContentStreamBase.ByteArrayStream(d, null, guessMime(d[0])));
+ }
+
+ /**
+ * Reads a single node from zookeeper and return as byte array
+ */
+ private byte[] readPathFromZookeeper(String path) {
+ byte[] d;
+ try {
+ d = coreContainer.getZkController().getZkClient().getData(path, null, null, false);
+ } catch (KeeperException.NoNodeException e) {
+ throw new SolrException(SolrException.ErrorCode.NOT_FOUND, "No such node: " + path);
+ } catch (Exception e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unexpected error", e);
+ }
+ return d;
+ }
+
private void printStat(MapWriter.EntryWriter ew, Stat stat) throws IOException {
ew.put("version", stat.getVersion());
ew.put("aversion", stat.getAversion());
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperStatusHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperStatusHandler.java
index 5bf7770..4d1223b 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperStatusHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/ZookeeperStatusHandler.java
@@ -40,6 +40,7 @@ import org.apache.solr.core.CoreContainer;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -329,4 +330,9 @@ public class ZookeeperStatusHandler extends RequestHandlerBase {
}
return true;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.CONFIG_READ_PERM;
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/handler/tagger/TaggerRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/tagger/TaggerRequestHandler.java
index 903f0ae..1bb0cc0 100644
--- a/solr/core/src/java/org/apache/solr/handler/tagger/TaggerRequestHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/tagger/TaggerRequestHandler.java
@@ -70,6 +70,7 @@ import org.apache.solr.search.QParser;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.search.SolrReturnFields;
import org.apache.solr.search.SyntaxError;
+import org.apache.solr.security.AuthorizationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -239,6 +240,11 @@ public class TaggerRequestHandler extends RequestHandlerBase {
rsp.add("response", getDocList(rows, matchDocIdsBS));
}
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.READ_PERM;
+ }
+
private static class InputStringLazy implements Callable<String> {
final Reader inputReader;
String inputString;
diff --git a/solr/core/src/java/org/apache/solr/search/function/FileFloatSource.java b/solr/core/src/java/org/apache/solr/search/function/FileFloatSource.java
index 19ae494..f42fe54 100644
--- a/solr/core/src/java/org/apache/solr/search/function/FileFloatSource.java
+++ b/solr/core/src/java/org/apache/solr/search/function/FileFloatSource.java
@@ -47,6 +47,7 @@ import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.SchemaField;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.update.processor.UpdateRequestProcessor;
import org.apache.solr.util.VersionedFile;
import org.slf4j.Logger;
@@ -362,5 +363,10 @@ public class FileFloatSource extends ValueSource {
public String getDescription() {
return "Reload readerCache request handler";
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.UPDATE_PERM;
+ }
}
}
diff --git a/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java b/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
index bac5e8a..3f6137c 100644
--- a/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
+++ b/solr/core/src/java/org/apache/solr/security/PermissionNameProvider.java
@@ -33,6 +33,7 @@ import static java.util.stream.Collectors.toMap;
* at request time
*/
public interface PermissionNameProvider {
+ // 'null' means the permission applies to system-level, while '*' means any collection
enum Name {
COLL_EDIT_PERM("collection-admin-edit", null),
COLL_READ_PERM("collection-admin-read", null),
@@ -47,14 +48,14 @@ public interface PermissionNameProvider {
SCHEMA_EDIT_PERM("schema-edit", "*"),
SECURITY_EDIT_PERM("security-edit", null),
SECURITY_READ_PERM("security-read", null),
- METRICS_READ_PERM("metrics-read", null),
+ METRICS_READ_PERM("metrics-read", unmodifiableSet(new HashSet<>(asList("*", null)))),
+ HEALTH_PERM("health", unmodifiableSet(new HashSet<>(asList("*", null)))),
FILESTORE_READ_PERM("filestore-read", null),
FILESTORE_WRITE_PERM("filestore-write", null),
PACKAGE_EDIT_PERM("package-edit", null),
PACKAGE_READ_PERM("package-read", null),
- ALL("all", unmodifiableSet(new HashSet<>(asList("*", null))))
- ;
+ ALL("all", unmodifiableSet(new HashSet<>(asList("*", null))));
final String name;
final Set<String> collName;
diff --git a/solr/core/src/java/org/apache/solr/security/PublicKeyHandler.java b/solr/core/src/java/org/apache/solr/security/PublicKeyHandler.java
index 208fe6c..a0d7d01 100644
--- a/solr/core/src/java/org/apache/solr/security/PublicKeyHandler.java
+++ b/solr/core/src/java/org/apache/solr/security/PublicKeyHandler.java
@@ -77,4 +77,9 @@ public class PublicKeyHandler extends RequestHandlerBase {
public Category getCategory() {
return Category.ADMIN;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.ALL;
+ }
}
diff --git a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
index 9b9cdc5..55946f3 100644
--- a/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
+++ b/solr/core/src/test/org/apache/solr/BasicFunctionalityTest.java
@@ -53,6 +53,7 @@ import org.apache.solr.schema.IndexSchemaFactory;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.DocIterator;
import org.apache.solr.search.DocList;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.util.BaseTestHarness;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -455,7 +456,12 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 {
public void testRequestHandlerBaseException() {
final String tmp = "BOO! ignore_exception";
SolrRequestHandler handler = new RequestHandlerBase() {
- @Override
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.ALL;
+ }
+
+ @Override
public String getDescription() { return tmp; }
@Override
public void handleRequestBody
diff --git a/solr/core/src/test/org/apache/solr/core/MockQuerySenderListenerReqHandler.java b/solr/core/src/test/org/apache/solr/core/MockQuerySenderListenerReqHandler.java
index 1dcad73..4970f47 100644
--- a/solr/core/src/test/org/apache/solr/core/MockQuerySenderListenerReqHandler.java
+++ b/solr/core/src/test/org/apache/solr/core/MockQuerySenderListenerReqHandler.java
@@ -21,6 +21,7 @@ import org.apache.solr.metrics.SolrMetricsContext;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.common.util.NamedList;
+import org.apache.solr.security.AuthorizationContext;
import java.util.concurrent.atomic.AtomicInteger;
@@ -58,4 +59,9 @@ public class MockQuerySenderListenerReqHandler extends RequestHandlerBase {
String result = null;
return result;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.ALL;
+ }
}
diff --git a/solr/core/src/test/org/apache/solr/core/SolrCoreTest.java b/solr/core/src/test/org/apache/solr/core/SolrCoreTest.java
index 6c70bd3..3ab5b9d 100644
--- a/solr/core/src/test/org/apache/solr/core/SolrCoreTest.java
+++ b/solr/core/src/test/org/apache/solr/core/SolrCoreTest.java
@@ -27,6 +27,7 @@ import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.update.SolrCoreState;
import org.apache.solr.common.util.SolrNamedThreadFactory;
import org.apache.solr.util.RefCounted;
@@ -360,4 +361,9 @@ class EmptyRequestHandler extends RequestHandlerBase
}
@Override public String getDescription() { return null; }
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.ALL;
+ }
}
diff --git a/solr/core/src/test/org/apache/solr/handler/ThrowErrorOnInitRequestHandler.java b/solr/core/src/test/org/apache/solr/handler/ThrowErrorOnInitRequestHandler.java
index 39c758e..0ac4bf2 100644
--- a/solr/core/src/test/org/apache/solr/handler/ThrowErrorOnInitRequestHandler.java
+++ b/solr/core/src/test/org/apache/solr/handler/ThrowErrorOnInitRequestHandler.java
@@ -21,6 +21,7 @@ import java.io.IOException;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
/**
* throws a {@link java.lang.Error} on init for testing purposes
@@ -48,4 +49,9 @@ public class ThrowErrorOnInitRequestHandler extends RequestHandlerBase
}
throw new Error("Doing my job, throwing a java.lang.Error");
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.ALL;
+ }
}
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
index adcc305..a2e3a2a 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
@@ -37,6 +37,7 @@ import org.apache.solr.metrics.SolrMetricsContext;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.security.AuthorizationContext;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -534,5 +535,10 @@ public class MetricsHandlerTest extends SolrTestCaseJ4 {
public Boolean registerV2() {
return Boolean.FALSE;
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.METRICS_READ_PERM;
+ }
}
}
diff --git a/solr/core/src/test/org/apache/solr/pkg/TestPackages.java b/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
index de68236..4c91daa 100644
--- a/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
+++ b/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
@@ -52,6 +52,7 @@ import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.QParser;
import org.apache.solr.search.QParserPlugin;
+import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.util.LogLevel;
import org.apache.solr.util.SimplePostTool;
import org.apache.solr.util.plugin.SolrCoreAware;
@@ -669,6 +670,11 @@ public class TestPackages extends SolrCloudTestCase {
public String getDescription() {
return "test";
}
+
+ @Override
+ public Name getPermissionName(AuthorizationContext request) {
+ return Name.ALL;
+ }
}
public static class C2 extends QParserPlugin implements ResourceLoaderAware {
diff --git a/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc b/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc
index cd8c41e..2750c68 100644
--- a/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc
+++ b/solr/solr-ref-guide/src/major-changes-in-solr-9.adoc
@@ -80,6 +80,8 @@ All the usages are replaced by BaseHttpSolrClient.RemoteSolrException and BaseHt
* SOLR-15409: Zookeeper client libraries upgraded to 3.7.0, which may not be compatible with your existing server installations
+* SOLR-11623: Every request handler in Solr now implements PermissionNameProvider. Any custom or 3rd party request handler must also do this
+
== New Features & Enhancements
diff --git a/solr/solr-ref-guide/src/rule-based-authorization-plugin.adoc b/solr/solr-ref-guide/src/rule-based-authorization-plugin.adoc
index 68634d6..2d4f796 100644
--- a/solr/solr-ref-guide/src/rule-based-authorization-plugin.adoc
+++ b/solr/solr-ref-guide/src/rule-based-authorization-plugin.adoc
@@ -406,7 +406,8 @@ If edit permissions should only be applied to specific collections, a custom per
* *config-read*: this permission is allowed to read a collection's configuration using the <<config-api.adoc#,Config API>>, the <<request-parameters-api.adoc#,Request Parameters API>>, <<configsets-api.adoc#configsets-list,Configsets API>>, the Admin UI's <<configuration-files.adoc#files-screen,Files Screen>>, and other APIs accessing configuration.
Note that this allows configuration read permissions for _all_ collections.
If read permissions should only be applied to specific collections, a custom permission would need to be created.
-* *metrics-read*: this permission allows access to Solr's <<metrics-reporting.adoc#metrics-api,Metrics API>>.
+* *metrics-read*: this permission allows access to Solr's <<metrics-reporting.adoc#metrics-api,Metrics API>>, some <<implicit-requesthandlers#admin-handlers,implicit admin handlers>> such as `solr/<collection>/admin/mbeans` and `solr/<collection>/admin/segments` as well as other admin APIs exposing metrics.
+* *health*: this permission allows access to Solr's <<implicit-requesthandlers#admin-handlers,Health Check and Ping>> endpoints, typically used to monitor whether a node or core is healthy.
* *core-admin-edit*: Core admin commands that can mutate the system state.
* *core-admin-read*: Read operations on the core admin API
* *collection-admin-edit*: this permission is allowed to edit a collection's configuration using the <<collections-api.adoc#,Collections API>>.
diff --git a/solr/webapp/web/index.html b/solr/webapp/web/index.html
index 7e90c2b..64a5f8e 100644
--- a/solr/webapp/web/index.html
+++ b/solr/webapp/web/index.html
@@ -120,6 +120,13 @@ limitations under the License.
</div>
+ <div class="header-message" id="authz-failures" ng-show="showAuthzFailures" ng-cloak>
+
+ <h2>Permission failure</h2>
+ <p>Your user does not have the necessary role(s) to perform this action.</p>
+
+ </div>
+
<div id="loading" class="loader universal-loader" loading-status-message> </div>
<div id="connection-box" connection-message>
diff --git a/solr/webapp/web/js/angular/app.js b/solr/webapp/web/js/angular/app.js
index 1d04f97..437a7e2 100644
--- a/solr/webapp/web/js/angular/app.js
+++ b/solr/webapp/web/js/angular/app.js
@@ -387,6 +387,7 @@ solrAdminApp.config([
if (activeRequests == 0) {
$rootScope.$broadcast('loadingStatusInactive');
}
+ $rootScope.showAuthzFailures = false;
if ($rootScope.retryCount>0) {
$rootScope.connectionRecovered = true;
$rootScope.retryCount=0;
@@ -442,6 +443,9 @@ solrAdminApp.config([
sessionStorage.setItem("auth.location", $location.path());
$location.path('/login');
}
+ } else if (rejection.status === 403 && !isHandledBySchemaDesigner) {
+ // No permission
+ $rootScope.showAuthzFailures = true;
} else {
// schema designer prefers to handle errors itself
if (!isHandledBySchemaDesigner) {
diff --git a/solr/webapp/web/js/angular/controllers/security.js b/solr/webapp/web/js/angular/controllers/security.js
index 54e1d6c..473ffba 100644
--- a/solr/webapp/web/js/angular/controllers/security.js
+++ b/solr/webapp/web/js/angular/controllers/security.js
@@ -142,9 +142,10 @@ solrAdminApp.controller('SecurityController', function ($scope, $timeout, $cooki
return (!obj || (Array.isArray(obj) && obj.length === 0)) ? "null" : $scope.displayList(obj);
};
+ // TODO: Read this list from Solr to avoid duplication
$scope.predefinedPermissions = ["collection-admin-edit", "collection-admin-read", "core-admin-read", "core-admin-edit", "zk-read",
"read", "update", "all", "config-edit", "config-read", "schema-read", "schema-edit", "security-edit", "security-read",
- "metrics-read", "filestore-read", "filestore-write", "package-edit", "package-read"].sort();
+ "metrics-read", "health", "filestore-read", "filestore-write", "package-edit", "package-read"].sort();
$scope.predefinedPermissionCollection = {"read":"*", "update":"*", "config-edit":"*", "config-read":"*", "schema-edit":"*", "schema-read":"*"};