-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Configurable keyring backends (#278)
Makes keyring backends user-selectable, as discussed in #271 (comment). - [x] Refactor `client::keyring` functions into a `Keyring` struct which can be instantiated with a backend selection. - [x] Add a config option to control what backend is used. - [x] Improve error reporting when the `secret-service` backend isn't working. - [ ] Add a `flat-file` backend that stores keys in plaintext alongside the config file.
- Loading branch information
1 parent
8f226a4
commit c941934
Showing
11 changed files
with
573 additions
and
231 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
use std::fmt::Display; | ||
|
||
/// Error returned when a keyring operation fails. | ||
#[derive(Debug)] | ||
pub struct KeyringError(KeyringErrorImpl); | ||
|
||
#[derive(Debug)] | ||
enum KeyringErrorImpl { | ||
UnknownBackend { | ||
backend: String, | ||
}, | ||
NoDefaultSigningKey { | ||
backend: &'static str, | ||
}, | ||
AccessError { | ||
backend: &'static str, | ||
entry: KeyringEntry, | ||
action: KeyringAction, | ||
cause: KeyringErrorCause, | ||
}, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub(super) enum KeyringErrorCause { | ||
Backend(keyring::Error), | ||
Other(anyhow::Error), | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
enum KeyringEntry { | ||
AuthToken { registry: String }, | ||
SigningKey { registry: Option<String> }, | ||
} | ||
|
||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] | ||
pub(super) enum KeyringAction { | ||
Open, | ||
Get, | ||
Set, | ||
Delete, | ||
} | ||
|
||
impl From<keyring::Error> for KeyringErrorCause { | ||
fn from(error: keyring::Error) -> Self { | ||
KeyringErrorCause::Backend(error) | ||
} | ||
} | ||
|
||
impl From<anyhow::Error> for KeyringErrorCause { | ||
fn from(error: anyhow::Error) -> Self { | ||
KeyringErrorCause::Other(error) | ||
} | ||
} | ||
|
||
impl KeyringError { | ||
pub(super) fn unknown_backend(backend: String) -> Self { | ||
KeyringError(KeyringErrorImpl::UnknownBackend { backend }) | ||
} | ||
|
||
pub(super) fn no_default_signing_key(backend: &'static str) -> Self { | ||
KeyringError(KeyringErrorImpl::NoDefaultSigningKey { backend }) | ||
} | ||
|
||
pub(super) fn auth_token_access_error( | ||
backend: &'static str, | ||
registry: &(impl Display + ?Sized), | ||
action: KeyringAction, | ||
cause: impl Into<KeyringErrorCause>, | ||
) -> Self { | ||
KeyringError(KeyringErrorImpl::AccessError { | ||
backend, | ||
entry: KeyringEntry::AuthToken { | ||
registry: registry.to_string(), | ||
}, | ||
action, | ||
cause: cause.into(), | ||
}) | ||
} | ||
|
||
pub(super) fn signing_key_access_error( | ||
backend: &'static str, | ||
registry: Option<&(impl Display + ?Sized)>, | ||
action: KeyringAction, | ||
cause: impl Into<KeyringErrorCause>, | ||
) -> Self { | ||
KeyringError(KeyringErrorImpl::AccessError { | ||
backend, | ||
entry: KeyringEntry::SigningKey { | ||
registry: registry.map(|s| s.to_string()), | ||
}, | ||
action, | ||
cause: cause.into(), | ||
}) | ||
} | ||
} | ||
|
||
impl std::fmt::Display for KeyringErrorCause { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
KeyringErrorCause::Backend(e) => e.fmt(f), | ||
KeyringErrorCause::Other(e) => e.fmt(f), | ||
} | ||
} | ||
} | ||
|
||
impl std::fmt::Display for KeyringError { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "keyring error: ")?; | ||
match &self.0 { | ||
KeyringErrorImpl::UnknownBackend { backend } => { | ||
write!(f, "unknown backend '{backend}'. Run `warg config --keyring_backend <backend>` to configure a keyring backend supported on this platform.") | ||
} | ||
KeyringErrorImpl::NoDefaultSigningKey { backend } => { | ||
let _ = backend; | ||
write!(f, "no default signing key is set. Please create one by running `warg key set <alg:base64>` or `warg key new`") | ||
} | ||
KeyringErrorImpl::AccessError { | ||
backend, | ||
entry, | ||
action, | ||
cause, | ||
} => { | ||
match *action { | ||
KeyringAction::Open => write!(f, "failed to open ")?, | ||
KeyringAction::Get => write!(f, "failed to read ")?, | ||
KeyringAction::Set => write!(f, "failed to set ")?, | ||
KeyringAction::Delete => write!(f, "failed to delete ")?, | ||
}; | ||
match entry { | ||
KeyringEntry::AuthToken { registry } => { | ||
write!(f, "auth token for registry <{registry}>.")? | ||
} | ||
KeyringEntry::SigningKey { | ||
registry: Some(registry), | ||
} => write!(f, "signing key for registry <{registry}>.")?, | ||
KeyringEntry::SigningKey { registry: None } => { | ||
write!(f, "default signing key.")? | ||
} | ||
}; | ||
|
||
if *backend == "secret-service" | ||
&& matches!( | ||
cause, | ||
KeyringErrorCause::Backend(keyring::Error::PlatformFailure(_)) | ||
) | ||
{ | ||
write!( | ||
f, | ||
concat!(" Since you are using the 'secret-service' backend, ", | ||
"the likely cause of this error is that no secret service ", | ||
"implementation, such as GNOME Keyring or KWallet, is installed, ", | ||
"or one is installed but not correctly configured. Consult your OS ", | ||
"distribution's documentation for instructions on setting it up, or run ", | ||
"`warg config --keyring_backend <backend>` to use a different backend.") | ||
)?; | ||
} | ||
|
||
// The above will be followed by further information returned | ||
// from `self.source()`. | ||
Ok(()) | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl std::error::Error for KeyringError { | ||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
match &self.0 { | ||
KeyringErrorImpl::AccessError { cause, .. } => match cause { | ||
KeyringErrorCause::Backend(e) => Some(e), | ||
KeyringErrorCause::Other(e) => Some(e.as_ref()), | ||
}, | ||
_ => None, | ||
} | ||
} | ||
} |
Oops, something went wrong.