Skip to content

Commit 71e173f

Browse files
committed
ZOOKEEPER-4964. Check permissions individually during admin server auth
Reviewers: andor, phunt Author: ztzg
1 parent 641cf00 commit 71e173f

File tree

3 files changed

+59
-12
lines changed

3 files changed

+59
-12
lines changed

zookeeper-server/src/main/java/org/apache/zookeeper/server/admin/Commands.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,18 @@ private static List<Id> handleAuthentication(final HttpServletRequest request, f
227227
}
228228
}
229229

230+
/**
231+
* Grant or deny authorization for a command by matching
232+
* request-provided credentials with the ACLs present on a node.
233+
*
234+
* @param zkServer the ZooKeeper server object.
235+
* @param ids the credentials extracted from the Authorization header.
236+
* @param perm the set of permission bits required by the command.
237+
* @param path the ZooKeeper node path whose ACLs should be used
238+
* to satisfy the perm bits.
239+
* @throws KeeperException.NoAuthException if one or more perm
240+
* bits could not be satisfied.
241+
*/
230242
private static void handleAuthorization(final ZooKeeperServer zkServer,
231243
final List<Id> ids,
232244
final int perm,
@@ -237,7 +249,14 @@ private static void handleAuthorization(final ZooKeeperServer zkServer,
237249
throw new KeeperException.NoNodeException(path);
238250
}
239251
final List<ACL> acls = zkServer.getZKDatabase().aclForNode(dataNode);
240-
zkServer.checkACL(null, acls, perm, ids, path, null);
252+
// Check the individual bits of perm.
253+
final int bitWidth = Integer.SIZE - Integer.numberOfLeadingZeros(perm);
254+
for (int b = 0; b < bitWidth; b++) {
255+
final int permBit = 1 << b;
256+
if ((perm & permBit) != 0) {
257+
zkServer.checkACL(null, acls, permBit, ids, path, null);
258+
}
259+
}
241260
}
242261

