3e0446c9be
Add automated transaction syncing via Plaid and SimpleFIN providers, a Settings page for managing connections and account mappings, background scheduled syncing, and sync history logging. New backend: BankSync plugin project with PlaidSyncProvider and SimpleFinSyncProvider, SyncConnection/LinkedAccount/SyncLog models, AES-256 encryption service, BankSyncService orchestration, BankSyncBackgroundService (15-min tick), and BankSyncController (13 endpoints). EF migration creates sync_connections, linked_accounts, and sync_logs tables. New frontend: Settings view with 3 tabs (Profile, Bank Connections, Sync History), bankSync API service, BankSyncComplete SignalR notifications, and Settings nav item in sidebar + avatar dropdown. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
55 lines
1.8 KiB
C#
55 lines
1.8 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Purrse.Core.Interfaces.Services;
|
|
|
|
namespace Purrse.Api.Services;
|
|
|
|
public class EncryptionService : IEncryptionService
|
|
{
|
|
private readonly byte[] _key;
|
|
|
|
public EncryptionService(IConfiguration configuration)
|
|
{
|
|
var keyString = configuration["BankSync:EncryptionKey"]
|
|
?? throw new InvalidOperationException("BankSync:EncryptionKey is not configured");
|
|
_key = SHA256.HashData(Encoding.UTF8.GetBytes(keyString));
|
|
}
|
|
|
|
public string Encrypt(string plainText)
|
|
{
|
|
using var aes = Aes.Create();
|
|
aes.Key = _key;
|
|
aes.GenerateIV();
|
|
|
|
using var encryptor = aes.CreateEncryptor();
|
|
var plainBytes = Encoding.UTF8.GetBytes(plainText);
|
|
var cipherBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
|
|
|
|
// Prepend IV to ciphertext
|
|
var result = new byte[aes.IV.Length + cipherBytes.Length];
|
|
Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
|
|
Buffer.BlockCopy(cipherBytes, 0, result, aes.IV.Length, cipherBytes.Length);
|
|
|
|
return Convert.ToBase64String(result);
|
|
}
|
|
|
|
public string Decrypt(string cipherText)
|
|
{
|
|
var fullCipher = Convert.FromBase64String(cipherText);
|
|
|
|
using var aes = Aes.Create();
|
|
aes.Key = _key;
|
|
|
|
var iv = new byte[aes.BlockSize / 8];
|
|
var cipher = new byte[fullCipher.Length - iv.Length];
|
|
Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length);
|
|
Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, cipher.Length);
|
|
|
|
aes.IV = iv;
|
|
|
|
using var decryptor = aes.CreateDecryptor();
|
|
var plainBytes = decryptor.TransformFinalBlock(cipher, 0, cipher.Length);
|
|
return Encoding.UTF8.GetString(plainBytes);
|
|
}
|
|
}
|