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

feat: adds origin to config whoami command #307

Merged
merged 1 commit into from
Feb 26, 2021
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions crates/houston/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ pub use error::HoustonProblem;

pub use profile::mask_key;
/// Utilites for saving, loading, and deleting configuration profiles.
pub use profile::LoadOpts;
pub use profile::Profile;
pub use profile::{Credential, CredentialOrigin, LoadOpts, Profile};
39 changes: 31 additions & 8 deletions crates/houston/src/profile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ pub struct Opts {
pub api_key: Option<String>,
}

/// Struct containing info about an API Key
pub struct Credential {
/// Apollo API Key
pub api_key: String,

/// The origin of the credential
pub origin: CredentialOrigin,
}

/// Info about where the API key was retrieved
#[derive(Debug, Clone, PartialEq)]
pub enum CredentialOrigin {
/// The credential is from an environment variable
EnvVar,

/// The credential is from a profile
ConfigFile(String),
}

impl Profile {
fn base_dir(config: &Config) -> PathBuf {
config.home.join("profiles")
Expand All @@ -50,21 +69,25 @@ impl Profile {
/// if it finds it. Otherwise looks for credentials on the file system.
///
/// Takes an optional `profile` argument. Defaults to `"default"`.
pub fn get_api_key(name: &str, config: &Config) -> Result<String, HoustonProblem> {
let api_key: Result<String, HoustonProblem> = match &config.override_api_key {
Some(api_key) => Ok(api_key.to_string()),
pub fn get_credential(name: &str, config: &Config) -> Result<Credential, HoustonProblem> {
let credential = match &config.override_api_key {
Some(api_key) => Credential {
api_key: api_key.to_string(),
origin: CredentialOrigin::EnvVar,
},
None => {
let opts = LoadOpts { sensitive: true };
let profile = Profile::load(name, config, opts)?;
Ok(profile.sensitive.api_key)
Credential {
api_key: profile.sensitive.api_key,
origin: CredentialOrigin::ConfigFile(name.to_string()),
}
}
};

let api_key = api_key?;

tracing::debug!("using API key {}", mask_key(&api_key));
tracing::debug!("using API key {}", mask_key(&credential.api_key));

Ok(api_key)
Ok(credential)
}

/// Saves configuration options for a specific profile to the file system,
Expand Down
10 changes: 6 additions & 4 deletions crates/houston/tests/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ fn it_can_set_and_get_an_api_key_via_creds_file() {
assert!(sensitive_file.exists());

assert_eq!(
config::Profile::get_api_key(profile, &config)
.expect("retreiving api key for default profile failed"),
config::Profile::get_credential(profile, &config)
.expect("retreiving api key for default profile failed")
.api_key,
String::from(api_key)
);

Expand All @@ -41,8 +42,9 @@ fn it_can_get_an_api_key_via_env_var() {
let config = get_config(Some(api_key.to_string()));

assert_eq!(
config::Profile::get_api_key(profile, &config)
.expect("retreiving api key for default profile failed"),
config::Profile::get_credential(profile, &config)
.expect("retreiving api key for default profile failed")
.api_key,
String::from(api_key)
);
}
Expand Down
5 changes: 5 additions & 0 deletions crates/rover-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ authors = ["Apollo Developers <[email protected]>"]
edition = "2018"

[dependencies]

# workspace deps
houston = { path = "../houston" }

# crates.io deps
anyhow = "1"
graphql_client = "0.9"
http = "0.2"
Expand Down
10 changes: 6 additions & 4 deletions crates/rover-client/src/blocking/studio_client.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::blocking::Client;
use crate::headers;
use crate::RoverClientError;
use houston::Credential;

use graphql_client::GraphQLQuery;

/// Represents a client for making GraphQL requests to Apollo Studio.
pub struct StudioClient {
api_key: String,
pub credential: Credential,
client: reqwest::blocking::Client,
uri: String,
version: String,
Expand All @@ -14,9 +16,9 @@ pub struct StudioClient {
impl StudioClient {
/// Construct a new [StudioClient] from an `api_key`, a `uri`, and a `version`.
/// For use in Rover, the `uri` is usually going to be to Apollo Studio
pub fn new(api_key: &str, uri: &str, version: &str) -> StudioClient {
pub fn new(credential: Credential, uri: &str, version: &str) -> StudioClient {
StudioClient {
api_key: api_key.to_string(),
credential,
client: reqwest::blocking::Client::new(),
uri: uri.to_string(),
version: version.to_string(),
Expand All @@ -30,7 +32,7 @@ impl StudioClient {
&self,
variables: Q::Variables,
) -> Result<Q::ResponseData, RoverClientError> {
let h = headers::build_studio_headers(&self.api_key, &self.version)?;
let h = headers::build_studio_headers(&self.credential.api_key, &self.version)?;
let body = Q::build_query(variables);
tracing::trace!(request_headers = ?h);
tracing::trace!("Request Body: {}", serde_json::to_string(&body)?);
Expand Down
13 changes: 10 additions & 3 deletions crates/rover-client/src/query/config/whoami.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::blocking::StudioClient;
use crate::RoverClientError;
use houston::CredentialOrigin;

use graphql_client::*;

#[derive(GraphQLQuery)]
Expand All @@ -21,6 +23,7 @@ pub struct RegistryIdentity {
pub id: String,
pub graph_title: Option<String>,
pub key_actor_type: Actor,
pub credential_origin: CredentialOrigin,
}

#[derive(Debug, PartialEq)]
Expand All @@ -37,11 +40,12 @@ pub fn run(
client: &StudioClient,
) -> Result<RegistryIdentity, RoverClientError> {
let response_data = client.post::<WhoAmIQuery>(variables)?;
get_identity_from_response_data(response_data)
get_identity_from_response_data(response_data, client.credential.origin.clone())
}

fn get_identity_from_response_data(
response_data: who_am_i_query::ResponseData,
credential_origin: CredentialOrigin,
) -> Result<RegistryIdentity, RoverClientError> {
if let Some(me) = response_data.me {
// I believe for the purposes of the CLI, we only care about users and
Expand All @@ -64,6 +68,7 @@ fn get_identity_from_response_data(
id: me.id,
graph_title,
key_actor_type,
credential_origin,
})
} else {
Err(RoverClientError::InvalidKey)
Expand All @@ -87,12 +92,13 @@ mod tests {
}
});
let data: who_am_i_query::ResponseData = serde_json::from_value(json_response).unwrap();
let output = get_identity_from_response_data(data);
let output = get_identity_from_response_data(data, CredentialOrigin::EnvVar);

let expected_identity = RegistryIdentity {
id: "gh.nobodydefinitelyhasthisusernamelol".to_string(),
graph_title: None,
key_actor_type: Actor::USER,
credential_origin: CredentialOrigin::EnvVar,
};
assert!(output.is_ok());
assert_eq!(output.unwrap(), expected_identity);
Expand All @@ -111,12 +117,13 @@ mod tests {
}
});
let data: who_am_i_query::ResponseData = serde_json::from_value(json_response).unwrap();
let output = get_identity_from_response_data(data);
let output = get_identity_from_response_data(data, CredentialOrigin::EnvVar);

let expected_identity = RegistryIdentity {
id: "big-ol-graph-key-lolol".to_string(),
graph_title: Some("GraphKeyService".to_string()),
key_actor_type: Actor::GRAPH,
credential_origin: CredentialOrigin::EnvVar,
};
assert!(output.is_ok());
assert_eq!(output.unwrap(), expected_identity);
Expand Down
10 changes: 7 additions & 3 deletions src/command/config/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl Auth {
pub fn run(&self, config: config::Config) -> Result<RoverStdout> {
let api_key = api_key_prompt()?;
Profile::set_api_key(&self.profile_name, &config, &api_key)?;
Profile::get_api_key(&self.profile_name, &config).map(|_| {
Profile::get_credential(&self.profile_name, &config).map(|_| {
eprintln!("Successfully saved API key.");
})?;
Ok(RoverStdout::None)
Expand Down Expand Up @@ -78,7 +78,9 @@ mod tests {
let config = get_config(None);

Profile::set_api_key(DEFAULT_PROFILE, &config, DEFAULT_KEY).unwrap();
let result = Profile::get_api_key(DEFAULT_PROFILE, &config).unwrap();
let result = Profile::get_credential(DEFAULT_PROFILE, &config)
.unwrap()
.api_key;
assert_eq!(result, DEFAULT_KEY);
}

Expand All @@ -88,7 +90,9 @@ mod tests {
let config = get_config(None);

Profile::set_api_key(CUSTOM_PROFILE, &config, CUSTOM_KEY).unwrap();
let result = Profile::get_api_key(CUSTOM_PROFILE, &config).unwrap();
let result = Profile::get_credential(CUSTOM_PROFILE, &config)
.unwrap()
.api_key;
assert_eq!(result, CUSTOM_KEY);
}

Expand Down
56 changes: 39 additions & 17 deletions src/command/config/whoami.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ use ansi_term::Colour::Green;
use serde::Serialize;
use structopt::StructOpt;

use houston::CredentialOrigin;
use rover_client::query::config::whoami;

use crate::anyhow;
use crate::command::RoverStdout;
use crate::utils::client::StudioClientConfig;
use crate::utils::env::RoverEnvKey;
use crate::Result;

#[derive(Debug, Serialize, StructOpt)]
Expand All @@ -24,28 +26,48 @@ impl WhoAmI {

let identity = whoami::run(whoami::who_am_i_query::Variables {}, &client)?;

let message = match identity.key_actor_type {
whoami::Actor::GRAPH => Ok(format!(
"Key Info\n{}: {}\n{}: {}\n{}: {:?}",
Green.normal().paint("Graph Title"),
identity.graph_title.unwrap(),
Green.normal().paint("Unique Graph ID"),
identity.id,
Green.normal().paint("Key Type"),
identity.key_actor_type
)),
whoami::Actor::USER => Ok(format!(
"Key Info\n{}: {}\n{}: {:?}",
Green.normal().paint("User ID"),
identity.id,
Green.normal().paint("Key Type"),
identity.key_actor_type
)),
let mut message = format!(
"{}: {:?}\n",
Green.normal().paint("Key Type"),
identity.key_actor_type
);

match identity.key_actor_type {
whoami::Actor::GRAPH => {
if let Some(graph_title) = identity.graph_title {
message.push_str(&format!(
"{}: {}\n",
Green.normal().paint("Graph Title"),
&graph_title
));
}
message.push_str(&format!(
"{}: {}\n",
Green.normal().paint("Unique Graph ID"),
identity.id
));
Ok(())
}
whoami::Actor::USER => {
message.push_str(&format!(
"{}: {}\n",
Green.normal().paint("User ID"),
identity.id
));
Ok(())
}
_ => Err(anyhow!(
"The key provided is invalid. Rover only accepts personal and graph API keys"
)),
}?;

let origin = match client.credential.origin {
CredentialOrigin::ConfigFile(path) => format!("--profile {}", &path),
CredentialOrigin::EnvVar => format!("${}", &RoverEnvKey::Key),
};

message.push_str(&format!("{}: {}", Green.normal().paint("Origin"), &origin));

eprintln!("{}", message);

Ok(RoverStdout::None)
Expand Down
4 changes: 2 additions & 2 deletions src/utils/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl StudioClientConfig {
}

pub fn get_client(&self, profile_name: &str) -> Result<StudioClient> {
let api_key = config::Profile::get_api_key(profile_name, &self.config)?;
Ok(StudioClient::new(&api_key, &self.uri, &self.version))
let credential = config::Profile::get_credential(profile_name, &self.config)?;
Ok(StudioClient::new(credential, &self.uri, &self.version))
}
}