Purpose
Use aws profile definition from ~/.aws/config and ~/.aws/credentials in rusoto, exactly like aws cli and aws official SDKs.
Related issues
Purpose
Use aws profile definition from ~/.aws/config and ~/.aws/credentials in rusoto, exactly like aws cli and aws official SDKs.
Related issues
| use std::collections::HashMap; | |
| use std::env::var as env_var; | |
| use std::fs; | |
| use std::fs::File; | |
| use std::io::{BufRead, BufReader}; | |
| use std::path::{Path, PathBuf}; | |
| use std::str::FromStr; | |
| use std::sync::Arc; | |
| use regex::Regex; | |
| use rusoto_core::{HttpClient, Region}; | |
| use rusoto_credential::{AutoRefreshingProvider, CredentialsError, ProfileProvider}; | |
| use rusoto_sts::{StsAssumeRoleSessionCredentialsProvider, StsClient}; | |
| use dirs::home_dir; | |
| const SOURCE_PROFILE: &str = "source_profile"; | |
| const ROLE_ARN: &str = "role_arn"; | |
| pub fn create_assume_role_profile(default_region: Region, session_name: String) -> Option<Arc<AutoRefreshingProvider<StsAssumeRoleSessionCredentialsProvider>>> { | |
| let config = parse_config_file(default_profile_location().ok()?.as_path())?; | |
| let source_profile_name = config | |
| .get(&default_profile_name()) | |
| .and_then(|props| props.get(SOURCE_PROFILE)) | |
| .map(std::borrow::ToOwned::to_owned)?; | |
| let role_arn = config | |
| .get(&default_profile_name()) | |
| .and_then(|props| props.get(ROLE_ARN)) | |
| .map(std::borrow::ToOwned::to_owned)?; | |
| let source_profile = ProfileProvider::with_default_credentials(source_profile_name).ok()?; | |
| let source_profile_region_string = source_profile.region_from_profile().unwrap_or(None); | |
| let source_profile_region = | |
| if let Some(s) = source_profile_region_string { | |
| Region::from_str(&s).unwrap_or(default_region) | |
| } else { | |
| default_region | |
| }; | |
| let sts = StsClient::new_with( | |
| HttpClient::new().unwrap(), | |
| source_profile, | |
| source_profile_region, | |
| ); | |
| let provider = StsAssumeRoleSessionCredentialsProvider::new( | |
| sts, | |
| role_arn, | |
| session_name, | |
| None, | |
| None, | |
| None, | |
| None, | |
| ); | |
| let arc = Arc::new( | |
| rusoto_credential::AutoRefreshingProvider::new(provider).ok()? | |
| ); | |
| return Some(arc); | |
| } | |
| ///////////////////////////////////////// | |
| // Following definitions are from rusoto. | |
| // Quoted from rusoto-credentials/profile.rs | |
| const AWS_PROFILE: &str = "AWS_PROFILE"; | |
| const AWS_SHARED_CREDENTIALS_FILE: &str = "AWS_SHARED_CREDENTIALS_FILE"; | |
| const DEFAULT: &str = "default"; | |
| // Quoted from rusoto-credentials/profile.rs | |
| fn new_profile_regex() -> Regex { | |
| Regex::new(r"^\[(profile )?([^\]]+)\]$").expect("Failed to compile regex") | |
| } | |
| // Quoted from rusoto-credentials/profile.rs | |
| fn parse_config_file(file_path: &Path) -> Option<HashMap<String, HashMap<String, String>>> { | |
| match fs::metadata(file_path) { | |
| Err(_) => return None, | |
| Ok(metadata) => { | |
| if !metadata.is_file() { | |
| return None; | |
| } | |
| } | |
| }; | |
| let profile_regex = new_profile_regex(); | |
| let file = File::open(file_path).expect("expected file"); | |
| let file_lines = BufReader::new(&file); | |
| let result: (HashMap<String, HashMap<String, String>>, Option<String>) = file_lines | |
| .lines() | |
| .filter_map(|line| { | |
| line.ok() | |
| .map(|l| l.trim_matches(' ').to_owned()) | |
| .into_iter() | |
| .find(|l| !l.starts_with('#') && !l.is_empty()) | |
| }) | |
| .fold(Default::default(), |(mut result, profile), line| { | |
| if profile_regex.is_match(&line) { | |
| let caps = profile_regex.captures(&line).unwrap(); | |
| let next_profile = caps.get(2).map(|value| value.as_str().to_string()); | |
| (result, next_profile) | |
| } else { | |
| match &line | |
| .splitn(2, '=') | |
| .map(|value| value.trim_matches(' ')) | |
| .collect::<Vec<&str>>()[..] | |
| { | |
| [key, value] if !key.is_empty() && !value.is_empty() => { | |
| if let Some(current) = profile.clone() { | |
| let values = result.entry(current).or_insert_with(HashMap::new); | |
| (*values).insert((*key).to_string(), (*value).to_string()); | |
| } | |
| (result, profile) | |
| } | |
| _ => (result, profile), | |
| } | |
| } | |
| }); | |
| Some(result.0) | |
| } | |
| // Quoted from rusoto-credentials/profile.rs | |
| fn default_profile_location() -> Result<PathBuf, CredentialsError> { | |
| let env = non_empty_env_var(AWS_SHARED_CREDENTIALS_FILE); | |
| match env { | |
| Some(path) => Ok(PathBuf::from(path)), | |
| None => hardcoded_profile_location(), | |
| } | |
| } | |
| // Quoted from rusoto-credentials/profile.rs | |
| fn hardcoded_profile_location() -> Result<PathBuf, CredentialsError> { | |
| match home_dir() { | |
| Some(mut home_path) => { | |
| home_path.push(".aws"); | |
| home_path.push("config"); /// <<<<<<<<<< NOTE: original value is "credentials", but now "config". | |
| Ok(home_path) | |
| } | |
| None => Err(CredentialsError::new("Failed to determine home directory.")), | |
| } | |
| } | |
| // Quoted from rusoto-credentials/profile.rs | |
| fn default_profile_name() -> String { | |
| non_empty_env_var(AWS_PROFILE).unwrap_or_else(|| DEFAULT.to_owned()) | |
| } | |
| // Quoted from rusoto-credentials/profile.rs | |
| fn non_empty_env_var(name: &str) -> Option<String> { | |
| match env_var(name) { | |
| Ok(value) => { | |
| if value.is_empty() { | |
| None | |
| } else { | |
| Some(value) | |
| } | |
| } | |
| Err(_) => None, | |
| } | |
| } |
| The MIT License (MIT) | |
| Copyright (c) 2017 Rusoto Project Developers | |
| Copyright (c) 2020 tomykaira | |
| Permission is hereby granted, free of charge, to any person obtaining a copy | |
| of this software and associated documentation files (the "Software"), to deal | |
| in the Software without restriction, including without limitation the rights | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| copies of the Software, and to permit persons to whom the Software is | |
| furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in all | |
| copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| SOFTWARE. |