Files
Antifraude.Net/Antifraude.Net/ApiDenuncias/Services/EncryptedDenunciaStore.cs
2026-04-30 08:55:00 +02:00

210 lines
7.3 KiB
C#

using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using GestionaDenunciasAN.Models;
using GestionaDenunciasAN.Services;
using Microsoft.AspNetCore.DataProtection;
namespace ApiDenuncias.Services;
public sealed class EncryptedDenunciaStore : IDenunciaStore
{
private const string ProtectedStringPrefix = "enc:v1:";
private static readonly byte[] ProtectedBytesPrefix = Encoding.ASCII.GetBytes("enc:v1:");
private static readonly PropertyInfo[] ComplaintProperties = typeof(DenunciasGestiona)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(property => property.CanRead && property.CanWrite)
.ToArray();
private readonly MySqlDenunciaStore _inner;
private readonly IDataProtector _protector;
public EncryptedDenunciaStore(MySqlDenunciaStore inner, IDataProtectionProvider dataProtectionProvider)
{
_inner = inner;
_protector = dataProtectionProvider.CreateProtector("ApiDenuncias.DatabaseSensitiveData.v1");
}
public Task EnsureSchemaAsync(CancellationToken cancellationToken = default)
=> _inner.EnsureSchemaAsync(cancellationToken);
public async Task<List<DenunciasGestiona>> GetAllDenunciasAsync(CancellationToken cancellationToken = default)
=> (await _inner.GetAllDenunciasAsync(cancellationToken))
.Select(UnprotectComplaint)
.ToList();
public async Task<List<FicherosDenuncias>> GetAllFicherosAsync(CancellationToken cancellationToken = default)
=> (await _inner.GetAllFicherosAsync(cancellationToken))
.Select(UnprotectAttachment)
.ToList();
public async Task<List<FicherosDenuncias>> GetFicherosByDenunciaAsync(int denunciaId, CancellationToken cancellationToken = default)
=> (await _inner.GetFicherosByDenunciaAsync(denunciaId, cancellationToken))
.Select(UnprotectAttachment)
.ToList();
public async Task<DenunciasGestiona?> GetDenunciaByIdAsync(int denunciaId, CancellationToken cancellationToken = default)
{
var denuncia = await _inner.GetDenunciaByIdAsync(denunciaId, cancellationToken);
return denuncia is null ? null : UnprotectComplaint(denuncia);
}
public Task UpsertDenunciaAsync(DenunciasGestiona denuncia, CancellationToken cancellationToken = default)
=> _inner.UpsertDenunciaAsync(ProtectComplaint(denuncia), cancellationToken);
public Task UpsertFicherosAsync(IEnumerable<FicherosDenuncias> ficheros, CancellationToken cancellationToken = default)
=> _inner.UpsertFicherosAsync(ficheros.Select(ProtectAttachment).ToArray(), cancellationToken);
public Task MarkFicherosAsUploadedAsync(
int denunciaId,
IEnumerable<string> fileNames,
DateTime uploadedAtUtc,
CancellationToken cancellationToken = default)
=> _inner.MarkFicherosAsUploadedAsync(denunciaId, fileNames, uploadedAtUtc, cancellationToken);
private DenunciasGestiona ProtectComplaint(DenunciasGestiona source)
=> TransformComplaint(source, ProtectString);
private DenunciasGestiona UnprotectComplaint(DenunciasGestiona source)
=> TransformComplaint(source, UnprotectString);
private static DenunciasGestiona TransformComplaint(DenunciasGestiona source, Func<string, string> transformString)
{
var target = new DenunciasGestiona();
foreach (var property in ComplaintProperties)
{
var value = property.GetValue(source);
if (property.PropertyType == typeof(string))
{
property.SetValue(target, transformString((string?)value ?? string.Empty));
}
else
{
property.SetValue(target, value);
}
}
return target;
}
private FicherosDenuncias ProtectAttachment(FicherosDenuncias source)
{
var content = source.Fichero ?? [];
var hash = string.IsNullOrWhiteSpace(source.ContentSha256)
? ComputeSha256Hex(content)
: source.ContentSha256.Trim().ToLowerInvariant();
return new FicherosDenuncias
{
Id_Fichero = source.Id_Fichero,
Id_Tipo = source.Id_Tipo,
Descripcion = ProtectString(source.Descripcion ?? string.Empty),
Fecha = source.Fecha,
Observaciones = ProtectString(source.Observaciones ?? string.Empty),
Id_Denuncia = source.Id_Denuncia,
NombreFichero = source.NombreFichero,
Fichero = ProtectBytes(content),
Subido = source.Subido,
FechaSubida = source.FechaSubida,
ContentSha256 = hash
};
}
private FicherosDenuncias UnprotectAttachment(FicherosDenuncias source)
{
return new FicherosDenuncias
{
Id_Fichero = source.Id_Fichero,
Id_Tipo = source.Id_Tipo,
Descripcion = UnprotectString(source.Descripcion ?? string.Empty),
Fecha = source.Fecha,
Observaciones = UnprotectString(source.Observaciones ?? string.Empty),
Id_Denuncia = source.Id_Denuncia,
NombreFichero = source.NombreFichero,
Fichero = UnprotectBytes(source.Fichero ?? []),
Subido = source.Subido,
FechaSubida = source.FechaSubida,
ContentSha256 = source.ContentSha256
};
}
private string ProtectString(string value)
{
if (string.IsNullOrWhiteSpace(value) || value.StartsWith(ProtectedStringPrefix, StringComparison.Ordinal))
{
return value;
}
return ProtectedStringPrefix + _protector.Protect(value);
}
private string UnprotectString(string value)
{
if (string.IsNullOrWhiteSpace(value) || !value.StartsWith(ProtectedStringPrefix, StringComparison.Ordinal))
{
return value;
}
try
{
return _protector.Unprotect(value[ProtectedStringPrefix.Length..]);
}
catch
{
return value;
}
}
private byte[] ProtectBytes(byte[] value)
{
if (value.Length == 0 || StartsWith(value, ProtectedBytesPrefix))
{
return value;
}
var protectedBytes = _protector.Protect(value);
var base64Bytes = Encoding.ASCII.GetBytes(Convert.ToBase64String(protectedBytes));
return [.. ProtectedBytesPrefix, .. base64Bytes];
}
private byte[] UnprotectBytes(byte[] value)
{
if (value.Length == 0 || !StartsWith(value, ProtectedBytesPrefix))
{
return value;
}
try
{
var base64 = Encoding.ASCII.GetString(value, ProtectedBytesPrefix.Length, value.Length - ProtectedBytesPrefix.Length);
return _protector.Unprotect(Convert.FromBase64String(base64));
}
catch
{
return value;
}
}
private static bool StartsWith(byte[] value, byte[] prefix)
{
if (value.Length < prefix.Length)
{
return false;
}
for (var i = 0; i < prefix.Length; i++)
{
if (value[i] != prefix[i])
{
return false;
}
}
return true;
}
private static string ComputeSha256Hex(byte[] content)
=> Convert.ToHexString(SHA256.HashData(content)).ToLowerInvariant();
}