import json
import os
import re
import tempfile
from datetime import datetime, timedelta
from json import dumps
from re import search
from unittest import mock

import botocore
import botocore.exceptions
import pytest
from boto3 import client, resource, session
from mock import patch
from moto import mock_aws
from pytest import raises
from tzlocal import get_localzone

from prowler.providers.aws.aws_provider import AwsProvider, get_aws_region_for_sts
from prowler.providers.aws.config import (
    AWS_STS_GLOBAL_ENDPOINT_REGION,
    BOTO3_USER_AGENT_EXTRA,
    ROLE_SESSION_NAME,
)
from prowler.providers.aws.exceptions.exceptions import (
    AWSArgumentTypeValidationError,
    AWSIAMRoleARNInvalidResourceTypeError,
    AWSInvalidPartitionError,
    AWSInvalidProviderIdError,
    AWSNoCredentialsError,
)
from prowler.providers.aws.lib.arn.models import ARN
from prowler.providers.aws.lib.mutelist.mutelist import AWSMutelist
from prowler.providers.aws.models import (
    AWSAssumeRoleInfo,
    AWSCallerIdentity,
    AWSCredentials,
    AWSMFAInfo,
    AWSOrganizationsInfo,
)
from prowler.providers.common.models import Connection
from prowler.providers.common.provider import Provider
from tests.providers.aws.utils import (
    AWS_ACCOUNT_ARN,
    AWS_ACCOUNT_NUMBER,
    AWS_CHINA_PARTITION,
    AWS_COMMERCIAL_PARTITION,
    AWS_GOV_CLOUD_ACCOUNT_ARN,
    AWS_GOV_CLOUD_PARTITION,
    AWS_ISO_PARTITION,
    AWS_REGION_CN_NORTH_1,
    AWS_REGION_CN_NORTHWEST_1,
    AWS_REGION_EU_CENTRAL_1,
    AWS_REGION_EU_WEST_1,
    AWS_REGION_GOV_CLOUD_US_EAST_1,
    AWS_REGION_ISO_GLOBAL,
    AWS_REGION_US_EAST_1,
    AWS_REGION_US_EAST_2,
    EXAMPLE_AMI_ID,
    create_role,
    set_mocked_aws_provider,
)

make_api_call = botocore.client.BaseClient._make_api_call


def mock_get_caller_identity_china(self, operation_name, kwarg):
    if operation_name == "GetCallerIdentity":
        return {
            "UserId": "XXXXXXXXXXXXXXXXXXXXX",
            "Account": AWS_ACCOUNT_NUMBER,
            "Arn": f"arn:{AWS_CHINA_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:user/test-user",
        }

    return make_api_call(self, operation_name, kwarg)


def mock_get_caller_identity_gov_cloud(self, operation_name, kwarg):
    if operation_name == "GetCallerIdentity":
        return {
            "UserId": "XXXXXXXXXXXXXXXXXXXXX",
            "Account": AWS_ACCOUNT_NUMBER,
            "Arn": f"arn:{AWS_GOV_CLOUD_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:user/test-user",
        }

    return make_api_call(self, operation_name, kwarg)


def mock_recover_checks_from_aws_provider(*_):
    return [
        (
            "accessanalyzer_enabled_without_findings",
            "/root_dir/fake_path/accessanalyzer/accessanalyzer_enabled_without_findings",
        ),
        (
            "awslambda_function_url_cors_policy",
            "/root_dir/fake_path/awslambda/awslambda_function_url_cors_policy",
        ),
        (
            "ec2_securitygroup_allow_ingress_from_internet_to_any_port",
            "/root_dir/fake_path/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port",
        ),
    ]


def mock_recover_checks_from_aws_provider_lambda_service(*_):
    return [
        (
            "awslambda_function_invoke_api_operations_cloudtrail_logging_enabled",
            "/root_dir/fake_path/awslambda/awslambda_function_invoke_api_operations_cloudtrail_logging_enabled",
        ),
        (
            "awslambda_function_url_cors_policy",
            "/root_dir/fake_path/awslambda/awslambda_function_url_cors_policy",
        ),
        (
            "awslambda_function_no_secrets_in_code",
            "/root_dir/fake_path/awslambda/awslambda_function_no_secrets_in_code",
        ),
    ]


def mock_recover_checks_from_aws_provider_elb_service(*_):
    return [
        (
            "elb_insecure_ssl_ciphers",
            "/root_dir/fake_path/elb/elb_insecure_ssl_ciphers",
        ),
        (
            "elb_internet_facing",
            "/root_dir/fake_path/elb/elb_internet_facing",
        ),
        (
            "elb_logging_enabled",
            "/root_dir/fake_path/elb/elb_logging_enabled",
        ),
    ]


def mock_recover_checks_from_aws_provider_efs_service(*_):
    return [
        (
            "efs_encryption_at_rest_enabled",
            "/root_dir/fake_path/efs/efs_encryption_at_rest_enabled",
        ),
        (
            "efs_have_backup_enabled",
            "/root_dir/fake_path/efs/efs_have_backup_enabled",
        ),
        (
            "efs_not_publicly_accessible",
            "/root_dir/fake_path/efs/efs_not_publicly_accessible",
        ),
    ]


def mock_recover_checks_from_aws_provider_iam_service(*_):
    return [
        (
            "iam_customer_attached_policy_no_administrative_privileges",
            "/root_dir/fake_path/iam/iam_customer_attached_policy_no_administrative_privileges",
        ),
        (
            "iam_check_saml_providers_sts",
            "/root_dir/fake_path/iam/iam_check_saml_providers_sts",
        ),
        (
            "iam_password_policy_minimum_length_14",
            "/root_dir/fake_path/iam/iam_password_policy_minimum_length_14",
        ),
    ]


def mock_recover_checks_from_aws_provider_s3_service(*_):
    return [
        (
            "s3_account_level_public_access_blocks",
            "/root_dir/fake_path/s3/s3_account_level_public_access_blocks",
        ),
        (
            "s3_bucket_acl_prohibited",
            "/root_dir/fake_path/s3/s3_bucket_acl_prohibited",
        ),
        (
            "s3_bucket_policy_public_write_access",
            "/root_dir/fake_path/s3/s3_bucket_policy_public_write_access",
        ),
    ]


def mock_recover_checks_from_aws_provider_cloudwatch_service(*_):
    return [
        (
            "cloudwatch_changes_to_network_acls_alarm_configured",
            "/root_dir/fake_path/cloudwatch/cloudwatch_changes_to_network_acls_alarm_configured",
        ),
        (
            "cloudwatch_changes_to_network_gateways_alarm_configured",
            "/root_dir/cloudwatch/cloudwatch_changes_to_network_gateways_alarm_configured",
        ),
        (
            "cloudwatch_changes_to_network_route_tables_alarm_configured",
            "/root_dir/fake_path/cloudwatch/cloudwatch_changes_to_network_route_tables_alarm_configured",
        ),
    ]


def mock_recover_checks_from_aws_provider_ec2_service(*_):
    return [
        (
            "ec2_securitygroup_allow_ingress_from_internet_to_any_port",
            "/root_dir/fake_path/ec2/ec2_securitygroup_allow_ingress_from_internet_to_any_port",
        ),
        (
            "ec2_networkacl_allow_ingress_any_port",
            "/root_dir/fake_path/ec2/ec2_networkacl_allow_ingress_any_port",
        ),
        (
            "ec2_ami_public",
            "/root_dir/fake_path/ec2/ec2_ami_public",
        ),
    ]


def mock_recover_checks_from_aws_provider_rds_service(*_):
    return [
        (
            "rds_instance_backup_enabled",
            "/root_dir/fake_path/rds/rds_instance_backup_enabled",
        ),
        (
            "rds_instance_deletion_protection",
            "/root_dir/fake_path/rds/rds_instance_deletion_protection",
        ),
        (
            "rds_snapshots_public_access",
            "/root_dir/fake_path/rds/rds_snapshots_public_access",
        ),
    ]


def mock_recover_checks_from_aws_provider_cognito_service(*_):
    return []


