up
Some checks failed
Build Test Deploy / docs (push) Has been cancelled
Build Test Deploy / deploy (push) Has been cancelled
Build Test Deploy / build-test (push) Has been cancelled
Build Test Deploy / authority-container (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Build Test Deploy / docs (push) Has been cancelled
Build Test Deploy / deploy (push) Has been cancelled
Build Test Deploy / build-test (push) Has been cancelled
Build Test Deploy / authority-container (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
This commit is contained in:
112
src/StellaOps.Cryptography/CryptoProviderRegistry.cs
Normal file
112
src/StellaOps.Cryptography/CryptoProviderRegistry.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace StellaOps.Cryptography;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="ICryptoProviderRegistry"/> with deterministic provider ordering.
|
||||
/// </summary>
|
||||
public sealed class CryptoProviderRegistry : ICryptoProviderRegistry
|
||||
{
|
||||
private readonly ReadOnlyCollection<ICryptoProvider> providers;
|
||||
private readonly IReadOnlyDictionary<string, ICryptoProvider> providersByName;
|
||||
private readonly IReadOnlyList<string> preferredOrder;
|
||||
private readonly HashSet<string> preferredOrderSet;
|
||||
|
||||
public CryptoProviderRegistry(
|
||||
IEnumerable<ICryptoProvider> providers,
|
||||
IEnumerable<string>? preferredProviderOrder = null)
|
||||
{
|
||||
if (providers is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(providers));
|
||||
}
|
||||
|
||||
var providerList = providers.ToList();
|
||||
if (providerList.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("At least one crypto provider must be registered.", nameof(providers));
|
||||
}
|
||||
|
||||
providersByName = providerList.ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
|
||||
this.providers = new ReadOnlyCollection<ICryptoProvider>(providerList);
|
||||
|
||||
preferredOrder = preferredProviderOrder?
|
||||
.Where(name => providersByName.ContainsKey(name))
|
||||
.Select(name => providersByName[name].Name)
|
||||
.ToArray() ?? Array.Empty<string>();
|
||||
preferredOrderSet = new HashSet<string>(preferredOrder, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<ICryptoProvider> Providers => providers;
|
||||
|
||||
public bool TryResolve(string preferredProvider, out ICryptoProvider provider)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(preferredProvider))
|
||||
{
|
||||
provider = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
return providersByName.TryGetValue(preferredProvider, out provider!);
|
||||
}
|
||||
|
||||
public ICryptoProvider ResolveOrThrow(CryptoCapability capability, string algorithmId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(algorithmId))
|
||||
{
|
||||
throw new ArgumentException("Algorithm identifier is required.", nameof(algorithmId));
|
||||
}
|
||||
|
||||
foreach (var provider in EnumerateCandidates())
|
||||
{
|
||||
if (provider.Supports(capability, algorithmId))
|
||||
{
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"No crypto provider is registered for capability '{capability}' and algorithm '{algorithmId}'.");
|
||||
}
|
||||
|
||||
public ICryptoSigner ResolveSigner(
|
||||
CryptoCapability capability,
|
||||
string algorithmId,
|
||||
CryptoKeyReference keyReference,
|
||||
string? preferredProvider = null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(preferredProvider) &&
|
||||
providersByName.TryGetValue(preferredProvider!, out var hinted))
|
||||
{
|
||||
if (!hinted.Supports(capability, algorithmId))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Provider '{preferredProvider}' does not support capability '{capability}' and algorithm '{algorithmId}'.");
|
||||
}
|
||||
|
||||
return hinted.GetSigner(algorithmId, keyReference);
|
||||
}
|
||||
|
||||
var provider = ResolveOrThrow(capability, algorithmId);
|
||||
return provider.GetSigner(algorithmId, keyReference);
|
||||
}
|
||||
|
||||
private IEnumerable<ICryptoProvider> EnumerateCandidates()
|
||||
{
|
||||
foreach (var name in preferredOrder)
|
||||
{
|
||||
yield return providersByName[name];
|
||||
}
|
||||
|
||||
foreach (var provider in providers)
|
||||
{
|
||||
if (!preferredOrderSet.Contains(provider.Name))
|
||||
{
|
||||
yield return provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user