from json import dumps
from uuid import uuid4

import botocore
from boto3 import client
from freezegun import freeze_time
from mock import patch
from moto import mock_aws

from prowler.providers.aws.services.iam.iam_service import IAM, is_service_role
from tests.providers.aws.utils import (
    AWS_ACCOUNT_NUMBER,
    AWS_REGION_US_EAST_1,
    set_mocked_aws_provider,
)

TEST_DATETIME = "2023-01-01T12:01:01+00:00"

INLINE_POLICY_NOT_ADMIN = {
    "Version": "2012-10-17",
    "Statement": [{"Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "*"}],
}

ASSUME_ROLE_POLICY_DOCUMENT = {
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "test",
        "Effect": "Allow",
        "Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"},
        "Action": "sts:AssumeRole",
    },
}

SECURITY_AUDIT_POLICY_ARN = "arn:aws:iam::aws:policy/SecurityAudit"
READ_ONLY_ACCESS_POLICY_ARN = "arn:aws:iam::aws:policy/ReadOnlyAccess"
SUPPORT_SERVICE_ROLE_POLICY_ARN = "arn:aws:iam::aws:policy/AWSSupportAccess"
ADMINISTRATOR_ACCESS_POLICY_ARN = "arn:aws:iam::aws:policy/AdministratorAccess"

# Mocking Access Analyzer Calls
make_api_call = botocore.client.BaseClient._make_api_call


IAM_LAST_ACCESSED_SERVICES = [
    {
        "ServiceName": "AWS EC2",
        "ServiceNamespace": "ec2",
        "TotalAuthenticatedEntities": 1,
    },
    {
        "ServiceName": "AWS Identity and Access Management",
        "ServiceNamespace": "iam",
        "TotalAuthenticatedEntities": 0,
    },
]


def mock_make_api_call(self, operation_name, kwargs):
    """
    As you can see the operation_name has the list_analyzers snake_case form but
    we are using the ListAnalyzers form.
    Rationale -> https://github.com/boto/botocore/blob/develop/botocore/client.py#L810:L816
    We have to mock every AWS API call using Boto3
    """
    if operation_name == "GenerateServiceLastAccessedDetails":
        return {"JobId": str(uuid4())}
    if operation_name == "GetServiceLastAccessedDetails":
        return {
            "JobStatus": "COMPLETED",
            "JobType": "SERVICE_LEVEL",
            "JobCreationDate": "2023-10-19T06:11:11.449000+00:00",
            "ServicesLastAccessed": IAM_LAST_ACCESSED_SERVICES,
        }

    return make_api_call(self, operation_name, kwargs)


