fix: macOS keychain infinite prompt loop (#6620)
Signed-off-by: sheikhlimon <sheikhlimon404@gmail.com>
This commit is contained in:
@@ -12,7 +12,7 @@ use std::ffi::OsString;
|
|||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Mutex;
|
use std::sync::{Arc, Mutex};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const KEYRING_SERVICE: &str = "goose";
|
const KEYRING_SERVICE: &str = "goose";
|
||||||
@@ -105,6 +105,7 @@ pub struct Config {
|
|||||||
config_path: PathBuf,
|
config_path: PathBuf,
|
||||||
secrets: SecretStorage,
|
secrets: SecretStorage,
|
||||||
guard: Mutex<()>,
|
guard: Mutex<()>,
|
||||||
|
secrets_cache: Arc<Mutex<Option<HashMap<String, Value>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SecretStorage {
|
enum SecretStorage {
|
||||||
@@ -133,6 +134,7 @@ impl Default for Config {
|
|||||||
config_path,
|
config_path,
|
||||||
secrets,
|
secrets,
|
||||||
guard: Mutex::new(()),
|
guard: Mutex::new(()),
|
||||||
|
secrets_cache: Arc::new(Mutex::new(None)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,6 +238,7 @@ impl Config {
|
|||||||
service: service.to_string(),
|
service: service.to_string(),
|
||||||
},
|
},
|
||||||
guard: Mutex::new(()),
|
guard: Mutex::new(()),
|
||||||
|
secrets_cache: Arc::new(Mutex::new(None)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +256,7 @@ impl Config {
|
|||||||
path: secrets_path.as_ref().to_path_buf(),
|
path: secrets_path.as_ref().to_path_buf(),
|
||||||
},
|
},
|
||||||
guard: Mutex::new(()),
|
guard: Mutex::new(()),
|
||||||
|
secrets_cache: Arc::new(Mutex::new(None)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,28 +544,43 @@ impl Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_secrets(&self) -> Result<HashMap<String, Value>, ConfigError> {
|
pub fn all_secrets(&self) -> Result<HashMap<String, Value>, ConfigError> {
|
||||||
match &self.secrets {
|
let mut cache = self.secrets_cache.lock().unwrap();
|
||||||
SecretStorage::Keyring { service } => {
|
|
||||||
let result =
|
|
||||||
self.handle_keyring_operation(|entry| entry.get_password(), service, None);
|
|
||||||
|
|
||||||
match result {
|
let values = if let Some(ref cached_secrets) = *cache {
|
||||||
Ok(content) => {
|
cached_secrets.clone()
|
||||||
let values: HashMap<String, Value> = serde_json::from_str(&content)?;
|
} else {
|
||||||
Ok(values)
|
tracing::debug!("secrets cache miss, fetching from storage");
|
||||||
|
|
||||||
|
let loaded = match &self.secrets {
|
||||||
|
SecretStorage::Keyring { service } => {
|
||||||
|
let result =
|
||||||
|
self.handle_keyring_operation(|entry| entry.get_password(), service, None);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(content) => {
|
||||||
|
let values: HashMap<String, Value> = serde_json::from_str(&content)?;
|
||||||
|
values
|
||||||
|
}
|
||||||
|
Err(ConfigError::FallbackToFileStorage) => {
|
||||||
|
self.fallback_to_file_storage()?
|
||||||
|
}
|
||||||
|
Err(ConfigError::KeyringError(msg))
|
||||||
|
if msg.contains("No entry found")
|
||||||
|
|| msg.contains("No matching entry found") =>
|
||||||
|
{
|
||||||
|
HashMap::new()
|
||||||
|
}
|
||||||
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
Err(ConfigError::FallbackToFileStorage) => self.fallback_to_file_storage(),
|
|
||||||
Err(ConfigError::KeyringError(msg))
|
|
||||||
if msg.contains("No entry found")
|
|
||||||
|| msg.contains("No matching entry found") =>
|
|
||||||
{
|
|
||||||
Ok(HashMap::new())
|
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
}
|
||||||
}
|
SecretStorage::File { path } => self.read_secrets_from_file(path)?,
|
||||||
SecretStorage::File { path } => self.read_secrets_from_file(path),
|
};
|
||||||
}
|
|
||||||
|
*cache = Some(loaded.clone());
|
||||||
|
loaded
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an environment variable value into a JSON Value.
|
/// Parse an environment variable value into a JSON Value.
|
||||||
@@ -786,6 +805,9 @@ impl Config {
|
|||||||
std::fs::write(path, yaml_value)?;
|
std::fs::write(path, yaml_value)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.invalidate_secrets_cache();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,6 +842,9 @@ impl Config {
|
|||||||
std::fs::write(path, yaml_value)?;
|
std::fs::write(path, yaml_value)?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.invalidate_secrets_cache();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -858,6 +883,11 @@ impl Config {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn invalidate_secrets_cache(&self) {
|
||||||
|
let mut cache = self.secrets_cache.lock().unwrap();
|
||||||
|
*cache = None;
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if an error string indicates a keyring availability issue that should trigger fallback
|
/// Check if an error string indicates a keyring availability issue that should trigger fallback
|
||||||
fn is_keyring_availability_error(&self, error_str: &str) -> bool {
|
fn is_keyring_availability_error(&self, error_str: &str) -> bool {
|
||||||
error_str.contains("keyring")
|
error_str.contains("keyring")
|
||||||
|
|||||||
Reference in New Issue
Block a user