feat: Audit Logging infrastructure (Week 2 Phase 1 complete)
deploy / deploy (push) Failing after 1m19s
deploy / notify (push) Successful in 1s

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:
2026-08-18 01:15:59 +09:00
parent ddddeee4d9
commit 3a5f893b2a
4 changed files with 470 additions and 0 deletions
@@ -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; }
}