alien_bindings/providers/service_account/
aws_iam.rs1use crate::error::{binding_env_var, ErrorData, Result};
2use crate::traits::{
3 AwsServiceAccountInfo, Binding, ImpersonationRequest, ServiceAccount, ServiceAccountInfo,
4};
5use alien_aws_clients::{
6 sts::{AssumeRoleRequest, StsApi, StsClient},
7 AwsClientConfig,
8};
9use alien_core::bindings::AwsServiceAccountBinding;
10use alien_core::{AwsClientConfig as CoreAwsClientConfig, AwsCredentials, ClientConfig};
11use alien_error::Context;
12use async_trait::async_trait;
13use reqwest::Client;
14
15#[derive(Debug)]
17pub struct AwsIamServiceAccount {
18 client: StsClient,
19 config: AwsClientConfig,
20 binding: AwsServiceAccountBinding,
21}
22
23impl AwsIamServiceAccount {
24 pub fn new(
25 http_client: Client,
26 config: AwsClientConfig,
27 binding: AwsServiceAccountBinding,
28 ) -> Self {
29 let sts_client = StsClient::new(http_client, config.clone());
30 Self {
31 client: sts_client,
32 config,
33 binding,
34 }
35 }
36
37 fn get_role_arn(&self) -> Result<String> {
39 self.binding
40 .role_arn
41 .clone()
42 .into_value("service-account", "role_arn")
43 .context(ErrorData::BindingConfigInvalid {
44 env_var: binding_env_var("service-account"),
45 binding_name: "service-account".to_string(),
46 reason: "Failed to resolve role_arn from binding".to_string(),
47 })
48 }
49
50 fn get_role_name(&self) -> Result<String> {
52 self.binding
53 .role_name
54 .clone()
55 .into_value("service-account", "role_name")
56 .context(ErrorData::BindingConfigInvalid {
57 env_var: binding_env_var("service-account"),
58 binding_name: "service-account".to_string(),
59 reason: "Failed to resolve role_name from binding".to_string(),
60 })
61 }
62}
63
64impl Binding for AwsIamServiceAccount {}
65
66#[async_trait]
67impl ServiceAccount for AwsIamServiceAccount {
68 async fn get_info(&self) -> Result<ServiceAccountInfo> {
69 let role_name = self.get_role_name()?;
70 let role_arn = self.get_role_arn()?;
71
72 Ok(ServiceAccountInfo::Aws(AwsServiceAccountInfo {
73 role_name,
74 role_arn,
75 }))
76 }
77
78 async fn impersonate(&self, request: ImpersonationRequest) -> Result<ClientConfig> {
79 let role_arn = self.get_role_arn()?;
80 let session_name = request
81 .session_name
82 .unwrap_or_else(|| "alien-impersonation".to_string());
83 let duration = request.duration_seconds.unwrap_or(3600);
84
85 let assume_role_request = AssumeRoleRequest::builder()
86 .role_arn(role_arn.clone())
87 .role_session_name(session_name)
88 .duration_seconds(duration)
89 .build();
90
91 let response =
92 self.client
93 .assume_role(assume_role_request)
94 .await
95 .context(ErrorData::Other {
96 message: format!("Failed to assume IAM role '{}'", role_arn),
97 })?;
98
99 let credentials = response.assume_role_result.credentials;
100
101 let impersonated_config = CoreAwsClientConfig {
103 account_id: self.config.account_id.clone(),
104 region: self.config.region.clone(),
105 credentials: AwsCredentials::SessionCredentials {
106 access_key_id: credentials.access_key_id,
107 secret_access_key: credentials.secret_access_key,
108 session_token: credentials.session_token,
109 expires_at: credentials.expiration,
110 },
111 service_overrides: self.config.service_overrides.clone(),
112 };
113
114 Ok(ClientConfig::Aws(Box::new(impersonated_config)))
115 }
116
117 fn as_any(&self) -> &dyn std::any::Any {
118 self
119 }
120}