use std::fmt::{Debug, Formatter};
use reqsign_core::{Context, ProvideCredential, Result, Signer};
use crate::Credential;
use crate::assume_role::{AssumeRoleGrant, AssumeRoleOperation};
use crate::provide_credential::utils::sts_endpoint;
pub struct AssumeRoleCredentialProvider {
grant: AssumeRoleGrant,
duration_seconds: Option<u32>,
region: Option<String>,
use_regional_sts_endpoint: bool,
sts_signer: Signer<Credential>,
}
impl Debug for AssumeRoleCredentialProvider {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AssumeRoleCredentialProvider")
.finish_non_exhaustive()
}
}
impl AssumeRoleCredentialProvider {
pub fn new(role_arn: String, sts_signer: Signer<Credential>) -> Self {
Self {
grant: AssumeRoleGrant::new(role_arn, "reqsign"),
duration_seconds: Some(3_600),
region: None,
use_regional_sts_endpoint: false,
sts_signer,
}
}
pub fn with_role_session_name(mut self, name: String) -> Self {
self.grant.role_session_name = name;
self
}
pub fn with_external_id(mut self, id: String) -> Self {
self.grant.external_id = Some(id);
self
}
pub fn with_duration_seconds(mut self, seconds: u32) -> Self {
self.duration_seconds = Some(seconds);
self
}
pub fn with_policy(mut self, policy: String) -> Self {
self.grant.policy = Some(policy);
self
}
pub fn with_policy_arns(mut self, policy_arns: Vec<String>) -> Self {
self.grant.policy_arns = policy_arns;
self
}
pub fn with_tags(mut self, tags: Vec<(String, String)>) -> Self {
self.grant.tags = tags;
self
}
pub fn with_region(mut self, region: String) -> Self {
self.region = Some(region);
self
}
pub fn with_regional_sts_endpoint(mut self) -> Self {
self.use_regional_sts_endpoint = true;
self
}
pub fn with_mfa_serial(mut self, serial_number: String) -> Self {
self.grant.serial_number = Some(serial_number);
self
}
pub fn with_mfa_code(mut self, token_code: String) -> Self {
self.grant.token_code = Some(token_code);
self
}
pub fn from_env(ctx: &Context, sts_signer: Signer<Credential>) -> Option<Self> {
let role_arn = ctx.env_var("AWS_ROLE_ARN")?;
let mut provider = Self::new(role_arn, sts_signer);
if let Some(name) = ctx.env_var("AWS_ROLE_SESSION_NAME") {
provider = provider.with_role_session_name(name);
}
if let Some(id) = ctx.env_var("AWS_EXTERNAL_ID") {
provider = provider.with_external_id(id);
}
if let Some(region) = ctx.env_var("AWS_REGION") {
provider = provider.with_region(region);
}
if ctx.env_var("AWS_STS_REGIONAL_ENDPOINTS").as_deref() == Some("regional") {
provider = provider.with_regional_sts_endpoint();
}
Some(provider)
}
}
impl ProvideCredential for AssumeRoleCredentialProvider {
type Credential = Credential;
async fn provide_credential(&self, ctx: &Context) -> Result<Option<Self::Credential>> {
match self.region.as_deref() {
Some(region) => self.grant.validate_for_region(region)?,
None => self.grant.validate_for_partition("aws")?,
}
let endpoint = sts_endpoint(self.region.as_deref(), self.use_regional_sts_endpoint)?;
let operation = AssumeRoleOperation::new(endpoint, &self.grant, self.duration_seconds)?;
operation.execute(ctx, &self.sts_signer).await.map(Some)
}
}