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