Skip to content

Commit

Permalink
Add list-credentials command
Browse files Browse the repository at this point in the history
  • Loading branch information
sam701 committed Jan 9, 2020
1 parent 6f2c5b7 commit 61b3db7
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 24 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "awscredx"
version = "0.6.1"
version = "0.7.0"
authors = ["Alexei Samokvalov <[email protected]>"]
edition = "2018"

Expand Down
13 changes: 13 additions & 0 deletions src/credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ impl Display for CredentialsProfile {
}
}

pub struct CredentialsData<'a> {
pub profile_name: &'a str,
pub expires_at: &'a Option<DateTime<Utc>>,
}

impl CredentialsFile {
pub fn read<P: AsRef<Path>>(path: P, expirations_path: P) -> Result<Self, String> {
let mut cf = Self {
Expand Down Expand Up @@ -172,6 +177,14 @@ impl CredentialsFile {
.find(|p| p.profile_name == *profile_name)
.map(|p| &p.credentials)
}

pub fn get_current_credentials_data(&self) -> impl Iterator<Item=CredentialsData> + '_ {
self.profiles.iter()
.map(|x| CredentialsData{
profile_name: &x.profile_name.0,
expires_at: x.credentials.expires_at(),
})
}
}

fn read_profile_name(line: &str) -> Option<&str> {
Expand Down
91 changes: 69 additions & 22 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
extern crate ansi_term;
extern crate chrono;
extern crate clap;
extern crate rusoto_core;
extern crate rusoto_credential;
extern crate rusoto_sts;
extern crate toml;
extern crate serde;
extern crate custom_error;
extern crate chrono;
extern crate ansi_term;
extern crate linked_hash_map;
extern crate reqwest;
extern crate hyper;
extern crate hyper_proxy;
extern crate hyper_tls;
extern crate linked_hash_map;
extern crate reqwest;
extern crate rusoto_core;
extern crate rusoto_credential;
extern crate rusoto_sts;
extern crate serde;
extern crate toml;

use crate::config::Config;
use ansi_term::{Color, Style};
use chrono::{DateTime, Duration, Local};

use crate::config::Config;
use crate::credentials::CredentialsFile;

mod config;
mod state;
Expand All @@ -25,36 +28,42 @@ mod version;
mod util;

fn main() {
const COMMAND_INIT: &str = "init";
const COMMAND_ASSUME: &str = "assume";
const COMMAND_LIST_PROFILES: &str = "list-profiles";
const COMMAND_LIST_CREDENTIALS: &str = "list-credentials";
const COMMAND_VERSION: &str = "version";

let matches = clap::App::new("awscredx")
.version(version::VERSION)
.about(format!(r#"AWS credentials management, a.k.a. role assumption made easy.
Run '{}' to create the configuration file and set up shell scripts."#,
Style::new().fg(Color::Yellow).paint("awscredx init")).as_str())
.subcommand(clap::SubCommand::with_name("assume")
.subcommand(clap::SubCommand::with_name(COMMAND_ASSUME)
.about("Prints shell commands to assume the role for a given profile")
.arg(clap::Arg::with_name("profile-name")
.required(true)
.help("Profile name which role to assume")))
.subcommand(clap::SubCommand::with_name("init")
.subcommand(clap::SubCommand::with_name(COMMAND_INIT)
.about("Initializes local environment"))
.subcommand(clap::SubCommand::with_name("list-profiles")
.subcommand(clap::SubCommand::with_name(COMMAND_LIST_PROFILES)
.about("Lists configured profiles with their role ARNs"))
.subcommand(clap::SubCommand::with_name("version")
.subcommand(clap::SubCommand::with_name(COMMAND_LIST_CREDENTIALS)
.about("Lists current credentials with their expiration times"))
.subcommand(clap::SubCommand::with_name(COMMAND_VERSION)
.about("Shows current version and checks for newer version"))
.setting(clap::AppSettings::SubcommandRequiredElseHelp)
.get_matches();

match matches.subcommand() {
("assume", Some(arg)) => {
(COMMAND_ASSUME, Some(arg)) => {
let config = read_config();
assume::run(arg.value_of("profile-name").unwrap(), &config)
}
("init", _) =>
init::run(),
("list-profiles", _) =>
print_profiles(),
("version", _) =>
version::print_version(),
(COMMAND_INIT, _) => init::run(),
(COMMAND_LIST_PROFILES, _) => print_profiles(),
(COMMAND_LIST_CREDENTIALS, _) => print_credentials(),
(COMMAND_VERSION, _) => version::print_version(),
_ => unreachable!(),
}
}
Expand All @@ -79,11 +88,49 @@ fn print_profiles() {
let max_profile_name = c.profiles
.keys()
.map(|x| x.as_ref().len())
.max().unwrap();
.max()
.unwrap_or(0);
let width = max_profile_name + 2;
println!("{:width$}Main profile", &c.main_profile, width = width);
println!("{:width$}Main profile MFA session", &c.mfa_profile, width = width);
for (name, prof) in c.profiles.iter() {
println!("{:width$}{}", name, &prof.role_arn, width = width);
}
}

fn print_credentials() {
match CredentialsFile::read_default() {
Ok(cred_file) => {
let max_profile_width = cred_file.get_current_credentials_data()
.map(|x| x.profile_name.len())
.max()
.unwrap_or(0);
let width = max_profile_width + 2;
let prof_style = Style::new().fg(Color::White).bold();
let time_style = Style::new().fg(Color::Yellow);
for cred in cred_file.get_current_credentials_data() {
print!("{} expires ",
prof_style.paint(format!("{:width$}", cred.profile_name, width = width)),
);
match cred.expires_at {
Some(time) => {
let local_time: DateTime<Local> = (*time).into();
println!("at {} in {}",
time_style.paint(local_time.format("%H:%M").to_string()),
time_style.paint(format_duration(local_time - Local::now()))
);
}
None => println!("{}", time_style.paint("never")),
}
}
}
Err(e) => {
println!("Cannot read credentials file: {}", e);
::std::process::exit(3);
}
}
}

fn format_duration(d: Duration) -> String {
format!("{}:{}", d.num_hours(), d.num_minutes() % 60)
}

0 comments on commit 61b3db7

Please sign in to comment.