doum_cli/system/
secret.rs

1use anyhow::{Context, Result};
2use keyring::Entry;
3use serde::{Deserialize, Serialize};
4
5/// Trait for provider-specific secret implementations
6pub trait ProviderSecret: Serialize + for<'de> Deserialize<'de> {
7    /// Validate the secret before saving
8    fn validate(&self) -> Result<()>;
9
10    /// Get masked version for display
11    fn masked(&self) -> String;
12}
13
14/// Secret manager for handling keyring operations
15pub struct SecretManager;
16
17impl SecretManager {
18    /// Save a secret value to the system keyring
19    pub fn save<T: ProviderSecret>(service_name: &str, secret: &T) -> Result<()> {
20        // Validate secret
21        secret.validate()?;
22
23        // Save to Keyring
24        let entry = Entry::new(service_name, "doum-cli").context("Failed to access keyring")?;
25        let value = serde_json::to_string(secret).context("Failed to serialize secret to JSON")?;
26        entry
27            .set_password(&value)
28            .context("Failed to save to keyring")?;
29
30        Ok(())
31    }
32
33    /// Load secret from Keyring
34    pub fn load<T: ProviderSecret>(service_name: &str) -> Result<T> {
35        let entry = Entry::new(service_name, "doum-cli").context("Failed to access keyring")?;
36
37        let secret_json = entry
38            .get_password()
39            .context("Failed to retrieve from keyring")?;
40
41        serde_json::from_str(&secret_json).context("Failed to parse secret")
42    }
43
44    /// Delete secret from Keyring
45    pub fn delete(service_name: &str) -> Result<()> {
46        let entry = Entry::new(service_name, "doum-cli").context("Failed to access keyring")?;
47        entry
48            .delete_credential()
49            .context("Failed to delete from keyring")?;
50        Ok(())
51    }
52}