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

Conversation

dfoxfranke
Copy link
Contributor

@dfoxfranke dfoxfranke commented May 10, 2024

Makes keyring backends user-selectable, as discussed in #271 (comment).

  • Refactor client::keyring functions into a Keyring struct which can be instantiated with a backend selection.
  • Add a config option to control what backend is used.
  • 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.

This refactors `warg_client::keyring` to make everything a method
of a `Keyring` struct which can support multiple `CredentialBuilder`s.
Currently, all CLI commands still just use the system default
everywhere, but next, a config option can be added to customize it.
Create a `KeyringError` type providing structured information about
what went wrong. In the case of a platform error when using the
secret-service backend, provide a tip on how to remedy it.
@dfoxfranke
Copy link
Contributor Author

I'm marking this ready for review. The flat-file backend isn't implemented yet, but I think there's now enough in this PR to constitute an improvement for users, so it's suitable for merging.

@dfoxfranke dfoxfranke marked this pull request as ready for review May 13, 2024 17:07
Copy link
Collaborator

@lann lann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code lgtm; cc @calvinrp who has worked on keyring more recently

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

impl Keyring {
#[cfg(target_os = "linux")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think it worth pulling in the dependency just to use it in this one place where it barely provides any simplification. But if it gets pulled in later for wider use then, yes, this should be refactored.

if let Some(ref backend) = config.keyring_backend {
Self::new(backend.as_str())
} else {
Self::new(Self::SUPPORTED_BACKENDS[0])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Self::new(Self::SUPPORTED_BACKENDS[0])
Self::new(Self::DEFAULT_BACKEND)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calvin already merged, so I'll fix this in my next PR.

pub const DEFAULT_BACKEND: &'static str = Self::SUPPORTED_BACKENDS[0];

/// Returns a human-readable description of a keyring backend.
pub fn describe_backend(backend: &str) -> &'static str {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be difficult to make backend an enum?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered this but decided it would create more problems than it would solve. The set of supported backends is platform-dependent, so there would be a distinction between the enumeration of all understood backend identifiers, and the subset of those which are supported on the user's platform. So there would be a design trade-off between:

  1. Defining two separate enums, one which covers all backends and one which covers those which are supported on the platform.
  2. Defining only the supported enum, and needing a bunch of conditional attributes every time you want to match on it.
  3. Defining only the all-backends enum, and putting an is_supported(&self) -> bool method on it.

Then we have to decide how exactly we want our Serialize and Deserialize implementations to work and which type they should use, and probably put custom error reporting into this traits to not be too cryptic if the user copies a configuration file from one system to another where the backend isn't supported. Altogether it didn't seem worth it, so I just kept this stringly typed.

@calvinrp
Copy link
Collaborator

Thank you! This looks great.

@calvinrp calvinrp self-requested a review May 13, 2024 18:24
@calvinrp calvinrp merged commit c941934 into bytecodealliance:main May 13, 2024
6 checks passed
calvinrp pushed a commit that referenced this pull request May 15, 2024
This adds support for storing warg credentials as flat files in the warg
config directory, following up on #278.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants