uv_keyring/error.rs
1/*!
2
3Platform-independent error model.
4
5There is an escape hatch here for surfacing platform-specific
6error information returned by the platform-specific storage provider,
7but the concrete objects returned must be `Send` so they can be
8moved from one thread to another. (Since most platform errors
9are integer error codes, this requirement
10is not much of a burden on the platform-specific store providers.)
11 */
12
13use crate::Credential;
14
15#[derive(Debug, thiserror::Error)]
16/// Each variant of the `Error` enum provides a summary of the error.
17/// More details, if relevant, are contained in the associated value,
18/// which may be platform-specific.
19///
20/// This enum is non-exhaustive so that more values can be added to it
21/// without a `SemVer` break. Clients should always have default handling
22/// for variants they don't understand.
23#[non_exhaustive]
24pub enum Error {
25 /// This indicates runtime failure in the underlying
26 /// platform storage system. The details of the failure can
27 /// be retrieved from the attached platform error.
28 #[error("Platform secure storage failure")]
29 PlatformFailure(#[source] Box<dyn std::error::Error + Send + Sync>),
30 /// This indicates that the underlying secure storage
31 /// holding saved items could not be accessed. Typically, this
32 /// is because of access rules in the platform; for example, it
33 /// might be that the credential store is locked. The underlying
34 /// platform error will typically give the reason.
35 #[error("Couldn't access platform secure storage")]
36 NoStorageAccess(#[source] Box<dyn std::error::Error + Send + Sync>),
37 /// This indicates that there is no underlying credential
38 /// entry in the platform for this entry. Either one was
39 /// never set, or it was deleted.
40 #[error("No matching entry found in secure storage")]
41 NoEntry,
42 /// This indicates that the retrieved password blob was not
43 /// a UTF-8 string. The underlying bytes are available
44 /// for examination in the attached value.
45 #[error("Data is not UTF-8 encoded")]
46 BadEncoding(Vec<u8>),
47 /// This indicates that one of the entry's credential
48 /// attributes exceeded a
49 /// length limit in the underlying platform. The
50 /// attached values give the name of the attribute and
51 /// the platform length limit that was exceeded.
52 #[error("Attribute '{0}' is longer than platform limit of {1} chars")]
53 TooLong(String, u32),
54 /// This indicates that one of the entry's required credential
55 /// attributes was invalid. The
56 /// attached value gives the name of the attribute
57 /// and the reason it's invalid.
58 #[error("Attribute {0} is invalid: {1}")]
59 Invalid(String, String),
60 /// This indicates that there is more than one credential found in the store
61 /// that matches the entry. Its value is a vector of the matching credentials.
62 #[error("Entry is matched by multiple credentials: {0:?}")]
63 Ambiguous(Vec<Box<Credential>>),
64 /// This indicates that there was no default credential builder to use;
65 /// the client must set one before creating entries.
66 #[error("No default credential builder is available; set one before creating entries")]
67 NoDefaultCredentialBuilder,
68}
69
70pub type Result<T> = std::result::Result<T, Error>;
71
72/// Try to interpret a byte vector as a password string
73pub fn decode_password(bytes: Vec<u8>) -> Result<String> {
74 String::from_utf8(bytes).map_err(|err| Error::BadEncoding(err.into_bytes()))
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn test_bad_password() {
83 // malformed sequences here taken from:
84 // https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
85 for bytes in [b"\x80".to_vec(), b"\xbf".to_vec(), b"\xed\xa0\xa0".to_vec()] {
86 match decode_password(bytes.clone()) {
87 Err(Error::BadEncoding(str)) => assert_eq!(str, bytes),
88 Err(other) => panic!("Bad password ({bytes:?}) decode gave wrong error: {other}"),
89 Ok(s) => panic!("Bad password ({bytes:?}) decode gave results: {s:?}"),
90 }
91 }
92 }
93}