79 lines
2.4 KiB
C#
79 lines
2.4 KiB
C#
using Microsoft.Extensions.Configuration;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Dapper;
|
|
using Npgsql;
|
|
|
|
namespace Registration.Infra.Repositories;
|
|
|
|
public interface IMemberRepository
|
|
{
|
|
Task EnsureCreated();
|
|
Task<bool> GetIsRegistered(string ssn);
|
|
Task<bool> AddRegistration(string ssn);
|
|
Task ClearRegistrations();
|
|
}
|
|
|
|
public class MemberRepository(IConfiguration configuration) : IMemberRepository
|
|
{
|
|
private readonly string _connectionString = configuration.GetConnectionString("DefaultConnection")
|
|
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
|
|
|
|
private readonly string _pepper = configuration["Security:SsnPepper"]
|
|
?? throw new InvalidOperationException("Security pepper 'SsnPepper' not found.");
|
|
|
|
private NpgsqlConnection CreateConnection() => new(_connectionString);
|
|
|
|
private string ComputeBlindIndex(string ssn)
|
|
{
|
|
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_pepper));
|
|
var hashedBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(ssn));
|
|
return Convert.ToBase64String(hashedBytes);
|
|
}
|
|
|
|
public async Task EnsureCreated()
|
|
{
|
|
using var connection = CreateConnection();
|
|
|
|
var sql = @"
|
|
CREATE TABLE IF NOT EXISTS members (
|
|
id SERIAL PRIMARY KEY,
|
|
ssn_index VARCHAR(255) NOT NULL UNIQUE
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_members_ssn_index ON members(ssn_index);
|
|
";
|
|
|
|
await connection.ExecuteAsync(sql);
|
|
}
|
|
|
|
public async Task<bool> AddRegistration(string ssn)
|
|
{
|
|
using var connection = CreateConnection();
|
|
|
|
var sql = "INSERT INTO members (ssn_index) VALUES (@SsnIndex) ON CONFLICT DO NOTHING";
|
|
var ssnIndex = ComputeBlindIndex(ssn);
|
|
|
|
var rows = await connection.ExecuteAsync(sql, new { SsnIndex = ssnIndex });
|
|
|
|
return rows > 0;
|
|
}
|
|
|
|
public async Task<bool> GetIsRegistered(string ssn)
|
|
{
|
|
using var connection = CreateConnection();
|
|
|
|
var ssnIndex = ComputeBlindIndex(ssn);
|
|
var sql = "SELECT EXISTS(SELECT 1 FROM members WHERE ssn_index = @SsnIndex)";
|
|
|
|
return await connection.ExecuteScalarAsync<bool>(sql, new { SsnIndex = ssnIndex });
|
|
}
|
|
|
|
public async Task ClearRegistrations()
|
|
{
|
|
using var connection = CreateConnection();
|
|
|
|
var sql = "DELETE FROM members";
|
|
await connection.ExecuteAsync(sql);
|
|
}
|
|
}
|
|
|