use async_trait::async_trait;
use hyper::Uri;
use std::time::Duration;
use crate::request::HttpClient;
use crate::{
parse_credentials_from_aws_service, AwsCredentials, CredentialsError, ProvideAwsCredentials,
};
const AWS_CREDENTIALS_PROVIDER_IP: &str = "169.254.169.254";
const AWS_CREDENTIALS_PROVIDER_PATH: &str = "latest/meta-data/iam/security-credentials";
#[derive(Clone, Debug)]
pub struct InstanceMetadataProvider {
client: HttpClient,
timeout: Duration,
metadata_ip_addr: String,
}
impl InstanceMetadataProvider {
pub fn new() -> Self {
InstanceMetadataProvider {
client: HttpClient::new(),
timeout: Duration::from_secs(30),
metadata_ip_addr: AWS_CREDENTIALS_PROVIDER_IP.to_string(),
}
}
pub fn set_timeout(&mut self, timeout: Duration) {
self.timeout = timeout;
}
pub fn set_ip_addr_with_port(&mut self, ip: &str, port: &str) {
self.metadata_ip_addr = format!("{}:{}", ip, port);
}
}
impl Default for InstanceMetadataProvider {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl ProvideAwsCredentials for InstanceMetadataProvider {
async fn credentials(&self) -> Result<AwsCredentials, CredentialsError> {
let role_name = get_role_name(&self.client, self.timeout, &self.metadata_ip_addr)
.await
.map_err(|err| CredentialsError {
message: format!("Could not get credentials from iam: {}", err.to_string()),
})?;
let cred_str = get_credentials_from_role(
&self.client,
self.timeout,
&role_name,
&self.metadata_ip_addr,
)
.await
.map_err(|err| CredentialsError {
message: format!("Could not get credentials from iam: {}", err.to_string()),
})?;
parse_credentials_from_aws_service(&cred_str)
}
}
async fn get_role_name(
client: &HttpClient,
timeout: Duration,
ip_addr: &str,
) -> Result<String, CredentialsError> {
let role_name_address = format!("http://{}/{}/", ip_addr, AWS_CREDENTIALS_PROVIDER_PATH);
let uri = match role_name_address.parse::<Uri>() {
Ok(u) => u,
Err(e) => return Err(CredentialsError::new(e)),
};
Ok(client.get(uri, timeout).await?)
}
async fn get_credentials_from_role(
client: &HttpClient,
timeout: Duration,
role_name: &str,
ip_addr: &str,
) -> Result<String, CredentialsError> {
let credentials_provider_url = format!(
"http://{}/{}/{}",
ip_addr, AWS_CREDENTIALS_PROVIDER_PATH, role_name
);
let uri = match credentials_provider_url.parse::<Uri>() {
Ok(u) => u,
Err(e) => return Err(CredentialsError::new(e)),
};
Ok(client.get(uri, timeout).await?)
}