Skip to main content

alien_bindings/providers/service_account/
grpc.rs

1use crate::{
2    error::{Error, ErrorData},
3    grpc::service_account_service::alien_bindings::service_account::{
4        service_account_service_client::ServiceAccountServiceClient, GetInfoRequest,
5        ImpersonateRequest as GrpcImpersonateRequest,
6    },
7    grpc::status_conversion::status_to_alien_error,
8    traits::{
9        AwsServiceAccountInfo, AzureServiceAccountInfo, Binding, GcpServiceAccountInfo,
10        ImpersonationRequest, ServiceAccount, ServiceAccountInfo,
11    },
12};
13
14use alien_core::ClientConfig;
15use alien_error::{AlienError, Context, IntoAlienError};
16use async_trait::async_trait;
17use tonic::{transport::Channel, Request, Status};
18
19/// gRPC implementation of the `ServiceAccount` trait.
20///
21/// This implementation communicates with an alien-runtime gRPC server
22/// to manage service account operations.
23#[derive(Debug)]
24pub struct GrpcServiceAccount {
25    client: ServiceAccountServiceClient<Channel>,
26    binding_name: String,
27}
28
29impl GrpcServiceAccount {
30    /// Creates a new gRPC service account instance from binding parameters.
31    pub async fn new(binding_name: String, grpc_address: String) -> Result<Self, Error> {
32        let channel = crate::providers::grpc_provider::create_grpc_channel(grpc_address).await?;
33        Self::new_from_channel(channel, binding_name).await
34    }
35
36    /// Creates a new gRPC service account instance from a channel.
37    pub async fn new_from_channel(channel: Channel, binding_name: String) -> Result<Self, Error> {
38        let client = ServiceAccountServiceClient::new(channel);
39
40        Ok(Self {
41            client,
42            binding_name,
43        })
44    }
45
46    fn client(&self) -> ServiceAccountServiceClient<Channel> {
47        self.client.clone()
48    }
49}
50
51impl Binding for GrpcServiceAccount {}
52
53#[async_trait]
54impl ServiceAccount for GrpcServiceAccount {
55    async fn get_info(&self) -> Result<ServiceAccountInfo, Error> {
56        let mut client = self.client();
57
58        let request = GetInfoRequest {
59            binding_name: self.binding_name.clone(),
60        };
61
62        let response = client
63            .get_info(Request::new(request))
64            .await
65            .map_err(|e| status_to_alien_error(e, "get_info"))?
66            .into_inner();
67
68        let info = response.info.ok_or_else(|| {
69            AlienError::new(ErrorData::Other {
70                message: "Service account info is missing from response".to_string(),
71            })
72        })?;
73
74        // Convert from protobuf to trait types
75        use crate::grpc::service_account_service::alien_bindings::service_account::service_account_info::Info;
76
77        let service_account_info = match info.info {
78            Some(Info::Aws(aws)) => ServiceAccountInfo::Aws(AwsServiceAccountInfo {
79                role_name: aws.role_name,
80                role_arn: aws.role_arn,
81            }),
82            Some(Info::Gcp(gcp)) => ServiceAccountInfo::Gcp(GcpServiceAccountInfo {
83                email: gcp.email,
84                unique_id: gcp.unique_id,
85            }),
86            Some(Info::Azure(azure)) => ServiceAccountInfo::Azure(AzureServiceAccountInfo {
87                client_id: azure.client_id,
88                resource_id: azure.resource_id,
89                principal_id: azure.principal_id,
90            }),
91            None => {
92                return Err(AlienError::new(ErrorData::Other {
93                    message: "Service account info variant is missing".to_string(),
94                }))
95            }
96        };
97
98        Ok(service_account_info)
99    }
100
101    async fn impersonate(&self, request: ImpersonationRequest) -> Result<ClientConfig, Error> {
102        let mut client = self.client();
103
104        let grpc_request = GrpcImpersonateRequest {
105            binding_name: self.binding_name.clone(),
106            session_name: request.session_name,
107            duration_seconds: request.duration_seconds,
108            scopes: request.scopes.unwrap_or_default(),
109        };
110
111        let response = client
112            .impersonate(Request::new(grpc_request))
113            .await
114            .map_err(|e| status_to_alien_error(e, "impersonate"))?
115            .into_inner();
116
117        // Deserialize ClientConfig from JSON
118        let client_config: ClientConfig = serde_json::from_str(&response.client_config_json)
119            .into_alien_error()
120            .context(ErrorData::Other {
121                message: "Failed to deserialize ClientConfig from gRPC response".to_string(),
122            })?;
123
124        Ok(client_config)
125    }
126
127    fn as_any(&self) -> &dyn std::any::Any {
128        self
129    }
130}