feat: created initial parity gman generalization for vault provider

This commit is contained in:
2026-06-02 13:59:32 -06:00
parent 695a684b8d
commit 156de15a33
3 changed files with 51 additions and 19 deletions
+1 -1
View File
@@ -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");
+40 -15
View File
@@ -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<Regex> = LazyLock::new(|| Regex::new(r"\{\{(.+)}}
#[derive(Debug, Default, Clone)]
pub struct Vault {
local_provider: LocalProvider,
pub(crate) provider: SupportedProvider,
}
pub type GlobalVault = Arc<Vault>;
@@ -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<PathBuf> {
self.local_provider
.password_file
.clone()
.with_context(|| "A password file is required for the local provider")
pub fn local_password_file(&self) -> Result<PathBuf> {
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<String> {
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<Vec<String>> {
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());
}
}
+10 -3
View File
@@ -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()