use crate::{
error::{ErrorData, Result},
variables::VariableInterpolator,
BindingTarget, PermissionContext,
};
use alien_core::PermissionSet;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "PascalCase")]
pub struct AzureRoleDefinition {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub is_custom: bool,
pub description: String,
pub actions: Vec<String>,
pub not_actions: Vec<String>,
pub data_actions: Vec<String>,
pub not_data_actions: Vec<String>,
pub assignable_scopes: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AzureRoleAssignmentProperties {
pub role_definition_id: String,
pub principal_id: String,
pub scope: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct AzureRoleAssignment {
pub properties: AzureRoleAssignmentProperties,
}
pub struct AzureRuntimePermissionsGenerator;
impl AzureRuntimePermissionsGenerator {
pub fn new() -> Self {
Self
}
pub fn generate_role_definition(
&self,
permission_set: &PermissionSet,
binding_target: BindingTarget,
context: &PermissionContext,
) -> Result<AzureRoleDefinition> {
let azure_platform_permissions =
permission_set.platforms.azure.as_ref().ok_or_else(|| {
alien_error::AlienError::new(ErrorData::PlatformNotSupported {
platform: "azure".to_string(),
permission_set_id: permission_set.id.clone(),
})
})?;
let role_name = self.generate_role_name(&permission_set.id);
let mut all_actions = Vec::new();
let mut all_data_actions = Vec::new();
let mut assignable_scopes = Vec::new();
for platform_permission in azure_platform_permissions {
if let Some(actions) = &platform_permission.grant.actions {
all_actions.extend(actions.clone());
}
if let Some(data_actions) = &platform_permission.grant.data_actions {
all_data_actions.extend(data_actions.clone());
}
let binding_spec = match binding_target {
BindingTarget::Stack => {
platform_permission.binding.stack.as_ref().ok_or_else(|| {
alien_error::AlienError::new(ErrorData::BindingTargetNotSupported {
platform: "azure".to_string(),
binding_target: "stack".to_string(),
permission_set_id: permission_set.id.clone(),
})
})?
}
BindingTarget::Resource => platform_permission
.binding
.resource
.as_ref()
.ok_or_else(|| {
alien_error::AlienError::new(ErrorData::BindingTargetNotSupported {
platform: "azure".to_string(),
binding_target: "resource".to_string(),
permission_set_id: permission_set.id.clone(),
})
})?,
};
let interpolated_scope =
VariableInterpolator::interpolate_variables(&binding_spec.scope, context)?;
assignable_scopes.push(interpolated_scope);
}
all_actions.sort();
all_actions.dedup();
all_data_actions.sort();
all_data_actions.dedup();
assignable_scopes.sort();
assignable_scopes.dedup();
Ok(AzureRoleDefinition {
name: role_name,
id: None, is_custom: true,
description: permission_set.description.clone(),
actions: all_actions,
not_actions: vec![],
data_actions: all_data_actions,
not_data_actions: vec![],
assignable_scopes,
})
}
pub fn generate_role_assignment(
&self,
permission_set: &PermissionSet,
binding_target: BindingTarget,
context: &PermissionContext,
) -> Result<AzureRoleAssignment> {
let azure_platform_permissions =
permission_set.platforms.azure.as_ref().ok_or_else(|| {
alien_error::AlienError::new(ErrorData::PlatformNotSupported {
platform: "azure".to_string(),
permission_set_id: permission_set.id.clone(),
})
})?;
let role_definition_id = format!(
"/subscriptions/{}/providers/Microsoft.Authorization/roleDefinitions/${{roleDefinitionGuid}}",
context.subscription_id.as_deref().unwrap_or("SUBSCRIPTION_ID")
);
let principal_id = context
.principal_id
.as_deref()
.unwrap_or("PRINCIPAL_ID")
.to_string();
let first_platform_permission = &azure_platform_permissions[0];
let binding_spec = match binding_target {
BindingTarget::Stack => first_platform_permission
.binding
.stack
.as_ref()
.ok_or_else(|| {
alien_error::AlienError::new(ErrorData::BindingTargetNotSupported {
platform: "azure".to_string(),
binding_target: "stack".to_string(),
permission_set_id: permission_set.id.clone(),
})
})?,
BindingTarget::Resource => first_platform_permission
.binding
.resource
.as_ref()
.ok_or_else(|| {
alien_error::AlienError::new(ErrorData::BindingTargetNotSupported {
platform: "azure".to_string(),
binding_target: "resource".to_string(),
permission_set_id: permission_set.id.clone(),
})
})?,
};
let interpolated_scope =
VariableInterpolator::interpolate_variables(&binding_spec.scope, context)?;
Ok(AzureRoleAssignment {
properties: AzureRoleAssignmentProperties {
role_definition_id,
principal_id,
scope: interpolated_scope,
},
})
}
fn generate_role_name(&self, permission_set_id: &str) -> String {
permission_set_id
.split('/')
.map(|part| {
part.split('-')
.map(|word| {
let mut chars = word.chars();
match chars.next() {
None => String::new(),
Some(first) => {
first.to_uppercase().collect::<String>() + chars.as_str()
}
}
})
.collect::<Vec<String>>()
.join(" ")
})
.collect::<Vec<String>>()
.join(" ")
}
}
impl Default for AzureRuntimePermissionsGenerator {
fn default() -> Self {
Self::new()
}
}