feat(auth): Program.cs integration for Audit Logging (AEG-AUTH-001 Part 2)

- Register IAuthAuditSql (AuthAuditSql) in DI
- Add AuthAuditMiddleware to pipeline after authentication
- Fix AuthAuditSql using statement + NpgsqlInet construction
- Fix GetAuditLogsEndpoint response mapping (AuthAuditLogEntry → AuditLogItem)
- Build verified (Release mode, 0 errors)

Implements audit trail for all /api/auth/* endpoints:
- Captures event type, status, IP, user agent, endpoint, error details
- Immutable append-only storage with compliance views
- Async non-blocking logging with graceful failure handling
- Ready for 0047 migration application and testing

WBS: AEG-X-005 (JWT/OIDC/fail-closed) + AEG-AUTH-001 (Audit)
Gate: G3 (Shadow Run API)
Status: CODE_COMPLETE → MIGRATION_READY

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 01:46:25 +09:00
parent 3a5f893b2a
commit f20d19cf4b
3 changed files with 26 additions and 3 deletions
@@ -1,6 +1,7 @@
using Dapper;
using System.Data;
using NpgsqlTypes;
using KArtSell.BuildingBlocks.Data;
namespace KArtSell.Host.Features.Audit;
@@ -46,7 +47,7 @@ public sealed class AuthAuditSql : IAuthAuditSql
entry.IdentityId,
entry.Username,
entry.Role,
IpAddress = entry.IpAddress != null ? NpgsqlInet.Parse(entry.IpAddress) : (NpgsqlInet?)null,
IpAddress = entry.IpAddress != null ? new NpgsqlInet(entry.IpAddress) : (NpgsqlInet?)null,
entry.UserAgent,
entry.Endpoint,
entry.HttpMethod,
@@ -47,13 +47,29 @@ public sealed class GetAuditLogsEndpoint : Endpoint<GetAuditLogsRequest, GetAudi
ct: ct
);
var items = logs.Select(entry => new AuditLogItem
{
AuditId = entry.AuditId,
EventType = entry.EventType,
IdentityId = entry.IdentityId,
Username = entry.Username,
Role = entry.Role,
IpAddress = entry.IpAddress,
Endpoint = entry.Endpoint,
HttpMethod = entry.HttpMethod,
Status = entry.Status,
ErrorCode = entry.ErrorCode,
ErrorMessage = entry.ErrorMessage,
OccurredAt = entry.OccurredAt
}).ToList();
await Send.OkAsync(new GetAuditLogsResponse
{
Items = logs.ToList(),
Items = items,
Total = total,
Page = req.Page,
PageSize = req.PageSize
}, 200, ct);
}, ct);
}
}
+6
View File
@@ -9,6 +9,8 @@ using KArtSell.Host.Infrastructure;
using KArtSell.Host.OpenApi;
using KArtSell.Host.Observability;
using KArtSell.Host.Features.Observability;
using KArtSell.Host.Features.Audit;
using KArtSell.Host.Middleware;
using KArtSell.BuildingBlocks.Data;
using KArtSell.BuildingBlocks.Reliability;
using KArtSell.BuildingBlocks.Time;
@@ -233,6 +235,9 @@ builder.Services.AddScoped<KArtSell.Modules.ModelOperations.Compliance.LogAuditE
builder.Services.AddScoped<KArtSell.Modules.ModelOperations.Compliance.ProcessGdprRequestHandler>();
builder.Services.AddScoped<KArtSell.Modules.ModelOperations.Compliance.GdprRedactionJob>();
// Authentication Audit Logging (AEG-AUTH-001)
builder.Services.AddScoped<IAuthAuditSql, AuthAuditSql>();
builder.Services.AddProblemDetails();
const string authenticationScheme = "KArtSell";
@@ -342,6 +347,7 @@ if (app.Environment.IsDevelopment())
}
app.UseAuthentication();
app.UseMiddleware<AuthAuditMiddleware>();
app.UseAuthorization();
app.UseFastEndpoints(config => config.Endpoints.RoutePrefix = "api");
app.MapHub<KArtSell.Host.Consumers.ShadowRunHub>("/api/hubs/shadow-run");