Implement hashi corp vault
This commit is contained in:
		| @@ -15,6 +15,23 @@ namespace Core.Blueprint.KeyVault.Configuration | |||||||
|     public static class RegisterBlueprint |     public static class RegisterBlueprint | ||||||
|     { |     { | ||||||
|         public static IServiceCollection AddKeyVault(this IServiceCollection services, IConfiguration configuration) |         public static IServiceCollection AddKeyVault(this IServiceCollection services, IConfiguration configuration) | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty; | ||||||
|  |  | ||||||
|  |             if(environment ==  "Local") | ||||||
|  |             { | ||||||
|  |                 var vaultSettings = configuration.GetSection("Vault").Get<VaultOptions>(); | ||||||
|  |  | ||||||
|  |                 if (string.IsNullOrEmpty(vaultSettings?.Address) || string.IsNullOrEmpty(vaultSettings.Token) || | ||||||
|  |                     string.IsNullOrEmpty(vaultSettings?.SecretPath) || string.IsNullOrEmpty(vaultSettings.SecretMount)) | ||||||
|  |                 { | ||||||
|  |                     throw new ArgumentNullException("Vault options are not configured correctly."); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 services.AddSingleton(vaultSettings); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|             { |             { | ||||||
|                 var keyVaultUriString = configuration["ConnectionStrings:KeyVaultDAL"]; |                 var keyVaultUriString = configuration["ConnectionStrings:KeyVaultDAL"]; | ||||||
|  |  | ||||||
| @@ -25,8 +42,8 @@ namespace Core.Blueprint.KeyVault.Configuration | |||||||
|  |  | ||||||
|                 var keyVaultUri = new Uri(keyVaultUriString); |                 var keyVaultUri = new Uri(keyVaultUriString); | ||||||
|  |  | ||||||
|             // Register SecretClient as a singleton |  | ||||||
|                 services.AddSingleton(_ => new SecretClient(keyVaultUri, new DefaultAzureCredential())); |                 services.AddSingleton(_ => new SecretClient(keyVaultUri, new DefaultAzureCredential())); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             services.AddSingleton<IKeyVaultProvider, KeyVaultProvider>(); |             services.AddSingleton<IKeyVaultProvider, KeyVaultProvider>(); | ||||||
|             return services; |             return services; | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								Core.Blueprint.KeyVault/Configuration/VaultOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Core.Blueprint.KeyVault/Configuration/VaultOptions.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.KeyVault.Configuration | ||||||
|  | { | ||||||
|  |     public class VaultOptions | ||||||
|  |     { | ||||||
|  |         public string Address { get; set; } = string.Empty; | ||||||
|  |         public string Token { get; set; } = string.Empty; | ||||||
|  |         public string SecretMount { get; set; } = string.Empty; | ||||||
|  |         public string SecretPath { get; set; } = string.Empty; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -10,7 +10,9 @@ | |||||||
|     <PackageReference Include="Azure.Identity" Version="1.13.1" /> |     <PackageReference Include="Azure.Identity" Version="1.13.1" /> | ||||||
|     <PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" /> |     <PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" /> | ||||||
|     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" /> |     <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.0" /> | ||||||
|     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" /> |     <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" /> | ||||||
|  |     <PackageReference Include="VaultSharp" Version="1.17.5.1" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|  |  | ||||||
| </Project> | </Project> | ||||||
|   | |||||||
| @@ -1,32 +1,62 @@ | |||||||
| using Azure; | using Azure.Security.KeyVault.Secrets; | ||||||
| using Azure.Security.KeyVault.Secrets; | using VaultSharp; | ||||||
|  | using VaultSharp.V1.AuthMethods.Token; | ||||||
|  | using Core.Blueprint.KeyVault.Configuration; | ||||||
|  | using Microsoft.Extensions.Configuration; | ||||||
|  |  | ||||||
|  | namespace Core.Blueprint.KeyVault; | ||||||
|  |  | ||||||
| namespace Core.Blueprint.KeyVault |  | ||||||
| { |  | ||||||
| /// <summary> | /// <summary> | ||||||
|     /// Provides operations for managing secrets in Azure Key Vault. | /// Provides operations for managing secrets in Azure Key Vault or HashiCorp Vault transparently based on the environment. | ||||||
| /// </summary> | /// </summary> | ||||||
|     public sealed class KeyVaultProvider(SecretClient keyVaultProvider): IKeyVaultProvider | public sealed class KeyVaultProvider : IKeyVaultProvider | ||||||
| { | { | ||||||
|  |     private readonly string environment; | ||||||
|  |     private readonly SecretClient? azureClient; | ||||||
|  |     private readonly IVaultClient? hashiClient; | ||||||
|  |     private readonly VaultOptions? hashiOptions; | ||||||
|  |  | ||||||
|  |     public KeyVaultProvider(IConfiguration configuration) | ||||||
|  |     { | ||||||
|  |         environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"; | ||||||
|  |  | ||||||
|  |         if (environment == "Local") | ||||||
|  |         { | ||||||
|  |             hashiOptions = configuration.GetSection("Vault").Get<VaultOptions>(); | ||||||
|  |             hashiClient = new VaultClient(new VaultClientSettings( | ||||||
|  |                 hashiOptions?.Address, | ||||||
|  |                 new TokenAuthMethodInfo(hashiOptions?.Token) | ||||||
|  |             )); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|         /// Creates a new secret in Azure Key Vault. |     /// Creates a new secret in Azure Key Vault or HashiCorp Vault. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="keyVaultRequest">The request containing the name and value of the secret.</param> |     /// <param name="keyVaultRequest">The request containing the name and value of the secret.</param> | ||||||
|     /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> |     /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
|     /// <returns>A <see cref="KeyVaultResponse"/> containing the details of the created secret.</returns> |     /// <returns>A <see cref="KeyVaultResponse"/> containing the details of the created secret.</returns> | ||||||
|     public async ValueTask<KeyVaultResponse> CreateSecretAsync(KeyVaultRequest keyVaultRequest, CancellationToken cancellationToken) |     public async ValueTask<KeyVaultResponse> CreateSecretAsync(KeyVaultRequest keyVaultRequest, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|             KeyVaultResponse _response = new(); |         if (environment == "Local") | ||||||
|             KeyVaultSecret azureResponse = await keyVaultProvider.SetSecretAsync(new KeyVaultSecret(keyVaultRequest.Name, keyVaultRequest.Value), cancellationToken); |         { | ||||||
|  |             await hashiClient!.V1.Secrets.KeyValue.V2.WriteSecretAsync( | ||||||
|  |                 path: hashiOptions!.SecretPath, | ||||||
|  |                 data: new Dictionary<string, object> { { keyVaultRequest.Name, keyVaultRequest.Value } }, | ||||||
|  |                 mountPoint: hashiOptions.SecretMount | ||||||
|  |             ); | ||||||
|  |             return new KeyVaultResponse { Name = keyVaultRequest.Name, Value = keyVaultRequest.Value }; | ||||||
|  |         } | ||||||
|  |  | ||||||
|             _response.Value = azureResponse.Value; |         KeyVaultSecret azureResponse = await azureClient!.SetSecretAsync( | ||||||
|             _response.Name = azureResponse.Name; |             new KeyVaultSecret(keyVaultRequest.Name, keyVaultRequest.Value), cancellationToken | ||||||
|  |         ); | ||||||
|  |  | ||||||
|             return _response; |         return new KeyVaultResponse { Name = azureResponse.Name, Value = azureResponse.Value }; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|         /// Deletes a secret from Azure Key Vault if it exists. |     /// Deletes a secret from Azure Key Vault or HashiCorp Vault if it exists. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="secretName">The name of the secret to delete.</param> |     /// <param name="secretName">The name of the secret to delete.</param> | ||||||
|     /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> |     /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
| @@ -35,10 +65,20 @@ namespace Core.Blueprint.KeyVault | |||||||
|     /// </returns> |     /// </returns> | ||||||
|     public async ValueTask<Tuple<string, bool>> DeleteSecretAsync(string secretName, CancellationToken cancellationToken) |     public async ValueTask<Tuple<string, bool>> DeleteSecretAsync(string secretName, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|  |         if (environment == "Local") | ||||||
|  |         { | ||||||
|  |             await hashiClient!.V1.Secrets.KeyValue.V2.DeleteSecretAsync( | ||||||
|  |                 path: hashiOptions!.SecretPath, | ||||||
|  |                 mountPoint: hashiOptions.SecretMount | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             return new("Key Deleted", true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         var existingSecret = await this.GetSecretAsync(secretName, cancellationToken); |         var existingSecret = await this.GetSecretAsync(secretName, cancellationToken); | ||||||
|         if (existingSecret != null) |         if (existingSecret != null) | ||||||
|         { |         { | ||||||
|                 await keyVaultProvider.StartDeleteSecretAsync(secretName, cancellationToken); |             await azureClient!.StartDeleteSecretAsync(secretName, cancellationToken); | ||||||
|             return new("Key Deleted", true); |             return new("Key Deleted", true); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -46,7 +86,7 @@ namespace Core.Blueprint.KeyVault | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|         /// Retrieves a secret from Azure Key Vault. |     /// Retrieves a secret from Azure Key Vault or HashiCorp Vault. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="secretName">The name of the secret to retrieve.</param> |     /// <param name="secretName">The name of the secret to retrieve.</param> | ||||||
|     /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> |     /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
| @@ -56,18 +96,27 @@ namespace Core.Blueprint.KeyVault | |||||||
|     /// </returns> |     /// </returns> | ||||||
|     public async ValueTask<Tuple<KeyVaultResponse, string?>> GetSecretAsync(string secretName, CancellationToken cancellationToken) |     public async ValueTask<Tuple<KeyVaultResponse, string?>> GetSecretAsync(string secretName, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|             KeyVaultSecret azureResponse = await keyVaultProvider.GetSecretAsync(secretName, cancellationToken: cancellationToken); |         if (environment == "Local") | ||||||
|  |  | ||||||
|             if (azureResponse == null) |  | ||||||
|         { |         { | ||||||
|  |             var secret = await hashiClient!.V1.Secrets.KeyValue.V2.ReadSecretAsync( | ||||||
|  |                 path: hashiOptions!.SecretPath, | ||||||
|  |                 mountPoint: hashiOptions.SecretMount | ||||||
|  |             ); | ||||||
|  |  | ||||||
|  |             if (secret.Data.Data.TryGetValue(secretName, out var value)) | ||||||
|  |             { | ||||||
|  |                 return new(new KeyVaultResponse { Name = secretName, Value = value?.ToString() ?? "" }, string.Empty); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             return new(new KeyVaultResponse(), "Key Not Found"); |             return new(new KeyVaultResponse(), "Key Not Found"); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         KeyVaultSecret azureResponse = await azureClient!.GetSecretAsync(secretName, cancellationToken: cancellationToken); | ||||||
|         return new(new KeyVaultResponse { Name = secretName, Value = azureResponse.Value }, string.Empty); |         return new(new KeyVaultResponse { Name = secretName, Value = azureResponse.Value }, string.Empty); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// <summary> |     /// <summary> | ||||||
|         /// Updates an existing secret in Azure Key Vault. If the secret does not exist, an error is returned. |     /// Updates an existing secret in Azure Key Vault or HashiCorp Vault. If the secret does not exist, an error is returned. | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     /// <param name="newSecret">The updated secret information.</param> |     /// <param name="newSecret">The updated secret information.</param> | ||||||
|     /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> |     /// <param name="cancellationToken">The cancellation token to cancel the operation.</param> | ||||||
| @@ -76,18 +125,12 @@ namespace Core.Blueprint.KeyVault | |||||||
|     /// </returns> |     /// </returns> | ||||||
|     public async ValueTask<Tuple<KeyVaultResponse, string>> UpdateSecretAsync(KeyVaultRequest newSecret, CancellationToken cancellationToken) |     public async ValueTask<Tuple<KeyVaultResponse, string>> UpdateSecretAsync(KeyVaultRequest newSecret, CancellationToken cancellationToken) | ||||||
|     { |     { | ||||||
|             KeyVaultResponse _response = new(); |  | ||||||
|         var existingSecret = await this.GetSecretAsync(newSecret.Name, cancellationToken); |         var existingSecret = await this.GetSecretAsync(newSecret.Name, cancellationToken); | ||||||
|         if (existingSecret == null) |         if (existingSecret == null) | ||||||
|         { |         { | ||||||
|             return new(new KeyVaultResponse(), "Key Not Found"); |             return new(new KeyVaultResponse(), "Key Not Found"); | ||||||
|         } |         } | ||||||
|             KeyVaultSecret azureResponse = await keyVaultProvider.SetSecretAsync(new KeyVaultSecret(newSecret.Name, newSecret.Value), cancellationToken); |  | ||||||
|  |  | ||||||
|             _response.Value = azureResponse.Value; |         return new(await CreateSecretAsync(newSecret, cancellationToken), string.Empty); | ||||||
|             _response.Name = azureResponse.Name; |  | ||||||
|  |  | ||||||
|             return new(new KeyVaultResponse { Name = newSecret.Name, Value = azureResponse.Value }, string.Empty); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Sergio Matias Urquin
					Sergio Matias Urquin