windows_native_keyring_store/
store.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3use std::time::{SystemTime, UNIX_EPOCH};
4
5use keyring_core::api::{CredentialPersistence, CredentialStoreApi};
6use keyring_core::attributes::parse_attributes;
7use keyring_core::{Entry, Error, Result};
8
9use crate::cred::Cred;
10
11/// The store for Windows native credentials
12#[derive(Debug, Clone)]
13pub struct Store {
14    pub id: String,
15    pub delimiters: [String; 3],
16    pub service_no_divider: bool,
17}
18
19impl Store {
20    /// Create the default store: prefix and suffix empty, divider '.'.
21    ///
22    /// This is the configuration that matches the config for this store
23    /// in earlier versions of keyring.
24    pub fn new() -> Result<Arc<Self>> {
25        Ok(Self::new_internal(
26            ["".to_string(), ".".to_string(), "".to_string()],
27            false,
28        ))
29    }
30
31    /// Create a custom-configured store.
32    ///
33    /// The delimiter config options are `prefix`, `divider`, and `suffix`. They
34    /// default to `keyring:`, `@`, and the empty string, respectively.
35    ///
36    /// If you want to be sure that key descriptions cannot be ambiguous, specify
37    /// the config option `service_no_divider` to `true`.
38    pub fn new_with_configuration(config: &HashMap<&str, &str>) -> Result<Arc<Self>> {
39        let config = parse_attributes(
40            &["prefix", "divider", "suffix", "service_no_divider"],
41            Some(config),
42        )?;
43        let prefix = config
44            .get("prefix")
45            .map(|s| s.as_str())
46            .unwrap_or("")
47            .to_string();
48        let divider = config
49            .get("divider")
50            .map(|s| s.as_str())
51            .unwrap_or(".")
52            .to_string();
53        let suffix = config
54            .get("suffix")
55            .map(|s| s.as_str())
56            .unwrap_or("")
57            .to_string();
58        let service_no_divider = match config.get("service_no_divider").map(|s| s.as_str()) {
59            None => false,
60            Some("true") => true,
61            Some("false") => false,
62            Some(_) => {
63                return Err(Error::Invalid(
64                    "service_no_divider".to_string(),
65                    "must be true or false".to_string(),
66                ));
67            }
68        };
69        Ok(Self::new_internal(
70            [prefix, divider, suffix],
71            service_no_divider,
72        ))
73    }
74
75    fn new_internal(delimiters: [String; 3], service_no_divider: bool) -> Arc<Self> {
76        let now = SystemTime::now();
77        let elapsed = if now.lt(&UNIX_EPOCH) {
78            UNIX_EPOCH.duration_since(now).unwrap()
79        } else {
80            now.duration_since(UNIX_EPOCH).unwrap()
81        };
82        Arc::new(Store {
83            id: format!(
84                "Crate version {}, Instantiated at {}",
85                env!("CARGO_PKG_VERSION"),
86                elapsed.as_secs_f64()
87            ),
88            delimiters,
89            service_no_divider,
90        })
91    }
92}
93
94impl CredentialStoreApi for Store {
95    /// See the keyring-core API docs.
96    fn vendor(&self) -> String {
97        "Windows Credential Manager, https://crates.io/crates/windows-native-keyring-store"
98            .to_string()
99    }
100
101    /// See the keyring-core API docs.
102    fn id(&self) -> String {
103        self.id.clone()
104    }
105
106    /// See the keyring-core API docs.
107    ///
108    /// Building a credential does not create a key in the store.
109    /// It's setting a password that does that.
110    fn build(
111        &self,
112        service: &str,
113        user: &str,
114        modifiers: Option<&HashMap<&str, &str>>,
115    ) -> Result<Entry> {
116        let mods = parse_attributes(&["target"], modifiers)?;
117        let target = mods.get("target").map(|s| s.as_str());
118        let cred = Cred::build_from_specifiers(
119            target,
120            &self.delimiters,
121            self.service_no_divider,
122            service,
123            user,
124        )?;
125        Ok(Entry::new_with_credential(Arc::new(cred)))
126    }
127
128    /// See the keyring-core API docs.
129    fn as_any(&self) -> &dyn std::any::Any {
130        self
131    }
132
133    /// See the keyring-core API docs.
134    ///
135    /// Since this keystore keeps credentials in kernel memory, they vanish on reboot
136    fn persistence(&self) -> CredentialPersistence {
137        CredentialPersistence::UntilDelete
138    }
139
140    /// See the keychain-core API docs.
141    fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        std::fmt::Debug::fmt(self, f)
143    }
144}