From 8f9599772d7083d42a9659fa7304539071c64d96 Mon Sep 17 00:00:00 2001 From: Daniel Brotsky Date: Tue, 6 Aug 2024 08:48:57 -0700 Subject: [PATCH 1/3] Allow empty user in CLI. Fixes hwchen/keyring-rs#201. --- examples/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cli.rs b/examples/cli.rs index 0b4598c..50b2e42 100644 --- a/examples/cli.rs +++ b/examples/cli.rs @@ -6,7 +6,7 @@ use keyring::{Entry, Error, Result}; fn main() { let mut args: Cli = Cli::parse(); - if args.user.is_empty() || args.user.eq_ignore_ascii_case("") { + if args.user.eq_ignore_ascii_case("") { args.user = whoami::username() } let entry = match args.entry_for() { From 8b0374d6fd33340fca63062ec00cdb6cf66078d3 Mon Sep 17 00:00:00 2001 From: Daniel Brotsky Date: Tue, 6 Aug 2024 17:53:58 -0700 Subject: [PATCH 2/3] Make debug formatting useful on credentials. As reported in hwchen/keyring-rs#201, the existing debug print of credentials gives no information about the underlying platform credential. This fixes that. --- src/credential.rs | 24 +++++++++++++++++++----- src/ios.rs | 6 ++++++ src/keyutils.rs | 6 ++++++ src/macos.rs | 6 ++++++ src/mock.rs | 5 +++++ src/secret_service.rs | 7 ++++++- src/windows.rs | 6 ++++++ 7 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/credential.rs b/src/credential.rs index fbdf91c..54917ec 100644 --- a/src/credential.rs +++ b/src/credential.rs @@ -27,9 +27,10 @@ if matches!(persistence, credential::CredentialPersistence::UntilDelete) { } ``` */ -use super::Result; use std::any::Any; +use super::Result; + /// The API that [credentials](Credential) implement. pub trait CredentialApi { /// Set the credential's password (a string). @@ -66,17 +67,30 @@ pub trait CredentialApi { /// can do platform-specific things with it (e.g., /// query its attributes in the underlying store). fn as_any(&self) -> &dyn Any; -} -impl std::fmt::Debug for Credential { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.as_any().fmt(f) + /// The Debug trait call for the object. + /// + /// This is used to implement the Debug trait on this type; it + /// allows generic code to provide debug printing as provided by + /// the underlying concrete object. + /// + /// We provide a (useless) default implementation for backward + /// compatibility with existing implementors who may have not + /// implemented the Debug trait for their credential objects + fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self.as_any(), f) } } /// A thread-safe implementation of the [Credential API](CredentialApi). pub type Credential = dyn CredentialApi + Send + Sync; +impl std::fmt::Debug for Credential { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.debug_fmt(f) + } +} + /// A descriptor for the lifetime of stored credentials, returned from /// a credential store's [persistence](CredentialBuilderApi::persistence) call. #[non_exhaustive] diff --git a/src/ios.rs b/src/ios.rs index 32430fe..d377206 100644 --- a/src/ios.rs +++ b/src/ios.rs @@ -16,6 +16,7 @@ wildcards when looking up credentials by attribute value.) On iOS, the target parameter is ignored, because there is only one keychain that can be targeted to store a generic credential. */ + use security_framework::base::Error; use security_framework::passwords::{ delete_generic_password, get_generic_password, set_generic_password, @@ -87,6 +88,11 @@ impl CredentialApi for IosCredential { fn as_any(&self) -> &dyn std::any::Any { self } + + /// Expose the concrete debug formatter for use via the [Credential] trait + fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } } impl IosCredential { diff --git a/src/keyutils.rs b/src/keyutils.rs index fa17708..c70c3c4 100644 --- a/src/keyutils.rs +++ b/src/keyutils.rs @@ -97,6 +97,7 @@ Alternatively, you can drop the secret-service credential store altogether with `--no-default-features` and `--features linux-no-secret-service`. */ + use super::credential::{ Credential, CredentialApi, CredentialBuilder, CredentialBuilderApi, CredentialPersistence, }; @@ -223,6 +224,11 @@ impl CredentialApi for KeyutilsCredential { fn as_any(&self) -> &dyn std::any::Any { self } + + /// Expose the concrete debug formatter for use via the [Credential] trait + fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } } impl KeyutilsCredential { diff --git a/src/macos.rs b/src/macos.rs index 78efee5..fc13161 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -27,6 +27,7 @@ name as the target parameter to `Entry::new_with_target`. Any name other than one of the OS-supplied keychains (User, Common, System, and Dynamic) will be mapped to `User`. */ + use security_framework::base::Error; use security_framework::os::macos::keychain::{SecKeychain, SecPreferencesDomain}; use security_framework::os::macos::passwords::find_generic_password; @@ -110,6 +111,11 @@ impl CredentialApi for MacCredential { fn as_any(&self) -> &dyn std::any::Any { self } + + /// Expose the concrete debug formatter for use via the [Credential] trait + fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } } impl MacCredential { diff --git a/src/mock.rs b/src/mock.rs index 61f98d9..adb0f5f 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -174,6 +174,11 @@ impl CredentialApi for MockCredential { fn as_any(&self) -> &dyn std::any::Any { self } + + /// Expose the concrete debug formatter for use via the [Credential] trait + fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } } impl MockCredential { diff --git a/src/secret_service.rs b/src/secret_service.rs index 67af8eb..2f8cdc8 100644 --- a/src/secret_service.rs +++ b/src/secret_service.rs @@ -196,6 +196,11 @@ impl CredentialApi for SsCredential { fn as_any(&self) -> &dyn std::any::Any { self } + + /// Expose the concrete debug formatter for use via the [Credential] trait + fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } } impl SsCredential { @@ -266,7 +271,7 @@ impl SsCredential { Ok(()) } - /// Map a function over all of the items matching this credential. + /// Map a function over the items matching this credential. /// /// Items are unlocked before the function is applied. /// diff --git a/src/windows.rs b/src/windows.rs index 9bebe8e..52871d9 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -29,6 +29,7 @@ the order in which they were made. Careful testing has shown that modifying the same entry in the same (almost simultaneous) order from different threads produces different results on different runs. */ + use byteorder::{ByteOrder, LittleEndian}; use std::iter::once; use std::mem::MaybeUninit; @@ -166,6 +167,11 @@ impl CredentialApi for WinCredential { fn as_any(&self) -> &dyn std::any::Any { self } + + /// Expose the concrete debug formatter for use via the [Credential] trait + fn debug_fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } } impl WinCredential { From fc10542cba2918570c5ba05eb9958b90a80810de Mon Sep 17 00:00:00 2001 From: Daniel Brotsky Date: Tue, 6 Aug 2024 18:32:08 -0700 Subject: [PATCH 3/3] Bump version to 3.1.0. This adds API to credentials to provide better debugging, and implements that API in the built-in credential stores so it's exposed in the CLI. This is a dot release rather than a full release because the additional API is implemented in a backward-compatible way. Clients *can* extend their implementations to take advantage of it, but they don't have to. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3560f32..1183dde 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ keywords = ["password", "credential", "keychain", "keyring", "cross-platform"] license = "MIT OR Apache-2.0" name = "keyring" repository = "https://github.com/hwchen/keyring-rs.git" -version = "3.0.5" +version = "3.1.0" rust-version = "1.75" edition = "2021" exclude = [".github/"]