# Patch every AWS call using Boto3
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
class Test_IAM_Service:
    # Test IAM Client
    @mock_aws
    def test_get_client(self):
        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert iam.client.__class__.__name__ == "IAM"

    # Test IAM Session
    @mock_aws
    def test__get_session__(self):
        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert iam.session.__class__.__name__ == "Session"

    # Test IAM Get Credential Report
    @freeze_time(TEST_DATETIME)
    @mock_aws
    def test_get_credential_report(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Create IAM User
        username = "user1"
        iam_client.create_user(
            UserName=username,
        )
        # Expected credential report
        expected_credential_report = {
            "user": username,
            "arn": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:user/{username}",
            "user_creation_time": TEST_DATETIME,
            "password_enabled": "false",
            "password_last_used": "not_supported",
            "password_last_changed": TEST_DATETIME,
            "password_next_rotation": "not_supported",
            "mfa_active": "false",
            "access_key_1_active": "false",
            "access_key_1_last_rotated": "N/A",
            "access_key_1_last_used_date": "N/A",
            "access_key_1_last_used_region": "not_supported",
            "access_key_1_last_used_service": "not_supported",
            "access_key_2_active": "false",
            "access_key_2_last_rotated": "N/A",
            "access_key_2_last_used_date": "N/A",
            "access_key_2_last_used_region": "not_supported",
            "access_key_2_last_used_service": "not_supported",
            "cert_1_active": "false",
            "cert_1_last_rotated": "N/A",
            "cert_2_active": "false",
            "cert_2_last_rotated": "N/A",
        }

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert len(iam.credential_report) == 1
        assert iam.credential_report[0].get("user")
        assert iam.credential_report[0]["user"] == expected_credential_report["user"]

        assert iam.credential_report[0].get("arn")
        assert iam.credential_report[0]["arn"] == expected_credential_report["arn"]

        assert iam.credential_report[0].get("user_creation_time")
        assert (
            iam.credential_report[0]["user_creation_time"]
            == expected_credential_report["user_creation_time"]
        )

        assert iam.credential_report[0].get("password_enabled")
        assert (
            iam.credential_report[0]["password_enabled"]
            == expected_credential_report["password_enabled"]
        )

        assert iam.credential_report[0].get("password_last_used")
        assert (
            iam.credential_report[0]["password_last_used"]
            == expected_credential_report["password_last_used"]
        )

        assert iam.credential_report[0].get("password_last_changed")
        assert (
            iam.credential_report[0]["password_last_changed"]
            == expected_credential_report["password_last_changed"]
        )

        assert iam.credential_report[0].get("password_next_rotation")
        assert (
            iam.credential_report[0]["password_next_rotation"]
            == expected_credential_report["password_next_rotation"]
        )

        assert iam.credential_report[0].get("mfa_active")
        assert (
            iam.credential_report[0]["mfa_active"]
            == expected_credential_report["mfa_active"]
        )

        assert iam.credential_report[0].get("access_key_1_active")
        assert (
            iam.credential_report[0]["access_key_1_active"]
            == expected_credential_report["access_key_1_active"]
        )

        assert iam.credential_report[0].get("access_key_1_last_rotated")
        assert (
            iam.credential_report[0]["access_key_1_last_rotated"]
            == expected_credential_report["access_key_1_last_rotated"]
        )

        assert iam.credential_report[0].get("access_key_1_last_used_date")
        assert (
            iam.credential_report[0]["access_key_1_last_used_date"]
            == expected_credential_report["access_key_1_last_used_date"]
        )

        assert iam.credential_report[0].get("access_key_1_last_used_region")
        assert (
            iam.credential_report[0]["access_key_1_last_used_region"]
            == expected_credential_report["access_key_1_last_used_region"]
        )

        assert iam.credential_report[0].get("access_key_1_last_used_service")
        assert (
            iam.credential_report[0]["access_key_1_last_used_service"]
            == expected_credential_report["access_key_1_last_used_service"]
        )

        assert iam.credential_report[0].get("access_key_2_active")
        assert (
            iam.credential_report[0]["access_key_2_active"]
            == expected_credential_report["access_key_2_active"]
        )

        assert iam.credential_report[0].get("access_key_2_last_rotated")
        assert (
            iam.credential_report[0]["access_key_2_last_rotated"]
            == expected_credential_report["access_key_2_last_rotated"]
        )

        assert iam.credential_report[0].get("access_key_2_last_used_date")
        assert (
            iam.credential_report[0]["access_key_2_last_used_date"]
            == expected_credential_report["access_key_2_last_used_date"]
        )

        assert iam.credential_report[0].get("access_key_2_last_used_region")
        assert (
            iam.credential_report[0]["access_key_2_last_used_region"]
            == expected_credential_report["access_key_2_last_used_region"]
        )

        assert iam.credential_report[0].get("access_key_2_last_used_service")
        assert (
            iam.credential_report[0]["access_key_2_last_used_service"]
            == expected_credential_report["access_key_2_last_used_service"]
        )

        assert iam.credential_report[0].get("cert_1_active")
        assert (
            iam.credential_report[0]["cert_1_active"]
            == expected_credential_report["cert_1_active"]
        )

        assert iam.credential_report[0].get("cert_1_last_rotated")
        assert (
            iam.credential_report[0]["cert_1_last_rotated"]
            == expected_credential_report["cert_1_last_rotated"]
        )

        assert iam.credential_report[0].get("cert_2_active")
        assert (
            iam.credential_report[0]["cert_2_active"]
            == expected_credential_report["cert_2_active"]
        )

        assert iam.credential_report[0].get("cert_2_last_rotated")
        assert (
            iam.credential_report[0]["cert_2_last_rotated"]
            == expected_credential_report["cert_2_last_rotated"]
        )

    # Test IAM Get Roles
    @mock_aws
    def test_get_roles(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Create 2 IAM Roles
        service_policy_document = {
            "Version": "2008-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {"Service": "ec2.amazonaws.com"},
                    "Action": "sts:AssumeRole",
                }
            ],
        }
        policy_document = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {"AWS": f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:root"},
                    "Action": "sts:AssumeRole",
                }
            ],
        }
        # Hybrid role - assumable by both service and AWS account
        hybrid_policy_document = {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {"Service": "cloudformation.amazonaws.com"},
                    "Action": "sts:AssumeRole",
                },
                {
                    "Effect": "Allow",
                    "Principal": {"AWS": "arn:aws:iam::123456789012:root"},
                    "Action": "sts:AssumeRole",
                },
            ],
        }
        service_role = iam_client.create_role(
            RoleName="test-1",
            AssumeRolePolicyDocument=dumps(service_policy_document),
            Tags=[
                {"Key": "test", "Value": "test"},
            ],
        )["Role"]
        role = iam_client.create_role(
            RoleName="test-2",
            AssumeRolePolicyDocument=dumps(policy_document),
            Tags=[
                {"Key": "test", "Value": "test"},
            ],
        )["Role"]
        hybrid_role = iam_client.create_role(
            RoleName="test-3",
            AssumeRolePolicyDocument=dumps(hybrid_policy_document),
            Tags=[
                {"Key": "test", "Value": "test"},
            ],
        )["Role"]

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.roles) == len(iam_client.list_roles()["Roles"])
        assert iam.roles[0].tags == [
            {"Key": "test", "Value": "test"},
        ]
        assert iam.roles[1].tags == [
            {"Key": "test", "Value": "test"},
        ]
        assert is_service_role(service_role)
        assert not is_service_role(role)
        # Hybrid role should return False even though it has a service principal
        assert not is_service_role(hybrid_role)

    # Test IAM Get Groups
    @mock_aws
    def test_get_groups(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Create 2 IAM Groups
        iam_client.create_group(
            GroupName="group1",
        )
        iam_client.create_group(
            GroupName="group2",
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert len(iam.groups) == len(iam_client.list_groups()["Groups"])

    # Test IAM Get Users
    @mock_aws
    def test_get_users(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Create 2 IAM Users
        iam_client.create_user(
            UserName="user1",
            Tags=[
                {"Key": "test", "Value": "test"},
            ],
        )
        iam_client.create_user(
            UserName="user2",
            Tags=[
                {"Key": "test", "Value": "test"},
            ],
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert len(iam.users) == len(iam_client.list_users()["Users"])
        assert iam.users[0].tags == [
            {"Key": "test", "Value": "test"},
        ]
        assert iam.users[1].tags == [
            {"Key": "test", "Value": "test"},
        ]

    # Test IAM Get Account Summary
    @mock_aws
    def test_get_account_summary(self):
        # Generate IAM Client
        iam_client = client("iam")
        account_summary = iam_client.get_account_summary()["SummaryMap"]

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert iam.account_summary["SummaryMap"] == account_summary

    # Test IAM Get Password Policy
    @mock_aws
    def test_get_password_policy(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Update Password Policy
        min_password_length = 123
        require_symbols = False
        require_numbers = True
        require_upper = True
        require_lower = False
        allow_users_to_change = True
        max_password_age = 123
        password_reuse_prevention = 24
        hard_expiry = True

        iam_client.update_account_password_policy(
            MinimumPasswordLength=min_password_length,
            RequireSymbols=require_symbols,
            RequireNumbers=require_numbers,
            RequireUppercaseCharacters=require_upper,
            RequireLowercaseCharacters=require_lower,
            AllowUsersToChangePassword=allow_users_to_change,
            MaxPasswordAge=max_password_age,
            PasswordReusePrevention=password_reuse_prevention,
            HardExpiry=hard_expiry,
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert iam.password_policy.length == min_password_length
        assert iam.password_policy.symbols == require_symbols
        assert iam.password_policy.numbers == require_numbers
        assert iam.password_policy.uppercase == require_upper
        assert iam.password_policy.lowercase == require_lower
        assert iam.password_policy.allow_change == allow_users_to_change
        assert iam.password_policy.expiration is True
        assert iam.password_policy.max_age == max_password_age
        assert iam.password_policy.reuse_prevention == password_reuse_prevention
        assert iam.password_policy.hard_expiry == hard_expiry

    # Test IAM List MFA Device
    @mock_aws
    def test__list_mfa_devices_arn__(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Generate IAM user
        iam_client.create_user(
            UserName="user1",
        )
        # Create virtual MFA device
        mfa_device_name = "test-mfa-device"
        virtual_mfa_device = iam_client.create_virtual_mfa_device(
            VirtualMFADeviceName=mfa_device_name,
        )
        iam_client.enable_mfa_device(
            UserName="user1",
            SerialNumber=virtual_mfa_device["VirtualMFADevice"]["SerialNumber"],
            AuthenticationCode1="123456",
            AuthenticationCode2="123456",
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.users) == 1
        assert len(iam.users[0].mfa_devices) == 1
        assert (
            iam.users[0].mfa_devices[0].serial_number
            == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa/{mfa_device_name}"
        )
        assert iam.users[0].mfa_devices[0].type == "mfa"

    # Test IAM List MFA Device
    @mock_aws
    def test__list_mfa_devices_number__(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Generate IAM user
        iam_client.create_user(
            UserName="user1",
        )
        # Create Unknown MFA device
        hardware_mfa_devide = "XXXXXXXXX"
        iam_client.enable_mfa_device(
            UserName="user1",
            SerialNumber=hardware_mfa_devide,
            AuthenticationCode1="123456",
            AuthenticationCode2="123456",
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.users) == 1
        assert len(iam.users[0].mfa_devices) == 1
        assert iam.users[0].mfa_devices[0].serial_number == hardware_mfa_devide
        assert iam.users[0].mfa_devices[0].type == "hardware"

    # Test IAM List Virtual MFA Device
    @mock_aws
    def test_list_virtual_mfa_devices(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Generate IAM user
        username = "user1"
        iam_client.create_user(
            UserName=username,
        )
        # Create virtual MFA device
        mfa_device_name = "test-mfa-device"
        virtual_mfa_device = iam_client.create_virtual_mfa_device(
            VirtualMFADeviceName=mfa_device_name,
        )
        iam_client.enable_mfa_device(
            UserName=username,
            SerialNumber=virtual_mfa_device["VirtualMFADevice"]["SerialNumber"],
            AuthenticationCode1="123456",
            AuthenticationCode2="123456",
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.virtual_mfa_devices) == 1
        assert (
            iam.virtual_mfa_devices[0]["SerialNumber"]
            == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:mfa/{mfa_device_name}"
        )
        assert iam.virtual_mfa_devices[0]["User"]["UserName"] == username

    # Test IAM Get Group Users
    @mock_aws
    def test_get_group_users(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Generate IAM user
        username = "user1"
        iam_client.create_user(
            UserName=username,
        )
        # Generate IAM group
        group = "test-group"
        iam_client.create_group(GroupName=group)
        # Add user to group
        iam_client.add_user_to_group(GroupName=group, UserName=username)

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.groups) == 1
        assert iam.groups[0].name == group
        assert iam.groups[0].arn == f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:group/{group}"
        assert len(iam.groups[0].users) == 1
        assert iam.groups[0].users[0].name == username

    # Test IAM List Attached Group Policies
    @mock_aws
    def test_list_attached_group_policies(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Generate IAM user
        username = "user1"
        iam_client.create_user(
            UserName=username,
        )
        # Generate IAM group
        group = "test-group"
        iam_client.create_group(GroupName=group)

        # Create a new IAM Policy
        policy_document = """
{
  "Version": "2012-10-17",
  "Statement":
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::example_bucket"
    }
}
"""
        policy_name = "policy1"
        policy = iam_client.create_policy(
            PolicyName=policy_name,
            PolicyDocument=policy_document,
        )

        # Attach group policy
        iam_client.attach_group_policy(
            GroupName=group, PolicyArn=policy["Policy"]["Arn"]
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.groups) == 1
        assert iam.groups[0].name == group
        assert len(iam.groups[0].attached_policies) == 1
        assert iam.groups[0].attached_policies[0]["PolicyName"] == policy_name
        assert (
            iam.groups[0].attached_policies[0]["PolicyArn"] == policy["Policy"]["Arn"]
        )

    # Test IAM List Attached Role Policies
    @mock_aws(config={"iam": {"load_aws_managed_policies": True}})
    def test_list_attached_role_policies(self):
        iam = client("iam")
        role_name = "test"
        assume_role_policy_document = {
            "Version": "2012-10-17",
            "Statement": {
                "Sid": "test",
                "Effect": "Allow",
                "Principal": {"AWS": "*"},
                "Action": "sts:AssumeRole",
            },
        }
        response = iam.create_role(
            RoleName=role_name,
            AssumeRolePolicyDocument=dumps(assume_role_policy_document),
        )
        iam.attach_role_policy(
            RoleName=role_name,
            PolicyArn=READ_ONLY_ACCESS_POLICY_ARN,
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.roles) == 1
        assert iam.roles[0].name == role_name
        assert iam.roles[0].arn == response["Role"]["Arn"]
        assert len(iam.roles[0].attached_policies) == 1
        assert iam.roles[0].attached_policies[0]["PolicyName"] == "ReadOnlyAccess"
        assert (
            iam.roles[0].attached_policies[0]["PolicyArn"]
            == READ_ONLY_ACCESS_POLICY_ARN
        )

    @mock_aws(config={"iam": {"load_aws_managed_policies": True}})
    def test__get_entities_attached_to_support_roles__no_roles(self):
        iam_client = client("iam")
        _ = iam_client.list_entities_for_policy(
            PolicyArn=SUPPORT_SERVICE_ROLE_POLICY_ARN,
            EntityFilter="Role",
        )["PolicyRoles"]

        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert len(iam.entities_role_attached_to_support_policy) == 0

    @mock_aws(config={"iam": {"load_aws_managed_policies": True}})
    def test__get_entities_attached_to_support_roles__(self):
        iam_client = client("iam")
        role_name = "test_support"
        assume_role_policy_document = {
            "Version": "2012-10-17",
            "Statement": {
                "Sid": "test",
                "Effect": "Allow",
                "Principal": {"AWS": "*"},
                "Action": "sts:AssumeRole",
            },
        }
        iam_client.create_role(
            RoleName=role_name,
            AssumeRolePolicyDocument=dumps(assume_role_policy_document),
        )
        iam_client.attach_role_policy(
            RoleName=role_name,
            PolicyArn=SUPPORT_SERVICE_ROLE_POLICY_ARN,
        )

        iam_client.list_entities_for_policy(
            PolicyArn=SUPPORT_SERVICE_ROLE_POLICY_ARN,
            EntityFilter="Role",
        )["PolicyRoles"]

        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert len(iam.entities_role_attached_to_support_policy) == 1
        assert iam.entities_role_attached_to_support_policy[0]["RoleName"] == role_name

    @mock_aws
    def test__get_entities_attached_to_securityaudit_roles__no_roles(self):
        iam_client = client("iam")
        _ = iam_client.list_entities_for_policy(
            PolicyArn=SECURITY_AUDIT_POLICY_ARN,
            EntityFilter="Role",
        )["PolicyRoles"]

        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert len(iam.entities_role_attached_to_securityaudit_policy) == 0

    @mock_aws(config={"iam": {"load_aws_managed_policies": True}})
    def test__get_entities_attached_to_securityaudit_roles__(self):
        iam_client = client("iam")
        role_name = "test_securityaudit"
        assume_role_policy_document = {
            "Version": "2012-10-17",
            "Statement": {
                "Sid": "test",
                "Effect": "Allow",
                "Principal": {"AWS": "*"},
                "Action": "sts:AssumeRole",
            },
        }
        iam_client.create_role(
            RoleName=role_name,
            AssumeRolePolicyDocument=dumps(assume_role_policy_document),
        )
        iam_client.attach_role_policy(
            RoleName=role_name,
            PolicyArn=SECURITY_AUDIT_POLICY_ARN,
        )

        iam_client.list_entities_for_policy(
            PolicyArn=SECURITY_AUDIT_POLICY_ARN,
            EntityFilter="Role",
        )["PolicyRoles"]

        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert len(iam.entities_role_attached_to_securityaudit_policy) == 1
        assert (
            iam.entities_role_attached_to_securityaudit_policy[0]["RoleName"]
            == role_name
        )

    @mock_aws
    def test__list_policies(self):
        iam_client = client("iam")
        policy_name = "policy1"
        policy_document = {
            "Version": "2012-10-17",
            "Statement": [
                {"Effect": "Allow", "Action": "logs:CreateLogGroup", "Resource": "*"},
            ],
        }
        iam_client.create_policy(
            PolicyName=policy_name,
            PolicyDocument=dumps(policy_document),
            Tags=[
                {"Key": "string", "Value": "string"},
            ],
        )
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        custom_policies = 0
        for policy in iam.policies.values():
            if policy.type == "Custom":
                custom_policies += 1
                assert policy.name == "policy1"
                assert policy.tags == [
                    {"Key": "string", "Value": "string"},
                ]
        assert custom_policies == 1

    @mock_aws
    def test_list_policies_version(self):
        iam_client = client("iam")
        policy_name = "policy2"
        policy_document = {
            "Version": "2012-10-17",
            "Statement": [
                {"Effect": "Allow", "Action": "*", "Resource": "*"},
            ],
        }
        iam_client.create_policy(
            PolicyName=policy_name, PolicyDocument=dumps(policy_document)
        )
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        custom_policies = 0
        for policy in iam.policies.values():
            if policy.type == "Custom":
                custom_policies += 1
                assert policy.name == "policy2"
                assert policy.document["Statement"][0]["Effect"] == "Allow"
                assert policy.document["Statement"][0]["Action"] == "*"
                assert policy.document["Statement"][0]["Resource"] == "*"
        assert custom_policies == 1

    # Test IAM List SAML Providers
    @mock_aws
    def test_list_saml_providers(self):
        iam_client = client("iam")
        xml_template = r"""<EntityDescriptor
    xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
    entityID="loadbalancer-9.siroe.com">
    <SPSSODescriptor
        AuthnRequestsSigned="false"
        WantAssertionsSigned="false"
        protocolSupportEnumeration=
            "urn:oasis:names:tc:SAML:2.0:protocol">
        <KeyDescriptor use="signing">
            <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
                <X509Data>
                    <X509Certificate>
MIICYDCCAgqgAwIBAgICBoowDQYJKoZIhvcNAQEEBQAwgZIxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
EwpDYWxpZm9ybmlhMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEeMBwGA1UEChMVU3VuIE1pY3Jvc3lz
dGVtcyBJbmMuMRowGAYDVQQLExFJZGVudGl0eSBTZXJ2aWNlczEcMBoGA1UEAxMTQ2VydGlmaWNh
dGUgTWFuYWdlcjAeFw0wNjExMDIxOTExMzRaFw0xMDA3MjkxOTExMzRaMDcxEjAQBgNVBAoTCXNp
cm9lLmNvbTEhMB8GA1UEAxMYbG9hZGJhbGFuY2VyLTkuc2lyb2UuY29tMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQCjOwa5qoaUuVnknqf5pdgAJSEoWlvx/jnUYbkSDpXLzraEiy2UhvwpoBgB
EeTSUaPPBvboCItchakPI6Z/aFdH3Wmjuij9XD8r1C+q//7sUO0IGn0ORycddHhoo0aSdnnxGf9V
tREaqKm9dJ7Yn7kQHjo2eryMgYxtr/Z5Il5F+wIDAQABo2AwXjARBglghkgBhvhCAQEEBAMCBkAw
DgYDVR0PAQH/BAQDAgTwMB8GA1UdIwQYMBaAFDugITflTCfsWyNLTXDl7cMDUKuuMBgGA1UdEQQR
MA+BDW1hbGxhQHN1bi5jb20wDQYJKoZIhvcNAQEEBQADQQB/6DOB6sRqCZu2OenM9eQR0gube85e
nTTxU4a7x1naFxzYXK1iQ1vMARKMjDb19QEJIEJKZlDK4uS7yMlf1nFS
                    </X509Certificate>
                </X509Data>
            </KeyInfo>
        </KeyDescriptor>
</EntityDescriptor>"""
        saml_provider_name = "test"
        saml_arn = iam_client.create_saml_provider(
            SAMLMetadataDocument=xml_template, Name=saml_provider_name
        )["SAMLProviderArn"]

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.saml_providers) == 1
        assert saml_arn in iam.saml_providers
        assert iam.saml_providers[saml_arn].name == saml_provider_name
        assert iam.saml_providers[saml_arn].arn == saml_arn

    # Test IAM User Inline Policy
    @mock_aws
    def test_list_inline_user_policies(self):
        # IAM Client
        iam_client = client("iam")
        # Create IAM User
        user_name = "test_user"
        user_arn = iam_client.create_user(UserName=user_name)["User"]["Arn"]

        # Put User Policy
        policy_name = "test_not_admin_inline_policy"
        _ = iam_client.put_user_policy(
            UserName=user_name,
            PolicyName=policy_name,
            PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.users) == 1
        assert iam.users[0].name == user_name
        assert iam.users[0].arn == user_arn
        assert iam.users[0].mfa_devices == []
        assert iam.users[0].password_last_used is None
        assert iam.users[0].attached_policies == []
        assert iam.users[0].inline_policies == [policy_name]
        assert iam.users[0].tags == []

        policy = iam.policies[f"{user_arn}:policy/{policy_name}"]
        assert policy.name == policy_name
        assert policy.arn == user_arn
        assert policy.version_id == "v1"
        assert policy.type == "Inline"
        assert policy.attached
        assert policy.document == INLINE_POLICY_NOT_ADMIN
        assert policy.entity == user_name

    # Test IAM Group Inline Policy
    @mock_aws
    def test_list_inline_group_policies(self):
        # IAM Client
        iam_client = client("iam")
        # Create IAM Group
        group_name = "test_group"
        group_arn = iam_client.create_group(GroupName=group_name)["Group"]["Arn"]

        # Put User Policy
        policy_name = "test_not_admin_inline_policy"
        _ = iam_client.put_group_policy(
            GroupName=group_name,
            PolicyName=policy_name,
            PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
        )

        iam_client.delete_policy
        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.groups) == 1
        assert iam.groups[0].name == group_name
        assert iam.groups[0].arn == group_arn
        assert iam.groups[0].attached_policies == []
        assert iam.groups[0].inline_policies == [policy_name]
        assert iam.groups[0].users == []

        policy = iam.policies[f"{group_arn}:policy/{policy_name}"]
        assert policy.name == policy_name
        assert policy.arn == group_arn
        assert policy.version_id == "v1"
        assert policy.type == "Inline"
        assert policy.attached
        assert policy.document == INLINE_POLICY_NOT_ADMIN
        assert policy.entity == group_name

    # Test IAM Role Inline Policy
    @mock_aws
    def test_list_inline_role_policies(self):
        # IAM Client
        iam_client = client("iam")
        # Create IAM Role
        role_name = "test_role"
        role_arn = iam_client.create_role(
            RoleName=role_name,
            AssumeRolePolicyDocument=dumps(ASSUME_ROLE_POLICY_DOCUMENT),
        )["Role"]["Arn"]

        # Put User Policy
        policy_name = "test_not_admin_inline_policy"
        _ = iam_client.put_role_policy(
            RoleName=role_name,
            PolicyName=policy_name,
            PolicyDocument=dumps(INLINE_POLICY_NOT_ADMIN),
        )

        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.roles) == 1
        assert iam.roles[0].name == role_name
        assert iam.roles[0].arn == role_arn
        assert iam.roles[0].assume_role_policy == ASSUME_ROLE_POLICY_DOCUMENT
        assert not iam.roles[0].is_service_role
        assert iam.roles[0].attached_policies == []
        assert iam.roles[0].inline_policies == [policy_name]
        assert iam.roles[0].tags == []

        policy = iam.policies[f"{role_arn}:policy/{policy_name}"]
        assert policy.name == policy_name
        assert policy.arn == role_arn
        assert policy.version_id == "v1"
        assert policy.type == "Inline"
        assert policy.attached
        assert policy.document == INLINE_POLICY_NOT_ADMIN
        assert policy.entity == role_name

    # Test IAM List Attached Group Policies
    @mock_aws
    def test_get_user_temporary_credentials_usage(self):
        # Generate IAM Client
        iam_client = client("iam")
        # Generate IAM user
        username = "test-user"
        user = iam_client.create_user(
            UserName=username,
        )
        user_arn = user["User"]["Arn"]
        # Create Access Key
        access_key = iam_client.create_access_key(UserName="test-user")
        access_key_id = access_key["AccessKey"]["AccessKeyId"]
        # IAM client for this test class
        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)

        assert len(iam.users) == 1

        assert len(iam.access_keys_metadata) == 1
        assert iam.access_keys_metadata[(username, user_arn)]

        assert iam.access_keys_metadata[(username, user_arn)][0]["UserName"] == username
        assert (
            iam.access_keys_metadata[(username, user_arn)][0]["AccessKeyId"]
            == access_key_id
        )
        assert iam.access_keys_metadata[(username, user_arn)][0]["Status"] == "Active"
        assert iam.access_keys_metadata[(username, user_arn)][0]["CreateDate"]

        assert (
            iam.last_accessed_services[(username, user_arn)]
            == IAM_LAST_ACCESSED_SERVICES
        )

        assert iam.user_temporary_credentials_usage[(username, user_arn)]

    @mock_aws(config={"iam": {"load_aws_managed_policies": True}})
    def test_list_entities_attached_to_cloudshell_policy(self):
        iam_client = client("iam")
        user_name = "test_cloudshell_policy_user"
        iam_client.create_user(
            UserName=user_name,
        )
        iam_client.attach_user_policy(
            UserName=user_name,
            PolicyArn="arn:aws:iam::aws:policy/AWSCloudShellFullAccess",
        )
        group_name = "test_cloudshell_policy_group"
        iam_client.create_group(
            GroupName=group_name,
        )
        iam_client.attach_group_policy(
            GroupName=group_name,
            PolicyArn="arn:aws:iam::aws:policy/AWSCloudShellFullAccess",
        )
        role_name = "test_cloudshell_policy_role"
        role_policy = {
            "Version": "2012-10-17",
        }
        iam_client.create_role(
            RoleName=role_name,
            AssumeRolePolicyDocument=dumps(role_policy),
        )
        iam_client.attach_role_policy(
            RoleName=role_name,
            PolicyArn="arn:aws:iam::aws:policy/AWSCloudShellFullAccess",
        )

        aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
        iam = IAM(aws_provider)
        assert len(iam.entities_attached_to_cloudshell_policy) == 3
        assert iam.entities_attached_to_cloudshell_policy["Users"] == [user_name]
        assert iam.entities_attached_to_cloudshell_policy["Groups"] == [group_name]
        assert iam.entities_attached_to_cloudshell_policy["Roles"] == [role_name]
