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
97#[typetag::serde(name = "service-account")]
99impl ResourceDefinition for ServiceAccount {
100 fn resource_type() -> ResourceType {
101 Self::RESOURCE_TYPE.clone()
102 }
103
104 fn get_resource_type(&self) -> ResourceType {
105 Self::resource_type()
106 }
107
108 fn id(&self) -> &str {
109 &self.id
110 }
111
112 fn get_dependencies(&self) -> Vec<ResourceRef> {
113 Vec::new()
116 }
117
118 fn validate_update(&self, new_config: &dyn ResourceDefinition) -> Result<()> {
119 let new_service_account = new_config
120 .as_any()
121 .downcast_ref::<ServiceAccount>()
122 .ok_or_else(|| {
123 AlienError::new(ErrorData::UnexpectedResourceType {
124 resource_id: self.id.clone(),
125 expected: Self::RESOURCE_TYPE,
126 actual: new_config.get_resource_type(),
127 })
128 })?;
129
130 if self.id != new_service_account.id {
131 return Err(AlienError::new(ErrorData::InvalidResourceUpdate {
132 resource_id: self.id.clone(),
133 reason: "the 'id' field is immutable".to_string(),
134 }));
135 }
136
137 Ok(())
138 }
139
140 fn as_any(&self) -> &dyn Any {
141 self
142 }
143
144 fn as_any_mut(&mut self) -> &mut dyn Any {
145 self
146 }
147
148 fn box_clone(&self) -> Box<dyn ResourceDefinition> {
149 Box::new(self.clone())
150 }
151
152 fn resource_eq(&self, other: &dyn ResourceDefinition) -> bool {
153 other.as_any().downcast_ref::<ServiceAccount>() == Some(self)
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
159#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
160#[serde(rename_all = "camelCase")]
161pub struct ServiceAccountOutputs {
162 pub identity: String,
167
168 pub resource_id: String,
173}
174
175#[typetag::serde(name = "service-account")]
176impl ResourceOutputsDefinition for ServiceAccountOutputs {
177 fn resource_type() -> ResourceType {
178 ServiceAccount::RESOURCE_TYPE.clone()
179 }
180
181 fn as_any(&self) -> &dyn Any {
182 self
183 }
184
185 fn box_clone(&self) -> Box<dyn ResourceOutputsDefinition> {
186 Box::new(self.clone())
187 }
188
189 fn outputs_eq(&self, other: &dyn ResourceOutputsDefinition) -> bool {
190 other.as_any().downcast_ref::<ServiceAccountOutputs>() == Some(self)
191 }
192}