doum_cli/system/
secret.rs1use anyhow::{Context, Result};
2use keyring::Entry;
3use serde::{Deserialize, Serialize};
4
5pub trait ProviderSecret: Serialize + for<'de> Deserialize<'de> {
7 fn validate(&self) -> Result<()>;
9
10 fn masked(&self) -> String;
12}
13
14const SECRET_SERVICE_NAME: &str = "doum-cli";
16
17pub struct SecretManager;
19
20impl SecretManager {
21 pub fn save<T: ProviderSecret>(service_name: &str, secret: &T) -> Result<()> {
23 secret.validate()?;
25
26 let entry =
28 Entry::new(service_name, SECRET_SERVICE_NAME).context("Failed to access keyring")?;
29 let value = serde_json::to_string(secret).context("Failed to serialize secret to JSON")?;
30 entry
31 .set_password(&value)
32 .context("Failed to save to keyring")?;
33
34 Ok(())
35 }
36
37 pub fn load<T: ProviderSecret>(service_name: &str) -> Result<T> {
39 let entry =
40 Entry::new(service_name, SECRET_SERVICE_NAME).context("Failed to access keyring")?;
41
42 let secret_json = entry
43 .get_password()
44 .context("Failed to retrieve from keyring")?;
45
46 serde_json::from_str(&secret_json).context("Failed to parse secret")
47 }
48
49 pub fn delete(service_name: &str) -> Result<()> {
51 let entry =
52 Entry::new(service_name, SECRET_SERVICE_NAME).context("Failed to access keyring")?;
53 entry
54 .delete_credential()
55 .context("Failed to delete from keyring")?;
56 Ok(())
57 }
58}