using Microsoft.Extensions.Options; using SIGCM2.Application.Audit; namespace SIGCM2.Infrastructure.Audit; /// UDT-010 — ISecurityEventLogger implementation. Unlike AuditLogger this is NOT /// fail-closed on missing actor: login failures have no ActorUserId by design. /// Ip/UserAgent are pulled from IAuditContext when available (null in pre-auth paths). public sealed class SecurityEventLogger : ISecurityEventLogger { private readonly ISecurityEventRepository _repo; private readonly IAuditContext _context; private readonly IOptions _options; public SecurityEventLogger( ISecurityEventRepository repo, IAuditContext context, IOptions options) { _repo = repo; _context = context; _options = options; } public async Task LogAsync( string action, string result, int? actorUserId = null, string? attemptedUsername = null, Guid? sessionId = null, string? failureReason = null, object? metadata = null, CancellationToken ct = default) { var sanitized = metadata is null ? null : JsonSanitizer.Sanitize(metadata, _options.Value.SanitizedKeys); await _repo.InsertAsync( occurredAt: DateTime.UtcNow, actorUserId: actorUserId, attemptedUsername: attemptedUsername, sessionId: sessionId, action: action, result: result, failureReason: failureReason, ipAddress: _context.Ip, userAgent: _context.UserAgent, metadata: sanitized, ct: ct); } }