1
0
mirror of https://github.com/Llewellynvdm/starship.git synced 2024-11-15 17:47:13 +00:00

fix(aws): Only display AWS if there are credentials configured (#3504)

* only display aws on credential_process defined

* add check for both credential_process and valid credentials

* fix tests

* update aws module documentation

* add better explanation of requirements to documentation

* add support for AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, and update docs

* remove credential_process env var
This commit is contained in:
Allan Lago 2022-02-16 17:19:13 -05:00 committed by GitHub
parent 532efaadfe
commit e70454956f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 216 additions and 1 deletions

View File

@ -270,11 +270,18 @@ format = "$all$directory$character"
## AWS ## AWS
The `aws` module shows the current AWS region and profile. This is based on The `aws` module shows the current AWS region and profile when
credentials or a `credential_process` have been setup. This is based on
`AWS_REGION`, `AWS_DEFAULT_REGION`, and `AWS_PROFILE` env var with `AWS_REGION`, `AWS_DEFAULT_REGION`, and `AWS_PROFILE` env var with
`~/.aws/config` file. This module also shows an expiration timer when using temporary `~/.aws/config` file. This module also shows an expiration timer when using temporary
credentials. credentials.
The module will display a profile only if its credentials are present in
`~/.aws/credentials` or a `credential_process` is defined in
`~/.aws/config`. Alternatively, having any of the `AWS_ACCESS_KEY_ID`,
`AWS_SECRET_ACCESS_KEY`, or `AWS_SESSION_TOKEN` env vars defined will
also suffice.
When using [aws-vault](https://github.com/99designs/aws-vault) the profile When using [aws-vault](https://github.com/99designs/aws-vault) the profile
is read from the `AWS_VAULT` env var and the credentials expiration date is read from the `AWS_VAULT` env var and the credentials expiration date
is read from the `AWS_SESSION_EXPIRATION` env var. is read from the `AWS_SESSION_EXPIRATION` env var.

View File

@ -118,6 +118,59 @@ fn alias_region(region: String, aliases: &HashMap<String, &str>) -> String {
} }
} }
fn get_credential_process(context: &Context, aws_profile: Option<&Profile>) -> Option<String> {
let contents = read_file(get_config_file_path(context)?).ok()?;
let profile_line = if let Some(aws_profile) = aws_profile {
format!("[profile {}]", aws_profile)
} else {
"[default]".to_string()
};
let cred_proc_line = contents
.lines()
.skip_while(|line| line != &profile_line)
.skip(1)
.take_while(|line| !line.starts_with('['))
.find(|line| line.starts_with("credential_process"))?;
let cred_proc = cred_proc_line.split('=').nth(1)?.trim();
Some(cred_proc.to_string())
}
fn get_defined_credentials(context: &Context, aws_profile: Option<&Profile>) -> Option<String> {
let valid_env_vars = vec![
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
"AWS_SESSION_TOKEN",
];
// accept if set through environment variable
if let Some(aws_identity_cred) = valid_env_vars
.iter()
.find_map(|env_var| context.get_env(env_var))
{
return Some(aws_identity_cred);
}
let contents = read_file(get_credentials_file_path(context)?).ok()?;
let profile_line = if let Some(aws_profile) = aws_profile {
format!("[{}]", aws_profile)
} else {
"[default]".to_string()
};
let aws_key_id_line = contents
.lines()
.skip_while(|line| line != &profile_line)
.skip(1)
.take_while(|line| !line.starts_with('['))
.find(|line| line.starts_with("aws_access_key_id"))?;
let aws_key_id = aws_key_id_line.split('=').nth(1)?.trim();
Some(aws_key_id.to_string())
}
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("aws"); let mut module = context.new_module("aws");
let config: AwsConfig = AwsConfig::try_load(module.config); let config: AwsConfig = AwsConfig::try_load(module.config);
@ -127,6 +180,13 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
return None; return None;
} }
// only display if credential_process is defined or has valid credentials
if get_credential_process(context, aws_profile.as_ref()).is_none()
&& get_defined_credentials(context, aws_profile.as_ref()).is_none()
{
return None;
}
let mapped_region = if let Some(aws_region) = aws_region { let mapped_region = if let Some(aws_region) = aws_region {
Some(alias_region(aws_region, &config.region_aliases)) Some(alias_region(aws_region, &config.region_aliases))
} else { } else {
@ -193,6 +253,7 @@ mod tests {
fn region_set() { fn region_set() {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-northeast-2") .env("AWS_REGION", "ap-northeast-2")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -206,6 +267,7 @@ mod tests {
fn region_set_with_alias() { fn region_set_with_alias() {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-southeast-2") .env("AWS_REGION", "ap-southeast-2")
.env("AWS_ACCESS_KEY_ID", "dummy")
.config(toml::toml! { .config(toml::toml! {
[aws.region_aliases] [aws.region_aliases]
ap-southeast-2 = "au" ap-southeast-2 = "au"
@ -221,6 +283,7 @@ mod tests {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-northeast-2") .env("AWS_REGION", "ap-northeast-2")
.env("AWS_DEFAULT_REGION", "ap-northeast-1") .env("AWS_DEFAULT_REGION", "ap-northeast-1")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -234,6 +297,7 @@ mod tests {
fn profile_set() { fn profile_set() {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts") .env("AWS_PROFILE", "astronauts")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -248,6 +312,7 @@ mod tests {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_VAULT", "astronauts-vault") .env("AWS_VAULT", "astronauts-vault")
.env("AWS_PROFILE", "astronauts-profile") .env("AWS_PROFILE", "astronauts-profile")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -262,6 +327,7 @@ mod tests {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWSU_PROFILE", "astronauts-awsu") .env("AWSU_PROFILE", "astronauts-awsu")
.env("AWS_PROFILE", "astronauts-profile") .env("AWS_PROFILE", "astronauts-profile")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -276,6 +342,7 @@ mod tests {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWSUME_PROFILE", "astronauts-awsume") .env("AWSUME_PROFILE", "astronauts-awsume")
.env("AWS_PROFILE", "astronauts-profile") .env("AWS_PROFILE", "astronauts-profile")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -290,6 +357,7 @@ mod tests {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts") .env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-2") .env("AWS_REGION", "ap-northeast-2")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -341,6 +409,7 @@ mod tests {
file.write_all( file.write_all(
"[default] "[default]
region = us-east-1 region = us-east-1
credential_process = /opt/bin/awscreds-retriever
[profile astronauts] [profile astronauts]
region = us-east-2 region = us-east-2
@ -372,6 +441,7 @@ region = us-east-1
[profile astronauts] [profile astronauts]
region = us-east-2 region = us-east-2
credential_process = /opt/bin/awscreds-retriever
" "
.as_bytes(), .as_bytes(),
)?; )?;
@ -397,6 +467,7 @@ region = us-east-2
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts") .env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-1") .env("AWS_REGION", "ap-northeast-1")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -412,6 +483,7 @@ region = us-east-2
fn profile_set_with_display_all() { fn profile_set_with_display_all() {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts") .env("AWS_PROFILE", "astronauts")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -425,6 +497,7 @@ region = us-east-2
fn region_set_with_display_all() { fn region_set_with_display_all() {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-northeast-1") .env("AWS_REGION", "ap-northeast-1")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -439,6 +512,7 @@ region = us-east-2
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts") .env("AWS_PROFILE", "astronauts")
.env("AWS_DEFAULT_REGION", "ap-northeast-1") .env("AWS_DEFAULT_REGION", "ap-northeast-1")
.env("AWS_ACCESS_KEY_ID", "dummy")
.config(toml::toml! { .config(toml::toml! {
[aws] [aws]
format = "on [$symbol$region]($style) " format = "on [$symbol$region]($style) "
@ -457,6 +531,7 @@ region = us-east-2
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts") .env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-1") .env("AWS_REGION", "ap-northeast-1")
.env("AWS_ACCESS_KEY_ID", "dummy")
.config(toml::toml! { .config(toml::toml! {
[aws] [aws]
format = "on [$symbol$profile]($style) " format = "on [$symbol$profile]($style) "
@ -474,6 +549,7 @@ region = us-east-2
fn region_set_with_display_profile() { fn region_set_with_display_profile() {
let actual = ModuleRenderer::new("aws") let actual = ModuleRenderer::new("aws")
.env("AWS_REGION", "ap-northeast-1") .env("AWS_REGION", "ap-northeast-1")
.env("AWS_ACCESS_KEY_ID", "dummy")
.config(toml::toml! { .config(toml::toml! {
[aws] [aws]
format = "on [$symbol$profile]($style) " format = "on [$symbol$profile]($style) "
@ -500,6 +576,7 @@ region = us-east-2
}) })
.env("AWS_PROFILE", "astronauts") .env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-2") .env("AWS_REGION", "ap-northeast-2")
.env("AWS_ACCESS_KEY_ID", "dummy")
.env( .env(
"AWS_SESSION_EXPIRATION", "AWS_SESSION_EXPIRATION",
now_plus_half_hour.to_rfc3339_opts(SecondsFormat::Secs, true), now_plus_half_hour.to_rfc3339_opts(SecondsFormat::Secs, true),
@ -580,6 +657,7 @@ expiration={}
}) })
.env("AWS_PROFILE", "astronauts") .env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-2") .env("AWS_REGION", "ap-northeast-2")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect(); .collect();
let expected = Some(format!( let expected = Some(format!(
"on {}", "on {}",
@ -610,6 +688,7 @@ expiration={}
}) })
.env("AWS_PROFILE", "astronauts") .env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-2") .env("AWS_REGION", "ap-northeast-2")
.env("AWS_ACCESS_KEY_ID", "dummy")
.env( .env(
"AWS_SESSION_EXPIRATION", "AWS_SESSION_EXPIRATION",
now.to_rfc3339_opts(SecondsFormat::Secs, true), now.to_rfc3339_opts(SecondsFormat::Secs, true),
@ -638,4 +717,133 @@ expiration={}
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }
#[test]
fn missing_any_credentials() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let config_path = dir.path().join("config");
let mut file = File::create(&config_path)?;
file.write_all(
"[default]
region = us-east-1
output = json
[profile astronauts]
region = us-east-2
"
.as_bytes(),
)?;
let actual = ModuleRenderer::new("aws")
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
.collect();
let expected = None;
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn access_key_credential_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let credentials_path = dir.path().join("credentials");
let mut file = File::create(&credentials_path)?;
file.write_all(
"[astronauts]
aws_access_key_id=dummy
aws_secret_access_key=dummy
"
.as_bytes(),
)?;
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_REGION", "ap-northeast-2")
.env(
"AWS_CREDENTIALS_FILE",
credentials_path.to_string_lossy().as_ref(),
)
.collect();
let expected = Some(format!(
"on {}",
Color::Yellow
.bold()
.paint("☁️ astronauts (ap-northeast-2) ")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn credential_process_set() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let config_path = dir.path().join("config");
let mut file = File::create(&config_path)?;
file.write_all(
"[default]
region = ap-northeast-2
credential_process = /opt/bin/awscreds-retriever
"
.as_bytes(),
)?;
let actual = ModuleRenderer::new("aws")
.env("AWS_CONFIG_FILE", config_path.to_string_lossy().as_ref())
.collect();
let expected = Some(format!(
"on {}",
Color::Yellow.bold().paint("☁️ (ap-northeast-2) ")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn access_key_env_var_set() {
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_ACCESS_KEY_ID", "dummy")
.collect();
let expected = Some(format!(
"on {}",
Color::Yellow.bold().paint("☁️ astronauts ")
));
assert_eq!(expected, actual);
}
#[test]
fn secret_access_key_env_var_set() {
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_SECRET_ACCESS_KEY", "dummy")
.collect();
let expected = Some(format!(
"on {}",
Color::Yellow.bold().paint("☁️ astronauts ")
));
assert_eq!(expected, actual);
}
#[test]
fn session_token_env_var_set() {
let actual = ModuleRenderer::new("aws")
.env("AWS_PROFILE", "astronauts")
.env("AWS_SESSION_TOKEN", "dummy")
.collect();
let expected = Some(format!(
"on {}",
Color::Yellow.bold().paint("☁️ astronauts ")
));
assert_eq!(expected, actual);
}
} }