Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable keyring backends #278

Merged
merged 3 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ pub struct Config {
/// Disable interactive prompts.
#[serde(default)]
pub disable_interactive: bool,

/// Use the specified backend for keyring access.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub keyring_backend: Option<String>,
}

impl Config {
Expand Down Expand Up @@ -207,6 +211,7 @@ impl Config {
ignore_federation_hints: self.ignore_federation_hints,
auto_accept_federation_hints: self.auto_accept_federation_hints,
disable_interactive: self.disable_interactive,
keyring_backend: self.keyring_backend.clone(),
};

serde_json::to_writer_pretty(
Expand Down
201 changes: 0 additions & 201 deletions crates/client/src/keyring.rs

This file was deleted.

176 changes: 176 additions & 0 deletions crates/client/src/keyring/error.rs
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,
}
}
}
Loading
Loading