Skip to main content

alien_bindings/providers/service_account/
gcp_service_account.rs

1use crate::error::{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/// GCP Service Account binding implementation
13#[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    /// Get the service account email from the binding, resolving template expressions if needed
30    fn get_email(&self) -> Result<String> {
31        self.binding
32            .email
33            .clone()
34            .into_value("service-account", "email")
35            .context(ErrorData::BindingConfigInvalid {
36                binding_name: "service-account".to_string(),
37                reason: "Failed to resolve email from binding".to_string(),
38            })
39    }
40
41    /// Get the unique ID from the binding, resolving template expressions if needed
42    fn get_unique_id(&self) -> Result<String> {
43        self.binding
44            .unique_id
45            .clone()
46            .into_value("service-account", "unique_id")
47            .context(ErrorData::BindingConfigInvalid {
48                binding_name: "service-account".to_string(),
49                reason: "Failed to resolve unique_id from binding".to_string(),
50            })
51    }
52}
53
54impl Binding for GcpServiceAccount {}
55
56#[async_trait]
57impl ServiceAccount for GcpServiceAccount {
58    async fn get_info(&self) -> Result<ServiceAccountInfo> {
59        let email = self.get_email()?;
60        let unique_id = self.get_unique_id()?;
61
62        Ok(ServiceAccountInfo::Gcp(GcpServiceAccountInfo {
63            email,
64            unique_id,
65        }))
66    }
67
68    async fn impersonate(&self, request: ImpersonationRequest) -> Result<ClientConfig> {
69        let email = self.get_email()?;
70        let scopes = request
71            .scopes
72            .unwrap_or_else(|| vec!["https://www.googleapis.com/auth/cloud-platform".to_string()]);
73
74        let impersonated_config = CoreGcpClientConfig {
75            project_id: self.config.project_id.clone(),
76            region: self.config.region.clone(),
77            credentials: GcpCredentials::ImpersonatedServiceAccount {
78                source: Box::new(self.config.clone()),
79                config: GcpImpersonationConfig {
80                    service_account_email: email,
81                    scopes,
82                    delegates: None,
83                    lifetime: request
84                        .duration_seconds
85                        .map(|seconds| format!("{}s", seconds.clamp(1, 3600))),
86                    target_project_id: None,
87                    target_region: None,
88                },
89            },
90            service_overrides: self.config.service_overrides.clone(),
91            project_number: self.config.project_number.clone(),
92        };
93
94        Ok(ClientConfig::Gcp(Box::new(impersonated_config)))
95    }
96
97    fn as_any(&self) -> &dyn std::any::Any {
98        self
99    }
100}