line-gestao-api/Services/SystemAuditService.cs

116 lines
3.7 KiB
C#

using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text.Json;
using System.Text.Json.Serialization;
using line_gestao_api.Data;
using line_gestao_api.Models;
namespace line_gestao_api.Services;
public class SystemAuditService : ISystemAuditService
{
private const int ActionMaxLength = 20;
private static readonly JsonSerializerOptions JsonOptions = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
private readonly AppDbContext _db;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ITenantProvider _tenantProvider;
public SystemAuditService(
AppDbContext db,
IHttpContextAccessor httpContextAccessor,
ITenantProvider tenantProvider)
{
_db = db;
_httpContextAccessor = httpContextAccessor;
_tenantProvider = tenantProvider;
}
public async Task LogAsync(string action, Guid targetTenantId, object? metadata = null, CancellationToken cancellationToken = default)
{
var actorTenantId = _tenantProvider.ActorTenantId;
if (!actorTenantId.HasValue || actorTenantId.Value == Guid.Empty)
{
return;
}
var user = _httpContextAccessor.HttpContext?.User;
var userId = ResolveUserId(user);
var userName = ResolveUserName(user);
var userEmail = ResolveUserEmail(user);
var request = _httpContextAccessor.HttpContext?.Request;
var requestPath = request?.Path.Value;
var requestMethod = request?.Method;
var ipAddress = _httpContextAccessor.HttpContext?.Connection?.RemoteIpAddress?.ToString();
var safeMetadataJson = JsonSerializer.Serialize(metadata ?? new { }, JsonOptions);
var normalizedAction = NormalizeAction(action);
_db.AuditLogs.Add(new AuditLog
{
TenantId = actorTenantId.Value,
ActorUserId = userId,
ActorTenantId = actorTenantId.Value,
TargetTenantId = targetTenantId,
OccurredAtUtc = DateTime.UtcNow,
Action = normalizedAction,
Page = "System",
EntityName = "System",
EntityId = targetTenantId.ToString(),
EntityLabel = null,
ChangesJson = "[]",
MetadataJson = safeMetadataJson,
UserId = userId,
UserName = userName,
UserEmail = userEmail,
RequestPath = requestPath,
RequestMethod = requestMethod,
IpAddress = ipAddress
});
await _db.SaveChangesAsync(cancellationToken);
}
private static string NormalizeAction(string? action)
{
var normalized = (action ?? string.Empty).Trim().ToUpperInvariant();
if (string.IsNullOrEmpty(normalized))
{
return "UNKNOWN";
}
if (normalized.Length <= ActionMaxLength)
{
return normalized;
}
return normalized[..ActionMaxLength];
}
private static Guid? ResolveUserId(ClaimsPrincipal? user)
{
var raw = user?.FindFirstValue(ClaimTypes.NameIdentifier)
?? user?.FindFirstValue(JwtRegisteredClaimNames.Sub)
?? user?.FindFirstValue("sub");
return Guid.TryParse(raw, out var parsed) ? parsed : null;
}
private static string? ResolveUserName(ClaimsPrincipal? user)
{
return user?.FindFirstValue("name")
?? user?.FindFirstValue(ClaimTypes.Name)
?? user?.Identity?.Name;
}
private static string? ResolveUserEmail(ClaimsPrincipal? user)
{
return user?.FindFirstValue(ClaimTypes.Email)
?? user?.FindFirstValue(JwtRegisteredClaimNames.Email)
?? user?.FindFirstValue("email");
}
}