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:
@@ -1,6 +1,7 @@
|
|||||||
using Dapper;
|
using Dapper;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using NpgsqlTypes;
|
using NpgsqlTypes;
|
||||||
|
using KArtSell.BuildingBlocks.Data;
|
||||||
|
|
||||||
namespace KArtSell.Host.Features.Audit;
|
namespace KArtSell.Host.Features.Audit;
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ public sealed class AuthAuditSql : IAuthAuditSql
|
|||||||
entry.IdentityId,
|
entry.IdentityId,
|
||||||
entry.Username,
|
entry.Username,
|
||||||
entry.Role,
|
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.UserAgent,
|
||||||
entry.Endpoint,
|
entry.Endpoint,
|
||||||
entry.HttpMethod,
|
entry.HttpMethod,
|
||||||
|
|||||||
@@ -47,13 +47,29 @@ public sealed class GetAuditLogsEndpoint : Endpoint<GetAuditLogsRequest, GetAudi
|
|||||||
ct: ct
|
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
|
await Send.OkAsync(new GetAuditLogsResponse
|
||||||
{
|
{
|
||||||
Items = logs.ToList(),
|
Items = items,
|
||||||
Total = total,
|
Total = total,
|
||||||
Page = req.Page,
|
Page = req.Page,
|
||||||
PageSize = req.PageSize
|
PageSize = req.PageSize
|
||||||
}, 200, ct);
|
}, ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ using KArtSell.Host.Infrastructure;
|
|||||||
using KArtSell.Host.OpenApi;
|
using KArtSell.Host.OpenApi;
|
||||||
using KArtSell.Host.Observability;
|
using KArtSell.Host.Observability;
|
||||||
using KArtSell.Host.Features.Observability;
|
using KArtSell.Host.Features.Observability;
|
||||||
|
using KArtSell.Host.Features.Audit;
|
||||||
|
using KArtSell.Host.Middleware;
|
||||||
using KArtSell.BuildingBlocks.Data;
|
using KArtSell.BuildingBlocks.Data;
|
||||||
using KArtSell.BuildingBlocks.Reliability;
|
using KArtSell.BuildingBlocks.Reliability;
|
||||||
using KArtSell.BuildingBlocks.Time;
|
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.ProcessGdprRequestHandler>();
|
||||||
builder.Services.AddScoped<KArtSell.Modules.ModelOperations.Compliance.GdprRedactionJob>();
|
builder.Services.AddScoped<KArtSell.Modules.ModelOperations.Compliance.GdprRedactionJob>();
|
||||||
|
|
||||||
|
// Authentication Audit Logging (AEG-AUTH-001)
|
||||||
|
builder.Services.AddScoped<IAuthAuditSql, AuthAuditSql>();
|
||||||
|
|
||||||
builder.Services.AddProblemDetails();
|
builder.Services.AddProblemDetails();
|
||||||
|
|
||||||
const string authenticationScheme = "KArtSell";
|
const string authenticationScheme = "KArtSell";
|
||||||
@@ -342,6 +347,7 @@ if (app.Environment.IsDevelopment())
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
|
app.UseMiddleware<AuthAuditMiddleware>();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
app.UseFastEndpoints(config => config.Endpoints.RoutePrefix = "api");
|
app.UseFastEndpoints(config => config.Endpoints.RoutePrefix = "api");
|
||||||
app.MapHub<KArtSell.Host.Consumers.ShadowRunHub>("/api/hubs/shadow-run");
|
app.MapHub<KArtSell.Host.Consumers.ShadowRunHub>("/api/hubs/shadow-run");
|
||||||
|
|||||||
Reference in New Issue
Block a user