Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions src/ModularPipelines.Azure/AzureKeyVault.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,51 @@
using System.Collections.Concurrent;
using Azure.Core;
using Azure.Security.KeyVault.Certificates;
using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Secrets;

namespace ModularPipelines.Azure;

/// <summary>
/// Provides access to Azure Key Vault clients with caching.
/// </summary>
/// <remarks>
/// <para>
/// Azure SDK clients (SecretClient, CertificateClient, KeyClient) are thread-safe and designed
/// to be long-lived and reused. This class caches clients by vault URI to avoid the overhead
/// of creating new clients on every call.
/// </para>
/// <para>
/// <strong>Important:</strong> Clients are cached by vault URI only. Within a scope, the first
/// TokenCredential used for a vault URI will be used for all subsequent requests to that vault.
/// This is intentional since AzureKeyVault is registered as Scoped and typically the same
/// credential is used throughout a pipeline execution scope.
/// </para>
/// </remarks>
internal class AzureKeyVault : IAzureKeyVault
{
// Cache by vault URI only. Within a scope, the same credential is typically used.
// TokenCredential lacks value equality, so including it in the key would cause cache misses
// for logically identical credentials (e.g., two DefaultAzureCredential instances).
private readonly ConcurrentDictionary<string, SecretClient> _secretClients = new();
private readonly ConcurrentDictionary<string, CertificateClient> _certificateClients = new();
private readonly ConcurrentDictionary<string, KeyClient> _keyClients = new();

public SecretClient GetSecretClient(Uri vaultUri, TokenCredential tokenCredential)
{
return new SecretClient(vaultUri, tokenCredential);
var key = vaultUri.ToString();
return _secretClients.GetOrAdd(key, _ => new SecretClient(vaultUri, tokenCredential));
}

public CertificateClient GetCertificateClient(Uri vaultUri, TokenCredential tokenCredential)
{
return new CertificateClient(vaultUri, tokenCredential);
var key = vaultUri.ToString();
return _certificateClients.GetOrAdd(key, _ => new CertificateClient(vaultUri, tokenCredential));
}

public KeyClient GetKeyClient(Uri vaultUri, TokenCredential tokenCredential)
{
return new KeyClient(vaultUri, tokenCredential);
var key = vaultUri.ToString();
return _keyClients.GetOrAdd(key, _ => new KeyClient(vaultUri, tokenCredential));
}
}
32 changes: 17 additions & 15 deletions src/ModularPipelines/Context/Hasher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@

namespace ModularPipelines.Context;

/// <summary>
/// Provides hashing operations using various algorithms.
/// </summary>
/// <remarks>
/// Uses the static HashData methods available in .NET 5+ which are thread-safe
/// and don't require disposal, avoiding resource leaks.
/// </remarks>
internal class Hasher : IHasher
{
private readonly IHex _hex;
Expand All @@ -16,36 +23,31 @@ public Hasher(IHex hex, IBase64 base64)

public string Sha1(string input, HashType hashType = HashType.Hex)
{
return ComputeHash(SHA1.Create(), input, hashType);
var bytes = System.Security.Cryptography.SHA1.HashData(Encoding.UTF8.GetBytes(input));
return hashType == HashType.Hex ? _hex.ToHex(bytes) : _base64.ToBase64String(bytes);
}

public string Sha256(string input, HashType hashType = HashType.Hex)
{
return ComputeHash(SHA256.Create(), input, hashType);
var bytes = System.Security.Cryptography.SHA256.HashData(Encoding.UTF8.GetBytes(input));
return hashType == HashType.Hex ? _hex.ToHex(bytes) : _base64.ToBase64String(bytes);
}

public string Sha384(string input, HashType hashType = HashType.Hex)
{
return ComputeHash(SHA384.Create(), input, hashType);
var bytes = System.Security.Cryptography.SHA384.HashData(Encoding.UTF8.GetBytes(input));
return hashType == HashType.Hex ? _hex.ToHex(bytes) : _base64.ToBase64String(bytes);
}

public string Sha512(string input, HashType hashType = HashType.Hex)
{
return ComputeHash(SHA512.Create(), input, hashType);
var bytes = System.Security.Cryptography.SHA512.HashData(Encoding.UTF8.GetBytes(input));
return hashType == HashType.Hex ? _hex.ToHex(bytes) : _base64.ToBase64String(bytes);
}

public string Md5(string input, HashType hashType = HashType.Hex)
{
return ComputeHash(MD5.Create(), input, hashType);
}

private string ComputeHash(HashAlgorithm hashAlgorithm, string input, HashType hashType)
{
using (hashAlgorithm)
{
var bytes = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(input));

return hashType == HashType.Hex ? _hex.ToHex(bytes) : _base64.ToBase64String(bytes);
}
var bytes = System.Security.Cryptography.MD5.HashData(Encoding.UTF8.GetBytes(input));
return hashType == HashType.Hex ? _hex.ToHex(bytes) : _base64.ToBase64String(bytes);
}
}
Loading