alien-permissions 1.8.0

Deploy software into your customers' cloud accounts and keep it fully managed
Documentation
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:microsoft_storage_storage_accounts_read"
    );
    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:microsoft_storage_storage_accounts_read".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:microsoft_container_registry_registries_write_permissions"
                .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"));
}