fix(gateway): preserve raw client correlation id in denial audit DetailsJson + add wiring test (§1.2)

This commit is contained in:
Joseph Doherty
2026-06-15 09:56:24 -04:00
parent a59fc998e3
commit 55526d5e56
3 changed files with 49 additions and 5 deletions
@@ -548,6 +548,33 @@ public sealed class MxAccessGatewayServiceConstraintTests
Assert.Equal("42", enforcer.RecordedDenials[0].Target);
}
/// <summary>
/// End-to-end wiring (M-2): the per-request <c>ClientCorrelationId</c> must propagate
/// all the way through <c>Invoke</c> -> <c>ApplyConstraintsAsync</c> -> the unary write
/// enforce helper -> <c>RecordDenialAsync</c>, so the recorded denial carries the exact
/// id the client sent (including non-GUID trace ids used by Rust/Python/Java clients).
/// </summary>
[Fact]
public async Task Invoke_Write_WithDeniedHandle_ThreadsClientCorrelationIdIntoRecordedDenial()
{
const string CorrelationId = "rust-client-Write-7";
PredicateConstraintEnforcer enforcer = new()
{
DenyWriteHandle = (serverHandle, itemHandle) => serverHandle == 7 && itemHandle == 42,
};
FakeSessionManager sessionManager = CreateSessionManagerWithSeed();
MxAccessGatewayService service = CreateService(sessionManager, enforcer);
MxCommandRequest request = CreateWriteRequest(serverHandle: 7, itemHandle: 42);
request.ClientCorrelationId = CorrelationId;
await Assert.ThrowsAsync<RpcException>(
async () => await service.Invoke(request, new TestServerCallContext()));
Assert.Single(enforcer.RecordedDenials);
Assert.Equal(CorrelationId, enforcer.RecordedDenials[0].CorrelationId);
}
/// <summary>
/// Unary <c>WriteSecured</c> against a denied handle takes the same enforce path
/// and rejects identically — proving the four-arm switch in