feat: Audit Logging infrastructure (Week 2 Phase 1 complete)
Comprehensive authentication event auditing for compliance and forensics. ## Database (0047_audit_logging_enhancement.sql) - auth_audit_log table with immutability trigger - 8 performance indexes (identity, occurred_at, event_type, etc.) - INET type for IP address storage - Compliance views: v_auth_audit_summary, v_auth_failures - Ready for monthly partitioning (scalability) ## Backend Implementation ### AuthAuditSql.cs (IAuthAuditSql) - LogAuthEventAsync: Record authentication events - GetAuditLogsAsync: Paginated audit log retrieval - GetAuditLogsCountAsync: Total count for reporting - INET casting for CIDR operations - Prepared statements (SQL injection safe) ### AuthAuditMiddleware.cs - Logs all /api/auth/* and /api/admin/* requests - Captures: event type, status, IP, user agent, endpoint, method - Error details: HTTP status code, error message - Async logging (non-blocking request path) - Graceful failure handling (audit failures don't break requests) ### GetAuditLogsEndpoint.cs - GET /api/admin/audit-logs - RBAC protected (Admin/SecurityOfficer) - Filters: date range, event type, username - Pagination: page/pageSize (max 1000) - Response: items[], total, page metadata ## Features - ✅ Immutable audit trail (trigger prevents modifications) - ✅ Forensic details (IP, User-Agent, correlation ID) - ✅ Compliance ready (ISO 27001, SOC2) - ✅ Performance optimized (8 indexes, view materialization) - ✅ Scalable (monthly partitioning ready) - ✅ Non-blocking (async logging) ## Testing (Next: Integration tests) - Unit: AuthAuditSql queries - Integration: Middleware logging verification - E2E: Full audit trail capture Status: Code complete, ready for Program.cs integration Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
using FastEndpoints;
|
||||
using KArtSell.Host.Features.Audit;
|
||||
|
||||
namespace KArtSell.Host.Endpoints.Audit;
|
||||
|
||||
/// <summary>
|
||||
/// GET /api/admin/audit-logs - Retrieve authentication audit logs
|
||||
/// Requires Admin or SecurityOfficer role
|
||||
/// </summary>
|
||||
public sealed class GetAuditLogsEndpoint : Endpoint<GetAuditLogsRequest, GetAuditLogsResponse>
|
||||
{
|
||||
private readonly IAuthAuditSql _auditSql;
|
||||
|
||||
public GetAuditLogsEndpoint(IAuthAuditSql auditSql)
|
||||
{
|
||||
_auditSql = auditSql;
|
||||
}
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/audit-logs");
|
||||
Roles("Admin", "SecurityOfficer");
|
||||
Description(x => x
|
||||
.WithName("Get Audit Logs")
|
||||
.WithDescription("Retrieve authentication audit logs for compliance reporting")
|
||||
.Accepts<GetAuditLogsRequest>("application/json")
|
||||
.Produces<GetAuditLogsResponse>(200, "application/json"));
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetAuditLogsRequest req, CancellationToken ct)
|
||||
{
|
||||
var logs = await _auditSql.GetAuditLogsAsync(
|
||||
startDate: req.StartDate,
|
||||
endDate: req.EndDate,
|
||||
eventType: req.EventType,
|
||||
username: req.Username,
|
||||
limit: req.PageSize > 1000 ? 1000 : req.PageSize, // Cap at 1000
|
||||
offset: (req.Page - 1) * req.PageSize,
|
||||
ct: ct
|
||||
);
|
||||
|
||||
var total = await _auditSql.GetAuditLogsCountAsync(
|
||||
startDate: req.StartDate,
|
||||
endDate: req.EndDate,
|
||||
eventType: req.EventType,
|
||||
username: req.Username,
|
||||
ct: ct
|
||||
);
|
||||
|
||||
await Send.OkAsync(new GetAuditLogsResponse
|
||||
{
|
||||
Items = logs.ToList(),
|
||||
Total = total,
|
||||
Page = req.Page,
|
||||
PageSize = req.PageSize
|
||||
}, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetAuditLogsRequest
|
||||
{
|
||||
public DateTime? StartDate { get; set; }
|
||||
public DateTime? EndDate { get; set; }
|
||||
public string? EventType { get; set; } // LOGIN, LOGOUT, MFA_SETUP, etc.
|
||||
public string? Username { get; set; }
|
||||
public int Page { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 50;
|
||||
}
|
||||
|
||||
public class GetAuditLogsResponse
|
||||
{
|
||||
public required List<AuditLogItem> Items { get; set; }
|
||||
public int Total { get; set; }
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
}
|
||||
|
||||
public class AuditLogItem
|
||||
{
|
||||
public Guid AuditId { get; set; }
|
||||
public required string EventType { get; set; }
|
||||
public Guid? IdentityId { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? Role { get; set; }
|
||||
public string? IpAddress { get; set; }
|
||||
public string? Endpoint { get; set; }
|
||||
public string? HttpMethod { get; set; }
|
||||
public required string Status { get; set; }
|
||||
public string? ErrorCode { get; set; }
|
||||
public string? ErrorMessage { get; set; }
|
||||
public DateTime OccurredAt { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user