use anyhow::{bail, Context, Result};
use regex::Regex;
use uuid::Uuid;
pub struct AwsTempCreds {
pub access_key: String,
pub secret_key: String,
pub session_token: String,
pub region: String,
}
pub async fn assume_role(arn: &str, external_id: &str, url: &str) -> Result<AwsTempCreds> {
let region = extract_region(url)?;
let config = aws_config::defaults(aws_config::BehaviorVersion::latest())
.region(aws_config::Region::new(region.clone()))
.load()
.await;
let sts = aws_sdk_sts::Client::new(&config);
let resp = sts
.assume_role()
.role_arn(arn)
.role_session_name(format!("session-{}", Uuid::new_v4()))
.external_id(external_id)
.send()
.await
.context("STS AssumeRole failed")?;
let creds = resp
.credentials()
.ok_or_else(|| anyhow::anyhow!("no credentials in AssumeRole response"))?;
let access_key = creds.access_key_id().to_string();
let secret_key = creds.secret_access_key().to_string();
let session_token = creds.session_token().to_string();
if access_key.is_empty() || secret_key.is_empty() {
bail!("empty credentials from STS AssumeRole");
}
Ok(AwsTempCreds {
access_key,
secret_key,
session_token,
region,
})
}
fn extract_region(url: &str) -> Result<String> {
let re = Regex::new(r"codecommit::([a-z0-9-]+)://")?;
if let Some(caps) = re.captures(url) {
return Ok(caps[1].to_string());
}
let re2 = Regex::new(r"git-codecommit\.([a-z0-9-]+)\.amazonaws\.com")?;
if let Some(caps) = re2.captures(url) {
return Ok(caps[1].to_string());
}
Ok("us-east-1".to_string())
}