alien_bindings/providers/service_account/
gcp_service_account.rs1use crate::error::{binding_env_var, ErrorData, Result};
2use crate::traits::{
3 Binding, GcpServiceAccountInfo, ImpersonationRequest, ServiceAccount, ServiceAccountInfo,
4};
5use alien_core::bindings::GcpServiceAccountBinding;
6use alien_core::{ClientConfig, GcpClientConfig as CoreGcpClientConfig, GcpCredentials};
7use alien_error::Context;
8use alien_gcp_clients::{GcpClientConfig, GcpImpersonationConfig};
9use async_trait::async_trait;
10use reqwest::Client;
11
12#[derive(Debug)]
14pub struct GcpServiceAccount {
15 config: GcpClientConfig,
16 binding: GcpServiceAccountBinding,
17}
18
19impl GcpServiceAccount {
20 pub fn new(
21 http_client: Client,
22 config: GcpClientConfig,
23 binding: GcpServiceAccountBinding,
24 ) -> Self {
25 let _ = http_client;
26 Self { config, binding }
27 }
28
29 fn get_email(&self) -> Result<String> {
31 self.binding
32 .email
33 .clone()
34 .into_value("service-account", "email")
35 .context(ErrorData::BindingConfigInvalid {
36 env_var: binding_env_var("service-account"),
37 binding_name: "service-account".to_string(),
38 reason: "Failed to resolve email from binding".to_string(),
39 })
40 }
41
42 fn get_unique_id(&self) -> Result<String> {
44 self.binding
45 .unique_id
46 .clone()
47 .into_value("service-account", "unique_id")
48 .context(ErrorData::BindingConfigInvalid {
49 env_var: binding_env_var("service-account"),
50 binding_name: "service-account".to_string(),
51 reason: "Failed to resolve unique_id from binding".to_string(),
52 })
53 }
54}
55
56impl Binding for GcpServiceAccount {}
57
58#[async_trait]
59impl ServiceAccount for GcpServiceAccount {
60 async fn get_info(&self) -> Result<ServiceAccountInfo> {
61 let email = self.get_email()?;
62 let unique_id = self.get_unique_id()?;
63
64 Ok(ServiceAccountInfo::Gcp(GcpServiceAccountInfo {
65 email,
66 unique_id,
67 }))
68 }
69
70 async fn impersonate(&self, request: ImpersonationRequest) -> Result<ClientConfig> {
71 let email = self.get_email()?;
72 let scopes = request
73 .scopes
74 .unwrap_or_else(|| vec!["https://www.googleapis.com/auth/cloud-platform".to_string()]);
75
76 let impersonated_config = CoreGcpClientConfig {
77 project_id: self.config.project_id.clone(),
78 region: self.config.region.clone(),
79 credentials: GcpCredentials::ImpersonatedServiceAccount {
80 source: Box::new(self.config.clone()),
81 config: GcpImpersonationConfig {
82 service_account_email: email,
83 scopes,
84 delegates: None,
85 lifetime: request
86 .duration_seconds
87 .map(|seconds| format!("{}s", seconds.clamp(1, 3600))),
88 target_project_id: None,
89 target_region: None,
90 },
91 },
92 service_overrides: self.config.service_overrides.clone(),
93 project_number: self.config.project_number.clone(),
94 };
95
96 Ok(ClientConfig::Gcp(Box::new(impersonated_config)))
97 }
98
99 fn as_any(&self) -> &dyn std::any::Any {
100 self
101 }
102}