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;