This is an automated email from the ASF dual-hosted git repository.
andor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zookeeper.git
The following commit(s) were added to refs/heads/master by this push:
new 71e173fcb ZOOKEEPER-4964. Check permissions individually during admin
server auth
71e173fcb is described below
commit 71e173fcbcc9deb784081cf867bd045df3c32635
Author: Andor Molnar <[email protected]>
AuthorDate: Tue Aug 19 13:32:52 2025 -0500
ZOOKEEPER-4964. Check permissions individually during admin server auth
Reviewers: andor, phunt
Author: ztzg
---
.../apache/zookeeper/server/admin/Commands.java | 21 +++++++++-
.../zookeeper/server/admin/CommandAuthTest.java | 46 +++++++++++++++++-----
.../admin/SnapshotAndRestoreCommandTest.java | 4 +-
3 files changed, 59 insertions(+), 12 deletions(-)
diff --git
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java
index cd5adc76a..ae7c43691 100644
---
a/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java
+++
b/zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java
@@ -227,6 +227,18 @@ private static List<Id> handleAuthentication(final
HttpServletRequest request, f
}
}
+ /**
+ * Grant or deny authorization for a command by matching
+ * request-provided credentials with the ACLs present on a node.
+ *
+ * @param zkServer the ZooKeeper server object.
+ * @param ids the credentials extracted from the Authorization header.
+ * @param perm the set of permission bits required by the command.
+ * @param path the ZooKeeper node path whose ACLs should be used
+ * to satisfy the perm bits.
+ * @throws KeeperException.NoAuthException if one or more perm
+ * bits could not be satisfied.
+ */
private static void handleAuthorization(final ZooKeeperServer zkServer,
final List<Id> ids,
final int perm,
@@ -237,7 +249,14 @@ private static void handleAuthorization(final
ZooKeeperServer zkServer,
throw new KeeperException.NoNodeException(path);
}
final List<ACL> acls = zkServer.getZKDatabase().aclForNode(dataNode);
- zkServer.checkACL(null, acls, perm, ids, path, null);
+ // Check the individual bits of perm.
+ final int bitWidth = Integer.SIZE - Integer.numberOfLeadingZeros(perm);
+ for (int b = 0; b < bitWidth; b++) {
+ final int permBit = 1 << b;
+ if ((perm & permBit) != 0) {
+ zkServer.checkACL(null, acls, permBit, ids, path, null);
+ }
+ }
}
/**
diff --git
a/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandAuthTest.java
b/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandAuthTest.java
index a6f200a10..ed5dea56c 100644
---
a/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandAuthTest.java
+++
b/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandAuthTest.java
@@ -29,9 +29,11 @@
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
@@ -185,6 +187,22 @@ public void testAuthCheck_noACL(final AuthSchema
authSchema) throws Exception {
assertEquals(HttpURLConnection.HTTP_OK,
authTestConn.getResponseCode());
}
+ @ParameterizedTest
+ @EnumSource(value = AuthSchema.class, names = {"DIGEST"})
+ public void testAuthCheck_noPerms(final AuthSchema authSchema) throws
Exception {
+ // The extra ACL entry gives Perms.READ perms to the "invalid"
+ // DIGEST authInfo---but that should not permit access, as
+ // AuthTestCommand requires Perms.ADMIN.
+ setupRootACL(authSchema, ZooDefs.Ids.READ_ACL_UNSAFE);
+ try {
+ final HttpURLConnection authTestConn =
sendAuthTestCommandRequest(authSchema, false);
+ assertEquals(HttpURLConnection.HTTP_FORBIDDEN,
authTestConn.getResponseCode());
+ } finally {
+ addAuthInfo(zk, authSchema);
+ resetRootACL(zk);
+ }
+ }
+
@Test
public void testAuthCheck_invalidServerRequiredConfig() {
assertThrows("An active server is required for auth check",
@@ -300,19 +318,29 @@ public void clearTLS() {
}
private void setupRootACL(final AuthSchema authSchema) throws Exception {
+ setupRootACL(authSchema, Collections.<ACL>emptyList());
+ }
+
+ private void setupRootACL(final AuthSchema authSchema, final List<ACL>
extraEntries) throws Exception {
+ final List<ACL> aclEntries = new ArrayList<>();
+
switch (authSchema) {
case DIGEST:
- setupRootACLForDigest(zk);
+ aclEntries.addAll(genACLForDigest());
break;
case X509:
- setupRootACLForX509(zk);
+ aclEntries.addAll(genACLForX509());
break;
case IP:
- setupRootACLForIP(zk);
+ aclEntries.addAll(genACLForIP());
break;
default:
throw new IllegalArgumentException("Unknown auth schema");
}
+
+ aclEntries.addAll(extraEntries);
+
+ zk.setACL(Commands.ROOT_PATH, aclEntries, -1);
}
private HttpURLConnection sendAuthTestCommandRequest(final AuthSchema
authSchema, final boolean validAuthInfo) throws Exception {
@@ -343,22 +371,22 @@ public static void resetRootACL(final ZooKeeper zk)
throws Exception {
zk.setACL(Commands.ROOT_PATH, OPEN_ACL_UNSAFE, -1);
}
- public static void setupRootACLForDigest(final ZooKeeper zk) throws
Exception {
+ public static List<ACL> genACLForDigest() throws Exception {
final String idPassword = String.format("%s:%s", ROOT_USER,
ROOT_PASSWORD);
final String digest =
DigestAuthenticationProvider.generateDigest(idPassword);
final ACL acl = new ACL(ZooDefs.Perms.ALL, new Id(DIGEST_SCHEMA,
digest));
- zk.setACL(Commands.ROOT_PATH, Collections.singletonList(acl), -1);
+ return Collections.singletonList(acl);
}
- private static void setupRootACLForX509(final ZooKeeper zk) throws
Exception {
+ private static List<ACL> genACLForX509() throws Exception {
final ACL acl = new ACL(ZooDefs.Perms.ALL, new Id(X509_SCHEMA,
X509_SUBJECT_PRINCIPAL));
- zk.setACL(Commands.ROOT_PATH, Collections.singletonList(acl), -1);
+ return Collections.singletonList(acl);
}
- private static void setupRootACLForIP(final ZooKeeper zk) throws Exception
{
+ private static List<ACL> genACLForIP() throws Exception {
final ACL acl = new ACL(ZooDefs.Perms.ALL, new Id(IP_SCHEMA,
"127.0.0.1"));
- zk.setACL(Commands.ROOT_PATH, Collections.singletonList(acl), -1);
+ return Collections.singletonList(acl);
}
public static void addAuthInfoForDigest(final ZooKeeper zk) {
diff --git
a/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/SnapshotAndRestoreCommandTest.java
b/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/SnapshotAndRestoreCommandTest.java
index a2f31fe86..03da02db8 100644
---
a/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/SnapshotAndRestoreCommandTest.java
+++
b/zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/SnapshotAndRestoreCommandTest.java
@@ -21,7 +21,7 @@
import static
org.apache.zookeeper.server.ZooKeeperServer.ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED;
import static
org.apache.zookeeper.server.admin.CommandAuthTest.addAuthInfoForDigest;
import static org.apache.zookeeper.server.admin.CommandAuthTest.resetRootACL;
-import static
org.apache.zookeeper.server.admin.CommandAuthTest.setupRootACLForDigest;
+import static
org.apache.zookeeper.server.admin.CommandAuthTest.genACLForDigest;
import static
org.apache.zookeeper.server.admin.Commands.ADMIN_RATE_LIMITER_INTERVAL;
import static
org.apache.zookeeper.server.admin.Commands.RestoreCommand.ADMIN_RESTORE_ENABLED;
import static
org.apache.zookeeper.server.admin.Commands.SnapshotCommand.ADMIN_SNAPSHOT_ENABLED;
@@ -119,7 +119,7 @@ public void setup() throws Exception {
zk = ClientBase.createZKClient(hostPort);
// setup root ACL
- setupRootACLForDigest(zk);
+ zk.setACL(Commands.ROOT_PATH, genACLForDigest(), -1);
// add auth
addAuthInfoForDigest(zk);