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
@@ -121,8 +121,10 @@ public sealed class ConstraintEnforcer(
/// <param name="target">The target being accessed (tag address or handle).</param>
/// <param name="failure">The constraint failure details.</param>
/// <param name="correlationId">
/// The per-request client correlation id, if any. Persisted as the audit record's
/// <c>CorrelationId</c> when it parses as a GUID; a non-GUID value is dropped (left null).
/// The per-request client correlation id, if any. Persisted as the audit record's typed
/// <c>CorrelationId</c> when it parses as a GUID; a non-GUID value leaves that column null.
/// The raw string is always preserved in <c>DetailsJson["clientCorrelationId"]</c> so a
/// non-GUID id (e.g. from Rust/Python/Java clients) is never silently lost.
/// </param>
/// <param name="cancellationToken">Token to observe for cancellation.</param>
public async Task RecordDenialAsync(
@@ -153,6 +155,11 @@ public sealed class ConstraintEnforcer(
["message"] = failure.Message,
["commandKind"] = commandKind,
["target"] = target,
// Always preserve the raw client correlation id here so it is never silently
// lost: the typed CorrelationId column only retains GUID-parseable ids, but
// clients (Rust/Python/Java) commonly send non-GUID or empty trace ids. The
// raw id is a client trace id, not a secret, so storing it is fine.
["clientCorrelationId"] = correlationId ?? "",
}),
};