alien_bindings/providers/service_account/
aws_iam.rs1use crate::error::{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 binding_name: "service-account".to_string(),
45 reason: "Failed to resolve role_arn from binding".to_string(),
46 })
47 }
48
49 fn get_role_name(&self) -> Result<String> {
51 self.binding
52 .role_name
53 .clone()
54 .into_value("service-account", "role_name")
55 .context(ErrorData::BindingConfigInvalid {
56 binding_name: "service-account".to_string(),
57 reason: "Failed to resolve role_name from binding".to_string(),
58 })
59 }
60}
61
62impl Binding for AwsIamServiceAccount {}
63
64#[async_trait]
65impl ServiceAccount for AwsIamServiceAccount {
66 async fn get_info(&self) -> Result<ServiceAccountInfo> {
67 let role_name = self.get_role_name()?;
68 let role_arn = self.get_role_arn()?;
69
70 Ok(ServiceAccountInfo::Aws(AwsServiceAccountInfo {
71 role_name,
72 role_arn,
73 }))
74 }
75
76 async fn impersonate(&self, request: ImpersonationRequest) -> Result<ClientConfig> {
77 let role_arn = self.get_role_arn()?;
78 let session_name = request
79 .session_name
80 .unwrap_or_else(|| "alien-impersonation".to_string());
81 let duration = request.duration_seconds.unwrap_or(3600);
82
83 let assume_role_request = AssumeRoleRequest::builder()
84 .role_arn(role_arn.clone())
85 .role_session_name(session_name)
86 .duration_seconds(duration)
87 .build();
88
89 let response =
90 self.client
91 .assume_role(assume_role_request)
92 .await
93 .context(ErrorData::Other {
94 message: format!("Failed to assume IAM role '{}'", role_arn),
95 })?;
96
97 let credentials = response.assume_role_result.credentials;
98
99 let impersonated_config = CoreAwsClientConfig {
101 account_id: self.config.account_id.clone(),
102 region: self.config.region.clone(),
103 credentials: AwsCredentials::AccessKeys {
104 access_key_id: credentials.access_key_id,
105 secret_access_key: credentials.secret_access_key,
106 session_token: Some(credentials.session_token),
107 },
108 service_overrides: self.config.service_overrides.clone(),
109 };
110
111 Ok(ClientConfig::Aws(Box::new(impersonated_config)))
112 }
113
114 fn as_any(&self) -> &dyn std::any::Any {
115 self
116 }
117}