1use std::sync::Arc;
2
3use futures::future::BoxFuture;
4
5use crate::service::make_service;
6use crate::token::TemporaryToken;
7use crate::{ClientOptions, RequestSigner, Result, RetryConfig, TokenProvider};
8
9use self::credential::{AssumeRoleProvider, AwsAuthorizer, CredentialExt};
10use crate::CredentialProvider;
11
12mod builder;
13pub(crate) mod credential;
14
15pub use builder::*;
16pub use credential::AwsCredential;
17
18pub type AwsCredentialProvider = Arc<dyn CredentialProvider<Credential = AwsCredential>>;
19
20#[derive(Debug, Clone)]
21pub struct AmazonConfig {
22 pub region: String,
23 pub credentials: AwsCredentialProvider,
24 pub retry_config: RetryConfig,
25 pub client_options: ClientOptions,
26 pub skip_signature: bool,
27}
28
29impl AmazonConfig {
30 pub(crate) async fn get_credential(&self) -> Result<Option<Arc<AwsCredential>>> {
31 Ok(match self.skip_signature {
32 false => Some(self.credentials.get_credential().await?),
33 true => None,
34 })
35 }
36}
37
38impl RequestSigner for AmazonConfig {
39 fn sign<'a>(
40 &'a self,
41 req: reqwest::RequestBuilder,
42 ) -> BoxFuture<'a, Result<reqwest::RequestBuilder>> {
43 Box::pin(async move {
44 if let Some(cred) = self.get_credential().await? {
45 let authorizer = AwsAuthorizer::new(&cred, "execute-api", &self.region);
46 Ok(req.with_aws_sigv4(Some(authorizer), None))
47 } else {
48 Ok(req)
49 }
50 })
51 }
52}
53
54pub async fn assume_role_with_base(
61 role_arn: &str,
62 region: &str,
63 sts_endpoint: Option<&str>,
64 policy: Option<String>,
65 base_credentials: AwsCredentialProvider,
66) -> Result<TemporaryToken<Arc<AwsCredential>>> {
67 let endpoint = sts_endpoint
68 .map(|s| s.to_owned())
69 .unwrap_or_else(|| format!("https://sts.{region}.amazonaws.com"));
70
71 let provider = AssumeRoleProvider {
72 role_arn: role_arn.to_owned(),
73 session_name: "TrestleVending".to_owned(),
74 endpoint,
75 base_credentials,
76 region: region.to_owned(),
77 policy,
78 };
79
80 let client = ClientOptions::default().client()?;
81 let service = make_service(client.clone(), None);
82 provider
83 .fetch_token(&client, &service, &RetryConfig::default())
84 .await
85}
86
87pub async fn assume_role(
94 role_arn: &str,
95 region: &str,
96 sts_endpoint: Option<&str>,
97 policy: Option<String>,
98) -> Result<TemporaryToken<Arc<AwsCredential>>> {
99 let base_credentials = AmazonBuilder::from_env()
100 .with_region(region)
101 .build(None)?
102 .credentials;
103 assume_role_with_base(role_arn, region, sts_endpoint, policy, base_credentials).await
104}