feat(api): audit context middleware + scoped impl (UDT-010 B4)
Wires the request-scoped audit context per design #D-2:
Middleware pipeline in Program.cs:
app.UseCors()
app.UseMiddleware<CorrelationIdMiddleware>() // PRE-AUTH
app.UseAuthentication()
app.UseMiddleware<AuditActorMiddleware>() // POST-AUTH
app.UseAuthorization()
app.MapControllers()
SIGCM2.Api/Middleware/CorrelationIdMiddleware.cs:
- Preserves client-sent X-Correlation-Id header when a valid GUID, otherwise
generates Guid.NewGuid(). Stores in HttpContext.Items (audit:correlationId).
- Captures Ip (Connection.RemoteIpAddress) + UserAgent header into Items.
- Echoes the correlation id back via response header (OnStarting + immediate
set — immediate set makes unit testing against DefaultHttpContext reliable).
SIGCM2.Api/Middleware/AuditActorMiddleware.cs:
- Reads JWT 'sub' claim from authenticated HttpContext.User, parses to int,
stores as audit:actorUserId. Anonymous / non-numeric sub leaves it unset.
SIGCM2.Infrastructure/Audit/AuditContext.cs (IAuditContext scoped impl):
- Reads Items entries via IHttpContextAccessor. Returns null / Guid.Empty
when no HttpContext is available (jobs, tests without middleware).
- ActorRoleId intentionally null for now — rol code → id resolution is
deferred; the logger may resolve it at persist time in a later batch.
DI registration (Infrastructure/DependencyInjection.cs):
- services.AddScoped<IAuditContext, AuditContext>()
Tests (Strict TDD):
- CorrelationIdMiddlewareTests (6): generates/preserves/handles-malformed
correlation id, sets response header, captures ip/ua, calls next.
- AuditActorMiddlewareTests (5): authenticated/anonymous/no-sub/non-numeric/
calls-next.
- AuditContextTests (7): reads from Items, null-http-context defaults,
ActorRoleId currently null.
Suite: 355/355 Application.Tests + 141/141 Api.Tests = 496/496 passing.
Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-3/9, design#D-2, tasks#B4}
This commit is contained in:
38
src/api/SIGCM2.Infrastructure/Audit/AuditContext.cs
Normal file
38
src/api/SIGCM2.Infrastructure/Audit/AuditContext.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using SIGCM2.Application.Audit;
|
||||
|
||||
namespace SIGCM2.Infrastructure.Audit;
|
||||
|
||||
/// UDT-010 — scoped IAuditContext implementation backed by HttpContext.Items entries
|
||||
/// populated by the middleware pipeline (CorrelationIdMiddleware + AuditActorMiddleware).
|
||||
/// Returns defaults (null / Guid.Empty) when no HttpContext is available.
|
||||
public sealed class AuditContext : IAuditContext
|
||||
{
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
|
||||
public AuditContext(IHttpContextAccessor accessor)
|
||||
{
|
||||
_accessor = accessor;
|
||||
}
|
||||
|
||||
private HttpContext? Http => _accessor.HttpContext;
|
||||
|
||||
public int? ActorUserId =>
|
||||
Http?.Items.TryGetValue("audit:actorUserId", out var v) == true ? v as int? : null;
|
||||
|
||||
// Reserved: the pipeline does not currently resolve rol code -> id. Logger-side resolution
|
||||
// may populate this in a future batch.
|
||||
public int? ActorRoleId =>
|
||||
Http?.Items.TryGetValue("audit:actorRoleId", out var v) == true ? v as int? : null;
|
||||
|
||||
public string? Ip =>
|
||||
Http?.Items.TryGetValue("audit:ip", out var v) == true ? v as string : null;
|
||||
|
||||
public string? UserAgent =>
|
||||
Http?.Items.TryGetValue("audit:userAgent", out var v) == true ? v as string : null;
|
||||
|
||||
public Guid CorrelationId =>
|
||||
Http?.Items.TryGetValue("audit:correlationId", out var v) == true
|
||||
? (v as Guid?) ?? Guid.Empty
|
||||
: Guid.Empty;
|
||||
}
|
||||
@@ -71,6 +71,7 @@ public static class DependencyInjection
|
||||
|
||||
// UDT-010: Audit options (SanitizedKeys blacklist) — overridable via appsettings "Audit".
|
||||
services.Configure<AuditOptions>(configuration.GetSection(AuditOptions.SectionName));
|
||||
services.AddScoped<IAuditContext, SIGCM2.Infrastructure.Audit.AuditContext>();
|
||||
|
||||
// Dispatcher
|
||||
services.AddScoped<IDispatcher, Dispatcher>();
|
||||
|
||||
Reference in New Issue
Block a user