mod common;
use alien_core::PermissionGrant;
use alien_permissions::generators::{AzureRoleDefinitionRef, AzureRuntimePermissionsGenerator};
use alien_permissions::BindingTarget;
use common::*;
use rstest::rstest;
#[rstest]
#[case::stack_binding(
BindingTarget::Stack,
"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-observability-prod"
)]
#[case::resource_binding(
BindingTarget::Resource,
"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-observability-prod/providers/Microsoft.Storage/storageAccounts/stcxpaymentsprod"
)]
fn test_azure_predefined_grant_plan(
#[case] binding_target: BindingTarget,
#[case] expected_scope: &str,
) {
let generator = AzureRuntimePermissionsGenerator::new();
let permission_set = create_azure_storage_data_read_permission_set();
let context = create_test_context();
let result = generator
.generate_grant_plan(&permission_set, binding_target, &context)
.expect("Should generate Azure grant plan successfully");
assert!(result.custom_roles.is_empty());
assert_eq!(result.bindings.len(), 1);
assert_eq!(result.bindings[0].permission_set_id, "storage/data-read");
assert_eq!(result.bindings[0].role_name, "Storage Blob Data Reader");
assert_eq!(result.bindings[0].scope, expected_scope);
assert_eq!(
result.bindings[0].role_definition,
AzureRoleDefinitionRef::Predefined {
role_definition_id: "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Authorization/roleDefinitions/2a2b9908-6ea1-4ae2-8e65-a410df84e7d1".to_string(),
}
);
}
#[test]
fn test_azure_custom_grant_plan() {
let generator = AzureRuntimePermissionsGenerator::new();
let permission_set = create_azure_custom_permission_set();
let context = create_test_context();
let result = generator
.generate_grant_plan(&permission_set, BindingTarget::Stack, &context)
.expect("Should generate Azure grant plan successfully");
assert_eq!(result.custom_roles.len(), 1);
assert_eq!(result.bindings.len(), 1);
assert_eq!(result.custom_roles[0].key, "storage/metadata-read:0");
assert_eq!(
result.custom_roles[0].role_definition.actions,
vec!["Microsoft.Storage/storageAccounts/read"]
);
assert_eq!(
result.bindings[0].role_definition,
AzureRoleDefinitionRef::Custom {
key: "storage/metadata-read:0".to_string(),
}
);
}
#[test]
fn test_azure_hybrid_grant_plan() {
let generator = AzureRuntimePermissionsGenerator::new();
let permission_set = create_azure_hybrid_permission_set();
let context = create_test_context();
let result = generator
.generate_grant_plan(&permission_set, BindingTarget::Stack, &context)
.expect("Should generate Azure grant plan successfully");
assert_eq!(result.custom_roles.len(), 1);
assert_eq!(result.bindings.len(), 2);
assert!(matches!(
result.bindings[0].role_definition,
AzureRoleDefinitionRef::Predefined { .. }
));
assert_eq!(
result.bindings[1].role_definition,
AzureRoleDefinitionRef::Custom {
key: "artifact-registry/provision:0".to_string(),
}
);
}
#[test]
fn test_azure_role_definition_generation_for_residual_custom_role() {
let generator = AzureRuntimePermissionsGenerator::new();
let mut permission_set = create_azure_custom_permission_set();
permission_set.id = "complex/permission-set/with-dashes".to_string();
let context = create_test_context();
let result = generator
.generate_role_definition(&permission_set, BindingTarget::Stack, &context)
.expect("Should generate Azure role definition successfully");
assert_eq!(result.name, "Complex Permission Set With Dashes (my-stack)");
assert!(result.is_custom);
assert_eq!(
result.actions,
vec!["Microsoft.Storage/storageAccounts/read"]
);
assert!(result.data_actions.is_empty());
}
#[test]
fn test_azure_missing_platform_error() {
let generator = AzureRuntimePermissionsGenerator::new();
let permission_set = create_aws_storage_data_read_permission_set();
let context = create_test_context();
let result = generator.generate_grant_plan(&permission_set, BindingTarget::Stack, &context);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Platform 'azure' is not supported"));
}
#[test]
fn test_azure_missing_binding_target_error() {
let generator = AzureRuntimePermissionsGenerator::new();
let mut permission_set = create_azure_storage_data_read_permission_set();
if let Some(azure_permissions) = &mut permission_set.platforms.azure {
azure_permissions[0].binding.resource = None;
}
let context = create_test_context();
let result = generator.generate_grant_plan(&permission_set, BindingTarget::Resource, &context);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Binding target 'resource' is not supported"));
}
#[test]
fn test_azure_empty_grant_error() {
let generator = AzureRuntimePermissionsGenerator::new();
let mut permission_set = create_azure_storage_data_read_permission_set();
if let Some(azure_permissions) = &mut permission_set.platforms.azure {
azure_permissions[0].grant = PermissionGrant {
actions: None,
permissions: None,
predefined_roles: None,
residual_permissions: None,
data_actions: None,
};
}
let context = create_test_context();
let result = generator.generate_grant_plan(&permission_set, BindingTarget::Stack, &context);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("has no predefined role or residual actions"));
}
#[test]
fn test_azure_unknown_predefined_role_error() {
let generator = AzureRuntimePermissionsGenerator::new();
let mut permission_set = create_azure_storage_data_read_permission_set();
if let Some(azure_permissions) = &mut permission_set.platforms.azure {
azure_permissions[0].grant.predefined_roles = Some(vec!["Not A Real Role".to_string()]);
}
let context = create_test_context();
let result = generator.generate_grant_plan(&permission_set, BindingTarget::Stack, &context);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("references unknown predefined role"));
}
#[test]
fn test_azure_wildcard_scope_error() {
let generator = AzureRuntimePermissionsGenerator::new();
let mut permission_set = create_azure_storage_data_read_permission_set();
if let Some(azure_permissions) = &mut permission_set.platforms.azure {
azure_permissions[0]
.binding
.stack
.as_mut()
.expect("stack binding")
.scope = "/subscriptions/${subscriptionId}/resourceGroups/${stackPrefix}-*".to_string();
}
let context = create_test_context();
let result = generator.generate_grant_plan(&permission_set, BindingTarget::Stack, &context);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("uses wildcard scope"));
}