#[derive(Debug, Clone)]
pub struct AwsCredentials {
pub access_key_id: String,
pub secret_access_key: String,
pub session_token: Option<String>,
}
impl AwsCredentials {
pub fn from_environment() -> Option<Self> {
if let (Ok(key_id), Ok(secret)) = (
std::env::var("AWS_ACCESS_KEY_ID"),
std::env::var("AWS_SECRET_ACCESS_KEY"),
) && !key_id.is_empty()
&& !secret.is_empty()
{
return Some(Self {
access_key_id: key_id,
secret_access_key: secret,
session_token: std::env::var("AWS_SESSION_TOKEN")
.ok()
.filter(|s| !s.is_empty()),
});
}
let profile = std::env::var("AWS_PROFILE").unwrap_or_else(|_| "default".to_string());
Self::from_credentials_file(&profile)
}
fn from_credentials_file(profile: &str) -> Option<Self> {
let home = std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.ok()?;
let path = std::path::Path::new(&home).join(".aws").join("credentials");
let content = std::fs::read_to_string(&path).ok()?;
let section_header = format!("[{profile}]");
let mut in_section = false;
let mut key_id = None;
let mut secret = None;
let mut token = None;
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with('[') {
in_section = trimmed == section_header;
continue;
}
if !in_section {
continue;
}
if let Some((k, v)) = trimmed.split_once('=') {
let k = k.trim();
let v = v.trim();
match k {
"aws_access_key_id" => key_id = Some(v.to_string()),
"aws_secret_access_key" => secret = Some(v.to_string()),
"aws_session_token" => token = Some(v.to_string()),
_ => {}
}
}
}
Some(Self {
access_key_id: key_id?,
secret_access_key: secret?,
session_token: token,
})
}
pub fn detect_region() -> Option<String> {
if let Ok(r) = std::env::var("AWS_REGION")
&& !r.is_empty()
{
return Some(r);
}
if let Ok(r) = std::env::var("AWS_DEFAULT_REGION")
&& !r.is_empty()
{
return Some(r);
}
let profile = std::env::var("AWS_PROFILE").unwrap_or_else(|_| "default".to_string());
let home = std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))
.ok()?;
let path = std::path::Path::new(&home).join(".aws").join("config");
let content = std::fs::read_to_string(&path).ok()?;
let section_header = if profile == "default" {
"[default]".to_string()
} else {
format!("[profile {profile}]")
};
let mut in_section = false;
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with('[') {
in_section = trimmed == section_header;
continue;
}
if !in_section {
continue;
}
if let Some((k, v)) = trimmed.split_once('=')
&& k.trim() == "region"
{
let v = v.trim();
if !v.is_empty() {
return Some(v.to_string());
}
}
}
None
}
}
#[derive(Debug, Clone)]
pub enum BedrockAuth {
SigV4(AwsCredentials),
BearerToken(String),
}