Skip to content

Commit

Permalink
cargo fmt, clippy
Browse files Browse the repository at this point in the history
  • Loading branch information
wiimmers authored and Nick Wimmers committed May 3, 2024
1 parent b6d3920 commit 2e30071
Show file tree
Hide file tree
Showing 9 changed files with 772 additions and 771 deletions.
19 changes: 11 additions & 8 deletions src/credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ in a thread-safe way, a requirement captured in the [CredentialBuilder] and
[CredentialApi] types that wrap them.
*/
use super::Result;
use std::{collections::HashMap, any::Any};
use std::{any::Any, collections::HashMap};

/// The API that [credentials](Credential) implement.
pub trait CredentialApi {
Expand Down Expand Up @@ -94,25 +94,28 @@ pub type CredentialBuilder = dyn CredentialBuilderApi + Send + Sync;

/// The API that [credential search](CredentialSearch) implements.
pub trait CredentialSearchApi {
fn by(&self, by: &str, query: &str) -> Result<HashMap<String, HashMap<String, String>>>;
fn by(&self, by: &str, query: &str) -> Result<HashMap<String, HashMap<String, String>>>;
}

/// A thread-safe implementation of the [CredentialSearch API](CredentialSearchApi).
pub type CredentialSearch = dyn CredentialSearchApi + Send + Sync;
pub type CredentialSearch = dyn CredentialSearchApi + Send + Sync;

/// Type alias to shorten the long (and ugly) Credential Search Result HashMap.
pub type CredentialSearchResult = Result<HashMap<String, HashMap<String, String>>>;

/// The API that [credential list](CredentialList) implements.
pub trait CredentialListApi {
fn list_credentials(search_result: Result<HashMap<String, HashMap<String, String>>>, limit: Limit) -> Result<()>;
fn list_credentials(
search_result: Result<HashMap<String, HashMap<String, String>>>,
limit: Limit,
) -> Result<()>;
}

/// A thread-safe implementation of the [CredentialList API](CredentialListApi).
pub type CredentialList = dyn CredentialListApi + Send + Sync;
pub type CredentialList = dyn CredentialListApi + Send + Sync;

/// Type matching enum, allows for constraint of the amount of results returned to the user.
/// Type matching enum, allows for constraint of the amount of results returned to the user.
pub enum Limit {
All,
Max(i64)
All,
Max(i64),
}
248 changes: 124 additions & 124 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,124 +1,124 @@
/*!
Platform-independent error model.
There is an escape hatch here for surfacing platform-specific
error information returned by the platform-specific storage provider,
but (like all credential-related data) the concrete objects returned
must be both Send and Sync so credentials remain Send + Sync.
(Since most platform errors are integer error codes, this requirement
is not much of a burden on the platform-specific store providers.)
*/

use crate::Credential;

#[derive(Debug)]
/// Each variant of the `Error` enum provides a summary of the error.
/// More details, if relevant, are contained in the associated value,
/// which may be platform-specific.
///
/// Because future releases may add variants to this enum, clients should
/// always be prepared for that.
#[non_exhaustive]
pub enum Error {
/// This indicates runtime failure in the underlying
/// platform storage system. The details of the failure can
/// be retrieved from the attached platform error.
PlatformFailure(Box<dyn std::error::Error + Send + Sync>),
/// This indicates that the underlying secure storage
/// holding saved items could not be accessed. Typically this
/// is because of access rules in the platform; for example, it
/// might be that the credential store is locked. The underlying
/// platform error will typically give the reason.
NoStorageAccess(Box<dyn std::error::Error + Send + Sync>),
/// This indicates that there is no underlying credential
/// entry in the platform for this entry. Either one was
/// never set, or it was deleted.
NoEntry,
/// This indicates that the retrieved password blob was not
/// a UTF-8 string. The underlying bytes are available
/// for examination in the attached value.
BadEncoding(Vec<u8>),
/// This indicates that one of the entry's credential
/// attributes exceeded a
/// length limit in the underlying platform. The
/// attached values give the name of the attribute and
/// the platform length limit that was exceeded.
TooLong(String, u32),
/// This indicates that one of the entry's required credential
/// attributes was invalid. The
/// attached value gives the name of the attribute
/// and the reason it's invalid.
Invalid(String, String),
/// This indicates that there is more than one credential found in the store
/// that matches the entry. Its value is a vector of the matching credentials.
Ambiguous(Vec<Box<Credential>>),

SearchError(String),
}

pub type Result<T> = std::result::Result<T, Error>;

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::PlatformFailure(err) => write!(f, "Platform secure storage failure: {err}"),
Error::NoStorageAccess(err) => {
write!(f, "Couldn't access platform secure storage: {err}")
}
Error::NoEntry => write!(f, "No matching entry found in secure storage"),
Error::BadEncoding(_) => write!(f, "Password cannot be UTF-8 encoded"),
Error::TooLong(name, len) => write!(
f,
"Attribute '{name}' is longer than platform limit of {len} chars"
),
Error::Invalid(attr, reason) => {
write!(f, "Attribute {attr} is invalid: {reason}")
}
Error::Ambiguous(items) => {
write!(
f,
"Entry is matched by {} credendials: {items:?}",
items.len(),
)
}
Error::SearchError(reason) => {
write!(f, "Error searching for credential: {}", reason)
}
}
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::PlatformFailure(err) => Some(err.as_ref()),
Error::NoStorageAccess(err) => Some(err.as_ref()),
_ => None,
}
}
}

