58 lines
1.8 KiB
C#
58 lines
1.8 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;
|
||
|
|
|
||
|
|
public AuditLogger(
|
||
|
|
IAuditContext context,
|
||
|
|
IAuditEventRepository repo,
|
||
|
|
IOptions<AuditOptions> options)
|
||
|
|
{
|
||
|
|
_context = context;
|
||
|
|
_repo = repo;
|
||
|
|
_options = options;
|
||
|
|
}
|
||
|
|
|
||
|
|
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: DateTime.UtcNow,
|
||
|
|
actorUserId: _context.ActorUserId,
|
||
|
|
actorRoleId: _context.ActorRoleId,
|
||
|
|
action: action,
|
||
|
|
targetType: targetType,
|
||
|
|
targetId: targetId,
|
||
|
|
correlationId: correlationId,
|
||
|
|
ipAddress: _context.Ip,
|
||
|
|
userAgent: _context.UserAgent,
|
||
|
|
metadata: sanitized,
|
||
|
|
ct: ct);
|
||
|
|
}
|
||
|
|
}
|