def mock_recover_checks_from_aws_provider_eks_service(*_):
    return [
        (
            "eks_cluster_not_publicly_accessible",
            "/root_dir/fake_path/eks/eks_cluster_not_publicly_accessible",
        ),
        (
            "eks_cluster_uses_a_supported_version",
            "/root_dir/fake_path/eks/eks_cluster_uses_a_supported_version",
        ),
        (
            "eks_cluster_network_policy_enabled",
            "/root_dir/fake_path/eks/eks_cluster_network_policy_enabled",
        ),
        (
            "eks_control_plane_logging_all_types_enabled",
            "/root_dir/fake_path/eks/eks_control_plane_logging_all_types_enabled",
        ),
    ]


class TestAWSProvider:
    @mock_aws
    def test_aws_provider_default(self):
        mfa = False
        scan_unused_services = True
        aws_provider = AwsProvider(
            mfa=mfa,
            scan_unused_services=scan_unused_services,
        )

        assert aws_provider.type == "aws"
        assert aws_provider.scan_unused_services is True
        assert aws_provider.audit_config
        assert aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1

    @mock_aws
    def test_aws_provider_with_static_credentials(self):
        # Create a mock IAM user
        iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
        username = "test-user"
        iam_user = iam_client.create_user(UserName=username)["User"]
        # Create a mock IAM access keys
        access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
            "AccessKey"
        ]

        credentials = {
            "aws_access_key_id": access_key["AccessKeyId"],
            "aws_secret_access_key": access_key["SecretAccessKey"],
        }

        aws_provider = AwsProvider(**credentials)
        assert aws_provider.type == "aws"
        # Session
        assert aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
        assert aws_provider.session.current_session.profile_name == "default"
        assert aws_provider.session.original_session.region_name == AWS_REGION_US_EAST_1
        assert aws_provider.session.original_session.profile_name == "default"

        # Identity
        assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
        assert aws_provider.identity.account_arn == AWS_ACCOUNT_ARN
        assert (
            aws_provider.identity.identity_arn
            == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/{username}"
        )
        assert aws_provider.identity.partition == AWS_COMMERCIAL_PARTITION
        assert aws_provider.identity.profile is None
        assert aws_provider.identity.profile_region == AWS_REGION_US_EAST_1

    @mock_aws
    def test_aws_provider_with_session_credentials(self):
        sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
        session_token = sts_client.get_session_token()

        session_credentials = {
            "aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
            "aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
            "aws_session_token": session_token["Credentials"]["SessionToken"],
        }

        aws_provider = AwsProvider(**session_credentials)
        assert aws_provider.type == "aws"
        # Session
        assert aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
        assert aws_provider.session.current_session.profile_name == "default"
        assert aws_provider.session.original_session.region_name == AWS_REGION_US_EAST_1
        assert aws_provider.session.original_session.profile_name == "default"

        # Identity
        assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
        assert aws_provider.identity.account_arn == AWS_ACCOUNT_ARN
        # moto is the default user created by moto
        assert (
            aws_provider.identity.identity_arn
            == f"arn:aws:sts::{AWS_ACCOUNT_NUMBER}:user/moto"
        )
        assert aws_provider.identity.partition == AWS_COMMERCIAL_PARTITION
        assert aws_provider.identity.profile is None
        assert aws_provider.identity.profile_region == AWS_REGION_US_EAST_1

    @mock_aws
    def test_aws_provider_organizations_delegated_administrator(self):
        organizations_client = client("organizations", region_name=AWS_REGION_EU_WEST_1)
        organization = organizations_client.create_organization()["Organization"]
        organizations_client.tag_resource(
            ResourceId=AWS_ACCOUNT_NUMBER,
            Tags=[
                {"Key": "tagged", "Value": "true"},
            ],
        )

        aws_provider = AwsProvider()

        assert isinstance(aws_provider.organizations_metadata, AWSOrganizationsInfo)
        assert aws_provider.organizations_metadata.account_email == "master@example.com"
        assert aws_provider.organizations_metadata.account_name == "master"
        assert aws_provider.organizations_metadata.account_tags == {"tagged": "true"}
        assert (
            aws_provider.organizations_metadata.organization_account_arn
            == f"arn:aws:organizations::{AWS_ACCOUNT_NUMBER}:account/{organization['Id']}/{AWS_ACCOUNT_NUMBER}"
        )
        assert aws_provider.organizations_metadata.organization_id == organization["Id"]
        assert (
            aws_provider.organizations_metadata.organization_arn == organization["Arn"]
        )

    @mock_aws
    def test_aws_provider_organizations_none_organizations_metadata(self):
        aws_provider = AwsProvider()

        assert isinstance(aws_provider.organizations_metadata, AWSOrganizationsInfo)
        assert aws_provider.organizations_metadata.account_email == ""
        assert aws_provider.organizations_metadata.account_name == ""
        assert aws_provider.organizations_metadata.account_tags == []
        assert aws_provider.organizations_metadata.organization_account_arn == ""
        assert aws_provider.organizations_metadata.organization_id == ""
        assert aws_provider.organizations_metadata.organization_arn == ""

    @mock_aws
    def test_aws_provider_organizations_with_role(self):
        iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
        policy_name = "describe_organizations_policy"
        policy_document = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "organizations:DescribeAccount",
                        "organizations:ListTagsForResource",
                    ],
                    "Resource": "*",
                },
            ],
        }

        policy = iam_client.create_policy(
            PolicyName=policy_name,
            PolicyDocument=dumps(policy_document),
        )["Policy"]

        assume_policy_document = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"},
                    "Action": "sts:AssumeRole",
                }
            ],
        }
        role_name = "organizations_role"
        organizations_role = iam_client.create_role(
            RoleName=role_name, AssumeRolePolicyDocument=dumps(assume_policy_document)
        )["Role"]
        iam_client.attach_role_policy(
            RoleName=role_name,
            PolicyArn=policy["Arn"],
        )
        organizations_client = client("organizations", region_name=AWS_REGION_EU_WEST_1)
        organization = organizations_client.create_organization()["Organization"]
        organizations_client.tag_resource(
            ResourceId=AWS_ACCOUNT_NUMBER,
            Tags=[
                {"Key": "tagged", "Value": "true"},
            ],
        )

        organizations_role = organizations_role["Arn"]
        session_duration = 900
        aws_provider = AwsProvider(
            organizations_role_arn=organizations_role,
            session_duration=session_duration,
        )

        assert isinstance(aws_provider.organizations_metadata, AWSOrganizationsInfo)
        assert aws_provider.organizations_metadata.account_email == "master@example.com"
        assert aws_provider.organizations_metadata.account_name == "master"
        assert aws_provider.organizations_metadata.account_tags == {"tagged": "true"}
        assert (
            aws_provider.organizations_metadata.organization_account_arn
            == f"arn:aws:organizations::{AWS_ACCOUNT_NUMBER}:account/{organization['Id']}/{AWS_ACCOUNT_NUMBER}"
        )
        assert aws_provider.organizations_metadata.organization_id == organization["Id"]
        assert (
            aws_provider.organizations_metadata.organization_arn == organization["Arn"]
        )

    @mock_aws
    def test_aws_provider_session_with_mfa(self):
        mfa = True

        with patch(
            "prowler.providers.aws.aws_provider.AwsProvider.input_role_mfa_token_and_code",
            return_value=AWSMFAInfo(
                arn=f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa/test-role-mfa",
                totp="111111",
            ),
        ):
            aws_provider = AwsProvider(mfa=mfa)

            assert aws_provider.type == "aws"
            assert aws_provider.scan_unused_services is False
            assert aws_provider.audit_config != {}
            assert (
                aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
            )
            assert (
                aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
            )

    @mock_aws
    def test_aws_provider_assume_role_with_mfa(self):
        # Variables
        mfa = True
        role_name = "test-role"
        role_arn = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
        session_duration = 900
        role_session_name = "ProwlerAssessmentSession"
        external_id = "test-external-id"

        with patch(
            "prowler.providers.aws.aws_provider.AwsProvider.input_role_mfa_token_and_code",
            return_value=AWSMFAInfo(
                arn=f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa/test-role-mfa",
                totp="111111",
            ),
        ):
            aws_provider = AwsProvider(
                mfa=mfa,
                role_arn=role_arn,
                session_duration=session_duration,
                role_session_name=role_session_name,
                external_id=external_id,
            )
            assert (
                aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
            )
            assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
            assert aws_provider.identity.account_arn == AWS_ACCOUNT_ARN
            assert aws_provider.identity.partition == AWS_COMMERCIAL_PARTITION
            assert isinstance(
                aws_provider._assumed_role_configuration.info, AWSAssumeRoleInfo
            )
            assert aws_provider._assumed_role_configuration.info == AWSAssumeRoleInfo(
                role_arn=ARN(arn=role_arn),
                session_duration=session_duration,
                external_id=external_id,
                mfa_enabled=True,  # <- MFA configuration
                role_session_name=role_session_name,
                sts_region=AWS_REGION_US_EAST_1,
            )

            credentials = aws_provider._assumed_role_configuration.credentials
            assert isinstance(credentials, AWSCredentials)

            assert credentials.aws_access_key_id
            assert len(credentials.aws_access_key_id) == 20
            assert search(r"^ASIA.*$", credentials.aws_access_key_id)

            assert credentials.aws_session_token
            assert len(credentials.aws_session_token) == 356
            assert search(r"^FQoGZXIvYXdzE.*$", credentials.aws_session_token)

            assert credentials.aws_secret_access_key
            assert len(credentials.aws_secret_access_key) == 40

            assert credentials.expiration
            # assert credentials.expiration == datetime.now(tzinfo=tzutc())

    @mock_aws
    def test_aws_provider_assume_role_without_mfa(self):
        # Variables
        mfa = False
        role_name = "test-role"
        role_arn = (
            f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
        )
        session_duration = 900
        role_session_name = "ProwlerAssessmentSession"

        aws_provider = AwsProvider(
            mfa=mfa,
            role_arn=role_arn,
            session_duration=session_duration,
            role_session_name=role_session_name,
        )
        assert aws_provider.session.current_session.region_name == AWS_REGION_US_EAST_1
        assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
        assert aws_provider.identity.account_arn == AWS_ACCOUNT_ARN
        assert aws_provider.identity.partition == AWS_COMMERCIAL_PARTITION
        assert isinstance(
            aws_provider._assumed_role_configuration.info, AWSAssumeRoleInfo
        )
        assert aws_provider._assumed_role_configuration.info == AWSAssumeRoleInfo(
            role_arn=ARN(arn=role_arn),
            session_duration=session_duration,
            external_id=None,
            mfa_enabled=False,  # <- MFA configuration
            role_session_name=role_session_name,
            sts_region=AWS_REGION_US_EAST_1,
        )

        credentials = aws_provider._assumed_role_configuration.credentials
        assert isinstance(credentials, AWSCredentials)

        assert credentials.aws_access_key_id
        assert len(credentials.aws_access_key_id) == 20
        assert search(r"^ASIA.*$", credentials.aws_access_key_id)

        assert credentials.aws_session_token
        assert len(credentials.aws_session_token) == 356
        assert search(r"^FQoGZXIvYXdzE.*$", credentials.aws_session_token)

        assert credentials.aws_secret_access_key
        assert len(credentials.aws_secret_access_key) == 40

        assert credentials.expiration
        # assert credentials.expiration == datetime.now(tzinfo=tzutc())

    @mock_aws
    def test_aws_provider_assume_role_without_mfa_gov_cloud(self, monkeypatch):
        # Set AWS_DEFAULT_REGION = 'us-gov-east-1' since is set by default to 'us-east-1
        monkeypatch.setenv("AWS_DEFAULT_REGION", AWS_REGION_GOV_CLOUD_US_EAST_1)

        # Variables
        mfa = False
        role_name = "test-role"
        role_arn = (
            f"arn:{AWS_GOV_CLOUD_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
        )
        session_duration = 900
        role_session_name = "ProwlerAssessmentSession"

        aws_provider = AwsProvider(
            mfa=mfa,
            role_arn=role_arn,
            session_duration=session_duration,
            role_session_name=role_session_name,
        )
        assert (
            aws_provider.session.current_session.region_name
            == AWS_REGION_GOV_CLOUD_US_EAST_1
        )
        assert aws_provider.identity.account == AWS_ACCOUNT_NUMBER
        assert aws_provider.identity.account_arn == AWS_GOV_CLOUD_ACCOUNT_ARN
        assert aws_provider.identity.partition == AWS_GOV_CLOUD_PARTITION
        assert isinstance(
            aws_provider._assumed_role_configuration.info, AWSAssumeRoleInfo
        )
        assert aws_provider._assumed_role_configuration.info == AWSAssumeRoleInfo(
            role_arn=ARN(arn=role_arn),
            session_duration=session_duration,
            external_id=None,
            mfa_enabled=False,  # <- MFA configuration
            role_session_name=role_session_name,
            sts_region=AWS_REGION_GOV_CLOUD_US_EAST_1,
        )

        credentials = aws_provider._assumed_role_configuration.credentials
        assert isinstance(credentials, AWSCredentials)

        assert credentials.aws_access_key_id
        assert len(credentials.aws_access_key_id) == 20
        assert search(r"^ASIA.*$", credentials.aws_access_key_id)

        assert credentials.aws_session_token
        assert len(credentials.aws_session_token) == 356
        assert search(r"^FQoGZXIvYXdzE.*$", credentials.aws_session_token)

        assert credentials.aws_secret_access_key
        assert len(credentials.aws_secret_access_key) == 40

        assert credentials.expiration
        # assert credentials.expiration == datetime.now(tzinfo=tzutc())

    @mock_aws
    def test_aws_provider_config(self):
        config = """
aws:
    test_key: value"""

        config_file_input = tempfile.NamedTemporaryFile(delete=False)
        config_file_input.write(bytes(config, encoding="raw_unicode_escape"))
        config_file_input.close()
        config_file_input = config_file_input.name
        aws_provider = AwsProvider(
            config_path=config_file_input,
        )

        os.remove(config_file_input)

        assert aws_provider.audit_config == {"test_key": "value"}

    @mock_aws
    def test_aws_provider_mutelist(self):
        mutelist = {
            "Mutelist": {
                "Accounts": {
                    AWS_ACCOUNT_NUMBER: {
                        "Checks": {
                            "test-check": {
                                "Regions": [],
                                "Resources": [],
                                "Tags": [],
                                "Exceptions": {
                                    "Accounts": [],
                                    "Regions": [],
                                    "Resources": [],
                                    "Tags": [],
                                },
                            }
                        }
                    }
                }
            }
        }

        mutelist_file = tempfile.NamedTemporaryFile(delete=False)
        with open(mutelist_file.name, "w") as mutelist_file:
            mutelist_file.write(json.dumps(mutelist, indent=4))

        aws_provider = AwsProvider(mutelist_path=mutelist_file.name)

        os.remove(mutelist_file.name)

        assert isinstance(aws_provider.mutelist, AWSMutelist)
        assert aws_provider.mutelist.mutelist == mutelist["Mutelist"]
        assert aws_provider.mutelist.mutelist_file_path == mutelist_file.name

    @mock_aws
    def test_aws_provider_mutelist_none(self):
        with patch(
            "prowler.providers.aws.aws_provider.get_default_mute_file_path",
            return_value=None,
        ):
            aws_provider = AwsProvider(mutelist_path=None)

        assert isinstance(aws_provider.mutelist, AWSMutelist)
        assert aws_provider.mutelist.mutelist == {}
        assert aws_provider.mutelist.mutelist_file_path is None

    @mock_aws
    def test_aws_provider_mutelist_s3(self):
        # Create mutelist temp file
        mutelist = {
            "Mutelist": {
                "Accounts": {
                    AWS_ACCOUNT_NUMBER: {
                        "Checks": {
                            "test-check": {
                                "Regions": [],
                                "Resources": [],
                                "Tags": [],
                                "Exceptions": {
                                    "Accounts": [],
                                    "Regions": [],
                                    "Resources": [],
                                    "Tags": [],
                                },
                            }
                        }
                    }
                }
            }
        }

        mutelist_file = tempfile.NamedTemporaryFile(delete=False)
        with open(mutelist_file.name, "w") as mutelist_file:
            mutelist_file.write(json.dumps(mutelist, indent=4))

        # Create bucket and upload mutelist yaml
        s3_resource = resource("s3", region_name=AWS_REGION_US_EAST_1)
        bucket_name = "test-mutelist"
        mutelist_file_name = "mutelist.yaml"
        mutelist_bucket_object_uri = f"s3://{bucket_name}/{mutelist_file_name}"
        s3_resource.create_bucket(Bucket=bucket_name)
        s3_resource.Object(bucket_name, "mutelist.yaml").put(
            Body=open(
                mutelist_file.name,
                "rb",
            )
        )

        aws_provider = AwsProvider(mutelist_path=mutelist_bucket_object_uri)

        os.remove(mutelist_file.name)

        assert isinstance(aws_provider.mutelist, AWSMutelist)
        assert aws_provider.mutelist.mutelist == mutelist["Mutelist"]
        assert aws_provider.mutelist.mutelist_file_path == mutelist_bucket_object_uri

    @mock_aws
    def test_aws_provider_mutelist_lambda(self):
        # Create mutelist temp file
        mutelist = {
            "Mutelist": {
                "Accounts": {
                    AWS_ACCOUNT_NUMBER: {
                        "Checks": {
                            "test-check": {
                                "Regions": [],
                                "Resources": [],
                                "Tags": [],
                                "Exceptions": {
                                    "Accounts": [],
                                    "Regions": [],
                                    "Resources": [],
                                    "Tags": [],
                                },
                            }
                        }
                    }
                }
            }
        }
        lambda_mutelist_path = f"arn:aws:lambda:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:function:lambda-mutelist"
        aws_provider = AwsProvider()

        with patch(
            "prowler.providers.aws.lib.mutelist.mutelist.AWSMutelist.get_mutelist_file_from_lambda",
            return_value=mutelist["Mutelist"],
        ):
            aws_provider = AwsProvider(mutelist_path=lambda_mutelist_path)

        assert isinstance(aws_provider.mutelist, AWSMutelist)
        assert aws_provider.mutelist.mutelist == mutelist["Mutelist"]
        assert aws_provider.mutelist.mutelist_file_path == lambda_mutelist_path

    @mock_aws
    def test_aws_provider_mutelist_dynamodb(self):
        # Create mutelist temp file
        mutelist = {
            "Mutelist": {
                "Accounts": {
                    AWS_ACCOUNT_NUMBER: {
                        "Checks": {
                            "test-check": {
                                "Regions": [],
                                "Resources": [],
                                "Tags": [],
                                "Exceptions": {
                                    "Accounts": [],
                                    "Regions": [],
                                    "Resources": [],
                                    "Tags": [],
                                },
                            }
                        }
                    }
                }
            }
        }
        dynamodb_mutelist_path = f"arn:aws:dynamodb:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:table/mutelist-dynamo"
        aws_provider = AwsProvider()

        with patch(
            "prowler.providers.aws.lib.mutelist.mutelist.AWSMutelist.get_mutelist_file_from_dynamodb",
            return_value=mutelist["Mutelist"],
        ):
            aws_provider = AwsProvider(mutelist_path=dynamodb_mutelist_path)

        assert isinstance(aws_provider.mutelist, AWSMutelist)
        assert aws_provider.mutelist.mutelist == mutelist["Mutelist"]
        assert aws_provider.mutelist.mutelist_file_path == dynamodb_mutelist_path

    @mock_aws
    def test_empty_input_regions_in_arguments(self):
        aws_provider = AwsProvider(regions=None)

        assert isinstance(aws_provider, AwsProvider)

    @mock_aws
    def test_generate_regional_clients_all_enabled_regions(self):
        aws_provider = AwsProvider()
        response = aws_provider.generate_regional_clients("ec2")

        assert len(response.keys()) == 33

    @mock_aws
    def test_generate_regional_clients_with_enabled_regions(self):
        aws_provider = AwsProvider()
        enabled_regions = [AWS_REGION_EU_WEST_1]
        aws_provider._enabled_regions = enabled_regions

        response = aws_provider.generate_regional_clients("ec2")

        assert list(response.keys()) == enabled_regions

    @mock_aws
    def test_generate_regional_clients_with_enabled_regions_and_input_regions(self):
        region = [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
        aws_provider = AwsProvider(
            regions=region,
        )

        enabled_regions = [AWS_REGION_EU_WEST_1]
        aws_provider._enabled_regions = enabled_regions

        response = aws_provider.generate_regional_clients("ec2")

        assert list(response.keys()) == enabled_regions

    @mock_aws
    def test_generate_regional_clients_cn_partition(self):
        region = [AWS_REGION_CN_NORTH_1, AWS_REGION_CN_NORTHWEST_1]
        aws_provider = AwsProvider(
            regions=region,
        )

        response = aws_provider.generate_regional_clients("ec2")
        assert AWS_REGION_CN_NORTH_1 in response.keys()
        assert AWS_REGION_CN_NORTHWEST_1 in response.keys()

    @mock_aws
    def test_generate_regional_clients_cn_partition_not_present_service(self):
        region = ["cn-northwest-1", "cn-north-1"]
        aws_provider = AwsProvider(
            regions=region,
        )

        response = aws_provider.generate_regional_clients("shield")

        assert response == {}

    @mock_aws
    def test_get_default_region(self):
        region = [AWS_REGION_EU_WEST_1]
        aws_provider = AwsProvider(
            regions=region,
        )
        aws_provider._identity.profile_region = AWS_REGION_EU_WEST_1

        assert aws_provider.get_default_region("ec2") == AWS_REGION_EU_WEST_1

    @mock_aws
    def test_get_default_region_profile_region_not_audited(self):
        region = [AWS_REGION_EU_WEST_1]
        aws_provider = AwsProvider(
            regions=region,
        )
        aws_provider._identity.profile_region = AWS_REGION_US_EAST_2

        assert aws_provider.get_default_region("ec2") == AWS_REGION_EU_WEST_1

    @mock_aws
    def test_get_default_region_non_profile_region(self):
        region = [AWS_REGION_EU_WEST_1]
        aws_provider = AwsProvider(
            regions=region,
        )
        aws_provider._identity.profile_region = None

        assert aws_provider.get_default_region("ec2") == AWS_REGION_EU_WEST_1

    @mock_aws
    def test_get_default_region_non_profile_or_audited_region(self):
        aws_provider = AwsProvider()
        aws_provider._identity.profile_region = None
        assert aws_provider.get_default_region("ec2") == AWS_REGION_US_EAST_1

    @mock_aws
    def test_get_default_region_profile_region_not_present_in_service(self):
        region = [AWS_REGION_EU_WEST_1]
        aws_provider = AwsProvider(
            regions=region,
        )
        aws_provider._identity.profile_region = "non-existent-region"
        assert aws_provider.get_default_region("ec2") == AWS_REGION_EU_WEST_1

    @mock_aws
    def test_aws_gov_get_global_region(self):
        aws_provider = AwsProvider()
        aws_provider._identity.partition = AWS_GOV_CLOUD_PARTITION

        assert aws_provider.get_global_region() == AWS_REGION_GOV_CLOUD_US_EAST_1

    @mock_aws
    def test_aws_cn_get_global_region(self):
        aws_provider = AwsProvider()
        aws_provider._identity.partition = AWS_CHINA_PARTITION

        assert aws_provider.get_global_region() == AWS_REGION_CN_NORTH_1

    @mock_aws
    def test_aws_iso_get_global_region(self):
        aws_provider = AwsProvider()
        aws_provider._identity.partition = AWS_ISO_PARTITION

        assert aws_provider.get_global_region() == AWS_REGION_ISO_GLOBAL

    @mock_aws
    def test_get_available_aws_service_regions_with_us_east_1_audited(self):
        region = [AWS_REGION_US_EAST_1]
        aws_provider = AwsProvider(
            regions=region,
        )

        with patch(
            "prowler.providers.aws.aws_provider.parse_json_file",
            return_value={
                "services": {
                    "ec2": {
                        "regions": {
                            "aws": [
                                "af-south-1",
                                "ca-central-1",
                                "eu-central-1",
                                "eu-central-2",
                                "eu-north-1",
                                "eu-south-1",
                                "eu-south-2",
                                AWS_REGION_EU_WEST_1,
                                "eu-west-2",
                                "eu-west-3",
                                "me-central-1",
                                "me-south-1",
                                "sa-east-1",
                                AWS_REGION_US_EAST_1,
                                "us-east-2",
                                "us-west-1",
                                "us-west-2",
                            ],
                        }
                    }
                }
            },
        ):
            assert aws_provider.get_available_aws_service_regions(
                "ec2", "aws", {AWS_REGION_US_EAST_1}
            ) == {AWS_REGION_US_EAST_1}

    @mock_aws
    def test_get_available_aws_service_regions_with_all_regions_audited(self):
        aws_provider = AwsProvider()

        with patch(
            "prowler.providers.aws.aws_provider.parse_json_file",
            return_value={
                "services": {
                    "ec2": {
                        "regions": {
                            "aws": [
                                "af-south-1",
                                "ca-central-1",
                                "eu-central-1",
                                "eu-central-2",
                                "eu-north-1",
                                "eu-south-1",
                                "eu-south-2",
                                AWS_REGION_EU_WEST_1,
                                "eu-west-2",
                                "eu-west-3",
                                "me-central-1",
                                "me-south-1",
                                "sa-east-1",
                                AWS_REGION_US_EAST_1,
                                "us-east-2",
                                "us-west-1",
                                "us-west-2",
                            ],
                        }
                    }
                }
            },
        ):
            assert (
                len(aws_provider.get_available_aws_service_regions("ec2", "aws")) == 17
            )

    @mock_aws
    def test_get_tagged_resources(self):
        ec2_client = client("ec2", region_name=AWS_REGION_EU_CENTRAL_1)
        instances = ec2_client.run_instances(
            ImageId=EXAMPLE_AMI_ID,
            MinCount=1,
            MaxCount=1,
            InstanceType="t2.micro",
            TagSpecifications=[
                {
                    "ResourceType": "instance",
                    "Tags": [
                        {"Key": "MY_TAG1", "Value": "MY_VALUE1"},
                        {"Key": "MY_TAG2", "Value": "MY_VALUE2"},
                    ],
                },
                {
                    "ResourceType": "instance",
                    "Tags": [{"Key": "ami", "Value": "test"}],
                },
            ],
        )
        instance_id = instances["Instances"][0]["InstanceId"]
        instance_arn = f"arn:aws:ec2:{AWS_REGION_EU_CENTRAL_1}:{AWS_ACCOUNT_NUMBER}:ec2:instance/{instance_id}"
        image_id = ec2_client.create_image(Name="testami", InstanceId=instance_id)[
            "ImageId"
        ]
        image_arn = f"arn:aws:ec2:{AWS_REGION_EU_CENTRAL_1}:{AWS_ACCOUNT_NUMBER}:ec2:image/{image_id}"
        ec2_client.create_tags(
            Resources=[image_id], Tags=[{"Key": "ami", "Value": "test"}]
        )

        # Through the AWS provider
        region = [AWS_REGION_EU_CENTRAL_1]
        resource_tags = ["ami=test"]
        aws_provider = AwsProvider(
            regions=region,
            resource_tags=resource_tags,
        )

        tagged_resources = aws_provider.audit_resources
        assert len(tagged_resources) == 2
        assert image_arn in tagged_resources
        assert instance_arn in tagged_resources

        # Calling directly the function
        tagged_resources = aws_provider.get_tagged_resources(["MY_TAG1=MY_VALUE1"])

        assert len(tagged_resources) == 1
        assert instance_arn in tagged_resources

    @mock_aws
    def test_aws_provider_resource_tags(self):
        resource_arn = [AWS_ACCOUNT_ARN]
        aws_provider = AwsProvider(
            resource_arn=resource_arn,
        )

        assert aws_provider.audit_resources == [AWS_ACCOUNT_ARN]

    @mock_aws
    def test_validate_credentials_commercial_partition_with_regions(self):
        # Create a mock IAM user
        iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
        iam_user = iam_client.create_user(UserName="test-user")["User"]
        # Create a mock IAM access keys
        access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
            "AccessKey"
        ]
        access_key_id = access_key["AccessKeyId"]
        secret_access_key = access_key["SecretAccessKey"]

        # Create AWS session to validate
        current_session = session.Session(
            aws_access_key_id=access_key_id,
            aws_secret_access_key=secret_access_key,
            region_name=AWS_REGION_EU_WEST_1,
        )

        get_caller_identity = AwsProvider.validate_credentials(
            session=current_session, aws_region=AWS_REGION_EU_WEST_1
        )

        assert isinstance(get_caller_identity, AWSCallerIdentity)

        assert re.match("[0-9a-zA-Z]{20}", get_caller_identity.user_id)
        assert get_caller_identity.account == AWS_ACCOUNT_NUMBER
        assert get_caller_identity.region == AWS_REGION_EU_WEST_1

        assert isinstance(get_caller_identity.arn, ARN)
        assert get_caller_identity.arn.partition == AWS_COMMERCIAL_PARTITION
        assert get_caller_identity.arn.region is None
        assert get_caller_identity.arn.resource == "test-user"
        assert get_caller_identity.arn.resource_type == "user"

    @mock_aws
    @patch(
        "botocore.client.BaseClient._make_api_call", new=mock_get_caller_identity_china
    )
    def test_validate_credentials_china_partition(self):
        # Create a mock IAM user
        iam_client = client("iam", region_name=AWS_REGION_CN_NORTH_1)
        iam_user = iam_client.create_user(UserName="test-user")["User"]
        # Create a mock IAM access keys
        access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
            "AccessKey"
        ]
        access_key_id = access_key["AccessKeyId"]
        secret_access_key = access_key["SecretAccessKey"]

        # Create AWS session to validate
        current_session = session.Session(
            aws_access_key_id=access_key_id,
            aws_secret_access_key=secret_access_key,
            region_name=AWS_REGION_CN_NORTH_1,
        )

        # To use GovCloud or China it is either required:
        # - Set the AWS profile region with a valid partition region
        # - Use the -f/--region with a valid partition region
        get_caller_identity = AwsProvider.validate_credentials(
            session=current_session, aws_region=AWS_REGION_CN_NORTH_1
        )

        assert isinstance(get_caller_identity, AWSCallerIdentity)

        assert re.match("[0-9a-zA-Z]{20}", get_caller_identity.user_id)
        assert get_caller_identity.account == AWS_ACCOUNT_NUMBER
        assert get_caller_identity.region == AWS_REGION_CN_NORTH_1

        assert isinstance(get_caller_identity.arn, ARN)
        assert get_caller_identity.arn.partition == AWS_CHINA_PARTITION
        assert get_caller_identity.arn.region is None
        assert get_caller_identity.arn.resource == "test-user"
        assert get_caller_identity.arn.resource_type == "user"

    @mock_aws
    @patch(
        "botocore.client.BaseClient._make_api_call",
        new=mock_get_caller_identity_gov_cloud,
    )
    def test_validate_credentials_gov_cloud_partition(self):
        # Create a mock IAM user
        iam_client = client("iam", region_name=AWS_REGION_GOV_CLOUD_US_EAST_1)
        iam_user = iam_client.create_user(UserName="test-user")["User"]
        # Create a mock IAM access keys
        access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
            "AccessKey"
        ]
        access_key_id = access_key["AccessKeyId"]
        secret_access_key = access_key["SecretAccessKey"]

        # Create AWS session to validate
        current_session = session.Session(
            aws_access_key_id=access_key_id,
            aws_secret_access_key=secret_access_key,
            region_name=AWS_REGION_GOV_CLOUD_US_EAST_1,
        )

        # To use GovCloud or China it is either required:
        # - Set the AWS profile region with a valid partition region
        # - Use the -f/--region with a valid partition region
        get_caller_identity = AwsProvider.validate_credentials(
            session=current_session, aws_region=AWS_REGION_GOV_CLOUD_US_EAST_1
        )

        assert isinstance(get_caller_identity, AWSCallerIdentity)

        assert re.match("[0-9a-zA-Z]{20}", get_caller_identity.user_id)
        assert get_caller_identity.account == AWS_ACCOUNT_NUMBER
        assert get_caller_identity.region == AWS_REGION_GOV_CLOUD_US_EAST_1

        assert isinstance(get_caller_identity.arn, ARN)
        assert get_caller_identity.arn.partition == AWS_GOV_CLOUD_PARTITION
        assert get_caller_identity.arn.region is None
        assert get_caller_identity.arn.resource == "test-user"
        assert get_caller_identity.arn.resource_type == "user"

    @mock_aws
    def test_test_connection_with_env_credentials(self, monkeypatch):
        # Create a mock IAM user
        iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
        iam_user = iam_client.create_user(UserName="test-user")["User"]
        # Create a mock IAM access keys
        access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
            "AccessKey"
        ]

        monkeypatch.delenv("AWS_ACCESS_KEY_ID")
        monkeypatch.delenv("AWS_SECRET_ACCESS_KEY")
        monkeypatch.setenv("AWS_ACCESS_KEY_ID", access_key["AccessKeyId"])
        monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", access_key["SecretAccessKey"])
        connection = AwsProvider.test_connection()

        assert isinstance(connection, Connection)
        assert connection.is_connected
        assert connection.error is None

    def test_test_connection_without_credentials(self):
        with (
            mock.patch("boto3.Session.get_credentials", return_value=None),
            mock.patch("botocore.session.Session.get_scoped_config", return_value={}),
            mock.patch("botocore.credentials.EnvProvider.load", return_value=None),
            mock.patch(
                "botocore.credentials.SharedCredentialProvider.load", return_value=None
            ),
            mock.patch(
                "botocore.credentials.InstanceMetadataProvider.load", return_value=None
            ),
            mock.patch.dict(
                "os.environ",
                {
                    "AWS_ACCESS_KEY_ID": "",
                    "AWS_SECRET_ACCESS_KEY": "",
                    "AWS_SESSION_TOKEN": "",
                    "AWS_PROFILE": "",
                },
                clear=True,
            ),
        ):
            with raises(AWSNoCredentialsError) as exception:
                AwsProvider.test_connection(
                    profile=None
                )  # No profile to avoid ProfileNotFound error

            assert exception.type == AWSNoCredentialsError
            assert "AWSNoCredentialsError[1002]: No AWS credentials found" in str(
                exception.value
            )

    @mock_aws
    def test_test_connection_with_role_from_env(self, monkeypatch):
        # Create a mock IAM user
        iam_client = client("iam", region_name=AWS_REGION_US_EAST_1)
        iam_user = iam_client.create_user(UserName="test-user")["User"]
        # Create a mock IAM access keys
        access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
            "AccessKey"
        ]

        monkeypatch.setenv("AWS_ACCESS_KEY_ID", access_key["AccessKeyId"])
        monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", access_key["SecretAccessKey"])

        role_name = "test-role"
        role_arn = (
            f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
        )

        connection = AwsProvider.test_connection(
            role_arn=role_arn, role_session_name=ROLE_SESSION_NAME
        )

        assert isinstance(connection, Connection)
        assert connection.is_connected
        assert connection.error is None

    @mock_aws
    def test_test_connection_with_role_from_env_invalid_session_duration(self):
        role_name = "test-role"
        role_arn = (
            f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
        )
        with raises(AWSArgumentTypeValidationError) as exception:
            AwsProvider.test_connection(role_arn=role_arn, session_duration=899)

        assert exception.type == AWSArgumentTypeValidationError
        assert (
            exception.value.args[0]
            == "[1003] Session Duration must be between 900 and 43200 seconds."
        )

    @mock_aws
    def test_test_connection_with_role_from_env_invalid_session_duration_not_raise(
        self,
    ):
        role_name = "test-role"
        role_arn = (
            f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
        )
        connection = AwsProvider.test_connection(
            role_arn=role_arn, session_duration=899, raise_on_exception=False
        )

        assert isinstance(connection, Connection)
        assert not connection.is_connected
        assert isinstance(connection.error, AWSArgumentTypeValidationError)
        assert (
            connection.error.args[0]
            == "[1003] Session Duration must be between 900 and 43200 seconds."
        )

    @mock_aws
    def test_test_connection_with_role_from_env_invalid_session_name(self):
        role_name = "test-role"
        role_arn = (
            f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:role/{role_name}"
        )

        with raises(AWSArgumentTypeValidationError) as exception:
            AwsProvider.test_connection(role_arn=role_arn, role_session_name="???")

        assert exception.type == AWSArgumentTypeValidationError
        assert (
            exception.value.args[0]
            == "[1003] Role Session Name must be between 2 and 64 characters and may contain alphanumeric characters, periods, hyphens, and underscores."
        )

    @mock_aws
    def test_test_connection_with_role_from_env_invalid_role_arn(self):
        role_name = "test-role"
        role_arn = f"arn:{AWS_COMMERCIAL_PARTITION}:iam::{AWS_ACCOUNT_NUMBER}:not-role/{role_name}"

        with raises(AWSIAMRoleARNInvalidResourceTypeError) as exception:
            AwsProvider.test_connection(role_arn=role_arn)

        assert exception.type == AWSIAMRoleARNInvalidResourceTypeError
        assert (
            exception.value.args[0]
            == "[1010] AWS IAM Role ARN resource type is invalid"
        )

    @mock_aws
    def test_test_connection_with_static_credentials(self):
        # Create a mock IAM user
        iam_client = client("iam", region_name=AWS_REGION_EU_WEST_1)
        username = "test-user"
        iam_user = iam_client.create_user(UserName=username)["User"]
        # Create a mock IAM access keys
        access_key = iam_client.create_access_key(UserName=iam_user["UserName"])[
            "AccessKey"
        ]

        credentials = {
            "aws_access_key_id": access_key["AccessKeyId"],
            "aws_secret_access_key": access_key["SecretAccessKey"],
        }

        connection = AwsProvider.test_connection(**credentials)

        assert isinstance(connection, Connection)
        assert connection.is_connected
        assert connection.error is None

    @mock_aws
    def test_test_connection_with_session_credentials(self):
        sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
        session_token = sts_client.get_session_token()

        session_credentials = {
            "aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
            "aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
            "aws_session_token": session_token["Credentials"]["SessionToken"],
        }

        connection = AwsProvider.test_connection(**session_credentials)

        assert isinstance(connection, Connection)
        assert connection.is_connected
        assert connection.error is None

    @mock_aws
    def test_test_connection_with_own_account(self):
        sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
        session_token = sts_client.get_session_token()

        session_credentials = {
            "aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
            "aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
            "aws_session_token": session_token["Credentials"]["SessionToken"],
            "provider_id": AWS_ACCOUNT_NUMBER,
        }

        connection = AwsProvider.test_connection(**session_credentials)

        assert isinstance(connection, Connection)
        assert connection.is_connected
        assert connection.error is None

    @mock_aws
    def test_test_connection_with_different_account(self):
        sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
        session_token = sts_client.get_session_token()

        session_credentials = {
            "aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
            "aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
            "aws_session_token": session_token["Credentials"]["SessionToken"],
            "provider_id": "111122223333",
        }

        with raises(AWSInvalidProviderIdError) as exception:
            AwsProvider.test_connection(**session_credentials)

        assert exception.type == AWSInvalidProviderIdError
        assert (
            exception.value.args[0]
            == "[1015] The provided AWS credentials belong to a different account"
        )

    @mock_aws
    def test_test_connection_with_different_account_dont_raise(self):
        sts_client = client("sts", region_name=AWS_REGION_EU_WEST_1)
        session_token = sts_client.get_session_token()

        session_credentials = {
            "aws_access_key_id": session_token["Credentials"]["AccessKeyId"],
            "aws_secret_access_key": session_token["Credentials"]["SecretAccessKey"],
            "aws_session_token": session_token["Credentials"]["SessionToken"],
            "provider_id": "111122223333",
        }

        connection = AwsProvider.test_connection(
            **session_credentials, raise_on_exception=False
        )

        assert isinstance(connection, Connection)
        assert not connection.is_connected
        assert isinstance(connection.error, AWSInvalidProviderIdError)
        assert (
            connection.error.message
            == "The provided AWS credentials belong to a different account"
        )
        assert connection.error.code == 1015

    @mock_aws
    def test_test_connection_generic_exception(self):
        with patch(
            "prowler.providers.aws.aws_provider.AwsProvider.setup_session",
            side_effect=Exception(),
        ):
            connection = AwsProvider.test_connection(raise_on_exception=False)

        assert isinstance(connection, Connection)
        assert not connection.is_connected
        assert isinstance(connection.error, Exception)

    @mock_aws
    def test_create_sts_session(self):
        current_session = session.Session()
        aws_region = AWS_REGION_US_EAST_1
        sts_session = AwsProvider.create_sts_session(current_session, aws_region)

        assert sts_session._service_model.service_name == "sts"
        assert sts_session._client_config.region_name == aws_region
        assert sts_session._endpoint._endpoint_prefix == "sts"
        assert sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.com"

    @mock_aws
    def test_create_sts_session_gov_cloud(self):
        current_session = session.Session()
        aws_region = AWS_REGION_GOV_CLOUD_US_EAST_1
        sts_session = AwsProvider.create_sts_session(current_session, aws_region)

        assert sts_session._service_model.service_name == "sts"
        assert sts_session._client_config.region_name == aws_region
        assert sts_session._endpoint._endpoint_prefix == "sts"
        assert sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.com"

    @mock_aws
    def test_create_sts_session_china(self):
        current_session = session.Session()
        aws_region = AWS_REGION_CN_NORTH_1
        sts_session = AwsProvider.create_sts_session(current_session, aws_region)

        assert sts_session._service_model.service_name == "sts"
        assert sts_session._client_config.region_name == aws_region
        assert sts_session._endpoint._endpoint_prefix == "sts"
        assert (
            sts_session._endpoint.host == f"https://sts.{aws_region}.amazonaws.com.cn"
        )

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_elb_service,
    )
    def test_get_checks_from_input_arn_elb(self):
        expected_checks = [
            "elb_insecure_ssl_ciphers",
            "elb_internet_facing",
            "elb_logging_enabled",
        ]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:elasticloadbalancing:us-east-1:{AWS_ACCOUNT_NUMBER}:loadbalancer/test"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_efs_service,
    )
    def test_get_checks_from_input_arn_efs(self):
        expected_checks = [
            "efs_encryption_at_rest_enabled",
            "efs_have_backup_enabled",
            "efs_not_publicly_accessible",
        ]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:elasticfilesystem:us-east-1:{AWS_ACCOUNT_NUMBER}:file-system/fs-01234567"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_lambda_service,
    )
    def test_get_checks_from_input_arn_lambda(self):
        expected_checks = [
            "awslambda_function_invoke_api_operations_cloudtrail_logging_enabled",
            "awslambda_function_no_secrets_in_code",
            "awslambda_function_url_cors_policy",
        ]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            "arn:aws:lambda:us-east-1:123456789:function:test-lambda"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_iam_service,
    )
    def test_get_checks_from_input_arn_iam(self):
        expected_checks = [
            "iam_check_saml_providers_sts",
            "iam_customer_attached_policy_no_administrative_privileges",
            "iam_password_policy_minimum_length_14",
        ]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/user-name"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_s3_service,
    )
    def test_get_checks_from_input_arn_s3(self):
        expected_checks = [
            "s3_account_level_public_access_blocks",
            "s3_bucket_acl_prohibited",
            "s3_bucket_policy_public_write_access",
        ]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = ["arn:aws:s3:::bucket-name"]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_cloudwatch_service,
    )
    def test_get_checks_from_input_arn_cloudwatch(self):
        expected_checks = [
            "cloudwatch_changes_to_network_acls_alarm_configured",
            "cloudwatch_changes_to_network_gateways_alarm_configured",
            "cloudwatch_changes_to_network_route_tables_alarm_configured",
        ]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:logs:us-east-1:{AWS_ACCOUNT_NUMBER}:destination:testDestination"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_eks_service,
    )
    def test_get_checks_from_input_arn_eks(self):
        expected_checks = [
            "eks_cluster_not_publicly_accessible",
            "eks_cluster_uses_a_supported_version",
            "eks_cluster_network_policy_enabled",
            "eks_control_plane_logging_all_types_enabled",
        ]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:eks:us-east-1:{AWS_ACCOUNT_NUMBER}:cluster/test-eks"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert set(recovered_checks) == set(expected_checks)

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_cognito_service,
    )
    def test_get_checks_from_input_arn_cognito(self):
        expected_checks = []

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:cognito-idp:us-east-1:{AWS_ACCOUNT_NUMBER}:userpool/test"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_ec2_service,
    )
    def test_get_checks_from_input_arn_ec2_security_group(self):
        expected_checks = ["ec2_securitygroup_allow_ingress_from_internet_to_any_port"]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:ec2:us-east-1:{AWS_ACCOUNT_NUMBER}:security-group/sg-1111111111"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_ec2_service,
    )
    def test_get_checks_from_input_arn_ec2_acl(self):
        expected_checks = ["ec2_networkacl_allow_ingress_any_port"]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:ec2:us-west-2:{AWS_ACCOUNT_NUMBER}:network-acl/acl-1"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_rds_service,
    )
    def test_get_checks_from_input_arn_rds_snapshots(self):
        expected_checks = ["rds_snapshots_public_access"]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:rds:us-east-2:{AWS_ACCOUNT_NUMBER}:snapshot:rds:snapshot-1",
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_ec2_service,
    )
    def test_get_checks_from_input_arn_ec2_ami(self):
        expected_checks = ["ec2_ami_public"]

        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:ec2:us-west-2:{AWS_ACCOUNT_NUMBER}:image/ami-1"
        ]
        recovered_checks = aws_provider.get_checks_from_input_arn()

        assert recovered_checks == expected_checks

    @mock_aws
    def test_get_regions_from_audit_resources_with_regions(self):
        audit_resources = [
            f"arn:aws:lambda:us-east-1:{AWS_ACCOUNT_NUMBER}:function:test-lambda",
            f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:policy/test",
            f"arn:aws:ec2:eu-west-1:{AWS_ACCOUNT_NUMBER}:security-group/sg-test",
            "arn:aws:s3:::bucket-name",
            "arn:aws:apigateway:us-east-2::/restapis/api-id/stages/stage-name",
        ]
        expected_regions = {"us-east-1", "eu-west-1", "us-east-2"}

        aws_provider = AwsProvider()
        recovered_regions = aws_provider.get_regions_from_audit_resources(
            audit_resources
        )
        assert recovered_regions == expected_regions

    @mock_aws
    def test_get_regions_from_audit_resources_without_regions(self):
        audit_resources = ["arn:aws:s3:::bucket-name"]

        aws_provider = AwsProvider()
        recovered_regions = aws_provider.get_regions_from_audit_resources(
            audit_resources
        )
        assert not recovered_regions

    def test_get_regions_all_count(self):
        assert len(AwsProvider.get_regions(partition=None)) == 37

    def test_get_regions_cn_count(self):
        assert len(AwsProvider.get_regions("aws-cn")) == 2

    def test_get_regions_aws_count(self):
        assert len(AwsProvider.get_regions(partition="aws")) == 33

    def test_get_all_regions(self):
        with patch(
            "prowler.providers.aws.aws_provider.read_aws_regions_file",
            return_value={
                "services": {
                    "acm": {
                        "regions": {
                            "aws": [
                                "af-south-1",
                            ],
                            "aws-cn": [
                                "cn-north-1",
                            ],
                            "aws-us-gov": [
                                "us-gov-west-1",
                            ],
                        }
                    }
                }
            },
        ):
            assert AwsProvider.get_regions(partition=None) == {
                "af-south-1",
                "cn-north-1",
                "us-gov-west-1",
            }

    def test_get_regions_with_us_gov_partition(self):
        with patch(
            "prowler.providers.aws.aws_provider.read_aws_regions_file",
            return_value={
                "services": {
                    "acm": {
                        "regions": {
                            "aws": [
                                "af-south-1",
                            ],
                            "aws-cn": [
                                "cn-north-1",
                            ],
                            "aws-us-gov": [
                                "us-gov-west-1",
                            ],
                        }
                    }
                }
            },
        ):
            assert AwsProvider.get_regions("aws-us-gov") == {
                "us-gov-west-1",
            }

    def test_get_regions_with_aws_partition(self):
        with patch(
            "prowler.providers.aws.aws_provider.read_aws_regions_file",
            return_value={
                "services": {
                    "acm": {
                        "regions": {
                            "aws": [
                                "af-south-1",
                            ],
                            "aws-cn": [
                                "cn-north-1",
                            ],
                            "aws-us-gov": [
                                "us-gov-west-1",
                            ],
                        }
                    }
                }
            },
        ):
            assert AwsProvider.get_regions("aws") == {
                "af-south-1",
            }

    def test_get_regions_with_cn_partition(self):
        with patch(
            "prowler.providers.aws.aws_provider.read_aws_regions_file",
            return_value={
                "services": {
                    "acm": {
                        "regions": {
                            "aws": [
                                "af-south-1",
                            ],
                            "aws-cn": [
                                "cn-north-1",
                            ],
                            "aws-us-gov": [
                                "us-gov-west-1",
                            ],
                        }
                    }
                }
            },
        ):
            assert AwsProvider.get_regions("aws-cn") == {
                "cn-north-1",
            }

    def test_get_regions_with_unknown_partition(self):
        with patch(
            "prowler.providers.aws.aws_provider.read_aws_regions_file",
            return_value={
                "services": {
                    "acm": {
                        "regions": {
                            "aws": [
                                "af-south-1",
                            ],
                            "aws-cn": [
                                "cn-north-1",
                            ],
                            "aws-us-gov": [
                                "us-gov-west-1",
                            ],
                        }
                    }
                }
            },
        ):
            partition = "unknown"
            with pytest.raises(AWSInvalidPartitionError) as exception:
                AwsProvider.get_regions(partition)

            assert exception.type == AWSInvalidPartitionError
        assert f"Invalid partition: {partition}" in exception.value.args[0]

    def test_get_aws_region_for_sts_input_regions_none_session_region_none(self):
        input_regions = None
        session_region = None
        assert (
            get_aws_region_for_sts(session_region, input_regions)
            == AWS_STS_GLOBAL_ENDPOINT_REGION
        )

    def test_get_aws_region_for_sts_input_regions_none_session_region_ireland(self):
        input_regions = None
        session_region = AWS_REGION_EU_WEST_1
        assert (
            get_aws_region_for_sts(session_region, input_regions)
            == AWS_REGION_EU_WEST_1
        )

    def test_get_aws_region_for_sts_input_regions_empty_session_region_none(self):
        input_regions = set()
        session_region = None
        assert (
            get_aws_region_for_sts(session_region, input_regions)
            == AWS_STS_GLOBAL_ENDPOINT_REGION
        )

    def test_get_aws_region_for_sts_input_regions_empty_session_region_ireland(self):
        input_regions = set()
        session_region = AWS_REGION_EU_WEST_1
        assert (
            get_aws_region_for_sts(session_region, input_regions)
            == AWS_REGION_EU_WEST_1
        )

    def test_get_aws_region_for_sts_input_regions_ireland_and_virgninia(self):
        input_regions = [AWS_REGION_EU_WEST_1, AWS_REGION_US_EAST_1]
        session_region = None
        assert (
            get_aws_region_for_sts(session_region, input_regions)
            == AWS_REGION_EU_WEST_1
        )

    @mock_aws
    def test_set_session_config_default(self):
        aws_provider = AwsProvider()
        session_config = aws_provider.set_session_config(None)

        assert session_config.user_agent_extra == BOTO3_USER_AGENT_EXTRA
        assert session_config.retries == {"max_attempts": 3, "mode": "standard"}

    @mock_aws
    def test_set_session_config_10_max_attempts(self):
        aws_provider = AwsProvider()
        session_config = aws_provider.set_session_config(10)

        assert session_config.user_agent_extra == BOTO3_USER_AGENT_EXTRA
        assert session_config.retries == {"max_attempts": 10, "mode": "standard"}

    @mock_aws
    @patch(
        "prowler.lib.check.utils.recover_checks_from_provider",
        new=mock_recover_checks_from_aws_provider_ec2_service,
    )
    def test_get_checks_to_execute_by_audit_resources(self):
        aws_provider = AwsProvider()
        aws_provider._audit_resources = [
            f"arn:aws:ec2:us-west-2:{AWS_ACCOUNT_NUMBER}:network-acl/acl-1"
        ]
        aws_provider.get_checks_to_execute_by_audit_resources() == {
            "ec2_networkacl_allow_ingress_any_port"
        }

    def test_update_provider_config_aws(self):
        aws_provider = set_mocked_aws_provider(
            audit_config={"shodan_api_key": "DEFAULT-KEY"}
        )

        with patch(
            "prowler.providers.common.provider.Provider.get_global_provider",
            return_value=aws_provider,
        ):
            assert {
                "shodan_api_key": "TEST-API-KEY"
            } == Provider.update_provider_config(
                aws_provider.audit_config, "shodan_api_key", "TEST-API-KEY"
            )

    def test_update_provider_config_aws_not_present(self):
        aws_provider = set_mocked_aws_provider(
            audit_config={"shodan_api_key": "DEFAULT-KEY"}
        )

        with patch(
            "prowler.providers.common.provider.Provider.get_global_provider",
            return_value=aws_provider,
        ):
            assert {"shodan_api_key": "DEFAULT-KEY"} == Provider.update_provider_config(
                aws_provider.audit_config, "not_found", "not_value"
            )

    @mock_aws
    def test_refresh_credentials_before_expiration(self):
        role_arn = create_role(AWS_REGION_EU_WEST_1)
        session_duration = 900
        aws_provider = AwsProvider(role_arn=role_arn, session_duration=session_duration)

        current_credentials = (
            aws_provider._assumed_role_configuration.credentials.__dict__
        )
        refreshed_credentials = {
            "access_key": current_credentials["aws_access_key_id"],
            "secret_key": current_credentials["aws_secret_access_key"],
            "token": current_credentials["aws_session_token"],
            "expiry_time": current_credentials.get(
                "expiration", current_credentials.get("expiry_time")
            ).isoformat(),
        }

        assert (
            AwsProvider.refresh_credentials(
                aws_provider._assumed_role_configuration, aws_provider._session
            )
            == refreshed_credentials
        )

    @mock_aws
    def test_refresh_credentials_after_expiration(self):
        role_arn = create_role(AWS_REGION_EU_WEST_1)
        session_duration_in_seconds = 900
        session_duration = session_duration_in_seconds
        aws_provider = AwsProvider(role_arn=role_arn, session_duration=session_duration)

        # Manually expire credentials
        aws_provider._assumed_role_configuration.credentials.expiration = datetime.now(
            get_localzone()
        ) - timedelta(seconds=session_duration_in_seconds)

        current_credentials = aws_provider._assumed_role_configuration.credentials

        # Refresh credentials
        refreshed_credentials = AwsProvider.refresh_credentials(
            aws_provider._assumed_role_configuration, aws_provider._session
        )

        # Assert that the refreshed credentials are different
        access_key = refreshed_credentials.get("access_key")
        assert access_key != current_credentials.aws_access_key_id

        secret_key = refreshed_credentials.get("secret_key")
        assert secret_key != current_credentials.aws_secret_access_key

        session_token = refreshed_credentials.get("token")
        assert session_token != current_credentials.aws_session_token

        expiry_time = refreshed_credentials.get("expiry_time")
        expiry_time_formatted = datetime.fromisoformat(expiry_time)
        assert expiry_time != current_credentials.expiration
        assert datetime.now(get_localzone()) < expiry_time_formatted

        # Assert credentials format
        assert len(access_key) == 20
        assert search(r"^ASIA.*$", access_key)

        assert len(secret_key) == 40

        assert len(session_token) == 356
        assert search(r"^FQoGZXIvYXdzE.*$", session_token)
