|
66 | 66 | import org.apache.zookeeper.server.ServerCnxn; |
67 | 67 | import org.apache.zookeeper.server.ServerMetrics; |
68 | 68 | import org.apache.zookeeper.server.auth.AuthenticationProvider; |
| 69 | +import org.apache.zookeeper.server.auth.DigestAuthenticationProvider; |
69 | 70 | import org.apache.zookeeper.server.auth.ProviderRegistry; |
70 | 71 | import org.apache.zookeeper.txn.CheckVersionTxn; |
71 | 72 | import org.apache.zookeeper.txn.CloseSessionTxn; |
|
74 | 75 | import org.apache.zookeeper.txn.DeleteTxn; |
75 | 76 | import org.apache.zookeeper.txn.ErrorTxn; |
76 | 77 | import org.apache.zookeeper.txn.MultiTxn; |
| 78 | +import org.apache.zookeeper.txn.SetACLTxn; |
77 | 79 | import org.apache.zookeeper.txn.SetDataTxn; |
78 | 80 | import org.apache.zookeeper.txn.Txn; |
79 | 81 | import org.apache.zookeeper.txn.TxnHeader; |
@@ -416,6 +418,115 @@ public void testEnhancedUsersRedactUnknownAndMalformedIdentities() throws Except |
416 | 418 | assertFalse(log.contains("synthetic")); |
417 | 419 | } |
418 | 420 |
|
| 421 | + @Test |
| 422 | + public void testAclModeSnapshotSurvivesOffToOnInterleaving() throws Exception { |
| 423 | + Request create = request(OpCode.create, createRecord("/mode-acl", new byte[0], CreateMode.PERSISTENT)); |
| 424 | + assertEquals(0, apply(create, OpCode.create, createTxn("/mode-acl", new byte[0], false)).err); |
| 425 | + String digest = DigestAuthenticationProvider.generateDigest("alice:synthetic-password"); |
| 426 | + List<ACL> acls = Collections.singletonList(new ACL(ZooDefs.Perms.ALL, new Id("digest", digest))); |
| 427 | + SetACLRequest record = new SetACLRequest("/mode-acl", acls, -1); |
| 428 | + AtomicInteger transitions = new AtomicInteger(); |
| 429 | + Request switching = new Request(cnxn, SESSION, 41, OpCode.setACL, ByteBuffer.wrap(serialize(record)), |
| 430 | + Collections.singletonList(new Id("ip", "127.0.0.1"))) { |
| 431 | + @Override |
| 432 | + public String getUsers() { |
| 433 | + System.setProperty(ENHANCED_ENABLE, "true"); |
| 434 | + transitions.incrementAndGet(); |
| 435 | + return super.getUsers(); |
| 436 | + } |
| 437 | + }; |
| 438 | + ProcessTxnResult changed = apply(switching, OpCode.setACL, new SetACLTxn("/mode-acl", acls, 1)); |
| 439 | + assertEquals(0, changed.err); |
| 440 | + System.setProperty(ENHANCED_ENABLE, "false"); |
| 441 | + AuditHelper.addAuditLog(switching, changed); |
| 442 | + Map<String, String> legacy = fields(capture.read(1).get(0)); |
| 443 | + assertEquals(1, transitions.get()); |
| 444 | + assertEquals("true", System.getProperty(ENHANCED_ENABLE)); |
| 445 | + assertNull("An in-flight legacy record must not be relabeled v2", legacy.get("schema_version")); |
| 446 | + assertEquals("digest:" + digest + ":cdrwa", legacy.get("acl")); |
| 447 | + assertEquals("success", legacy.get("result")); |
| 448 | + |
| 449 | + Request enhanced = request(OpCode.setACL, record); |
| 450 | + ProcessTxnResult updated = apply(enhanced, OpCode.setACL, new SetACLTxn("/mode-acl", acls, 2)); |
| 451 | + assertEquals(0, updated.err); |
| 452 | + AuditHelper.addAuditLog(enhanced, updated); |
| 453 | + String log = capture.read(1).get(0); |
| 454 | + assertWrite(fields(log), "setAcl", "/mode-acl", null, "committed", "0"); |
| 455 | + assertEquals("digest:alice:cdrwa", fields(log).get("acl")); |
| 456 | + assertFalse(log.contains(digest)); |
| 457 | + assertFalse(log.contains("synthetic-password")); |
| 458 | + } |
| 459 | + |
| 460 | + @Test |
| 461 | + public void testSuccessfulMultiKeepsOneModeAcrossMembers() throws Exception { |
| 462 | + Request request = request(OpCode.multi, new MultiOperationRecord(Arrays.asList( |
| 463 | + Op.create("/mode-multi", new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), |
| 464 | + Op.setData("/mode-multi", new byte[2], -1)))); |
| 465 | + ProcessTxnResult result = apply(request, OpCode.multi, new MultiTxn(Arrays.asList( |
| 466 | + txn(OpCode.create, createTxn("/mode-multi", new byte[1], false)), |
| 467 | + txn(OpCode.setData, new SetDataTxn("/mode-multi", new byte[2], 1))))); |
| 468 | + assertEquals(0, result.err); |
| 469 | + System.setProperty(ENHANCED_ENABLE, "false"); |
| 470 | + AtomicInteger events = new AtomicInteger(); |
| 471 | + AuditLogger delegate = new Slf4jAuditLogger(); |
| 472 | + Object previousLogger = replaceProviderField("auditLogger", (AuditLogger) event -> { |
| 473 | + delegate.logAuditEvent(event); |
| 474 | + if (events.incrementAndGet() == 1) { |
| 475 | + System.setProperty(ENHANCED_ENABLE, "true"); |
| 476 | + } |
| 477 | + }); |
| 478 | + try { |
| 479 | + AuditHelper.addAuditLog(request, result); |
| 480 | + List<String> logs = capture.read(2); |
| 481 | + assertEquals(2, events.get()); |
| 482 | + for (String log : logs) { |
| 483 | + assertNull("A multi must keep its captured legacy mode", fields(log).get("schema_version")); |
| 484 | + assertEquals("success", fields(log).get("result")); |
| 485 | + } |
| 486 | + |
| 487 | + Request following = request(OpCode.setData, new SetDataRequest("/mode-multi", new byte[3], -1)); |
| 488 | + ProcessTxnResult updated = apply(following, OpCode.setData, new SetDataTxn("/mode-multi", new byte[3], 2)); |
| 489 | + assertEquals(0, updated.err); |
| 490 | + AuditHelper.addAuditLog(following, updated); |
| 491 | + assertWrite(fields(capture.read(1).get(0)), "setData", "/mode-multi", "3", "committed", "0"); |
| 492 | + } finally { |
| 493 | + replaceProviderField("auditLogger", previousLogger); |
| 494 | + } |
| 495 | + } |
| 496 | + |
| 497 | + @Test |
| 498 | + public void testFailedMultiKeepsParentModeForRolledBackMembers() throws Exception { |
| 499 | + Request request = request(OpCode.multi, new MultiOperationRecord(Arrays.asList( |
| 500 | + Op.create("/mode-rolled", new byte[1], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT), |
| 501 | + Op.check("/missing", -1), |
| 502 | + Op.setData("/mode-rolled", new byte[2], -1)))); |
| 503 | + ProcessTxnResult result = apply(request, OpCode.multi, new MultiTxn(Arrays.asList( |
| 504 | + txn(OpCode.create, createTxn("/mode-rolled", new byte[1], false)), |
| 505 | + txn(OpCode.error, new ErrorTxn(Code.NONODE.intValue())), |
| 506 | + txn(OpCode.error, new ErrorTxn(Code.RUNTIMEINCONSISTENCY.intValue()))))); |
| 507 | + assertEquals(-101, result.err); |
| 508 | + assertNull(tree.getNode("/mode-rolled")); |
| 509 | + AtomicInteger events = new AtomicInteger(); |
| 510 | + AuditLogger delegate = new Slf4jAuditLogger(); |
| 511 | + Object previousLogger = replaceProviderField("auditLogger", (AuditLogger) event -> { |
| 512 | + delegate.logAuditEvent(event); |
| 513 | + if (events.incrementAndGet() == 1) { |
| 514 | + System.setProperty(ENHANCED_ENABLE, "false"); |
| 515 | + } |
| 516 | + }); |
| 517 | + try { |
| 518 | + AuditHelper.addAuditLog(request, result); |
| 519 | + List<String> logs = capture.read(3); |
| 520 | + assertEquals(3, events.get()); |
| 521 | + assertWrite(fields(logs.get(0)), "multiOperation", null, null, "failed", "-101"); |
| 522 | + assertWrite(fields(logs.get(1)), "create", "/mode-rolled", "1", "rolled_back", "0"); |
| 523 | + assertWrite(fields(logs.get(2)), "setData", "/mode-rolled", "2", "rolled_back", "-2"); |
| 524 | + assertEquals("2", fields(logs.get(2)).get("multi_index")); |
| 525 | + } finally { |
| 526 | + replaceProviderField("auditLogger", previousLogger); |
| 527 | + } |
| 528 | + } |
| 529 | + |
419 | 530 | @Test |
420 | 531 | public void testAuditDisabledSkipsRequestsAndProvider() throws Exception { |
421 | 532 | Object previous = replaceProviderField("auditEnabled", false); |
|
0 commit comments