Skip to content

Commit 3dcb161

Browse files
arkmishCopilot
andcommitted
Keep one audit mode through metadata and emission (C1 I3)
Carry the captured enhanced mode into event construction without rereading the property. Preserve public delegating overloads and keep every multi parent/member on the same mode. Cover deterministic ACL off-to-on and multi mode transitions with subsequent genuine v2 emission. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent fd72bc7 commit 3dcb161

4 files changed

Lines changed: 142 additions & 8 deletions

File tree

‎zookeeper-docs/src/main/resources/markdown/zookeeperAuditLogs.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,12 @@ Consumers should recognize v2 per record using `schema_version=2`, tolerate
210210
unknown additive fields, and accept omitted optional fields. Do not apply v2
211211
unescaping to legacy records. Prepare consumers for mixed legacy/v2 output before
212212
enabling the property on servers, then roll it out gradually. Roll back emission
213-
by removing the enhanced property or setting it to `false` on restart. The
214-
enhanced gate is checked when emitting events; the base audit enablement retains
213+
by removing the enhanced property or setting it to `false` on restart. Each
214+
audited request captures the enhanced mode before metadata extraction, and uses
215+
that same decision for user/ACL sanitization, schema construction and every
216+
parent/member record of a multi. An in-flight request keeps its captured mode if
217+
the property changes; a subsequent request captures the new value. Direct
218+
provider logging captures its mode per event. The base audit enablement retains
215219
its startup behavior. No wire, persistence, authentication, ACL, quota or payload
216220
limit changes are required.
217221

‎zookeeper-server/src/main/java/org/apache/zookeeper/audit/AuditHelper.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ private static void log(Request request, ProcessTxnResult rc, String path, Strin
383383
}
384384
ZKAuditProvider.log(getUsers(request, enhanced), operation, path, metadata.acl, metadata.createMode,
385385
request.cnxn.getSessionIdHex(), request.cnxn.getHostAddress(), result,
386-
metadata.dataLength, error, outcome, request.cxid, zxid, index);
386+
metadata.dataLength, error, outcome, request.cxid, zxid, index, enhanced);
387387
}
388388

389389
private static void auditError(int type, Exception e) {

‎zookeeper-server/src/main/java/org/apache/zookeeper/audit/ZKAuditProvider.java‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,19 @@ public static void log(String user, String operation, String znode, String acl,
9090
if (!isAuditEnabled()) {
9191
return;
9292
}
93+
log(user, operation, znode, acl, createMode, session, ip, result,
94+
dataLength, errorCode, outcome, cxid, zxid, multiIndex, isEnhancedAuditEnabled());
95+
}
96+
97+
static void log(String user, String operation, String znode, String acl,
98+
String createMode, String session, String ip, Result result,
99+
Integer dataLength, Integer errorCode, Outcome outcome,
100+
Integer cxid, Long zxid, Integer multiIndex, boolean enhanced) {
101+
if (!isAuditEnabled()) {
102+
return;
103+
}
93104
logAuditEvent(createLogEvent(user, operation, znode, acl, createMode, session, ip, result,
94-
dataLength, errorCode, outcome, cxid, zxid, multiIndex));
105+
dataLength, errorCode, outcome, cxid, zxid, multiIndex, enhanced));
95106
}
96107

97108
/**
@@ -101,7 +112,7 @@ static AuditEvent createLogEvent(String user, String operation, Result result) {
101112
AuditEvent event = new AuditEvent(result);
102113
event.addEntry(FieldName.USER, user);
103114
event.addEntry(FieldName.OPERATION, operation);
104-
addMetadata(event, null, null, null, null, null, null);
115+
addMetadata(event, null, null, null, null, null, null, isEnhancedAuditEnabled());
105116
return event;
106117
}
107118

@@ -118,6 +129,14 @@ static AuditEvent createLogEvent(String user, String operation, String znode, St
118129
String createMode, String session, String ip, Result result,
119130
Integer dataLength, Integer errorCode, Outcome outcome,
120131
Integer cxid, Long zxid, Integer multiIndex) {
132+
return createLogEvent(user, operation, znode, acl, createMode, session, ip, result,
133+
dataLength, errorCode, outcome, cxid, zxid, multiIndex, isEnhancedAuditEnabled());
134+
}
135+
136+
private static AuditEvent createLogEvent(String user, String operation, String znode, String acl,
137+
String createMode, String session, String ip, Result result,
138+
Integer dataLength, Integer errorCode, Outcome outcome,
139+
Integer cxid, Long zxid, Integer multiIndex, boolean enhanced) {
121140
AuditEvent event = new AuditEvent(result);
122141
event.addEntry(FieldName.SESSION, session);
123142
event.addEntry(FieldName.USER, user);
@@ -126,13 +145,13 @@ static AuditEvent createLogEvent(String user, String operation, String znode, St
126145
event.addEntry(FieldName.ZNODE, znode);
127146
event.addEntry(FieldName.ZNODE_TYPE, createMode);
128147
event.addEntry(FieldName.ACL, acl);
129-
addMetadata(event, dataLength, errorCode, outcome, cxid, zxid, multiIndex);
148+
addMetadata(event, dataLength, errorCode, outcome, cxid, zxid, multiIndex, enhanced);
130149
return event;
131150
}
132151

133152
private static void addMetadata(AuditEvent event, Integer dataLength, Integer errorCode, Outcome outcome,
134-
Integer cxid, Long zxid, Integer multiIndex) {
135-
if (isEnhancedAuditEnabled()) {
153+
Integer cxid, Long zxid, Integer multiIndex, boolean enhanced) {
154+
if (enhanced) {
136155
event.addEntry(FieldName.SCHEMA_VERSION, AuditConstants.SCHEMA_VERSION);
137156
event.addEntry(FieldName.DATA_LENGTH, valueOf(dataLength));
138157
event.addEntry(FieldName.ERROR_CODE, valueOf(errorCode));

‎zookeeper-server/src/test/java/org/apache/zookeeper/audit/AuditHelperTest.java‎

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.apache.zookeeper.server.ServerCnxn;
6767
import org.apache.zookeeper.server.ServerMetrics;
6868
import org.apache.zookeeper.server.auth.AuthenticationProvider;
69+
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;
6970
import org.apache.zookeeper.server.auth.ProviderRegistry;
7071
import org.apache.zookeeper.txn.CheckVersionTxn;
7172
import org.apache.zookeeper.txn.CloseSessionTxn;
@@ -74,6 +75,7 @@
7475
import org.apache.zookeeper.txn.DeleteTxn;
7576
import org.apache.zookeeper.txn.ErrorTxn;
7677
import org.apache.zookeeper.txn.MultiTxn;
78+
import org.apache.zookeeper.txn.SetACLTxn;
7779
import org.apache.zookeeper.txn.SetDataTxn;
7880
import org.apache.zookeeper.txn.Txn;
7981
import org.apache.zookeeper.txn.TxnHeader;
@@ -416,6 +418,115 @@ public void testEnhancedUsersRedactUnknownAndMalformedIdentities() throws Except
416418
assertFalse(log.contains("synthetic"));
417419
}
418420

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+
419530
@Test
420531
public void testAuditDisabledSkipsRequestsAndProvider() throws Exception {
421532
Object previous = replaceProviderField("auditEnabled", false);

0 commit comments

Comments
 (0)