Files
SIG-CM2.0/src/api/SIGCM2.Infrastructure/Audit/AuditLogger.cs
dmolinari a9838427a4 feat(udt-011): T400.30 — inject TimeProvider into Infrastructure critical services
AuditLogger, SecurityEventLogger: inject TimeProvider and use
_timeProvider.GetUtcNow().UtcDateTime for occurredAt timestamps.
JwtService: inject TimeProvider; use GetUtcNow() for token IssuedAt/Expires.
DI: update JwtService factory to pass sp.GetRequiredService<TimeProvider>().
Repositories: remove ?? DateTime.UtcNow fallback in UpdateAsync since callers
always provide FechaModificacion via domain mutators.
2026-04-18 10:12:24 -03:00

61 lines
1.9 KiB
C#

using Microsoft.Extensions.Options;
using SIGCM2.Application.Audit;
using SIGCM2.Domain.Exceptions;
namespace SIGCM2.Infrastructure.Audit;
/// UDT-010 — IAuditLogger implementation. Enriches from IAuditContext, sanitizes metadata,
/// persists via IAuditEventRepository. Fail-closed: throws AuditContextMissingException
/// when ActorUserId is null (system-emitted events should use a different path).
public sealed class AuditLogger : IAuditLogger
{
private readonly IAuditContext _context;
private readonly IAuditEventRepository _repo;
private readonly IOptions<AuditOptions> _options;
private readonly TimeProvider _timeProvider;
public AuditLogger(
IAuditContext context,
IAuditEventRepository repo,
IOptions<AuditOptions> options,
TimeProvider timeProvider)
{
_context = context;
_repo = repo;
_options = options;
_timeProvider = timeProvider;
}
public async Task LogAsync(
string action,
string targetType,
string targetId,
object? metadata = null,
CancellationToken ct = default)
{
if (_context.ActorUserId is null)
throw new AuditContextMissingException();
var sanitized = metadata is null
? null
: JsonSanitizer.Sanitize(metadata, _options.Value.SanitizedKeys);
var correlationId = _context.CorrelationId == Guid.Empty
? (Guid?)null
: _context.CorrelationId;
await _repo.InsertAsync(
occurredAt: _timeProvider.GetUtcNow().UtcDateTime,
actorUserId: _context.ActorUserId,
actorRoleId: _context.ActorRoleId,
action: action,
targetType: targetType,
targetId: targetId,
correlationId: correlationId,
ipAddress: _context.Ip,
userAgent: _context.UserAgent,
metadata: sanitized,
ct: ct);
}
}