From 1072075104053d083261e4484740dbe54ec5d1ea Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Tue, 2 Jun 2026 13:59:32 -0600 Subject: [PATCH] feat: created initial parity gman generalization for vault provider --- src/config/mod.rs | 2 +- src/vault/mod.rs | 55 +++++++++++++++++++++++++++++++++------------- src/vault/utils.rs | 13 ++++++++--- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 1e2c453..1d603a5 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -640,7 +640,7 @@ pub async fn create_config_file(config_path: &Path) -> Result<()> { let mut config = json!({}); let (model, clients_config) = create_client_config(client, &vault).await?; config["model"] = model.into(); - config["vault_password_file"] = vault.password_file()?.display().to_string().into(); + config["vault_password_file"] = vault.local_password_file()?.display().to_string().into(); config["stream"] = json!(true); config["save"] = json!(true); config["keybindings"] = json!("vi"); diff --git a/src/vault/mod.rs b/src/vault/mod.rs index 1626fbc..7a0c041 100644 --- a/src/vault/mod.rs +++ b/src/vault/mod.rs @@ -7,9 +7,10 @@ pub use utils::interpolate_secrets; use crate::cli::Cli; use crate::config::AppConfig; use crate::vault::utils::ensure_password_file_initialized; -use anyhow::{Context, Result}; +use anyhow::{Context, Result, anyhow}; use fancy_regex::Regex; use gman::providers::SecretProvider; +use gman::providers::SupportedProvider; use gman::providers::local::LocalProvider; use inquire::{Password, PasswordDisplayMode, required}; use std::sync::{Arc, LazyLock}; @@ -19,7 +20,7 @@ pub static SECRET_RE: LazyLock = LazyLock::new(|| Regex::new(r"\{\{(.+)}} #[derive(Debug, Default, Clone)] pub struct Vault { - local_provider: LocalProvider, + pub(crate) provider: SupportedProvider, } pub type GlobalVault = Arc; @@ -33,7 +34,11 @@ impl Vault { ..LocalProvider::default() }; - Self { local_provider } + Self { + provider: SupportedProvider::Local { + provider_def: local_provider, + }, + } } pub fn init(config: &AppConfig) -> Self { @@ -47,14 +52,34 @@ impl Vault { ensure_password_file_initialized(&mut local_provider) .expect("Failed to initialize password file"); - Self { local_provider } + Self { + provider: SupportedProvider::Local { + provider_def: local_provider, + }, + } } - pub fn password_file(&self) -> Result { - self.local_provider - .password_file - .clone() - .with_context(|| "A password file is required for the local provider") + pub fn local_password_file(&self) -> Result { + match &self.provider { + SupportedProvider::Local { provider_def } => provider_def + .password_file + .clone() + .with_context(|| "A password file is required for the local provider"), + _ => Err(anyhow!( + "password_file is only available for the local provider" + )), + } + } + + fn provider_ref(&self) -> &dyn SecretProvider { + match &self.provider { + SupportedProvider::Local { provider_def } => provider_def, + SupportedProvider::AwsSecretsManager { provider_def } => provider_def, + SupportedProvider::GcpSecretManager { provider_def } => provider_def, + SupportedProvider::AzureKeyVault { provider_def } => provider_def, + SupportedProvider::Gopass { provider_def } => provider_def, + SupportedProvider::OnePassword { provider_def } => provider_def, + } } pub fn add_secret(&self, secret_name: &str) -> Result<()> { @@ -66,7 +91,7 @@ impl Vault { let h = Handle::current(); tokio::task::block_in_place(|| { - h.block_on(self.local_provider.set_secret(secret_name, &secret_value)) + h.block_on(self.provider_ref().set_secret(secret_name, &secret_value)) })?; println!("✓ Secret '{secret_name}' added to the vault."); @@ -76,7 +101,7 @@ impl Vault { pub fn get_secret(&self, secret_name: &str, display_output: bool) -> Result { let h = Handle::current(); let secret = tokio::task::block_in_place(|| { - h.block_on(self.local_provider.get_secret(secret_name)) + h.block_on(self.provider_ref().get_secret(secret_name)) })?; if display_output { @@ -95,7 +120,7 @@ impl Vault { let h = Handle::current(); tokio::task::block_in_place(|| { h.block_on( - self.local_provider + self.provider_ref() .update_secret(secret_name, &secret_value), ) })?; @@ -106,7 +131,7 @@ impl Vault { pub fn delete_secret(&self, secret_name: &str) -> Result<()> { let h = Handle::current(); - tokio::task::block_in_place(|| h.block_on(self.local_provider.delete_secret(secret_name)))?; + tokio::task::block_in_place(|| h.block_on(self.provider_ref().delete_secret(secret_name)))?; println!("✓ Secret '{secret_name}' deleted from the vault."); Ok(()) @@ -115,7 +140,7 @@ impl Vault { pub fn list_secrets(&self, display_output: bool) -> Result> { let h = Handle::current(); let secrets = - tokio::task::block_in_place(|| h.block_on(self.local_provider.list_secrets()))?; + tokio::task::block_in_place(|| h.block_on(self.provider_ref().list_secrets()))?; if display_output { if secrets.is_empty() { @@ -193,6 +218,6 @@ mod tests { #[test] fn vault_default_creates_instance() { let vault = Vault::default(); - assert!(vault.password_file().is_err()); + assert!(vault.local_password_file().is_err()); } } diff --git a/src/vault/utils.rs b/src/vault/utils.rs index bc1ee55..48626f1 100644 --- a/src/vault/utils.rs +++ b/src/vault/utils.rs @@ -2,6 +2,7 @@ use crate::config::ensure_parent_exists; use crate::vault::{SECRET_RE, Vault}; use anyhow::Result; use anyhow::anyhow; +use gman::providers::SupportedProvider; use gman::providers::local::LocalProvider; use indoc::formatdoc; use inquire::validator::Validation; @@ -34,8 +35,14 @@ pub fn ensure_password_file_initialized(local_provider: &mut LocalProvider) -> R } pub fn create_vault_password_file(vault: &mut Vault) -> Result<()> { - let vault_password_file = vault - .local_provider + let SupportedProvider::Local { + provider_def: local_provider, + } = &mut vault.provider + else { + return Ok(()); + }; + + let vault_password_file = local_provider .password_file .clone() .ok_or_else(|| anyhow!("Password file is not configured"))?; @@ -148,7 +155,7 @@ pub fn create_vault_password_file(vault: &mut Vault) -> Result<()> { match password { Ok(pw) => { std::fs::write(&password_file, pw.as_bytes())?; - vault.local_provider.password_file = Some(password_file); + local_provider.password_file = Some(password_file); println!( "✓ Password file '{}' created.", vault_password_file.display()