Skip to main content

keyring_core/
api.rs

1/*!
2
3# Platform-independent secure storage model
4
5This module defines a plug and play model for credential stores.
6The model comprises two traits: [CredentialStoreApi] for
7store-level operations
8and [CredentialApi] for
9entry-level operations.  These traits must be implemented
10in a thread-safe way, a requirement captured in the [CredentialStore] and
11[Credential] types that wrap them.
12 */
13use std::any::Any;
14use std::collections::HashMap;
15use std::sync::Arc;
16
17use super::{Entry, Error, Result};
18
19/// The API that [credentials](Credential) implement.
20pub trait CredentialApi {
21    /// Set the entry's protected data to be the given string.
22    ///
23    /// This method has a default implementation in terms of
24    /// [set_secret](CredentialApi::set_secret), which see.
25    fn set_password(&self, password: &str) -> Result<()> {
26        self.set_secret(password.as_bytes())
27    }
28
29    /// Set the underlying credential's protected data to be the given byte array.
30    ///
31    /// - If the password cannot be stored in a credential,
32    ///   return an [Invalid](Error::Invalid) error.
33    /// - If the entry is a specifier, and there is no matching credential,
34    ///   create a matching credential and save the data in it.
35    /// - If the entry is a specifier, and there is more than one matching credential,
36    ///   return an [Ambiguous](Error::Ambiguous) error.
37    /// - If the entry is a wrapper, and the wrapped credential has been deleted,
38    ///   either recreate the wrapped credential and set its value or
39    ///   return a [NoEntry](Error::NoEntry) error.
40    /// - Otherwise, set the value of the single, matching credential.
41    ///
42    /// Note: If an entry is both a specifier and a wrapper, it's up to the store
43    /// whether to recreate a deleted credential or to fail with a NoEntry error.
44    fn set_secret(&self, secret: &[u8]) -> Result<()>;
45
46    /// Retrieve the protected data as a UTF-8 string from the underlying credential.
47    ///
48    /// This method has a default implementation in terms of
49    /// [get_secret](CredentialApi::get_secret), which see.
50    /// If the data in the credential is not valid UTF-8, the default implementation
51    /// returns a [BadEncoding](Error::BadEncoding) error containing the data.
52    fn get_password(&self) -> Result<String> {
53        let secret = self.get_secret()?;
54        super::error::decode_password(secret)
55    }
56
57    /// Retrieve the protected data as a byte array from the underlying credential.
58    ///
59    /// - If the entry is a specifier, and there is no matching credential,
60    ///   return a [NoEntry](Error::NoEntry) error.
61    /// - If the entry is a specifier, and there is more than one matching credential,
62    ///   return an [Ambiguous](Error::Ambiguous) error.
63    /// - If the entry is a wrapper, and the wrapped credential has been deleted,
64    ///   return a [NoEntry](Error::NoEntry) error.
65    /// - Otherwise, return the value of the single, matching credential.
66    fn get_secret(&self) -> Result<Vec<u8>>;
67
68    /// Return any store-specific decorations on this entry's credential.
69    ///
70    /// The expected error and success cases are the same as with
71    /// [get_secret](CredentialApi::get_secret), which see.
72    ///
73    /// For convenience, a default implementation of this method is
74    /// provided which doesn't return any attributes. Credential
75    /// store implementations which support attributes should
76    /// override this method.
77    fn get_attributes(&self) -> Result<HashMap<String, String>> {
78        // this should err in the same cases as get_secret, so first call that for effect
79        self.get_secret()?;
80        // if we got this far, return success with no attributes
81        Ok(HashMap::new())
82    }
83
84    /// Update the secure store attributes on this entry's credential.
85    ///
86    /// If the user supplies any attributes that cannot be updated,
87    /// return an appropriate [Invalid](Error::Invalid) error.
88    ///
89    /// Other expected error and success cases are the same as with
90    /// [get_secret](CredentialApi::get_secret), which see.
91    ///
92    /// For convenience, a default implementation of this method is
93    /// provided which returns a [NotSupportedByStore](Error::NotSupportedByStore) error.
94    fn update_attributes(&self, _: &HashMap<&str, &str>) -> Result<()> {
95        Err(Error::NotSupportedByStore(String::from(
96            "This store does not allow attribute updates",
97        )))
98    }
99
100    /// Delete the underlying credential.
101    ///
102    /// If the underlying credential doesn't exist, return
103    /// a [NoEntry](Error::NoEntry) error.
104    ///
105    /// If there is more than one matching credential,
106    /// return an [Ambiguous](Error::Ambiguous) error.
107    fn delete_credential(&self) -> Result<()>;
108
109    /// Return a wrapper for the underlying credential.
110    ///
111    /// If `self` is already a wrapper, you can return `None`
112    /// to give `self` back to the client.
113    /// Or you can return a new wrapper
114    /// for the same underlying credential. See the
115    /// [keyring-core wiki page](https://github.com/open-source-cooperative/keyring-rs/wiki/Keyring-Core#specifier-credentials-vs-wrapper-credentials)
116    /// for why the `None` option is available.
117    ///
118    /// If the underlying credential doesn't exist, return
119    /// a [NoEntry](Error::NoEntry) error.
120    ///
121    /// If there is more than one matching credential,
122    /// return an [Ambiguous](Error::Ambiguous) error.
123    fn get_credential(&self) -> Result<Option<Arc<Credential>>>;
124
125    /// Return the `<service, user>` pair for this credential, if any.
126    fn get_specifiers(&self) -> Option<(String, String)>;
127
128    /// Return the inner credential object cast to [Any].
129    ///
130    /// This call is used to expose the Debug trait for credentials.
131    fn as_any(&self) -> &dyn Any;
132
133    /// The Debug trait call for the object.
134    ///
135    /// This is used to implement the Debug trait on this type; it
136    /// allows generic code to provide debug printing as provided by
137    /// the underlying concrete object.
138    ///
139    /// We provide a (no-op) default implementation of this method.
140    fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        std::fmt::Debug::fmt(self.as_any(), f)
142    }
143}
144
145/// A thread-safe implementation of the [Credential API](CredentialApi).
146pub type Credential = dyn CredentialApi + Send + Sync;
147
148impl std::fmt::Debug for Credential {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        self.debug_fmt(f)
151    }
152}
153
154/// A descriptor for the lifetime of stored credentials, returned from
155/// a credential store's [persistence](CredentialStoreApi::persistence) call.
156///
157/// This enum may change even in minor and patch versions of the library, so it's
158/// marked as non-exhaustive.
159#[non_exhaustive]
160pub enum CredentialPersistence {
161    /// Credential storage is in the entry, so storage vanishes when the entry is dropped.
162    EntryOnly,
163    /// Credential storage is in process memory,
164    /// so storage vanishes when the process terminates
165    ProcessOnly,
166    /// Credential storage is in user-space memory, so storage vanishes when the user logs out
167    UntilLogout,
168    /// Credentials stored in kernel-space memory, so storage vanishes when the machine reboots
169    UntilReboot,
170    /// Credentials stored on disk, so storage vanishes when the credential is deleted
171    UntilDelete,
172    /// Placeholder for cases not (yet) handled here
173    Unspecified,
174}
175
176/// The API that [credential stores](CredentialStore) implement.
177pub trait CredentialStoreApi {
178    /// The name of the "vendor" that provides this store.
179    ///
180    /// This allows clients to conditionalize their code for specific vendors.
181    /// This string should not vary with versions of the store. It's recommended
182    /// that it include the crate URL for the module provider.
183    fn vendor(&self) -> String;
184
185    /// The ID of this credential store instance.
186    ///
187    /// IDs need not be unique across vendors or processes, but they
188    /// serve as instance IDs within a process.  If two credential store
189    /// instances in a process have the same vendor and id,
190    /// then they are the same instance.
191    ///
192    /// It's recommended that this include the version of the provider.
193    fn id(&self) -> String;
194
195    /// Create an entry specified by the given service and user,
196    /// perhaps with additional creation-time modifiers.
197    ///
198    /// The credential returned from this call must be a specifier,
199    /// meaning that it can be used to create a credential later
200    /// even if a matching credential existed in the store .
201    ///
202    /// This typically has no effect on the content of the underlying store.
203    /// A credential need not be persisted until its password is set.
204    fn build(
205        &self,
206        service: &str,
207        user: &str,
208        modifiers: Option<&HashMap<&str, &str>>,
209    ) -> Result<Entry>;
210
211    /// Search for credentials that match the given spec.
212    ///
213    /// Returns a list of the matching credentials.
214    ///
215    /// Should return an [Invalid](Error::Invalid) error if the spec is bad.
216    ///
217    /// The default implementation returns a
218    /// [NotSupportedByStore](Error::NotSupportedByStore) error; that is,
219    /// credential stores need not provide support for search.
220    fn search(&self, _spec: &HashMap<&str, &str>) -> Result<Vec<Entry>> {
221        let msg = "This store does not provide search capabilities";
222        Err(Error::NotSupportedByStore(msg.to_string()))
223    }
224
225    /// Return the inner store object cast to [Any].
226    ///
227    /// This call is used to expose the Debug trait for stores.
228    fn as_any(&self) -> &dyn Any;
229
230    /// The lifetime of credentials produced by this builder.
231    ///
232    /// A default implementation is provided for backward compatibility,
233    /// since this API was added in a minor release.  The default assumes
234    /// that keystores use disk-based credential storage.
235    fn persistence(&self) -> CredentialPersistence {
236        CredentialPersistence::UntilDelete
237    }
238
239    /// The Debug trait call for the object.
240    ///
241    /// This is used to implement the Debug trait on this type; it
242    /// allows generic code to provide debug printing as provided by
243    /// the underlying concrete object.
244    ///
245    /// We provide a (no-op) default implementation of this method.
246    fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247        std::fmt::Debug::fmt(self.as_any(), f)
248    }
249}
250
251impl std::fmt::Debug for CredentialStore {
252    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253        self.debug_fmt(f)
254    }
255}
256
257/// A thread-safe implementation of the [CredentialBuilder API](CredentialStoreApi).
258pub type CredentialStore = dyn CredentialStoreApi + Send + Sync;