From 7a21e04d904419a3164a310fa372d7ad94192182 Mon Sep 17 00:00:00 2001 From: pedrooot Date: Fri, 5 Dec 2025 15:10:00 +0100 Subject: [PATCH] feat(api): support alibabacloud provider --- .../migrations/0062_alibabacloud_provider.py | 37 +++++++++ api/src/backend/api/models.py | 10 +++ api/src/backend/api/specs/v1.yaml | 74 ++++++++++++++++++ api/src/backend/api/tests/test_utils.py | 2 + api/src/backend/api/tests/test_views.py | 75 +++++++++++++++++++ api/src/backend/api/utils.py | 20 +++-- .../api/v1/serializer_utils/providers.py | 42 +++++++++++ api/src/backend/api/v1/serializers.py | 47 +++++++++++- api/src/backend/conftest.py | 7 ++ 9 files changed, 305 insertions(+), 9 deletions(-) create mode 100644 api/src/backend/api/migrations/0062_alibabacloud_provider.py diff --git a/api/src/backend/api/migrations/0062_alibabacloud_provider.py b/api/src/backend/api/migrations/0062_alibabacloud_provider.py new file mode 100644 index 0000000000..9b7b3b1a22 --- /dev/null +++ b/api/src/backend/api/migrations/0062_alibabacloud_provider.py @@ -0,0 +1,37 @@ +# Generated by Django migration for Alibaba Cloud provider support + +from django.db import migrations + +import api.db_utils + + +class Migration(migrations.Migration): + dependencies = [ + ("api", "0061_daily_severity_summary"), + ] + + operations = [ + migrations.AlterField( + model_name="provider", + name="provider", + field=api.db_utils.ProviderEnumField( + choices=[ + ("aws", "AWS"), + ("azure", "Azure"), + ("gcp", "GCP"), + ("kubernetes", "Kubernetes"), + ("m365", "M365"), + ("github", "GitHub"), + ("mongodbatlas", "MongoDB Atlas"), + ("iac", "IaC"), + ("oraclecloud", "Oracle Cloud Infrastructure"), + ("alibabacloud", "Alibaba Cloud"), + ], + default="aws", + ), + ), + migrations.RunSQL( + "ALTER TYPE provider ADD VALUE IF NOT EXISTS 'alibabacloud';", + reverse_sql=migrations.RunSQL.noop, + ), + ] diff --git a/api/src/backend/api/models.py b/api/src/backend/api/models.py index f282bea806..50cc74a254 100644 --- a/api/src/backend/api/models.py +++ b/api/src/backend/api/models.py @@ -287,6 +287,7 @@ class Provider(RowLevelSecurityProtectedModel): MONGODBATLAS = "mongodbatlas", _("MongoDB Atlas") IAC = "iac", _("IaC") ORACLECLOUD = "oraclecloud", _("Oracle Cloud Infrastructure") + ALIBABACLOUD = "alibabacloud", _("Alibaba Cloud") @staticmethod def validate_aws_uid(value): @@ -391,6 +392,15 @@ class Provider(RowLevelSecurityProtectedModel): pointer="/data/attributes/uid", ) + @staticmethod + def validate_alibabacloud_uid(value): + if not re.match(r"^\d{16}$", value): + raise ModelValidationError( + detail="Alibaba Cloud account ID must be exactly 16 digits.", + code="alibabacloud-uid", + pointer="/data/attributes/uid", + ) + id = models.UUIDField(primary_key=True, default=uuid4, editable=False) inserted_at = models.DateTimeField(auto_now_add=True, editable=False) updated_at = models.DateTimeField(auto_now=True, editable=False) diff --git a/api/src/backend/api/specs/v1.yaml b/api/src/backend/api/specs/v1.yaml index 8aa1757457..785b266981 100644 --- a/api/src/backend/api/specs/v1.yaml +++ b/api/src/backend/api/specs/v1.yaml @@ -880,6 +880,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -890,6 +891,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -907,6 +909,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -919,6 +922,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -1419,6 +1423,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -1429,6 +1434,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -1446,6 +1452,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -1458,6 +1465,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -1866,6 +1874,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -1876,6 +1885,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -1893,6 +1903,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -1905,6 +1916,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -2311,6 +2323,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -2321,6 +2334,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -2338,6 +2352,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -2350,6 +2365,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -2744,6 +2760,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -2754,6 +2771,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -2771,6 +2789,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -2783,6 +2802,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -4635,6 +4655,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -4645,6 +4666,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -4662,6 +4684,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -4674,6 +4697,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -4824,6 +4848,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -4834,6 +4859,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -4851,6 +4877,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -4863,6 +4890,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -5005,6 +5033,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -5015,6 +5044,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -5031,6 +5061,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -5043,6 +5074,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - name: filter[search] @@ -5229,6 +5261,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -5239,6 +5272,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -5256,6 +5290,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -5268,6 +5303,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -5401,6 +5437,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -5411,6 +5448,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -5428,6 +5466,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -5440,6 +5479,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -6220,6 +6260,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -6230,6 +6271,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider__in] schema: @@ -6247,6 +6289,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -6259,6 +6302,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -6276,6 +6320,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -6286,6 +6331,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -6303,6 +6349,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -6315,6 +6362,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - name: filter[search] @@ -6926,6 +6974,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -6936,6 +6985,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -6953,6 +7003,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -6965,6 +7016,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -7309,6 +7361,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -7319,6 +7372,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -7336,6 +7390,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -7348,6 +7403,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -7587,6 +7643,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -7597,6 +7654,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -7614,6 +7672,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -7626,6 +7685,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -7871,6 +7931,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -7881,6 +7942,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -7898,6 +7960,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -7910,6 +7973,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -8718,6 +8782,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- * `aws` - AWS * `azure` - Azure @@ -8728,6 +8793,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud - in: query name: filter[provider_type__in] schema: @@ -8745,6 +8811,7 @@ paths: - m365 - mongodbatlas - oraclecloud + - alibabacloud description: |- Multiple values may be separated by commas. @@ -8757,6 +8824,7 @@ paths: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud explode: false style: form - in: query @@ -16199,6 +16267,7 @@ components: - mongodbatlas - iac - oraclecloud + - alibabacloud type: string description: |- * `aws` - AWS @@ -16210,6 +16279,7 @@ components: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud x-spec-enum-id: eca8c51e6bd28935 uid: type: string @@ -16325,6 +16395,7 @@ components: - mongodbatlas - iac - oraclecloud + - alibabacloud type: string x-spec-enum-id: eca8c51e6bd28935 description: |- @@ -16339,6 +16410,7 @@ components: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud uid: type: string title: Unique identifier for the provider, set by the provider @@ -16385,6 +16457,7 @@ components: - mongodbatlas - iac - oraclecloud + - alibabacloud type: string x-spec-enum-id: eca8c51e6bd28935 description: |- @@ -16399,6 +16472,7 @@ components: * `mongodbatlas` - MongoDB Atlas * `iac` - IaC * `oraclecloud` - Oracle Cloud Infrastructure + * `alibabacloud` - Alibaba Cloud uid: type: string minLength: 3 diff --git a/api/src/backend/api/tests/test_utils.py b/api/src/backend/api/tests/test_utils.py index 2229a2f98e..6f9146cd5c 100644 --- a/api/src/backend/api/tests/test_utils.py +++ b/api/src/backend/api/tests/test_utils.py @@ -16,6 +16,7 @@ from api.utils import ( return_prowler_provider, validate_invitation, ) +from prowler.providers.alibabacloud.alibabacloud_provider import AlibabacloudProvider from prowler.providers.aws.aws_provider import AwsProvider from prowler.providers.aws.lib.security_hub.security_hub import SecurityHubConnection from prowler.providers.azure.azure_provider import AzureProvider @@ -116,6 +117,7 @@ class TestReturnProwlerProvider: (Provider.ProviderChoices.MONGODBATLAS.value, MongodbatlasProvider), (Provider.ProviderChoices.ORACLECLOUD.value, OraclecloudProvider), (Provider.ProviderChoices.IAC.value, IacProvider), + (Provider.ProviderChoices.ALIBABACLOUD.value, AlibabacloudProvider), ], ) def test_return_prowler_provider(self, provider_type, expected_provider): diff --git a/api/src/backend/api/tests/test_views.py b/api/src/backend/api/tests/test_views.py index 35ab4270cd..b97363c11e 100644 --- a/api/src/backend/api/tests/test_views.py +++ b/api/src/backend/api/tests/test_views.py @@ -1165,6 +1165,11 @@ class TestProviderViewSet: "uid": "64b1d3c0e4b03b1234567890", "alias": "Atlas Organization", }, + { + "provider": "alibabacloud", + "uid": "1234567890123456", + "alias": "Alibaba Cloud Account", + }, ] ), ) @@ -1514,6 +1519,36 @@ class TestProviderViewSet: "mongodbatlas-uid", "uid", ), + # Alibaba Cloud UID validation - too short (not 16 digits) + ( + { + "provider": "alibabacloud", + "uid": "123456789012345", + "alias": "test", + }, + "alibabacloud-uid", + "uid", + ), + # Alibaba Cloud UID validation - too long (not 16 digits) + ( + { + "provider": "alibabacloud", + "uid": "12345678901234567", + "alias": "test", + }, + "alibabacloud-uid", + "uid", + ), + # Alibaba Cloud UID validation - contains non-digits + ( + { + "provider": "alibabacloud", + "uid": "123456789012345a", + "alias": "test", + }, + "alibabacloud-uid", + "uid", + ), ] ), ) @@ -2251,6 +2286,46 @@ class TestProviderSecretViewSet: "atlas_private_key": "private-key", }, ), + # Alibaba Cloud credentials (with access key only) + ( + Provider.ProviderChoices.ALIBABACLOUD.value, + ProviderSecret.TypeChoices.STATIC, + { + "access_key_id": "LTAI5t1234567890abcdef", + "access_key_secret": "my-secret-access-key", + }, + ), + # Alibaba Cloud credentials (with STS security token) + ( + Provider.ProviderChoices.ALIBABACLOUD.value, + ProviderSecret.TypeChoices.STATIC, + { + "access_key_id": "LTAI5t1234567890abcdef", + "access_key_secret": "my-secret-access-key", + "security_token": "my-security-token-for-sts", + }, + ), + # Alibaba Cloud RAM Role Assumption (minimal required fields) + ( + Provider.ProviderChoices.ALIBABACLOUD.value, + ProviderSecret.TypeChoices.ROLE, + { + "role_arn": "acs:ram::1234567890123456:role/ProwlerRole", + "access_key_id": "LTAI5t1234567890abcdef", + "access_key_secret": "my-secret-access-key", + }, + ), + # Alibaba Cloud RAM Role Assumption (with optional role_session_name) + ( + Provider.ProviderChoices.ALIBABACLOUD.value, + ProviderSecret.TypeChoices.ROLE, + { + "role_arn": "acs:ram::1234567890123456:role/ProwlerRole", + "access_key_id": "LTAI5t1234567890abcdef", + "access_key_secret": "my-secret-access-key", + "role_session_name": "ProwlerAuditSession", + }, + ), ], ) def test_provider_secrets_create_valid( diff --git a/api/src/backend/api/utils.py b/api/src/backend/api/utils.py index 866088f64a..9e708ca11b 100644 --- a/api/src/backend/api/utils.py +++ b/api/src/backend/api/utils.py @@ -11,6 +11,7 @@ from api.exceptions import InvitationTokenExpiredException from api.models import Integration, Invitation, Processor, Provider, Resource from api.v1.serializers import FindingMetadataSerializer from prowler.lib.outputs.jira.jira import Jira, JiraBasicAuthError +from prowler.providers.alibabacloud.alibabacloud_provider import AlibabacloudProvider from prowler.providers.aws.aws_provider import AwsProvider from prowler.providers.aws.lib.s3.s3 import S3 from prowler.providers.aws.lib.security_hub.security_hub import SecurityHub @@ -63,8 +64,9 @@ def merge_dicts(default_dict: dict, replacement_dict: dict) -> dict: def return_prowler_provider( provider: Provider, -) -> [ - AwsProvider +) -> ( + AlibabacloudProvider + | AwsProvider | AzureProvider | GcpProvider | GithubProvider @@ -73,14 +75,14 @@ def return_prowler_provider( | M365Provider | MongodbatlasProvider | OraclecloudProvider -]: +): """Return the Prowler provider class based on the given provider type. Args: provider (Provider): The provider object containing the provider type and associated secrets. Returns: - AwsProvider | AzureProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | OraclecloudProvider | MongodbatlasProvider: The corresponding provider class. + AlibabacloudProvider | AwsProvider | AzureProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OraclecloudProvider: The corresponding provider class. Raises: ValueError: If the provider type specified in `provider.provider` is not supported. @@ -104,6 +106,8 @@ def return_prowler_provider( prowler_provider = IacProvider case Provider.ProviderChoices.ORACLECLOUD.value: prowler_provider = OraclecloudProvider + case Provider.ProviderChoices.ALIBABACLOUD.value: + prowler_provider = AlibabacloudProvider case _: raise ValueError(f"Provider type {provider.provider} not supported") return prowler_provider @@ -169,7 +173,8 @@ def initialize_prowler_provider( provider: Provider, mutelist_processor: Processor | None = None, ) -> ( - AwsProvider + AlibabacloudProvider + | AwsProvider | AzureProvider | GcpProvider | GithubProvider @@ -186,9 +191,8 @@ def initialize_prowler_provider( mutelist_processor (Processor): The mutelist processor object containing the mutelist configuration. Returns: - AwsProvider | AzureProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | OraclecloudProvider | MongodbatlasProvider: An instance of the corresponding provider class - (`AwsProvider`, `AzureProvider`, `GcpProvider`, `GithubProvider`, `IacProvider`, `KubernetesProvider`, `M365Provider`, `OraclecloudProvider` or `MongodbatlasProvider`) initialized with the - provider's secrets. + AlibabacloudProvider | AwsProvider | AzureProvider | GcpProvider | GithubProvider | IacProvider | KubernetesProvider | M365Provider | MongodbatlasProvider | OraclecloudProvider: An instance of the corresponding provider class + initialized with the provider's secrets. """ prowler_provider = return_prowler_provider(provider) prowler_provider_kwargs = get_prowler_provider_kwargs(provider, mutelist_processor) diff --git a/api/src/backend/api/v1/serializer_utils/providers.py b/api/src/backend/api/v1/serializer_utils/providers.py index 4ec772e02f..3ed2a58ef3 100644 --- a/api/src/backend/api/v1/serializer_utils/providers.py +++ b/api/src/backend/api/v1/serializer_utils/providers.py @@ -304,6 +304,48 @@ from rest_framework_json_api import serializers }, "required": ["atlas_public_key", "atlas_private_key"], }, + { + "type": "object", + "title": "Alibaba Cloud Static Credentials", + "properties": { + "access_key_id": { + "type": "string", + "description": "The Alibaba Cloud access key ID for authentication.", + }, + "access_key_secret": { + "type": "string", + "description": "The Alibaba Cloud access key secret for authentication.", + }, + "security_token": { + "type": "string", + "description": "The STS security token for temporary credentials (optional).", + }, + }, + "required": ["access_key_id", "access_key_secret"], + }, + { + "type": "object", + "title": "Alibaba Cloud RAM Role Assumption", + "properties": { + "role_arn": { + "type": "string", + "description": "The ARN of the RAM role to assume (e.g., acs:ram::1234567890123456:role/ProwlerRole).", + }, + "access_key_id": { + "type": "string", + "description": "The Alibaba Cloud access key ID of the RAM user that will assume the role.", + }, + "access_key_secret": { + "type": "string", + "description": "The Alibaba Cloud access key secret of the RAM user that will assume the role.", + }, + "role_session_name": { + "type": "string", + "description": "An identifier for the role session (optional, defaults to 'ProwlerSession').", + }, + }, + "required": ["role_arn", "access_key_id", "access_key_secret"], + }, ] } ) diff --git a/api/src/backend/api/v1/serializers.py b/api/src/backend/api/v1/serializers.py index 47b91bc1cb..7cdc212e14 100644 --- a/api/src/backend/api/v1/serializers.py +++ b/api/src/backend/api/v1/serializers.py @@ -1388,12 +1388,23 @@ class BaseWriteProviderSecretSerializer(BaseWriteSerializer): serializer = OracleCloudProviderSecret(data=secret) elif provider_type == Provider.ProviderChoices.MONGODBATLAS.value: serializer = MongoDBAtlasProviderSecret(data=secret) + elif provider_type == Provider.ProviderChoices.ALIBABACLOUD.value: + serializer = AlibabaCloudProviderSecret(data=secret) else: raise serializers.ValidationError( {"provider": f"Provider type not supported {provider_type}"} ) elif secret_type == ProviderSecret.TypeChoices.ROLE: - serializer = AWSRoleAssumptionProviderSecret(data=secret) + if provider_type == Provider.ProviderChoices.AWS.value: + serializer = AWSRoleAssumptionProviderSecret(data=secret) + elif provider_type == Provider.ProviderChoices.ALIBABACLOUD.value: + serializer = AlibabaCloudRoleAssumptionProviderSecret(data=secret) + else: + raise serializers.ValidationError( + { + "secret_type": f"Role assumption not supported for provider type: {provider_type}" + } + ) elif secret_type == ProviderSecret.TypeChoices.SERVICE_ACCOUNT: serializer = GCPServiceAccountProviderSecret(data=secret) else: @@ -1530,6 +1541,40 @@ class OracleCloudProviderSecret(serializers.Serializer): resource_name = "provider-secrets" +class AlibabaCloudProviderSecret(serializers.Serializer): + access_key_id = serializers.CharField() + access_key_secret = serializers.CharField() + security_token = serializers.CharField(required=False) + + class Meta: + resource_name = "provider-secrets" + + +class AlibabaCloudRoleAssumptionProviderSecret(serializers.Serializer): + """Serializer for Alibaba Cloud RAM Role Assumption credentials. + + This allows assuming a RAM role using access keys, similar to AWS STS AssumeRole. + The SDK will automatically manage the STS token refresh. + """ + + role_arn = serializers.CharField( + help_text="ARN of the RAM role to assume (e.g., acs:ram::1234567890123456:role/ProwlerRole)" + ) + access_key_id = serializers.CharField( + help_text="Access Key ID of the RAM user that will assume the role" + ) + access_key_secret = serializers.CharField( + help_text="Access Key Secret of the RAM user that will assume the role" + ) + role_session_name = serializers.CharField( + required=False, + help_text="Session name for the assumed role session (optional, defaults to 'ProwlerSession')", + ) + + class Meta: + resource_name = "provider-secrets" + + class AWSRoleAssumptionProviderSecret(serializers.Serializer): role_arn = serializers.CharField() external_id = serializers.CharField() diff --git a/api/src/backend/conftest.py b/api/src/backend/conftest.py index 153487a83c..889299c13d 100644 --- a/api/src/backend/conftest.py +++ b/api/src/backend/conftest.py @@ -513,6 +513,12 @@ def providers_fixture(tenants_fixture): alias="mongodbatlas_testing", tenant_id=tenant.id, ) + provider9 = Provider.objects.create( + provider="alibabacloud", + uid="1234567890123456", + alias="alibabacloud_testing", + tenant_id=tenant.id, + ) return ( provider1, @@ -523,6 +529,7 @@ def providers_fixture(tenants_fixture): provider6, provider7, provider8, + provider9, )