From 03bc44a9f66f15e906c7af0555a4f62784a53703 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Fri, 12 Sep 2025 19:35:54 -0600 Subject: [PATCH] test: Added tests for the Azure Key Vault provider --- README.md | 32 +++++++++- src/providers/mod.rs | 6 +- tests/providers/azure_key_vault_tests.rs | 71 +++++++++++++++++++++ tests/providers/gcp_secret_manager_tests.rs | 5 +- tests/providers/mod.rs | 1 + 5 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 tests/providers/azure_key_vault_tests.rs diff --git a/README.md b/README.md index 4ac8f06..8fe8eef 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ gman aws sts get-caller-identity - [Local](#provider-local) - [AWS Secrets Manager](#provider-aws_secrets_manager) - [GCP Secret Manager](#provider-gcp_secret_manager) + - [Azure Key Vault](#provider-azure_key_vault) - [Run Configurations](#run-configurations) - [Environment Variable Secret Injection](#environment-variable-secret-injection) - [Inject Secrets via Command-Line Flags](#inject-secrets-via-command-line-flags) @@ -244,7 +245,7 @@ documented and added without breaking existing setups. The following table shows | `local` | ✅ | [Local](#provider-local) | | | [`aws_secrets_manager`](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html) | ✅ | [AWS Secrets Manager](#provider-aws_secrets_manager) | | | [`hashicorp_vault`](https://www.hashicorp.com/en/products/vault) | 🕒 | | | -| [`azure_key_vault`](https://azure.microsoft.com/en-us/products/key-vault/) | 🕒 | | | +| [`azure_key_vault`](https://azure.microsoft.com/en-us/products/key-vault/) | ✅ | [Azure Key Vault](#provider-azure_key_vault) | | | [`gcp_secret_manager`](https://cloud.google.com/security/products/secret-manager?hl=en) | ✅ | [GCP Secret Manager](#provider-gcp_secret_manager) | | | [`1password`](https://1password.com/) | 🕒 | | | | [`bitwarden`](https://bitwarden.com/) | 🕒 | | | @@ -360,6 +361,35 @@ Important notes: add a new version. - `get` returns the latest version; older versions remain unless you delete the secret. +### Provider: `azure_key_vault` + +The `azure_key_vault` provider uses Azure Key Vault as the backing storage location for secrets. + +- Requires: `vault_name` (Key Vault name; the endpoint is constructed as `https://.vault.azure.net`). + +Configuration example: + +```yaml +default_provider: azure +providers: + - name: azure + type: azure_key_vault + vault_name: my-vault-name +``` + +Authentication: +- Use the Azure CLI and ensure you are logged in: `az login`. +- If needed, select the correct subscription: `az account set -s `. +- The provider uses `DefaultAzureCredential`, which can authenticate via Azure CLI, environment variables, managed + identity, etc. + +Important notes: +- Deleting a secret removes the entire secret and all its versions, not just the latest version. Depending on your + vault’s soft-delete settings, the secret may enter a deleted state until purged. +- `set`/`update` create a new secret version each time; reads return the latest by default. +- Ensure your identity has the necessary Key Vault permissions (RBAC such as `Key Vault Secrets User`/`Administrator`, + or appropriate access policies) for get/set/list/delete. + ## Run Configurations Run configurations (or "profiles") tell `gman` how to inject secrets into a command. Three modes of secret injection are diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 9856421..5525e45 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -8,12 +8,12 @@ pub mod gcp_secret_manager; mod git_sync; pub mod local; -use std::fmt; use crate::providers::local::LocalProvider; use anyhow::{Result, anyhow}; use aws_secrets_manager::AwsSecretsManagerProvider; use gcp_secret_manager::GcpSecretManagerProvider; use serde::{Deserialize, Serialize}; +use std::fmt; use std::fmt::{Display, Formatter}; use validator::{Validate, ValidationErrors}; @@ -73,7 +73,7 @@ impl Validate for SupportedProvider { SupportedProvider::Local { provider_def } => provider_def.validate(), SupportedProvider::AwsSecretsManager { provider_def } => provider_def.validate(), SupportedProvider::GcpSecretManager { provider_def } => provider_def.validate(), - SupportedProvider::AzureKeyVault { provider_def } => provider_def.validate(), + SupportedProvider::AzureKeyVault { provider_def } => provider_def.validate(), } } } @@ -92,7 +92,7 @@ impl Display for SupportedProvider { SupportedProvider::Local { .. } => write!(f, "local"), SupportedProvider::AwsSecretsManager { .. } => write!(f, "aws_secrets_manager"), SupportedProvider::GcpSecretManager { .. } => write!(f, "gcp_secret_manager"), - SupportedProvider::AzureKeyVault { .. } => write!(f, "azure_key_vault"), + SupportedProvider::AzureKeyVault { .. } => write!(f, "azure_key_vault"), } } } diff --git a/tests/providers/azure_key_vault_tests.rs b/tests/providers/azure_key_vault_tests.rs new file mode 100644 index 0000000..b835f78 --- /dev/null +++ b/tests/providers/azure_key_vault_tests.rs @@ -0,0 +1,71 @@ +use gman::config::{Config, ProviderConfig}; +use gman::providers::azure_key_vault::AzureKeyVaultProvider; +use gman::providers::{SecretProvider, SupportedProvider}; +use pretty_assertions::{assert_eq, assert_str_eq}; +use validator::Validate; + +#[test] +fn test_azure_provider_name() { + let provider = AzureKeyVaultProvider { + vault_name: Some("my-vault".into()), + }; + assert_str_eq!(provider.name(), "AzureKeyVaultProvider"); +} + +#[test] +fn test_azure_provider_validation_ok() { + let provider = AzureKeyVaultProvider { + vault_name: Some("vault-prod".into()), + }; + assert!(provider.validate().is_ok()); +} + +#[test] +fn test_azure_provider_missing_vault_name() { + let provider = AzureKeyVaultProvider { vault_name: None }; + assert!(provider.validate().is_err()); +} + +#[test] +fn test_supported_provider_display_and_validate() { + let sp = SupportedProvider::AzureKeyVault { + provider_def: AzureKeyVaultProvider { + vault_name: Some("kv-demo".into()), + }, + }; + assert!(sp.validate().is_ok()); + assert_eq!(sp.to_string(), "azure_key_vault"); +} + +#[test] +fn test_provider_config_with_azure_deserialize_and_extract() { + // Minimal ProviderConfig YAML using the azure_key_vault variant + let yaml = r#"--- +name: azure +type: azure_key_vault +vault_name: my-vault +"#; + + let pc: ProviderConfig = serde_yaml::from_str(yaml).expect("valid provider config yaml"); + assert!(pc.validate().is_ok()); + + let mut pc_owned = pc.clone(); + let provider: &mut dyn SecretProvider = pc_owned.extract_provider(); + assert_eq!(provider.name(), "AzureKeyVaultProvider"); + + // Round-trip through Config with default_provider + let cfg_yaml = r#"--- +default_provider: azure +providers: + - name: azure + type: azure_key_vault + vault_name: my-vault +"#; + let cfg: Config = serde_yaml::from_str(cfg_yaml).expect("valid config yaml"); + assert!(cfg.validate().is_ok()); + + let extracted = cfg + .extract_provider_config(None) + .expect("should find default provider"); + assert_eq!(extracted.name.as_deref(), Some("azure")); +} diff --git a/tests/providers/gcp_secret_manager_tests.rs b/tests/providers/gcp_secret_manager_tests.rs index a3ccd37..29d8849 100644 --- a/tests/providers/gcp_secret_manager_tests.rs +++ b/tests/providers/gcp_secret_manager_tests.rs @@ -22,7 +22,9 @@ fn test_gcp_provider_validation_ok() { #[test] fn test_gcp_provider_missing_project() { - let provider = GcpSecretManagerProvider { gcp_project_id: None }; + let provider = GcpSecretManagerProvider { + gcp_project_id: None, + }; assert!(provider.validate().is_err()); } @@ -71,4 +73,3 @@ providers: .expect("should find default provider"); assert_eq!(extracted.name.as_deref(), Some("gcp")); } - diff --git a/tests/providers/mod.rs b/tests/providers/mod.rs index 3b7ea4d..c8f2ff9 100644 --- a/tests/providers/mod.rs +++ b/tests/providers/mod.rs @@ -1,4 +1,5 @@ mod aws_secrets_manager_tests; +mod azure_key_vault_tests; mod gcp_secret_manager_tests; mod local_tests; mod provider_tests;