Commit Graph

2 Commits

Author SHA1 Message Date
0b4af4c332 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}
2026-04-16 13:32:13 -03:00
08d6622e43 feat(infra): JsonSanitizer + AuditOptions binding (UDT-010 B3)
Adds the metadata sanitization layer per #REQ-AUD-5:

SIGCM2.Infrastructure/Audit/JsonSanitizer.cs (static class):
- Sanitize(object?, IReadOnlyCollection<string>) -> string?
- Serializes via System.Text.Json + JsonNode recursive traversal.
- Strips blacklisted keys at every nesting level (objects + arrays).
- Case-insensitive match (ToLowerInvariant on both sides).
- Null input -> null output (never throws).
- Output is always valid JSON (ISJSON=1 compatible — satisfies AuditEvent CHECK).

SIGCM2.Application/Audit/AuditOptions.cs:
- Documented the IConfiguration array-binding quirk: config is ADDITIVE
  (append at higher indices), not REPLACE. Intentional for security — defaults
  like 'password'/'token'/'cvv' must not be silently dropped.

SIGCM2.Infrastructure/DependencyInjection.cs:
- services.Configure<AuditOptions>(configuration.GetSection(AuditOptions.SectionName))
  wired in AddInfrastructure().

Tests (Strict TDD, RED -> GREEN):
- JsonSanitizerTests (10): null/empty-blacklist/flat/nested/arrays/case-insensitive/
  primitives/round-trip-valid-json/string-as-value/default-keys-effective.
- AuditOptionsBindingTests (2): defaults when section absent + additive override.

One test needed adjustment during GREEN: 'AlreadySerializedJsonString' originally
asserted against an encoding-specific literal; rewrote to use JsonDocument
round-trip (validates behavior without coupling to encoder quirks).

Suite: 348/348 Application.Tests + 130/130 Api.Tests = 478/478 passing.

Refs: sdd/udt-010-auditoria-trazabilidad/{spec#REQ-AUD-5, design#D-5, tasks#B3}
2026-04-16 13:28:37 -03:00