You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ja...@apache.org on 2021/11/19 09:02:28 UTC
[lucene-solr] branch branch_8x updated: SOLR-15768 Tune zookeeper request handler permissions (#2604)
This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
The following commit(s) were added to refs/heads/branch_8x by this push:
new c2f26ac SOLR-15768 Tune zookeeper request handler permissions (#2604)
c2f26ac is described below
commit c2f26ac784945dca6d096b58f2d0e98196562894
Author: Jan Høydahl <ja...@users.noreply.github.com>
AuthorDate: Fri Nov 19 10:02:07 2021 +0100
SOLR-15768 Tune zookeeper request handler permissions (#2604)
---
solr/CHANGES.txt | 2 +
.../solr/handler/admin/ZookeeperInfoHandler.java | 26 +++++--
.../solr/handler/admin/ZookeeperReadAPI.java | 91 ++++++++++++++++------
3 files changed, 91 insertions(+), 28 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 9c509d5..cfaca6f 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -17,6 +17,8 @@ Bug Fixes
* SOLR-15804: Admin UI once again can show files in the Core/Collection -> Files screen. Fixed regression from 8.11.0 (Karl Stoney, janhoy)
+* SOLR-15768: Set default permissions for the zookeeper endpoints so users won't need to configure custom permissions for these (janhoy)
+
* SOLR-15774: Avoid weird off-by-one errors with Angular's 'chosen' select box directive for the security and schema-designer screens in Admin UI (Timothy Potter)
================== 8.11.0 ==================
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 3396e8b..851a85b 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,8 @@ 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.security.PermissionNameProvider;
import org.apache.solr.util.SimplePostTool.BAOS;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
@@ -79,7 +82,8 @@ import static org.apache.solr.common.params.CommonParams.WT;
*
* @since solr 4.0
*/
-public final class ZookeeperInfoHandler extends RequestHandlerBase {
+public final class ZookeeperInfoHandler extends RequestHandlerBase implements PermissionNameProvider {
+ private static final String PARAM_DETAIL = "detail";
private final CoreContainer cores;
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -110,6 +114,18 @@ public final class ZookeeperInfoHandler extends RequestHandlerBase {
none, name, status
}
+ @Override
+ public PermissionNameProvider.Name getPermissionName(AuthorizationContext request) {
+ SolrParams params = request.getParams();
+ String path = params.get(PATH, "");
+ String detail = params.get(PARAM_DETAIL, "false");
+ if ("/security.json".equalsIgnoreCase(path) && "true".equalsIgnoreCase(detail)) {
+ return PermissionNameProvider.Name.SECURITY_READ_PERM;
+ } else {
+ return PermissionNameProvider.Name.ZK_READ_PERM;
+ }
+ }
+
/**
* Holds state of a single page of collections requested from the cloud panel.
*/
@@ -184,15 +200,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))
@@ -379,7 +395,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..68f6fee 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)
@@ -118,7 +120,7 @@ public class ZookeeperReadAPI {
}
rsp.add(path, (MapWriter) ew -> {
for (Map.Entry<String, Stat> e : stats.entrySet()) {
- ew.put(e.getKey(), (MapWriter) ew1 -> printStat(ew1, e.getValue()));
+ ew.put(e.getKey(), (MapWriter) ew1 -> printStat(ew1, e.getValue()));
}
});
} catch (KeeperException.NoNodeException e) {
@@ -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());