243262
/**

zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/CommandAuthTest.java

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@
2929
import java.net.HttpURLConnection;
3030
import java.net.URL;
3131
import java.nio.charset.StandardCharsets;
32+
import java.util.ArrayList;
3233
import java.util.Arrays;
3334
import java.util.Collections;
3435
import java.util.HashMap;
36+
import java.util.List;
3537
import java.util.Map;
3638
import javax.net.ssl.HttpsURLConnection;
3739
import javax.net.ssl.SSLContext;
@@ -185,6 +187,22 @@ public void testAuthCheck_noACL(final AuthSchema authSchema) throws Exception {
185187
assertEquals(HttpURLConnection.HTTP_OK, authTestConn.getResponseCode());
186188
}
187189

190+
@ParameterizedTest
191+
@EnumSource(value = AuthSchema.class, names = {"DIGEST"})
192+
public void testAuthCheck_noPerms(final AuthSchema authSchema) throws Exception {
193+
// The extra ACL entry gives Perms.READ perms to the "invalid"
194+
// DIGEST authInfo---but that should not permit access, as
195+
// AuthTestCommand requires Perms.ADMIN.
196+
setupRootACL(authSchema, ZooDefs.Ids.READ_ACL_UNSAFE);
197+
try {
198+
final HttpURLConnection authTestConn = sendAuthTestCommandRequest(authSchema, false);
199+
assertEquals(HttpURLConnection.HTTP_FORBIDDEN, authTestConn.getResponseCode());
200+
} finally {
201+
addAuthInfo(zk, authSchema);
202+
resetRootACL(zk);
203+
}
204+
}
205+
188206
@Test
189207
public void testAuthCheck_invalidServerRequiredConfig() {
190208
assertThrows("An active server is required for auth check",
@@ -300,19 +318,29 @@ public void clearTLS() {
300318
}
301319

302320
private void setupRootACL(final AuthSchema authSchema) throws Exception {
321+
setupRootACL(authSchema, Collections.<ACL>emptyList());
322+
}
323+
324+
private void setupRootACL(final AuthSchema authSchema, final List<ACL> extraEntries) throws Exception {
325+
final List<ACL> aclEntries = new ArrayList<>();
326+
303327
switch (authSchema) {
304328
case DIGEST:
305-
setupRootACLForDigest(zk);
329+
aclEntries.addAll(genACLForDigest());
306330
break;
307331
case X509:
308-
setupRootACLForX509(zk);
332+
aclEntries.addAll(genACLForX509());
309333
break;
310334
case IP:
311-
setupRootACLForIP(zk);
335+
aclEntries.addAll(genACLForIP());
312336
break;
313337
default:
314338
throw new IllegalArgumentException("Unknown auth schema");
315339
}
340+
341+
aclEntries.addAll(extraEntries);
342+
343+
zk.setACL(Commands.ROOT_PATH, aclEntries, -1);
316344
}
317345

318346
private HttpURLConnection sendAuthTestCommandRequest(final AuthSchema authSchema, final boolean validAuthInfo) throws Exception {
@@ -343,22 +371,22 @@ public static void resetRootACL(final ZooKeeper zk) throws Exception {
343371
zk.setACL(Commands.ROOT_PATH, OPEN_ACL_UNSAFE, -1);
344372
}
345373

346-
public static void setupRootACLForDigest(final ZooKeeper zk) throws Exception {
374+
public static List<ACL> genACLForDigest() throws Exception {
347375
final String idPassword = String.format("%s:%s", ROOT_USER, ROOT_PASSWORD);
348376
final String digest = DigestAuthenticationProvider.generateDigest(idPassword);
349377

350378
final ACL acl = new ACL(ZooDefs.Perms.ALL, new Id(DIGEST_SCHEMA, digest));
351-
zk.setACL(Commands.ROOT_PATH, Collections.singletonList(acl), -1);
379+
return Collections.singletonList(acl);
352380
}
353381

354-
private static void setupRootACLForX509(final ZooKeeper zk) throws Exception {
382+
private static List<ACL> genACLForX509() throws Exception {
355383
final ACL acl = new ACL(ZooDefs.Perms.ALL, new Id(X509_SCHEMA, X509_SUBJECT_PRINCIPAL));
356-
zk.setACL(Commands.ROOT_PATH, Collections.singletonList(acl), -1);
384+
return Collections.singletonList(acl);
357385
}
358386

359-
private static void setupRootACLForIP(final ZooKeeper zk) throws Exception {
387+
private static List<ACL> genACLForIP() throws Exception {
360388
final ACL acl = new ACL(ZooDefs.Perms.ALL, new Id(IP_SCHEMA, "127.0.0.1"));
361-
zk.setACL(Commands.ROOT_PATH, Collections.singletonList(acl), -1);
389+
return Collections.singletonList(acl);
362390
}
363391

364392
public static void addAuthInfoForDigest(final ZooKeeper zk) {

zookeeper-server/src/test/java/org/apache/zookeeper/server/admin/SnapshotAndRestoreCommandTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import static org.apache.zookeeper.server.ZooKeeperServer.ZOOKEEPER_SERIALIZE_LAST_PROCESSED_ZXID_ENABLED;
2222
import static org.apache.zookeeper.server.admin.CommandAuthTest.addAuthInfoForDigest;
2323
import static org.apache.zookeeper.server.admin.CommandAuthTest.resetRootACL;
24-
import static org.apache.zookeeper.server.admin.CommandAuthTest.setupRootACLForDigest;
24+
import static org.apache.zookeeper.server.admin.CommandAuthTest.genACLForDigest;
2525
import static org.apache.zookeeper.server.admin.Commands.ADMIN_RATE_LIMITER_INTERVAL;
2626
import static org.apache.zookeeper.server.admin.Commands.RestoreCommand.ADMIN_RESTORE_ENABLED;
2727
import static org.apache.zookeeper.server.admin.Commands.SnapshotCommand.ADMIN_SNAPSHOT_ENABLED;
@@ -119,7 +119,7 @@ public void setup() throws Exception {
119119
zk = ClientBase.createZKClient(hostPort);
120120

121121
// setup root ACL
122-
setupRootACLForDigest(zk);
122+
zk.setACL(Commands.ROOT_PATH, genACLForDigest(), -1);
123123

124124
// add auth
125125
addAuthInfoForDigest(zk);

0 commit comments

Comments
 (0)