crowbar 0.4.10

Securily generates temporary AWS credentials through Identity Providers using SAML
Documentation
use crate::config::app::AppProfile;
use crate::utils;
use anyhow::{anyhow, Context, Result};
use dirs::home_dir;
use ini::Ini;
use std::fs;
use std::fs::File;
use std::io::ErrorKind;
use std::path::PathBuf;

pub const AWS_CONFIG_FILE: &str = "AWS_CONFIG_FILE";
pub const PROFILE_KEY: &str = "credential_process";

#[derive(Clone)]
pub struct AwsConfig {
    pub profiles: Ini,
    pub location: PathBuf,
}

impl AwsConfig {
    pub fn new() -> Result<AwsConfig> {
        let location = default_config_location()?;
        let profiles: Ini = match File::open(&location) {
            Ok(mut file) => Ini::read_from(&mut file)?,
            Err(ref e) if e.kind() == ErrorKind::NotFound => {
                if let Some(parent) = location.parent() {
                    fs::create_dir_all(parent).with_context(|| {
                        format!("Unable to read configuration from {:?}", location)
                    })?;
                }
                Ini::new()
            }
            Err(_e) => return Err(anyhow!("Unable to create configuration structure!")),
        };

        Ok(AwsConfig { profiles, location })
    }

    pub fn write(self) -> Result<AwsConfig> {
        let location = &self.location;
        self.profiles
            .write_to_file(location)
            .with_context(|| format!("Unable to write AWS configuration at {:?}", location))?;

        Ok(self)
    }

    pub fn add_profile(mut self, profile: &AppProfile) -> Result<AwsConfig> {
        let name = profile.name.clone();
        let key = PROFILE_KEY.to_string();
        let value = format!("sh -c 'crowbar creds {} -p 2> /dev/tty'", &name);

        self.profiles
            .set_to(Some(format!("profile {}", &name)), key, value);

        Ok(self)
    }

    pub fn delete_profile(mut self, profile_name: &str) -> Result<AwsConfig> {
        let profile_name = format!("profile {}", profile_name);
        self.profiles.delete_from(Some(profile_name), PROFILE_KEY);

        Ok(self)
    }
}

fn default_config_location() -> Result<PathBuf> {
    let env = utils::non_empty_env_var(AWS_CONFIG_FILE);
    match env {
        Some(path) => Ok(PathBuf::from(path)),
        None => hardcoded_config_location(),
    }
}

fn hardcoded_config_location() -> Result<PathBuf> {
    match home_dir() {
        Some(mut home_path) => {
            home_path.push(".aws");
            home_path.push("config");
            Ok(home_path)
        }
        None => Err(anyhow!("Failed to determine home directory.")),
    }
}