/// Try to interpret a byte vector as a password string
pub fn decode_password(bytes: Vec<u8>) -> Result<String> {
String::from_utf8(bytes).map_err(|err| Error::BadEncoding(err.into_bytes()))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_bad_password() {
// malformed sequences here taken from:
// https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
for bytes in [b"\x80".to_vec(), b"\xbf".to_vec(), b"\xed\xa0\xa0".to_vec()] {
match decode_password(bytes.clone()) {
Err(Error::BadEncoding(str)) => assert_eq!(str, bytes),
Err(other) => panic!("Bad password ({bytes:?}) decode gave wrong error: {other}"),
Ok(s) => panic!("Bad password ({bytes:?}) decode gave results: {s:?}"),
}
}
}
}
/*!
Platform-independent error model.
There is an escape hatch here for surfacing platform-specific
error information returned by the platform-specific storage provider,
but (like all credential-related data) the concrete objects returned
must be both Send and Sync so credentials remain Send + Sync.
(Since most platform errors are integer error codes, this requirement
is not much of a burden on the platform-specific store providers.)
*/

use crate::Credential;

#[derive(Debug)]
/// Each variant of the `Error` enum provides a summary of the error.
/// More details, if relevant, are contained in the associated value,
/// which may be platform-specific.
///
/// Because future releases may add variants to this enum, clients should
/// always be prepared for that.
#[non_exhaustive]
pub enum Error {
/// This indicates runtime failure in the underlying
/// platform storage system. The details of the failure can
/// be retrieved from the attached platform error.
PlatformFailure(Box<dyn std::error::Error + Send + Sync>),
/// This indicates that the underlying secure storage
/// holding saved items could not be accessed. Typically this
/// is because of access rules in the platform; for example, it
/// might be that the credential store is locked. The underlying
/// platform error will typically give the reason.
NoStorageAccess(Box<dyn std::error::Error + Send + Sync>),
/// This indicates that there is no underlying credential
/// entry in the platform for this entry. Either one was
/// never set, or it was deleted.
NoEntry,
/// This indicates that the retrieved password blob was not
/// a UTF-8 string. The underlying bytes are available
/// for examination in the attached value.
BadEncoding(Vec<u8>),
/// This indicates that one of the entry's credential
/// attributes exceeded a
/// length limit in the underlying platform. The
/// attached values give the name of the attribute and
/// the platform length limit that was exceeded.
TooLong(String, u32),
/// This indicates that one of the entry's required credential
/// attributes was invalid. The
/// attached value gives the name of the attribute
/// and the reason it's invalid.
Invalid(String, String),
/// This indicates that there is more than one credential found in the store
/// that matches the entry. Its value is a vector of the matching credentials.
Ambiguous(Vec<Box<Credential>>),

SearchError(String),
}

pub type Result<T> = std::result::Result<T, Error>;

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::PlatformFailure(err) => write!(f, "Platform secure storage failure: {err}"),
Error::NoStorageAccess(err) => {
write!(f, "Couldn't access platform secure storage: {err}")
}
Error::NoEntry => write!(f, "No matching entry found in secure storage"),
Error::BadEncoding(_) => write!(f, "Password cannot be UTF-8 encoded"),
Error::TooLong(name, len) => write!(
f,
"Attribute '{name}' is longer than platform limit of {len} chars"
),
Error::Invalid(attr, reason) => {
write!(f, "Attribute {attr} is invalid: {reason}")
}
Error::Ambiguous(items) => {
write!(
f,
"Entry is matched by {} credendials: {items:?}",
items.len(),
)
}
Error::SearchError(reason) => {
write!(f, "Error searching for credential: {}", reason)
}
}
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::PlatformFailure(err) => Some(err.as_ref()),
Error::NoStorageAccess(err) => Some(err.as_ref()),
_ => None,
}
}
}

/// Try to interpret a byte vector as a password string
pub fn decode_password(bytes: Vec<u8>) -> Result<String> {
String::from_utf8(bytes).map_err(|err| Error::BadEncoding(err.into_bytes()))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_bad_password() {
// malformed sequences here taken from:
// https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
for bytes in [b"\x80".to_vec(), b"\xbf".to_vec(), b"\xed\xa0\xa0".to_vec()] {
match decode_password(bytes.clone()) {
Err(Error::BadEncoding(str)) => assert_eq!(str, bytes),
Err(other) => panic!("Bad password ({bytes:?}) decode gave wrong error: {other}"),
Ok(s) => panic!("Bad password ({bytes:?}) decode gave results: {s:?}"),
}
}
}
}
Loading

0 comments on commit 2e30071

Please sign in to comment.