alien_core/resources/
service_account.rs1use crate::error::{ErrorData, Result};
2use crate::permissions::{PermissionProfile, PermissionSet, PermissionSetReference};
3use crate::resource::{ResourceDefinition, ResourceOutputsDefinition, ResourceRef, ResourceType};
4use alien_error::AlienError;
5use bon::Builder;
6use serde::{Deserialize, Serialize};
7use std::any::Any;
8use std::fmt::Debug;
9
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
21#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
22#[serde(rename_all = "camelCase", deny_unknown_fields)]
23#[builder(start_fn = new)]
24pub struct ServiceAccount {
25 #[builder(start_fn)]
28 pub id: String,
29
30 #[builder(field)]
34 pub stack_permission_sets: Vec<PermissionSet>,
35}
36
37impl ServiceAccount {
38 pub const RESOURCE_TYPE: ResourceType = ResourceType::from_static("service-account");
40
41 pub fn id(&self) -> &str {
43 &self.id
44 }
45
46 pub fn from_permission_profile(
51 id: String,
52 profile: &PermissionProfile,
53 permission_set_resolver: impl Fn(&str) -> Option<PermissionSet>,
54 ) -> Result<Self> {
55 let mut stack_permission_sets = Vec::new();
56
57 if let Some(permission_set_refs) = profile.0.get("*") {
59 for permission_set_ref in permission_set_refs {
60 let permission_set = match permission_set_ref {
61 PermissionSetReference::Name(name) => {
62 permission_set_resolver(&name).ok_or_else(|| {
64 AlienError::new(ErrorData::GenericError {
65 message: format!(
66 "Permission set '{}' not found for service account '{}'",
67 name, id
68 ),
69 })
70 })?
71 }
72 PermissionSetReference::Inline(inline_permission_set) => {
73 inline_permission_set.clone()
75 }
76 };
77 stack_permission_sets.push(permission_set);
78 }
79 }
80
81 Ok(ServiceAccount {
82 id,
83 stack_permission_sets,
84 })
85 }
86}
87
88impl ServiceAccountBuilder {
89 pub fn stack_permission_set(mut self, permission_set: PermissionSet) -> Self {
92 self.stack_permission_sets.push(permission_set);
93 self
94 }
95}
96
97impl ResourceDefinition for ServiceAccount {
99 fn get_resource_type(&self) -> ResourceType {
100 Self::RESOURCE_TYPE
101 }
102
103 fn id(&self) -> &str {
104 &self.id
105 }
106
107 fn get_dependencies(&self) -> Vec<ResourceRef> {
108 Vec::new()
111 }
112
113 fn validate_update(&self, new_config: &dyn ResourceDefinition) -> Result<()> {
114 let new_service_account = new_config
115 .as_any()
116 .downcast_ref::<ServiceAccount>()
117 .ok_or_else(|| {
118 AlienError::new(ErrorData::UnexpectedResourceType {
119 resource_id: self.id.clone(),
120 expected: Self::RESOURCE_TYPE,
121 actual: new_config.get_resource_type(),
122 })
123 })?;
124
125 if self.id != new_service_account.id {
126 return Err(AlienError::new(ErrorData::InvalidResourceUpdate {
127 resource_id: self.id.clone(),
128 reason: "the 'id' field is immutable".to_string(),
129 }));
130 }
131
132 Ok(())
133 }
134
135 fn as_any(&self) -> &dyn Any {
136 self
137 }
138
139 fn as_any_mut(&mut self) -> &mut dyn Any {
140 self
141 }
142
143 fn box_clone(&self) -> Box<dyn ResourceDefinition> {
144 Box::new(self.clone())
145 }
146
147 fn resource_eq(&self, other: &dyn ResourceDefinition) -> bool {
148 other.as_any().downcast_ref::<ServiceAccount>() == Some(self)
149 }
150
151 fn to_json_value(&self) -> serde_json::Result<serde_json::Value> {
152 serde_json::to_value(self)
153 }
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
158#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
159#[serde(rename_all = "camelCase")]
160pub struct ServiceAccountOutputs {
161 pub identity: String,
166
167 pub resource_id: String,
172}
173
174impl ResourceOutputsDefinition for ServiceAccountOutputs {
175 fn get_resource_type(&self) -> ResourceType {
176 ServiceAccount::RESOURCE_TYPE.clone()
177 }
178
179 fn as_any(&self) -> &dyn Any {
180 self
181 }
182
183 fn box_clone(&self) -> Box<dyn ResourceOutputsDefinition> {
184 Box::new(self.clone())
185 }
186
187 fn outputs_eq(&self, other: &dyn ResourceOutputsDefinition) -> bool {
188 other.as_any().downcast_ref::<ServiceAccountOutputs>() == Some(self)
189 }
190
191 fn to_json_value(&self) -> serde_json::Result<serde_json::Value> {
192 serde_json::to_value(self)
193 }
194}