alien_permissions/generators/
azure_runtime.rs1use crate::{
2 error::{ErrorData, Result},
3 variables::VariableInterpolator,
4 BindingTarget, PermissionContext,
5};
6use alien_core::PermissionSet;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
11#[serde(rename_all = "PascalCase")]
12pub struct AzureRoleDefinition {
13 pub name: String,
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub id: Option<String>,
18 pub is_custom: bool,
20 pub description: String,
22 pub actions: Vec<String>,
24 pub not_actions: Vec<String>,
26 pub data_actions: Vec<String>,
28 pub not_data_actions: Vec<String>,
30 pub assignable_scopes: Vec<String>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
36#[serde(rename_all = "camelCase")]
37pub struct AzureRoleAssignmentProperties {
38 pub role_definition_id: String,
40 pub principal_id: String,
42 pub scope: String,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
48#[serde(rename_all = "camelCase")]
49pub struct AzureRoleAssignment {
50 pub properties: AzureRoleAssignmentProperties,
52}
53
54pub struct AzureRuntimePermissionsGenerator;
56
57impl AzureRuntimePermissionsGenerator {
58 pub fn new() -> Self {
60 Self
61 }
62
63 pub fn generate_role_definition(
68 &self,
69 permission_set: &PermissionSet,
70 binding_target: BindingTarget,
71 context: &PermissionContext,
72 ) -> Result<AzureRoleDefinition> {
73 let azure_platform_permissions =
74 permission_set.platforms.azure.as_ref().ok_or_else(|| {
75 alien_error::AlienError::new(ErrorData::PlatformNotSupported {
76 platform: "azure".to_string(),
77 permission_set_id: permission_set.id.clone(),
78 })
79 })?;
80
81 let role_name = self.generate_role_name(&permission_set.id);
82
83 let mut all_actions = Vec::new();
85 let mut all_data_actions = Vec::new();
86 let mut assignable_scopes = Vec::new();
87
88 for platform_permission in azure_platform_permissions {
89 if let Some(actions) = &platform_permission.grant.actions {
91 all_actions.extend(actions.clone());
92 }
93 if let Some(data_actions) = &platform_permission.grant.data_actions {
94 all_data_actions.extend(data_actions.clone());
95 }
96
97 let binding_spec = match binding_target {
99 BindingTarget::Stack => {
100 platform_permission.binding.stack.as_ref().ok_or_else(|| {
101 alien_error::AlienError::new(ErrorData::BindingTargetNotSupported {
102 platform: "azure".to_string(),
103 binding_target: "stack".to_string(),
104 permission_set_id: permission_set.id.clone(),
105 })
106 })?
107 }
108 BindingTarget::Resource => platform_permission
109 .binding
110 .resource
111 .as_ref()
112 .ok_or_else(|| {
113 alien_error::AlienError::new(ErrorData::BindingTargetNotSupported {
114 platform: "azure".to_string(),
115 binding_target: "resource".to_string(),
116 permission_set_id: permission_set.id.clone(),
117 })
118 })?,
119 };
120
121 let interpolated_scope =
123 VariableInterpolator::interpolate_variables(&binding_spec.scope, context)?;
124 assignable_scopes.push(interpolated_scope);
125 }
126
127 all_actions.sort();
129 all_actions.dedup();
130 all_data_actions.sort();
131 all_data_actions.dedup();
132 assignable_scopes.sort();
133 assignable_scopes.dedup();
134
135 Ok(AzureRoleDefinition {
136 name: role_name,
137 id: None, is_custom: true,
139 description: permission_set.description.clone(),
140 actions: all_actions,
141 not_actions: vec![],
142 data_actions: all_data_actions,
143 not_data_actions: vec![],
144 assignable_scopes,
145 })
146 }
147
148 pub fn generate_role_assignment(
153 &self,
154 permission_set: &PermissionSet,
155 binding_target: BindingTarget,
156 context: &PermissionContext,
157 ) -> Result<AzureRoleAssignment> {
158 let azure_platform_permissions =
159 permission_set.platforms.azure.as_ref().ok_or_else(|| {
160 alien_error::AlienError::new(ErrorData::PlatformNotSupported {
161 platform: "azure".to_string(),
162 permission_set_id: permission_set.id.clone(),
163 })
164 })?;
165
166 let role_definition_id = format!(
168 "/subscriptions/{}/providers/Microsoft.Authorization/roleDefinitions/${{roleDefinitionGuid}}",
169 context.subscription_id.as_deref().unwrap_or("SUBSCRIPTION_ID")
170 );
171
172 let principal_id = context
173 .principal_id
174 .as_deref()
175 .unwrap_or("PRINCIPAL_ID")
176 .to_string();
177
178 let first_platform_permission = &azure_platform_permissions[0];
181 let binding_spec = match binding_target {
182 BindingTarget::Stack => first_platform_permission
183 .binding
184 .stack
185 .as_ref()
186 .ok_or_else(|| {
187 alien_error::AlienError::new(ErrorData::BindingTargetNotSupported {
188 platform: "azure".to_string(),
189 binding_target: "stack".to_string(),
190 permission_set_id: permission_set.id.clone(),
191 })
192 })?,
193 BindingTarget::Resource => first_platform_permission
194 .binding
195 .resource
196 .as_ref()
197 .ok_or_else(|| {
198 alien_error::AlienError::new(ErrorData::BindingTargetNotSupported {
199 platform: "azure".to_string(),
200 binding_target: "resource".to_string(),
201 permission_set_id: permission_set.id.clone(),
202 })
203 })?,
204 };
205
206 let interpolated_scope =
208 VariableInterpolator::interpolate_variables(&binding_spec.scope, context)?;
209
210 Ok(AzureRoleAssignment {
211 properties: AzureRoleAssignmentProperties {
212 role_definition_id,
213 principal_id,
214 scope: interpolated_scope,
215 },
216 })
217 }
218
219 fn generate_role_name(&self, permission_set_id: &str) -> String {
221 permission_set_id
222 .split('/')
223 .map(|part| {
224 part.split('-')
225 .map(|word| {
226 let mut chars = word.chars();
227 match chars.next() {
228 None => String::new(),
229 Some(first) => {
230 first.to_uppercase().collect::<String>() + chars.as_str()
231 }
232 }
233 })
234 .collect::<Vec<String>>()
235 .join(" ")
236 })
237 .collect::<Vec<String>>()
238 .join(" ")
239 }
240}
241
242impl Default for AzureRuntimePermissionsGenerator {
243 fn default() -> Self {
244 Self::new()
245 }
246}