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, 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 = match config.get("prefix") {
44            Some(prefix) => prefix.to_string(),
45            None => "".to_string(),
46        };
47        let divider = match config.get("divider") {
48            Some(divider) => divider.to_string(),
49            None => ".".to_string(),
50        };
51        let suffix = match config.get("suffix") {
52            Some(suffix) => suffix.to_string(),
53            None => "".to_string(),
54        };
55        let service_no_divider = config
56            .get("service_no_divider")
57            .is_some_and(|s| s.eq("true"));
58        Ok(Self::new_internal(
59            [prefix, divider, suffix],
60            service_no_divider,
61        ))
62    }
63
64    fn new_internal(delimiters: [String; 3], service_no_divider: bool) -> Arc<Self> {
65        let now = SystemTime::now();
66        let elapsed = if now.lt(&UNIX_EPOCH) {
67            UNIX_EPOCH.duration_since(now).unwrap()
68        } else {
69            now.duration_since(UNIX_EPOCH).unwrap()
70        };
71        Arc::new(Store {
72            id: format!(
73                "Crate version {}, Instantiated at {}",
74                env!("CARGO_PKG_VERSION"),
75                elapsed.as_secs_f64()
76            ),
77            delimiters,
78            service_no_divider,
79        })
80    }
81}
82
83impl CredentialStoreApi for Store {
84    /// See the keyring-core API docs.
85    fn vendor(&self) -> String {
86        "Windows Credential Manager, https://crates.io/crates/windows-native-keyring-store"
87            .to_string()
88    }
89
90    /// See the keyring-core API docs.
91    fn id(&self) -> String {
92        self.id.clone()
93    }
94
95    /// See the keyring-core API docs.
96    ///
97    /// Building a credential does not create a key in the store.
98    /// It's setting a password that does that.
99    fn build(
100        &self,
101        service: &str,
102        user: &str,
103        modifiers: Option<&HashMap<&str, &str>>,
104    ) -> Result<Entry> {
105        let mods = parse_attributes(&["target", "persistence"], modifiers)?;
106        let target = mods.get("target").map(|s| s.as_str());
107        let persistence = mods
108            .get("persistence")
109            .map(|s| s.as_str())
110            .unwrap_or("Enterprise");
111        let cred = Cred::build_from_specifiers(
112            target,
113            &self.delimiters,
114            self.service_no_divider,
115            service,
116            user,
117            persistence.parse()?,
118        )?;
119        Ok(Entry::new_with_credential(Arc::new(cred)))
120    }
121
122    /// See the keyring-core API docs.
123    fn as_any(&self) -> &dyn std::any::Any {
124        self
125    }
126
127    /// See the keyring-core API docs.
128    ///
129    /// Since this keystore keeps credentials in kernel memory, they vanish on reboot
130    fn persistence(&self) -> CredentialPersistence {
131        CredentialPersistence::UntilDelete
132    }
133
134    /// See the keychain-core API docs.
135    fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        std::fmt::Debug::fmt(self, f)
137    }
138}