Files
SocialPub/PrivaPub/Infrastructure/Statistics/InteractionSalts.cs
T

44 lines
1.4 KiB
C#
Raw Normal View History

2026-10-03 10:56:13 +02:00
using MongoDB.Entities;
using PrivaPub.Models.Statistics;
using System.Collections.Concurrent;
using System.Security.Cryptography;
using System.Text;
namespace PrivaPub.Infrastructure.Statistics
{
public class InteractionSalts
{
readonly ConcurrentDictionary<DateTime, byte[]> _known = new();
public async Task<byte[]> For(DateTime day, CancellationToken token)
{
day = day.Date;
if (_known.TryGetValue(day, out var cached))
return cached;
InteractionSalt salt;
if (day == DateTime.UtcNow.Date)
salt = await DB.Default.UpdateAndGet<InteractionSalt>()
.Match(s => s.Day == day)
.Modify(b => b.SetOnInsert(s => s.Key, Convert.ToBase64String(RandomNumberGenerator.GetBytes(32))))
.Modify(b => b.SetOnInsert(s => s.ExpiresAt, day.AddHours(30)))
.Option(o => o.IsUpsert = true)
.ExecuteAsync(token);
else
salt = await DB.Default.Find<InteractionSalt>().Match(s => s.Day == day).ExecuteFirstAsync(token);
if (salt?.Key == default)
return default;
var key = Convert.FromBase64String(salt.Key);
foreach (var old in _known.Keys.Where(k => k < day.AddDays(-1)))
_known.TryRemove(old, out _);
_known[day] = key;
return key;
}
public static string Hash(byte[] salt, string actorUri) =>
salt == default || string.IsNullOrEmpty(actorUri)
? default
: Convert.ToBase64String(HMACSHA256.HashData(salt, Encoding.UTF8.GetBytes(actorUri))[..12]).TrimEnd('=').Replace('+', '-').Replace('/', '_');
}
}