2026-03-30 08:03:27 -05:00
using System.Text.Json ;
2026-04-02 13:48:45 -05:00
using System.Text.Json.Serialization.Metadata ;
2026-03-30 08:03:27 -05:00
2026-03-29 22:02:07 -05:00
using StevanFreeborn.Extensions.Configuration.Secure.Cryptography ;
using StevanFreeborn.Extensions.Configuration.Secure.Storage ;
namespace StevanFreeborn.Extensions.Configuration.Secure.Configuration ;
internal sealed class SecureConfig (
ISecureStorageProvider storageProvider ,
2026-04-02 13:48:45 -05:00
ICryptoProvider cryptoProvider ,
JsonSerializerOptions jsonOptions
2026-03-29 22:02:07 -05:00
) : ISecureConfig
{
private readonly ISecureStorageProvider _storageProvider = storageProvider ??
throw new ArgumentNullException ( nameof ( storageProvider ));
private readonly ICryptoProvider _cryptoProvider = cryptoProvider ??
throw new ArgumentNullException ( nameof ( cryptoProvider ));
2026-04-02 13:48:45 -05:00
private readonly JsonSerializerOptions _jsonOptions = jsonOptions ??
throw new ArgumentNullException ( nameof ( jsonOptions ));
public Task SetAsync < T >( string key , T value , CancellationToken ct = default )
{
var typeInfo = GetTypeInfo < T >();
return SetAsync ( key , value , typeInfo , ct );
}
public async Task SetAsync < T >( string key , T value , JsonTypeInfo < T > typeInfo , CancellationToken ct = default )
2026-03-29 22:02:07 -05:00
{
2026-03-30 08:03:27 -05:00
if ( string . IsNullOrWhiteSpace ( key ))
{
2026-04-02 13:48:45 -05:00
throw new ArgumentException ( "Cannot be null or whitespace." , nameof ( key ));
2026-03-30 08:03:27 -05:00
}
if ( value is null )
{
throw new ArgumentNullException ( nameof ( value ));
}
2026-04-02 13:48:45 -05:00
var json = JsonSerializer . Serialize ( value , typeInfo );
2026-03-30 08:03:27 -05:00
var encryptedValue = _cryptoProvider . Encrypt ( json );
await _storageProvider . WriteAsync ( key , encryptedValue , ct ). ConfigureAwait ( false );
2026-03-29 22:02:07 -05:00
}
2026-04-02 13:48:45 -05:00
public Task < T ?> GetAsync < T >( string key , CancellationToken ct = default )
2026-03-29 22:02:07 -05:00
{
2026-04-02 13:48:45 -05:00
var typeInfo = GetTypeInfo < T >();
return GetAsync ( key , typeInfo , ct );
}
public async Task < T ?> GetAsync < T >( string key , JsonTypeInfo < T > typeInfo , CancellationToken ct = default )
{
if ( string . IsNullOrWhiteSpace ( key ))
2026-03-30 08:03:27 -05:00
{
2026-04-02 13:48:45 -05:00
throw new ArgumentException ( "Cannot be null or whitespace." , nameof ( key ));
2026-03-30 08:03:27 -05:00
}
var encryptedData = await _storageProvider . ReadAsync ( key , ct ). ConfigureAwait ( false );
if ( string . IsNullOrWhiteSpace ( encryptedData ))
{
return default ;
}
var data = _cryptoProvider . Decrypt ( encryptedData );
2026-04-02 13:48:45 -05:00
return JsonSerializer . Deserialize ( data , typeInfo );
2026-03-29 22:02:07 -05:00
}
2026-03-30 08:03:27 -05:00
public Task < bool > DeleteAsync ( string key , CancellationToken ct = default )
2026-03-29 22:02:07 -05:00
{
2026-03-30 08:03:27 -05:00
return _storageProvider . DeleteAsync ( key , ct );
2026-03-29 22:02:07 -05:00
}
2026-04-02 13:48:45 -05:00
private JsonTypeInfo < T > GetTypeInfo < T >()
{
try
{
return ( JsonTypeInfo < T >?) _jsonOptions . GetTypeInfo ( typeof ( T ))
?? throw new InvalidOperationException ( $"AOT metadata for type '{typeof(T).Name}' is missing. Did you forget to register it via {nameof(SecureConfigBuilder.AddJsonAotContext)}()?" );
}
catch ( NotSupportedException ex )
{
throw new InvalidOperationException ( $"AOT metadata for type '{typeof(T).Name}' is missing. Did you forget to register it via {nameof(SecureConfigBuilder.AddJsonAotContext)}()?" , ex );
}
}
2026-03-29 22:02:07 -05:00
}