From d779fb9bf8d9ca41cc0faa1ebbd9a1345dba2897 Mon Sep 17 00:00:00 2001 From: Avery Harnish Date: Wed, 3 Mar 2021 12:27:50 -0600 Subject: [PATCH] feat: squash config show into config whoami --- crates/houston/src/profile/mod.rs | 78 ++++++++++++++++++------- crates/houston/src/profile/sensitive.rs | 2 +- src/command/config/mod.rs | 7 +-- src/command/config/show.rs | 32 ---------- src/command/config/whoami.rs | 16 ++++- src/error/metadata/mod.rs | 6 +- src/error/metadata/suggestion.rs | 7 --- src/utils/telemetry.rs | 6 +- 8 files changed, 79 insertions(+), 75 deletions(-) delete mode 100644 src/command/config/show.rs diff --git a/crates/houston/src/profile/mod.rs b/crates/houston/src/profile/mod.rs index a7600b7b2..6632d34e5 100644 --- a/crates/houston/src/profile/mod.rs +++ b/crates/houston/src/profile/mod.rs @@ -1,7 +1,6 @@ mod sensitive; use crate::{Config, HoustonProblem}; -use regex::Regex; use sensitive::Sensitive; use serde::{Deserialize, Serialize}; @@ -21,7 +20,7 @@ pub struct LoadOpts { } /// Represents all possible configuration options. -pub struct Opts { +pub struct ProfileData { /// Apollo API Key pub api_key: Option, } @@ -56,10 +55,10 @@ impl Profile { /// Writes an api_key to the filesystem (`$APOLLO_CONFIG_HOME/profiles//.sensitive`). pub fn set_api_key(name: &str, config: &Config, api_key: &str) -> Result<(), HoustonProblem> { - let opts = Opts { + let data = ProfileData { api_key: Some(api_key.to_string()), }; - Profile::save(name, config, opts)?; + Profile::save(name, config, data)?; Ok(()) } @@ -92,8 +91,8 @@ impl Profile { /// Saves configuration options for a specific profile to the file system, /// splitting sensitive information into a separate file. - pub fn save(name: &str, config: &Config, opts: Opts) -> Result<(), HoustonProblem> { - if let Some(api_key) = opts.api_key { + pub fn save(name: &str, config: &Config, data: ProfileData) -> Result<(), HoustonProblem> { + if let Some(api_key) = data.api_key { Sensitive { api_key }.save(name, config)?; } Ok(()) @@ -150,7 +149,7 @@ impl Profile { impl fmt::Display for Profile { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.sensitive) + write!(f, "{}", &self.sensitive) } } @@ -160,8 +159,15 @@ impl fmt::Display for Profile { // are printed, so we don't need to worry about strings 8 chars or less, // which this fn would just print back out pub fn mask_key(key: &str) -> String { - let ex = Regex::new(r"(?im)^(.{4})(.*)(.{4})$").expect("Could not create regular expression."); - ex.replace(key, "$1******************$3").to_string() + let mut masked_key = "".to_string(); + for (i, char) in key.chars().enumerate() { + if i <= 3 || i >= key.len() - 4 { + masked_key.push(char); + } else { + masked_key.push('*'); + } + } + masked_key } #[cfg(test)] @@ -169,17 +175,47 @@ mod tests { use super::mask_key; #[test] - #[allow(clippy::many_single_char_names)] - fn masks_valid_keys_properly() { - let a = "user:gh.foo:djru4788dhsg3657fhLOLO"; - assert_eq!(mask_key(a), "user******************LOLO".to_string()); - let b = "service:foo:dh47dh27sg18aj49dkLOLO"; - assert_eq!(mask_key(b), "serv******************LOLO".to_string()); - let c = "some nonsense"; - assert_eq!(mask_key(c), "some******************ense".to_string()); - let d = ""; - assert_eq!(mask_key(d), "".to_string()); - let e = "short"; - assert_eq!(mask_key(e), "short".to_string()); + fn it_can_mask_user_key() { + let input = "user:gh.foo:djru4788dhsg3657fhLOLO"; + assert_eq!( + mask_key(input), + "user**************************LOLO".to_string() + ); + } + + #[test] + fn it_can_mask_long_user_key() { + let input = "user:veryveryveryveryveryveryveryveryveryveryveryverylong"; + assert_eq!( + mask_key(input), + "user*************************************************long".to_string() + ); + } + + #[test] + fn it_can_mask_graph_key() { + let input = "service:foo:djru4788dhsg3657fhLOLO"; + assert_eq!( + mask_key(input), + "serv**************************LOLO".to_string() + ); + } + + #[test] + fn it_can_mask_nonsense() { + let input = "some nonsense"; + assert_eq!(mask_key(input), "some*****ense".to_string()); + } + + #[test] + fn it_can_mask_nothing() { + let input = ""; + assert_eq!(mask_key(input), "".to_string()); + } + + #[test] + fn it_can_mask_short() { + let input = "short"; + assert_eq!(mask_key(input), "short".to_string()); } } diff --git a/crates/houston/src/profile/sensitive.rs b/crates/houston/src/profile/sensitive.rs index 887f947d6..9226bfe23 100644 --- a/crates/houston/src/profile/sensitive.rs +++ b/crates/houston/src/profile/sensitive.rs @@ -40,6 +40,6 @@ impl Sensitive { impl fmt::Display for Sensitive { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "API Key: \"{}\"", self.api_key) + write!(f, "{}", super::mask_key(&self.api_key)) } } diff --git a/src/command/config/mod.rs b/src/command/config/mod.rs index 6cc423176..d4e43ea47 100644 --- a/src/command/config/mod.rs +++ b/src/command/config/mod.rs @@ -2,7 +2,6 @@ mod auth; mod clear; mod delete; mod list; -mod show; mod whoami; use serde::Serialize; @@ -34,9 +33,6 @@ pub enum Command { /// List all configuration profiles List(list::List), - /// View a configuration profile's details - Show(show::Show), - /// View the identity of a user/api key Whoami(whoami::WhoAmI), } @@ -50,10 +46,9 @@ impl Config { match &self.command { Command::Auth(command) => command.run(config), Command::List(command) => command.run(config), - Command::Show(command) => command.run(config), Command::Delete(command) => command.run(config), Command::Clear(command) => command.run(config), - Command::Whoami(command) => command.run(client_config), + Command::Whoami(command) => command.run(config, client_config), } } } diff --git a/src/command/config/show.rs b/src/command/config/show.rs deleted file mode 100644 index 2794c4d13..000000000 --- a/src/command/config/show.rs +++ /dev/null @@ -1,32 +0,0 @@ -use serde::Serialize; -use structopt::StructOpt; - -use houston as config; - -use crate::command::RoverStdout; -use crate::Result; -#[derive(Debug, Serialize, StructOpt)] -/// View a configuration profile's details -/// -/// If a profile has sensitive info, like an API key, pass --sensitive to see it. -pub struct Show { - #[structopt(default_value = "default")] - #[serde(skip_serializing)] - name: String, - - #[structopt(long = "sensitive")] - sensitive: bool, -} - -impl Show { - pub fn run(&self, config: config::Config) -> Result { - let opts = config::LoadOpts { - sensitive: self.sensitive, - }; - - let profile = config::Profile::load(&self.name, &config, opts)?; - - eprintln!("{}: {}", &self.name, profile); - Ok(RoverStdout::None) - } -} diff --git a/src/command/config/whoami.rs b/src/command/config/whoami.rs index bea5ceda0..98071a58e 100644 --- a/src/command/config/whoami.rs +++ b/src/command/config/whoami.rs @@ -11,6 +11,8 @@ use crate::utils::client::StudioClientConfig; use crate::utils::env::RoverEnvKey; use crate::Result; +use houston as config; + #[derive(Debug, Serialize, StructOpt)] pub struct WhoAmI { /// Name of configuration profile to use @@ -20,7 +22,11 @@ pub struct WhoAmI { } impl WhoAmI { - pub fn run(&self, client_config: StudioClientConfig) -> Result { + pub fn run( + &self, + config: config::Config, + client_config: StudioClientConfig, + ) -> Result { let client = client_config.get_client(&self.profile_name)?; eprintln!("Checking identity of your API key against the registry."); @@ -68,6 +74,14 @@ impl WhoAmI { message.push_str(&format!("{}: {}", Green.normal().paint("Origin"), &origin)); + let opts = config::LoadOpts { sensitive: true }; + let profile = config::Profile::load(&self.profile_name, &config, opts)?; + message.push_str(&format!( + "\n{}: {}", + Green.normal().paint("API Key"), + profile + )); + eprintln!("{}", message); Ok(RoverStdout::None) diff --git a/src/error/metadata/mod.rs b/src/error/metadata/mod.rs index d45b5eb42..47bdd1afb 100644 --- a/src/error/metadata/mod.rs +++ b/src/error/metadata/mod.rs @@ -89,9 +89,6 @@ impl From<&mut anyhow::Error> for Metadata { if let Some(houston_problem) = error.downcast_ref::() { let (suggestion, code) = match houston_problem { - HoustonProblem::NoNonSensitiveConfigFound(_) => { - (Some(Suggestion::RerunWithSensitive), None) - } HoustonProblem::CouldNotCreateConfigHome(_) | HoustonProblem::DefaultConfigDirNotFound | HoustonProblem::InvalidOverrideConfigDir(_) => { @@ -108,7 +105,8 @@ impl From<&mut anyhow::Error> for Metadata { } HoustonProblem::NoConfigProfiles => (Some(Suggestion::NewUserNoProfiles), None), HoustonProblem::ProfileNotFound(_) => (Some(Suggestion::ListProfiles), None), - HoustonProblem::TomlDeserialization(_) + HoustonProblem::NoNonSensitiveConfigFound(_) + | HoustonProblem::TomlDeserialization(_) | HoustonProblem::TomlSerialization(_) | HoustonProblem::IOError(_) => (Some(Suggestion::SubmitIssue), None), }; diff --git a/src/error/metadata/suggestion.rs b/src/error/metadata/suggestion.rs index 1a04c8567..e13877fb9 100644 --- a/src/error/metadata/suggestion.rs +++ b/src/error/metadata/suggestion.rs @@ -9,7 +9,6 @@ use crate::utils::env::RoverEnvKey; #[derive(Debug)] pub enum Suggestion { SubmitIssue, - RerunWithSensitive, SetConfigHome, MigrateConfigHomeOrCreateConfig, CreateConfig, @@ -35,12 +34,6 @@ impl Display for Suggestion { Suggestion::SubmitIssue => { format!("This error was unexpected! Please submit an issue with any relevant details about what you were trying to do: {}", Cyan.normal().paint("https://github.com/apollographql/rover/issues/new")) } - Suggestion::RerunWithSensitive => { - format!( - "Try re-running this command with the {} flag", - Yellow.normal().paint("`--sensitive`") - ) - } Suggestion::SetConfigHome => { format!( "You can override this path by setting the {} environment variable.", diff --git a/src/utils/telemetry.rs b/src/utils/telemetry.rs index 4a213297f..fcbb296b9 100644 --- a/src/utils/telemetry.rs +++ b/src/utils/telemetry.rs @@ -141,15 +141,15 @@ mod tests { #[test] fn it_can_serialize_commands_with_arguments() { - let args = vec![PKG_NAME, "config", "show", "default", "--sensitive"]; + let args = vec![PKG_NAME, "config", "list", "--help"]; let rover = Rover::from_iter(args); let actual_serialized_command = rover .serialize_command() .expect("could not serialize command"); let mut expected_arguments = HashMap::new(); - expected_arguments.insert("sensitive".to_string(), json!(true)); + expected_arguments.insert("help".to_string(), json!(true)); let expected_serialized_command = Command { - name: "config show".to_string(), + name: "config whoami".to_string(), arguments: expected_arguments, }; assert_eq!(actual_serialized_command, expected_serialized_command);