This is an automated email from the ASF dual-hosted git repository.
jerryshao pushed a commit to branch cherry-pick-15259af5-to-branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to
refs/heads/cherry-pick-15259af5-to-branch-1.3 by this push:
new a42474d818 Resolve branch-1.3 cherry-pick conflicts
a42474d818 is described below
commit a42474d81878e9cd1bf679400d5e99a6b4e7fac0
Author: Jerry Shao <[email protected]>
AuthorDate: Fri Sep 4 19:47:59 2026 +0800
Resolve branch-1.3 cherry-pick conflicts
- RequestContext.java: take the incoming state, which already bundles
the AUDIT_EXTRAS/REQUEST_QUERY_PARAMS plumbing added by #12691
(never separately backported) together with this PR's own changes;
it is self-contained.
- TableEvent.java / TableFailureEvent.java / TestTableEvent.java:
branch-1.3 never got #12691 (inner dispatchers contributing
customInfo to table events), so these classes never overrode
customInfo() and need no change here; keep them as-is instead of
pulling in the audit-extras test coverage.
- IcebergEvent.java / IcebergFailureEvent.java: apply this PR's actual
change (rename customInfo() -> ownCustomInfo()) but keep reading
icebergRequestContext.httpHeaders(), since IcebergRequestContext
never gained a customInfo() method here (the #12722 backport,
#12726, was closed rather than merged).
- TestIcebergRequestContext.java: restore branch-1.3's version;
the new/changed assertions target the #12722 audit-extras API this
branch doesn't have.
- TestIcebergTableEventDispatcher.java: remove; this file tests
IcebergTableEventDispatcher's audit-extras stashing, which came in
with #12722 and isn't present on branch-1.3.
- LanceRESTService.java: keep registering RequestContextFilter (this
PR's change), but drop the InterceptionService import, which
belongs to Lance's authorization feature that isn't on branch-1.3.
---
.../gravitino/listener/api/event/TableEvent.java | 9 -
.../listener/api/event/TableFailureEvent.java | 13 -
.../org/apache/gravitino/utils/RequestContext.java | 23 +-
.../listener/api/event/TestTableEvent.java | 99 ------
.../apache/gravitino/utils/TestRequestContext.java | 5 +-
.../gravitino/listener/api/event/IcebergEvent.java | 7 +-
.../listener/api/event/IcebergFailureEvent.java | 7 +-
.../TestIcebergTableEventDispatcher.java | 351 ---------------------
.../api/event/TestIcebergRequestContext.java | 77 -----
.../apache/gravitino/lance/LanceRESTService.java | 4 -
10 files changed, 7 insertions(+), 588 deletions(-)
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/TableEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/TableEvent.java
index ddd7e9d4ad..32407ed62c 100644
--- a/core/src/main/java/org/apache/gravitino/listener/api/event/TableEvent.java
+++ b/core/src/main/java/org/apache/gravitino/listener/api/event/TableEvent.java
@@ -48,13 +48,4 @@ public abstract class TableEvent extends Event {
public OperationStatus operationStatus() {
return OperationStatus.SUCCESS;
}
-<<<<<<< HEAD
-=======
-
- /** {@inheritDoc} */
- @Override
- protected Map<String, String> ownCustomInfo() {
- return customInfo;
- }
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
}
diff --git
a/core/src/main/java/org/apache/gravitino/listener/api/event/TableFailureEvent.java
b/core/src/main/java/org/apache/gravitino/listener/api/event/TableFailureEvent.java
index f41a653bb7..a1293d523f 100644
---
a/core/src/main/java/org/apache/gravitino/listener/api/event/TableFailureEvent.java
+++
b/core/src/main/java/org/apache/gravitino/listener/api/event/TableFailureEvent.java
@@ -45,18 +45,5 @@ public abstract class TableFailureEvent extends FailureEvent
{
*/
protected TableFailureEvent(String user, NameIdentifier identifier,
Exception exception) {
super(user, identifier, exception);
-<<<<<<< HEAD
-=======
- this.customInfo =
- customInfo == null || customInfo.isEmpty()
- ? ImmutableMap.of()
- : ImmutableMap.copyOf(customInfo);
- }
-
- /** {@inheritDoc} */
- @Override
- protected Map<String, String> ownCustomInfo() {
- return customInfo;
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
}
}
diff --git a/core/src/main/java/org/apache/gravitino/utils/RequestContext.java
b/core/src/main/java/org/apache/gravitino/utils/RequestContext.java
index 97f844d41d..01b6e4f607 100644
--- a/core/src/main/java/org/apache/gravitino/utils/RequestContext.java
+++ b/core/src/main/java/org/apache/gravitino/utils/RequestContext.java
@@ -19,15 +19,14 @@
package org.apache.gravitino.utils;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
/**
* Holds per-request context data in a {@link ThreadLocal} so that event
classes constructed on the
* servlet thread can capture it without carrying a servlet dependency.
*
-<<<<<<< HEAD
- * <p>Currently tracks two pieces of state:
-=======
* <p>Currently tracks four pieces of state:
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
*
* <ul>
* <li><b>remoteAddress</b> — the client IP resolved from {@code
X-Forwarded-For} or {@link
@@ -35,10 +34,6 @@ package org.apache.gravitino.utils;
* <li><b>operationOutcome</b> — set by {@link
org.apache.gravitino.listener.EventBus} when an
* operation-layer {@link org.apache.gravitino.listener.api.event.Event}
or {@link
* org.apache.gravitino.listener.api.event.FailureEvent} is dispatched,
so that {@code
-<<<<<<< HEAD
- * HttpAuditFilter} can skip emitting a redundant HTTP-level failure
event for the same
- * request.
-=======
* HttpAuditFilter} can skip emitting a redundant HTTP-level fallback
event for the same
* request. Exposed as two independent-looking flags ({@code
operationFailureFired}/{@code
* operationSuccessFired}) for callers, but backed by a single tri-state
value — normally a
@@ -57,7 +52,6 @@ package org.apache.gravitino.utils;
* redacted — see {@code AuditLogRedactor}) once per request by {@code
RequestContextFilter}
* and read (non-destructively) by every {@link
org.apache.gravitino.listener.api.event.Event}
* constructor.
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
* </ul>
*
* <p><b>Threading contract:</b> values must be set and cleared on the same
(servlet) thread. Event
@@ -73,13 +67,9 @@ public class RequestContext {
}
private static final ThreadLocal<String> REMOTE_ADDRESS = new
ThreadLocal<>();
-<<<<<<< HEAD
- private static final ThreadLocal<Boolean> OPERATION_FAILURE_FIRED = new
ThreadLocal<>();
-=======
private static final ThreadLocal<OperationOutcome> OPERATION_OUTCOME = new
ThreadLocal<>();
private static final ThreadLocal<Map<String, String>> AUDIT_EXTRAS = new
ThreadLocal<>();
private static final ThreadLocal<Map<String, String>> REQUEST_QUERY_PARAMS =
new ThreadLocal<>();
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
private RequestContext() {}
@@ -173,8 +163,6 @@ public class RequestContext {
}
/**
-<<<<<<< HEAD
-=======
* Stashes optional audit extras for the current request thread. An inner
dispatcher calls this
* before returning or throwing so the outer event dispatcher can attach the
extras to the
* existing table event.
@@ -235,18 +223,13 @@ public class RequestContext {
}
/**
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
* Removes all per-request bindings from the current thread. Must be called
in a {@code finally}
* block after the request completes to prevent thread-pool leaks.
*/
public static void clear() {
REMOTE_ADDRESS.remove();
-<<<<<<< HEAD
- OPERATION_FAILURE_FIRED.remove();
-=======
OPERATION_OUTCOME.remove();
AUDIT_EXTRAS.remove();
REQUEST_QUERY_PARAMS.remove();
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
}
}
diff --git
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestTableEvent.java
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestTableEvent.java
index 4a404a1bb4..bd07402602 100644
---
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestTableEvent.java
+++
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestTableEvent.java
@@ -206,105 +206,6 @@ public class TestTableEvent {
Assertions.assertEquals(OperationStatus.UNPROCESSED,
preEvent.operationStatus());
}
-<<<<<<< HEAD
-=======
- /**
- * End-to-end check through the real dispatcher that a stashed fact lands on
the success event and
- * that the dispatcher consumed the stash. Pins the success half of the
contract that {@code
- * TestTableEventDispatcher} exercises against mocks.
- */
- @Test
- void testCreateTableEventAttachesStashedExtras() {
- NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
table.name());
- RequestContext.setAuditExtras(ImmutableMap.of("audit.reason",
"policy-applied"));
- dispatcher.createTable(
- identifier,
- table.columns(),
- table.comment(),
- table.properties(),
- table.partitioning(),
- table.distribution(),
- table.sortOrder(),
- table.index());
-
- Event event = dummyEventListener.popPostEvent();
- Assertions.assertEquals(CreateTableEvent.class, event.getClass());
- Assertions.assertEquals("policy-applied",
event.customInfo().get("audit.reason"));
- dummyEventListener.popPreEvent();
- Assertions.assertTrue(RequestContext.takeAuditExtras().isEmpty());
- }
-
- /**
- * A contributor that rejected the operation is exactly the case where the
reason matters most, so
- * extras have to survive the exception path and reach the failure event.
The success and failure
- * paths read the stash in separate branches, so both need pinning.
- */
- @Test
- void testCreateTableFailureEventAttachesStashedExtras() {
- NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
table.name());
- RequestContext.setAuditExtras(ImmutableMap.of("audit.reason",
"validation-failed"));
- Assertions.assertThrowsExactly(
- GravitinoRuntimeException.class,
- () ->
- failureDispatcher.createTable(
- identifier,
- table.columns(),
- table.comment(),
- table.properties(),
- table.partitioning(),
- table.distribution(),
- table.sortOrder(),
- table.index()));
- Event event = dummyEventListener.popPostEvent();
- Assertions.assertEquals(CreateTableFailureEvent.class, event.getClass());
- Assertions.assertEquals("validation-failed",
event.customInfo().get("audit.reason"));
- }
-
- /**
- * Each table operation reads the stash in its own hand-written branch, so
create passing does not
- * imply alter and load pass. Covers the two remaining operations and, by
stashing a second fact
- * between them, that consecutive operations on one thread get their own
value.
- */
- @Test
- void testAlterAndLoadEventsAttachStashedExtras() {
- NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
table.name());
- RequestContext.setAuditExtras(ImmutableMap.of("audit.reason",
"policy-applied"));
- dispatcher.alterTable(identifier, TableChange.setProperty("a", "b"));
- Event alterEvent = dummyEventListener.popPostEvent();
- Assertions.assertEquals(AlterTableEvent.class, alterEvent.getClass());
- Assertions.assertEquals("policy-applied",
alterEvent.customInfo().get("audit.reason"));
- dummyEventListener.popPreEvent();
-
- RequestContext.setAuditExtras(ImmutableMap.of("audit.reason",
"cache-miss"));
- dispatcher.loadTable(identifier);
- Event loadEvent = dummyEventListener.popPostEvent();
- Assertions.assertEquals(LoadTableEvent.class, loadEvent.getClass());
- Assertions.assertEquals("cache-miss",
loadEvent.customInfo().get("audit.reason"));
- dummyEventListener.popPreEvent();
- }
-
- /**
- * customInfo now has two contributors: the request's automatically captured
query parameters
- * (from {@code Event}) and this dispatcher's explicitly stashed extras.
Pins that both are
- * visible on the event and that an explicit key wins over an automatic one
of the same name.
- */
- @Test
- void testCustomInfoMergesAutomaticQueryParamsWithExplicitExtras() {
- NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
table.name());
- RequestContext.setRequestQueryParams(
- ImmutableMap.of("details", "true", "audit.reason",
"from-query-param"));
- RequestContext.setAuditExtras(ImmutableMap.of("audit.reason",
"policy-applied"));
- dispatcher.loadTable(identifier);
-
- Event event = dummyEventListener.popPostEvent();
- Assertions.assertEquals("true", event.customInfo().get("details"));
- Assertions.assertEquals(
- "policy-applied",
- event.customInfo().get("audit.reason"),
- "explicit extras must override the automatically captured value for
the same key");
- }
-
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
@Test
void testCreateTableFailureEvent() {
NameIdentifier identifier = NameIdentifier.of("metalake", "catalog",
table.name());
diff --git
a/core/src/test/java/org/apache/gravitino/utils/TestRequestContext.java
b/core/src/test/java/org/apache/gravitino/utils/TestRequestContext.java
index 10a686d88d..253280a360 100644
--- a/core/src/test/java/org/apache/gravitino/utils/TestRequestContext.java
+++ b/core/src/test/java/org/apache/gravitino/utils/TestRequestContext.java
@@ -19,6 +19,8 @@
package org.apache.gravitino.utils;
+import java.util.Collections;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
@@ -67,8 +69,6 @@ public class TestRequestContext {
Assertions.assertEquals(
"main-thread-ip", RequestContext.getRemoteAddress(), "Main thread
value unchanged");
}
-<<<<<<< HEAD
-=======
/**
* Callers read the stash unconditionally on every table operation, so the
common case is that
@@ -214,5 +214,4 @@ public class TestRequestContext {
Assertions.assertFalse(
RequestContext.isOperationSuccessFired(), "success must not overwrite
a recorded failure");
}
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
}
diff --git
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/listener/api/event/IcebergEvent.java
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/listener/api/event/IcebergEvent.java
index d513facbaf..25307cd1ab 100644
---
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/listener/api/event/IcebergEvent.java
+++
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/listener/api/event/IcebergEvent.java
@@ -55,12 +55,7 @@ public abstract class IcebergEvent extends Event {
/** {@inheritDoc} */
@Override
-<<<<<<< HEAD
- public Map<String, String> customInfo() {
- return icebergRequestContext.httpHeaders();
-=======
protected Map<String, String> ownCustomInfo() {
- return icebergRequestContext.customInfo();
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
+ return icebergRequestContext.httpHeaders();
}
}
diff --git
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/listener/api/event/IcebergFailureEvent.java
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/listener/api/event/IcebergFailureEvent.java
index 0e3f21dd4f..92db423190 100644
---
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/listener/api/event/IcebergFailureEvent.java
+++
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/listener/api/event/IcebergFailureEvent.java
@@ -55,12 +55,7 @@ public abstract class IcebergFailureEvent extends
FailureEvent {
/** {@inheritDoc} */
@Override
-<<<<<<< HEAD
- public Map<String, String> customInfo() {
- return icebergRequestContext.httpHeaders();
-=======
protected Map<String, String> ownCustomInfo() {
- return icebergRequestContext.customInfo();
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
+ return icebergRequestContext.httpHeaders();
}
}
diff --git
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/dispatcher/TestIcebergTableEventDispatcher.java
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/dispatcher/TestIcebergTableEventDispatcher.java
deleted file mode 100644
index 8456cef293..0000000000
---
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/dispatcher/TestIcebergTableEventDispatcher.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.gravitino.iceberg.service.dispatcher;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import com.google.common.collect.ImmutableMap;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.LinkedList;
-import java.util.Map;
-import javax.servlet.http.HttpServletRequest;
-import org.apache.gravitino.listener.EventBus;
-import org.apache.gravitino.listener.api.EventListenerPlugin;
-import org.apache.gravitino.listener.api.event.Event;
-import org.apache.gravitino.listener.api.event.IcebergCreateTableEvent;
-import org.apache.gravitino.listener.api.event.IcebergCreateTableFailureEvent;
-import org.apache.gravitino.listener.api.event.IcebergEvent;
-import org.apache.gravitino.listener.api.event.IcebergFailureEvent;
-import org.apache.gravitino.listener.api.event.IcebergLoadTableEvent;
-import org.apache.gravitino.listener.api.event.IcebergLoadTableFailureEvent;
-import org.apache.gravitino.listener.api.event.IcebergRequestContext;
-import org.apache.gravitino.listener.api.event.IcebergUpdateTableEvent;
-import org.apache.gravitino.listener.api.event.IcebergUpdateTableFailureEvent;
-import org.apache.gravitino.utils.RequestContext;
-import org.apache.iceberg.PartitionSpec;
-import org.apache.iceberg.Schema;
-import org.apache.iceberg.TableMetadata;
-import org.apache.iceberg.catalog.Namespace;
-import org.apache.iceberg.catalog.TableIdentifier;
-import org.apache.iceberg.rest.requests.CreateTableRequest;
-import org.apache.iceberg.rest.requests.UpdateTableRequest;
-import org.apache.iceberg.rest.responses.LoadTableResponse;
-import org.apache.iceberg.types.Types.LongType;
-import org.apache.iceberg.types.Types.NestedField;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-/**
- * Covers the audit-extras path on {@link IcebergTableEventDispatcher}: how
facts stashed on {@link
- * RequestContext} reach the emitted Iceberg event, and that listener-failure
propagation is
- * identical with or without extras. Event field mapping itself is covered by
{@code
- * TestIcebergRequestContext}.
- */
-public class TestIcebergTableEventDispatcher {
-
- private static final String METALAKE = "metalake";
- private static final String CATALOG = "catalog";
- private static final Namespace NAMESPACE = Namespace.of("ns");
- private static final TableIdentifier TABLE_ID =
TableIdentifier.of(NAMESPACE, "table");
- private static final Schema TABLE_SCHEMA =
- new Schema(NestedField.required(1, "id", LongType.get()));
- private static final String REQUEST_HEADER = "X-Request-Id";
- private static final String REQUEST_HEADER_VALUE = "req-1";
- private static final String EXTRA_KEY = "audit.reason";
-
- @AfterEach
- void cleanup() {
- RequestContext.clear();
- Thread.interrupted();
- }
-
- /**
- * This change is meant to be purely additive: extras enrich the event, and
nothing about error
- * propagation moves. A synchronous listener failure therefore has to reach
the caller exactly as
- * it does today, whether or not extras were stashed. Asserting both halves
in one test is what
- * makes a future swallow branch visible, since adding one would only break
the extras case.
- */
- @Test
- void testAttachingExtrasDoesNotChangeListenerFailurePropagation() {
- EventListenerPlugin listener = mock(EventListenerPlugin.class);
- when(listener.transformPreEvent(any())).thenAnswer(invocation ->
invocation.getArgument(0));
- doThrow(new RuntimeException("listener
failed")).when(listener).onPostEvent(any(Event.class));
-
- IcebergTableEventDispatcher dispatcher = dispatcher(listener,
succeedingInner());
-
- RuntimeException withoutExtras =
- Assertions.assertThrows(RuntimeException.class, () ->
createTable(dispatcher));
- Assertions.assertEquals("listener failed", withoutExtras.getMessage());
-
- RequestContext.setAuditExtras(ImmutableMap.of(EXTRA_KEY,
"policy-applied"));
- RuntimeException withExtras =
- Assertions.assertThrows(RuntimeException.class, () ->
createTable(dispatcher));
- Assertions.assertEquals("listener failed", withExtras.getMessage());
- }
-
- /**
- * The point of routing extras through the request context is to enrich the
event the dispatcher
- * already emits rather than to publish a second one. Pins both halves:
headers ∪ extras reach
- * {@code customInfo}, extras stay off {@code httpHeaders}, and exactly one
post event is
- * produced.
- */
- @Test
- void testCreateWithExtrasMergesHeadersAndDoesNotTouchHttpHeaders() {
- RecordingListener listener = new RecordingListener();
- IcebergTableEventDispatcher dispatcher = dispatcher(listener,
succeedingInner());
- RequestContext.setAuditExtras(ImmutableMap.of(EXTRA_KEY,
"policy-applied"));
-
- createTable(dispatcher);
-
- Event event = listener.popPostEvent();
- Assertions.assertEquals(IcebergCreateTableEvent.class, event.getClass());
- assertHeadersMergedWithExtras(event, "policy-applied");
- Assertions.assertTrue(listener.postEvents.isEmpty());
- }
-
- @Test
- void testCreateWithoutExtrasKeepsHeaderOnlyCustomInfo() {
- RecordingListener listener = new RecordingListener();
- IcebergTableEventDispatcher dispatcher = dispatcher(listener,
succeedingInner());
-
- IcebergRequestContext context = requestContext();
- dispatcher.createTable(context, NAMESPACE, createTableRequest());
-
- Event event = listener.popPostEvent();
- Assertions.assertEquals(IcebergCreateTableEvent.class, event.getClass());
- // Content equality, not identity: Event.customInfo() now merges in the
request's
- // automatically captured query parameters, so the result is always a
freshly built map even
- // when that automatic contribution is empty.
- Assertions.assertEquals(context.httpHeaders(), event.customInfo());
- Assertions.assertFalse(event.customInfo().containsKey(EXTRA_KEY));
- }
-
- /**
- * {@code IcebergEvent}/{@code IcebergFailureEvent} used to override {@code
customInfo()} to
- * return only the request-context's own facts (headers ∪ extras), silently
discarding {@link
- * Event}'s automatically captured query parameters. Pins that the two are
now merged, for both
- * the success and failure event.
- */
- @Test
- void testCustomInfoMergesAutomaticQueryParamsWithRequestContextFacts() {
- RecordingListener listener = new RecordingListener();
- IcebergTableEventDispatcher dispatcher = dispatcher(listener,
succeedingInner());
- RequestContext.setRequestQueryParams(ImmutableMap.of("details", "true"));
-
- createTable(dispatcher);
-
- Event event = listener.popPostEvent();
- Assertions.assertEquals(IcebergCreateTableEvent.class, event.getClass());
- Assertions.assertEquals("true", event.customInfo().get("details"));
- Assertions.assertEquals(REQUEST_HEADER_VALUE,
event.customInfo().get(REQUEST_HEADER));
- }
-
- /**
- * A contributor that rejected the operation is exactly the case where the
reason matters most, so
- * extras have to survive the exception path and reach the failure event.
- */
- @Test
- void testCreateFailureEventMergesHeadersAndExtras() {
- RecordingListener listener = new RecordingListener();
- IcebergTableOperationDispatcher inner =
mock(IcebergTableOperationDispatcher.class);
- when(inner.createTable(any(), any(), any())).thenThrow(new
RuntimeException("create failed"));
- IcebergTableEventDispatcher dispatcher = dispatcher(listener, inner);
- RequestContext.setAuditExtras(ImmutableMap.of(EXTRA_KEY,
"validation-failed"));
-
- RuntimeException thrown =
- Assertions.assertThrows(RuntimeException.class, () ->
createTable(dispatcher));
- Assertions.assertEquals("create failed", thrown.getMessage());
-
- Event event = listener.popPostEvent();
- Assertions.assertEquals(IcebergCreateTableFailureEvent.class,
event.getClass());
- assertHeadersMergedWithExtras(event, "validation-failed");
- }
-
- /**
- * Each Iceberg table operation reads the stash in its own hand-written
branch, so create passing
- * does not imply update and load pass. Covers the two remaining operations
and, by stashing a
- * second fact between them, that consecutive operations on one thread get
their own value.
- */
- @Test
- void testUpdateAndLoadEventsAttachStashedExtras() {
- RecordingListener listener = new RecordingListener();
- IcebergTableEventDispatcher dispatcher = dispatcher(listener,
succeedingInner());
-
- RequestContext.setAuditExtras(ImmutableMap.of(EXTRA_KEY,
"policy-applied"));
- dispatcher.updateTable(requestContext(), TABLE_ID, updateTableRequest());
- Event updateEvent = listener.popPostEvent();
- Assertions.assertEquals(IcebergUpdateTableEvent.class,
updateEvent.getClass());
- assertHeadersMergedWithExtras(updateEvent, "policy-applied");
-
- RequestContext.setAuditExtras(ImmutableMap.of(EXTRA_KEY, "cache-miss"));
- dispatcher.loadTable(requestContext(), TABLE_ID);
- Event loadEvent = listener.popPostEvent();
- Assertions.assertEquals(IcebergLoadTableEvent.class, loadEvent.getClass());
- assertHeadersMergedWithExtras(loadEvent, "cache-miss");
- }
-
- @Test
- void testUpdateAndLoadFailureEventsAttachStashedExtras() {
- RecordingListener listener = new RecordingListener();
- IcebergTableOperationDispatcher inner =
mock(IcebergTableOperationDispatcher.class);
- when(inner.updateTable(any(), any(), any())).thenThrow(new
RuntimeException("update failed"));
- when(inner.loadTable(any(), any())).thenThrow(new RuntimeException("load
failed"));
- IcebergTableEventDispatcher dispatcher = dispatcher(listener, inner);
-
- RequestContext.setAuditExtras(ImmutableMap.of(EXTRA_KEY,
"validation-failed"));
- RuntimeException updateThrown =
- Assertions.assertThrows(
- RuntimeException.class,
- () -> dispatcher.updateTable(requestContext(), TABLE_ID,
updateTableRequest()));
- Assertions.assertEquals("update failed", updateThrown.getMessage());
- Event updateEvent = listener.popPostEvent();
- Assertions.assertEquals(IcebergUpdateTableFailureEvent.class,
updateEvent.getClass());
- assertHeadersMergedWithExtras(updateEvent, "validation-failed");
-
- RequestContext.setAuditExtras(ImmutableMap.of(EXTRA_KEY, "not-found"));
- RuntimeException loadThrown =
- Assertions.assertThrows(
- RuntimeException.class, () ->
dispatcher.loadTable(requestContext(), TABLE_ID));
- Assertions.assertEquals("load failed", loadThrown.getMessage());
- Event loadEvent = listener.popPostEvent();
- Assertions.assertEquals(IcebergLoadTableFailureEvent.class,
loadEvent.getClass());
- assertHeadersMergedWithExtras(loadEvent, "not-found");
- }
-
- /**
- * Server threads are pooled, so a fact left behind by one request would be
mis-attributed to
- * whichever request reuses the thread next. Pins that the dispatcher
consumes the stash rather
- * than reading it, by running a second operation on the same thread and
requiring clean extras.
- */
- @Test
- void testExtrasDoNotLeakIntoTheNextOperationOnTheSameThread() {
- RecordingListener listener = new RecordingListener();
- IcebergTableEventDispatcher dispatcher = dispatcher(listener,
succeedingInner());
- RequestContext.setAuditExtras(ImmutableMap.of(EXTRA_KEY,
"policy-applied"));
-
- createTable(dispatcher);
- Assertions.assertEquals("policy-applied",
listener.popPostEvent().customInfo().get(EXTRA_KEY));
-
- createTable(dispatcher);
-
- Event second = listener.popPostEvent();
- Assertions.assertEquals(IcebergCreateTableEvent.class, second.getClass());
- Assertions.assertFalse(
- second.customInfo().containsKey(EXTRA_KEY),
- "Extras must not survive into a later operation");
- // Content equality, not identity: Event.customInfo() now merges in the
request's
- // automatically captured query parameters, so the result is always a
freshly built map even
- // when that automatic contribution is empty.
- Assertions.assertEquals(
- ((IcebergEvent) second).icebergRequestContext().httpHeaders(),
second.customInfo());
- }
-
- private static void assertHeadersMergedWithExtras(Event event, String
extraValue) {
- Assertions.assertEquals(REQUEST_HEADER_VALUE,
event.customInfo().get(REQUEST_HEADER));
- Assertions.assertEquals(extraValue, event.customInfo().get(EXTRA_KEY));
- Map<String, String> headers;
- if (event instanceof IcebergEvent) {
- headers = ((IcebergEvent) event).icebergRequestContext().httpHeaders();
- } else {
- headers = ((IcebergFailureEvent)
event).icebergRequestContext().httpHeaders();
- }
- Assertions.assertFalse(headers.containsKey(EXTRA_KEY));
- Assertions.assertEquals(REQUEST_HEADER_VALUE, headers.get(REQUEST_HEADER));
- }
-
- private static IcebergTableEventDispatcher dispatcher(
- EventListenerPlugin listener, IcebergTableOperationDispatcher inner) {
- return new IcebergTableEventDispatcher(
- inner, new EventBus(Collections.singletonList(listener)), METALAKE);
- }
-
- private static IcebergTableOperationDispatcher succeedingInner() {
- IcebergTableOperationDispatcher inner =
mock(IcebergTableOperationDispatcher.class);
- LoadTableResponse response = loadTableResponse();
- when(inner.createTable(any(), any(), any())).thenReturn(response);
- when(inner.updateTable(any(), any(), any())).thenReturn(response);
- when(inner.loadTable(any(), any())).thenReturn(response);
- return inner;
- }
-
- private static void createTable(IcebergTableEventDispatcher dispatcher) {
- dispatcher.createTable(requestContext(), NAMESPACE, createTableRequest());
- }
-
- private static IcebergRequestContext requestContext() {
- return new IcebergRequestContext(
- requestWithHeader(REQUEST_HEADER, REQUEST_HEADER_VALUE), CATALOG);
- }
-
- private static CreateTableRequest createTableRequest() {
- return
CreateTableRequest.builder().withName(TABLE_ID.name()).withSchema(TABLE_SCHEMA).build();
- }
-
- private static UpdateTableRequest updateTableRequest() {
- return new UpdateTableRequest(Collections.emptyList(),
Collections.emptyList());
- }
-
- private static LoadTableResponse loadTableResponse() {
- TableMetadata metadata =
- TableMetadata.newTableMetadata(
- TABLE_SCHEMA,
- PartitionSpec.unpartitioned(),
- "file:///tmp/iceberg-audit-extras",
- ImmutableMap.of());
- return LoadTableResponse.builder().withTableMetadata(metadata).build();
- }
-
- private static HttpServletRequest requestWithHeader(String name, String
value) {
- HttpServletRequest request = mock(HttpServletRequest.class);
- Enumeration<String> headerNames =
Collections.enumeration(Collections.singleton(name));
- when(request.getRemoteHost()).thenReturn("localhost");
- when(request.getHeaderNames()).thenReturn(headerNames);
- when(request.getHeader(name)).thenReturn(value);
- return request;
- }
-
- private static final class RecordingListener implements EventListenerPlugin {
- private final LinkedList<Event> postEvents = new LinkedList<>();
-
- @Override
- public void init(Map<String, String> properties) {}
-
- @Override
- public void start() {}
-
- @Override
- public void stop() {}
-
- @Override
- public void onPostEvent(Event event) {
- postEvents.add(event);
- }
-
- Event popPostEvent() {
- Assertions.assertFalse(postEvents.isEmpty(), "No post events to pop");
- return postEvents.removeLast();
- }
- }
-}
diff --git
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/listener/api/event/TestIcebergRequestContext.java
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/listener/api/event/TestIcebergRequestContext.java
index b91adbf51d..eeff0219d2 100644
---
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/listener/api/event/TestIcebergRequestContext.java
+++
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/listener/api/event/TestIcebergRequestContext.java
@@ -67,83 +67,6 @@ class TestIcebergRequestContext {
.asyncPurge());
}
-<<<<<<< HEAD
-=======
- /**
- * Extras are a parallel map, not headers. A later audit formatter that
reads {@code
- * httpHeaders()} must not see a fact that was never on the wire.
- */
- @Test
- void testExtrasDoNotAppearOnHttpHeaders() {
- IcebergRequestContext original =
- new IcebergRequestContext(requestWithHeader("X-Request-Id", "req-1"),
"cat");
- IcebergRequestContext enriched =
- original.withAuditExtras(ImmutableMap.of("audit.reason",
"policy-applied"));
-
- Assertions.assertNotSame(original, enriched);
- Assertions.assertTrue(original.auditExtras().isEmpty());
- Assertions.assertEquals("policy-applied",
enriched.auditExtras().get("audit.reason"));
- Assertions.assertFalse(enriched.httpHeaders().containsKey("audit.reason"));
- Assertions.assertEquals("req-1",
enriched.httpHeaders().get("X-Request-Id"));
- }
-
- /**
- * Today's {@code customInfo()} is the headers map. Empty extras have to
keep that identity so
- * existing callers do not observe a copy.
- */
- @Test
- void testCustomInfoIsHeadersWhenExtrasEmpty() {
- IcebergRequestContext context =
- new IcebergRequestContext(requestWithHeader("X-Request-Id", "req-1"),
"cat");
- Assertions.assertSame(context.httpHeaders(), context.customInfo());
- Assertions.assertSame(
- context.httpHeaders(),
context.withAuditExtras(ImmutableMap.of()).customInfo());
- Assertions.assertSame(context.httpHeaders(),
context.withAuditExtras(null).customInfo());
- }
-
- @Test
- void testCustomInfoMergesHeadersAndExtras() {
- IcebergRequestContext context =
- new IcebergRequestContext(requestWithHeader("X-Request-Id", "req-1"),
"cat")
- .withAuditExtras(ImmutableMap.of("audit.reason",
"policy-applied"));
- Assertions.assertEquals("req-1", context.customInfo().get("X-Request-Id"));
- Assertions.assertEquals("policy-applied",
context.customInfo().get("audit.reason"));
- Assertions.assertFalse(context.httpHeaders().containsKey("audit.reason"));
- }
-
- /**
- * Failure events are the case where the reason matters most. Pins that
{@link
- * IcebergFailureEvent#customInfo()} is headers ∪ extras ∪ any automatically
captured request
- * query parameters, and that extras stay off {@code httpHeaders()}.
- */
- @Test
- void testFailureEventCustomInfoMergesHeadersAndExtras() {
- IcebergRequestContext context =
- new IcebergRequestContext(requestWithHeader("X-Request-Id", "req-1"),
"cat")
- .withAuditExtras(ImmutableMap.of("audit.reason",
"validation-failed"));
- IcebergLoadTableFailureEvent event =
- new IcebergLoadTableFailureEvent(
- context, NameIdentifier.of("ml", "cat", "ns", "t"), new
RuntimeException("boom"));
-
- Assertions.assertEquals("req-1", event.customInfo().get("X-Request-Id"));
- Assertions.assertEquals("validation-failed",
event.customInfo().get("audit.reason"));
-
Assertions.assertFalse(event.icebergRequestContext().httpHeaders().containsKey("audit.reason"));
- }
-
- @Test
- void testFailureEventCustomInfoIsHeaderOnlyWhenExtrasEmpty() {
- IcebergRequestContext context =
- new IcebergRequestContext(requestWithHeader("X-Request-Id", "req-1"),
"cat");
- IcebergLoadTableFailureEvent event =
- new IcebergLoadTableFailureEvent(
- context, NameIdentifier.of("ml", "cat", "ns", "t"), new
RuntimeException("boom"));
- // Content equality, not identity: Event.customInfo() now merges in the
request's
- // automatically captured query parameters, so the result is always a
freshly built map even
- // when that automatic contribution is empty.
- Assertions.assertEquals(context.httpHeaders(), event.customInfo());
- }
-
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
private static HttpServletRequest requestWithoutHeader() {
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getRemoteHost()).thenReturn("localhost");
diff --git
a/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/LanceRESTService.java
b/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/LanceRESTService.java
index 6adcf5818c..041e038198 100644
---
a/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/LanceRESTService.java
+++
b/lance/lance-rest-server/src/main/java/org/apache/gravitino/lance/LanceRESTService.java
@@ -36,11 +36,7 @@ import org.apache.gravitino.server.web.HttpAuditFilter;
import org.apache.gravitino.server.web.HttpServerMetricsSource;
import org.apache.gravitino.server.web.JettyServer;
import org.apache.gravitino.server.web.JettyServerConfig;
-<<<<<<< HEAD
-=======
import org.apache.gravitino.server.web.RequestContextFilter;
-import org.glassfish.hk2.api.InterceptionService;
->>>>>>> 15259af5d ([#12872] fix(core): Capture and redact request query
parameters in audit log entries (#12891))
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;