Skip to main content

keyring_manager/
lib.rs

1//! # Keyring library
2//!
3//! Allows for setting and getting passwords on Linux, OSX, Windows, and Android
4#![deny(clippy::all)]
5
6use log::*;
7use std::path::Path;
8
9mod error;
10mod escape;
11mod insecure;
12
13pub use error::{KeyringError, Result};
14use escape::*;
15use insecure::InsecureKeyringManager;
16
17/////////////////////////////////////////////////////////////////////////
18
19use cfg_if::*;
20cfg_if! {
21    if #[cfg(target_os = "linux")] {
22        mod linux;
23        use linux::LinuxKeyringManager as PlatformKeyringManager;
24    } else if #[cfg(target_os = "windows")] {
25        mod windows;
26        use windows::WindowsKeyringManager as PlatformKeyringManager;
27    } else if #[cfg(target_os = "macos")] {
28        mod macos;
29        use macos::MacosKeyringManager as PlatformKeyringManager;
30    } else if #[cfg(target_os = "ios")] {
31        mod ios;
32        use ios::IosKeyringManager as PlatformKeyringManager;
33    } else if #[cfg(target_os = "android")] {
34        pub(crate) mod android;
35        use android::AndroidKeyringManager as PlatformKeyringManager;
36        pub use android::AndroidKeyringContext;
37    } else {
38        struct PlatformKeyringManager {}
39
40        impl PlatformKeyringManager {
41            pub fn with_keyring<F, T>(&self, _service: &str, _key: &str, _func: F) -> Result<T>
42            where
43                F: FnOnce(&mut dyn Keyring) -> Result<T>,
44            {
45                Err(KeyringError::NoBackendFound)
46            }
47
48            pub fn list_keys(&self, _service: &str) -> Result<Vec<String>> {
49                Err(KeyringError::NoBackendFound)
50            }
51        }
52    }
53}
54/////////////////////////////////////////////////////////////////////////
55
56/// A single keyring entry, bound to one (application, service, key) triple.
57pub trait Keyring {
58    /// Store a value, overwriting any existing one.
59    fn set_value(&mut self, value: &str) -> Result<()>;
60    /// Read the stored value, or [`KeyringError::NoPasswordFound`] if absent.
61    fn get_value(&self) -> Result<String>;
62    /// Delete the stored value, or [`KeyringError::NoPasswordFound`] if absent.
63    fn delete_value(&mut self) -> Result<()>;
64}
65
66enum KeyringManagerKind {
67    #[allow(dead_code)]
68    Secure(PlatformKeyringManager),
69    Insecure(InsecureKeyringManager),
70}
71
72pub struct KeyringManager {
73    kind: KeyringManagerKind,
74}
75
76impl KeyringManager {
77    cfg_if! {
78        if #[cfg(all(target_os = "macos", feature = "macos-specify-keychain"))] {
79            /// Open a secure keyring backed by the OS keychain at a specific path (macOS only).
80            pub fn new_secure_with_path(application: &str, path: &Path) -> Result<Self> {
81                Ok(KeyringManager {
82                    kind: KeyringManagerKind::Secure(PlatformKeyringManager::with_path(application, path)?),
83                })
84            }
85        }
86    }
87    cfg_if! {
88        if #[cfg(target_os = "android")] {
89            /// Open a secure keyring backed by the OS keystore (Android).
90            pub fn new_secure(application: &str, context: AndroidKeyringContext) -> Result<Self> {
91                Ok(KeyringManager {
92                    kind: KeyringManagerKind::Secure(PlatformKeyringManager::new(application, context)?),
93                })
94            }
95        } else if #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos", target_os = "ios"))] {
96            /// Open a secure keyring backed by the OS keychain/credential store.
97            pub fn new_secure(application: &str) -> Result<Self> {
98                Ok(KeyringManager {
99                    kind: KeyringManagerKind::Secure(PlatformKeyringManager::new(application)?),
100                })
101            }
102        } else {
103            /// No secure backend exists on this platform.
104            pub fn new_secure(_application: &str) -> Result<Self> {
105                Err(KeyringError::NoBackendFound)
106            }
107        }
108    }
109
110    /// Open an insecure keyring backed by an on-disk file (fallback when no OS backend is available).
111    pub fn new_insecure(application: &str, insecure_keyring_file: &Path) -> Result<Self> {
112        Ok(KeyringManager {
113            kind: KeyringManagerKind::Insecure(InsecureKeyringManager::new(
114                application,
115                insecure_keyring_file,
116            )?),
117        })
118    }
119
120    /// Run a closure against the keyring entry for (service, key).
121    pub fn with_keyring<F, T>(&self, service: &str, key: &str, f: F) -> Result<T>
122    where
123        F: FnOnce(&mut dyn Keyring) -> Result<T>,
124    {
125        match &self.kind {
126            KeyringManagerKind::Secure(pkm) => pkm.with_keyring(service, key, f),
127            KeyringManagerKind::Insecure(ikm) => ikm.with_keyring(service, key, f),
128        }
129    }
130
131    /// List the key names stored under a service, in no particular order. An unknown service is
132    /// an empty list, not an error.
133    pub fn list_keys(&self, service: &str) -> Result<Vec<String>> {
134        match &self.kind {
135            KeyringManagerKind::Secure(pkm) => pkm.list_keys(service),
136            KeyringManagerKind::Insecure(ikm) => ikm.list_keys(service),
137        }
138    }
139}
140
141pub(crate) fn map_to_generic<E: std::fmt::Display>(e: E) -> KeyringError {
142    KeyringError::Generic(format!("{}", e))
143}
144
145pub(crate) fn map_log_err<T: std::error::Error>(e: T) -> T {
146    error!("{}", e);
147    e
148}
149
150cfg_if! {
151    if #[cfg(test)] {
152        mod tests;
153    } else if #[cfg(feature="keyring_manager_android_tests")] {
154        pub mod tests;
155    } else if #[cfg(feature="keyring_manager_ios_tests")] {
156        pub mod tests;
157    }
158}