Ejemplo n.º 1
0
    def list_from_aws(cls: Type["IAMRoleResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'role_1_arn': {role_1_dict},
             'role_2_arn': {role_2_dict},
             ...}

        Where the dicts represent results from list_roles and additional info per role from
        list_targets_by_role."""
        roles = {}
        paginator = client.get_paginator("list_roles")
        for resp in paginator.paginate():
            for role in resp.get("Roles", []):
                role_name = role["RoleName"]
                assume_role_policy_document = copy.deepcopy(
                    role["AssumeRolePolicyDocument"])
                assume_role_policy_document_text = policy_doc_dict_to_sorted_str(
                    assume_role_policy_document)
                role[
                    "AssumeRolePolicyDocumentText"] = assume_role_policy_document_text
                for statement in assume_role_policy_document.get(
                        "Statement", []):
                    for obj in statement.get("Condition", {}).values():
                        for obj_key in obj.keys():
                            if obj_key.lower() == "sts:externalid":
                                obj[obj_key] = "REMOVED"
                policies_result = get_attached_role_policies(client, role_name)
                policies = policies_result
                role["PolicyAttachments"] = policies
                resource_arn = role["Arn"]
                roles[resource_arn] = role
        return ListFromAWSResult(resources=roles)
Ejemplo n.º 2
0
    def list_from_aws(cls: Type["IAMPolicyResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'role_1_arn': {role_1_dict},
             'role_2_arn': {role_2_dict},
             ...}

        Where the dicts represent results from list_policies and additional info per role from
        list_targets_by_role."""
        policies = {}
        paginator = client.get_paginator("list_policies")

        for resp in paginator.paginate(Scope="Local"):
            for policy in resp.get("Policies", []):
                resource_arn = policy["Arn"]
                default_policy_version = policy["DefaultVersionId"]
                policy_version_resp = client.get_policy_version(
                    PolicyArn=resource_arn, VersionId=default_policy_version)
                default_policy_version_document_text = policy_version_resp[
                    "PolicyVersion"]["Document"]
                policy[
                    "DefaultVersionPolicyDocumentText"] = policy_doc_dict_to_sorted_str(
                        default_policy_version_document_text)
                policies[resource_arn] = policy
        return ListFromAWSResult(resources=policies)
    def list_from_aws(cls: Type["IAMPolicyResourceSpec"], client: BaseClient,
                      account_id: str, region: str) -> ListFromAWSResult:
        """Return a dict of dicts of the format:

            {'role_1_arn': {role_1_dict},
             'role_2_arn': {role_2_dict},
             ...}

        Where the dicts represent results from list_policies and additional info per role from
        list_targets_by_role."""
        policies = {}
        paginator = client.get_paginator("list_policies")

        for resp in paginator.paginate(Scope="Local"):
            for policy in resp.get("Policies", []):
                resource_arn = policy["Arn"]
                default_policy_version = policy["DefaultVersionId"]
                try:
                    default_policy_version_document_text = cls.get_policy_version_document_text(
                        client=client,
                        policy_arn=resource_arn,
                        policy_version=default_policy_version,
                    )
                    policy[
                        "DefaultVersionPolicyDocumentText"] = policy_doc_dict_to_sorted_str(
                            default_policy_version_document_text)
                    policies[resource_arn] = policy
                except ClientError as c_e:
                    error_code = getattr(c_e, "response",
                                         {}).get("Error", {}).get("Code", {})
                    if error_code != "NoSuchEntity":
                        raise c_e

        return ListFromAWSResult(resources=policies)
Ejemplo n.º 4
0
def compare_embedded_policy(source_policy, expected_policy_name,
                            expected_policy_document):
    if source_policy.pred != "embedded_policy":
        return False
    if len(source_policy.obj.simple_links) != 2:
        return False
    embedded_policy = source_policy.obj.simple_links[0]
    if embedded_policy.pred != "policy_name":
        return False
    if embedded_policy.obj != expected_policy_name:
        return False
    embedded_policy_document = source_policy.obj.simple_links[1]
    if embedded_policy_document.pred != "policy_document":
        return False
    got_policy_document = policy_doc_dict_to_sorted_str(
        json.loads(embedded_policy_document.obj))
    expected_policy_document = policy_doc_dict_to_sorted_str(
        json.loads(expected_policy_document))
    return got_policy_document == expected_policy_document
Ejemplo n.º 5
0
def get_embedded_role_policy(
    client: BaseClient, role_name: str, policy_name: str
) -> Dict[str, str]:
    """Get attached embedded policies"""
    resp = client.get_role_policy(RoleName=role_name, PolicyName=policy_name)
    policy_document = resp.get("PolicyDocument")
    policy_name = resp.get("PolicyName")
    policy_document = policy_doc_dict_to_sorted_str(policy_document)
    return {
        "PolicyName": policy_name,
        "PolicyDocument": policy_document,
    }
Ejemplo n.º 6
0
    def test(self):
        with tempfile.TemporaryDirectory() as temp_dir:
            resource_region_name = "us-east-1"
            # get moto"s enabled regions
            ec2_client = boto3.client("ec2", region_name=resource_region_name)
            all_regions = ec2_client.describe_regions(
                Filters=[{
                    "Name": "opt-in-status",
                    "Values": ["opt-in-not-required", "opted-in"]
                }])["Regions"]
            account_id = get_account_id()
            all_region_names = tuple(region["RegionName"]
                                     for region in all_regions)
            enabled_region_names = tuple(
                region["RegionName"] for region in all_regions
                if region["OptInStatus"] != "not-opted-in")
            delete_vpcs(all_region_names)
            # add a diverse set of resources which are supported by moto
            ## dynamodb
            # TODO moto is not returning TableId in list/describe
            #            dynamodb_table_1_arn = create_dynamodb_table(
            #                name="test_table_1",
            #                attr_name="test_hash_key_attr_1",
            #                attr_type="S",
            #                key_type="HASH",
            #                region_name=region_name,
            #            )
            ## s3
            bucket_1_name = "test_bucket"
            bucket_1_arn, bucket_1_creation_date = create_bucket(
                name=bucket_1_name,
                account_id=account_id,
                region_name=resource_region_name)
            ## ec2
            vpc_1_cidr = "10.0.0.0/16"
            vpc_1_id = create_vpc(cidr_block=vpc_1_cidr,
                                  region_name=resource_region_name)
            vpc_1_arn = VPCResourceSpec.generate_arn(
                resource_id=vpc_1_id,
                account_id=account_id,
                region=resource_region_name)
            subnet_1_cidr = "10.0.0.0/24"
            subnet_1_cidr_network = ipaddress.IPv4Network(subnet_1_cidr,
                                                          strict=False)
            subnet_1_first_ip, subnet_1_last_ip = (
                int(subnet_1_cidr_network[0]),
                int(subnet_1_cidr_network[-1]),
            )
            subnet_1_id = create_subnet(cidr_block=subnet_1_cidr,
                                        vpc_id=vpc_1_id,
                                        region_name=resource_region_name)
            subnet_1_arn = SubnetResourceSpec.generate_arn(
                resource_id=subnet_1_id,
                account_id=account_id,
                region=resource_region_name)
            fixed_bucket_1_arn = f"arn:aws:s3:::{bucket_1_name}"
            flow_log_1_id, flow_log_1_creation_time = create_flow_log(
                vpc_id=vpc_1_id,
                dest_bucket_arn=fixed_bucket_1_arn,
                region_name=resource_region_name,
            )
            flow_log_1_arn = FlowLogResourceSpec.generate_arn(
                resource_id=flow_log_1_id,
                account_id=account_id,
                region=resource_region_name)
            ebs_volume_1_size = 128
            ebs_volume_1_az = f"{resource_region_name}a"
            ebs_volume_1_arn, ebs_volume_1_create_time = create_volume(
                size=ebs_volume_1_size,
                az=ebs_volume_1_az,
                region_name=resource_region_name)
            ## iam
            policy_1_name = "test_policy_1"
            policy_1_arn, policy_1_id = create_iam_policy(
                name=policy_1_name,
                policy_doc={
                    "Version":
                    "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": "logs:CreateLogGroup",
                            "Resource": "*"
                        },
                    ],
                },
            )
            role_1_name = "test_role_1"
            role_1_assume_role_policy_doc = {
                "Version":
                "2012-10-17",
                "Statement": [{
                    "Action": "sts:AssumeRole",
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "lambda.amazonaws.com"
                    },
                    "Sid": "",
                }],
            }
            role_1_description = "Test Role 1"
            role_1_max_session_duration = 3600
            role_1_arn = create_iam_role(
                name=role_1_name,
                assume_role_policy_doc=role_1_assume_role_policy_doc,
                description=role_1_description,
                max_session_duration=role_1_max_session_duration,
            )
            ## lambda
            lambda_function_1_name = "test_lambda_function_1"
            lambda_function_1_runtime = "python3.7"
            lambda_function_1_handler = "lambda_function.lambda_handler"
            lambda_function_1_description = "Test Lambda Function 1"
            lambda_function_1_timeout = 30
            lambda_function_1_memory_size = 256
            lambda_function_1_arn = create_lambda_function(
                name=lambda_function_1_name,
                runtime=lambda_function_1_runtime,
                role_name=role_1_arn,
                handler=lambda_function_1_handler,
                description=lambda_function_1_description,
                timeout=lambda_function_1_timeout,
                memory_size=lambda_function_1_memory_size,
                publish=False,
                region_name=resource_region_name,
            )
            # scan
            test_scan_id = "test_scan_id"
            aws_config = AWSConfig(
                artifact_path=temp_dir,
                pruner_max_age_min=4320,
                graph_name="alti",
                concurrency=ConcurrencyConfig(max_account_scan_threads=1,
                                              max_svc_scan_threads=1,
                                              max_account_scan_tries=2),
                scan=ScanConfig(
                    accounts=(),
                    regions=(),
                    scan_sub_accounts=False,
                    preferred_account_scan_regions=(
                        "us-west-1",
                        "us-west-2",
                        "us-east-1",
                        "us-east-2",
                    ),
                ),
                accessor=Accessor(
                    credentials_cache=AWSCredentialsCache(cache={}),
                    multi_hop_accessors=[],
                    cache_creds=True,
                ),
                write_master_json=True,
            )
            resource_spec_classes = (
                # DynamoDbTableResourceSpec, TODO moto
                EBSVolumeResourceSpec,
                FlowLogResourceSpec,
                IAMPolicyResourceSpec,
                IAMRoleResourceSpec,
                LambdaFunctionResourceSpec,
                S3BucketResourceSpec,
                SubnetResourceSpec,
                VPCResourceSpec,
            )
            muxer = LocalAWSScanMuxer(
                scan_id=test_scan_id,
                config=aws_config,
                resource_spec_classes=resource_spec_classes,
            )
            with unittest.mock.patch(
                    "altimeter.aws.scan.account_scanner.get_all_enabled_regions"
            ) as mock_get_all_enabled_regions:
                mock_get_all_enabled_regions.return_value = enabled_region_names
                aws2n_result = aws2n(
                    scan_id=test_scan_id,
                    config=aws_config,
                    muxer=muxer,
                    load_neptune=False,
                )
                graph_set = GraphSet.from_json_file(
                    Path(aws2n_result.json_path))
                self.assertEqual(len(graph_set.errors), 0)
                self.assertEqual(graph_set.name, "alti")
                self.assertEqual(graph_set.version, "2")
                # now check each resource type
                self.maxDiff = None
                ## Accounts
                expected_account_resources = [
                    Resource(
                        resource_id=f"arn:aws::::account/{account_id}",
                        type="aws:account",
                        link_collection=LinkCollection(
                            simple_links=(SimpleLink(pred="account_id",
                                                     obj=account_id), ), ),
                    )
                ]
                account_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:account"
                ]
                self.assertCountEqual(account_resources,
                                      expected_account_resources)
                ## Regions
                expected_region_resources = [
                    Resource(
                        resource_id=
                        f"arn:aws:::{account_id}:region/{region['RegionName']}",
                        type="aws:region",
                        link_collection=LinkCollection(
                            simple_links=(
                                SimpleLink(pred="name",
                                           obj=region["RegionName"]),
                                SimpleLink(pred="opt_in_status",
                                           obj=region["OptInStatus"]),
                            ),
                            resource_links=(ResourceLink(
                                pred="account",
                                obj=f"arn:aws::::account/{account_id}"), ),
                        ),
                    ) for region in all_regions
                ]
                region_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:region"
                ]
                self.assertCountEqual(region_resources,
                                      expected_region_resources)
                ## IAM Policies
                expected_iam_policy_resources = [
                    Resource(
                        resource_id=policy_1_arn,
                        type="aws:iam:policy",
                        link_collection=LinkCollection(
                            simple_links=(
                                SimpleLink(pred="name", obj=policy_1_name),
                                SimpleLink(pred="policy_id", obj=policy_1_id),
                                SimpleLink(pred="default_version_id",
                                           obj="v1"),
                                SimpleLink(
                                    pred="default_version_policy_document_text",
                                    obj=
                                    '{"Statement": [{"Action": "logs:CreateLogGroup", "Effect": "Allow", "Resource": "*"}], "Version": "2012-10-17"}',
                                ),
                            ),
                            resource_links=(ResourceLink(
                                pred="account",
                                obj=f"arn:aws::::account/{account_id}"), ),
                        ),
                    )
                ]
                iam_policy_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:iam:policy"
                ]
                self.assertCountEqual(iam_policy_resources,
                                      expected_iam_policy_resources)
                ## IAM Roles
                expected_iam_role_resources = [
                    Resource(
                        resource_id=role_1_arn,
                        type="aws:iam:role",
                        link_collection=LinkCollection(
                            simple_links=(
                                SimpleLink(pred="name", obj=role_1_name),
                                SimpleLink(pred="max_session_duration",
                                           obj=role_1_max_session_duration),
                                SimpleLink(pred="description",
                                           obj=role_1_description),
                                SimpleLink(
                                    pred="assume_role_policy_document_text",
                                    obj=policy_doc_dict_to_sorted_str(
                                        role_1_assume_role_policy_doc),
                                ),
                            ),
                            multi_links=(MultiLink(
                                pred="assume_role_policy_document",
                                obj=LinkCollection(
                                    simple_links=(SimpleLink(
                                        pred="version", obj="2012-10-17"), ),
                                    multi_links=(MultiLink(
                                        pred="statement",
                                        obj=LinkCollection(
                                            simple_links=(
                                                SimpleLink(pred="effect",
                                                           obj="Allow"),
                                                SimpleLink(
                                                    pred="action",
                                                    obj="sts:AssumeRole"),
                                            ),
                                            multi_links=(MultiLink(
                                                pred="principal",
                                                obj=LinkCollection(
                                                    simple_links=(SimpleLink(
                                                        pred="service",
                                                        obj=
                                                        "lambda.amazonaws.com",
                                                    ), )),
                                            ), ),
                                        ),
                                    ), ),
                                ),
                            ), ),
                            resource_links=(ResourceLink(
                                pred="account",
                                obj="arn:aws::::account/123456789012"), ),
                        ),
                    )
                ]
                iam_role_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:iam:role"
                ]
                self.assertCountEqual(iam_role_resources,
                                      expected_iam_role_resources)

                ## Lambda functions
                expected_lambda_function_resources = [
                    Resource(
                        resource_id=lambda_function_1_arn,
                        type="aws:lambda:function",
                        link_collection=LinkCollection(
                            simple_links=(
                                SimpleLink(pred="function_name",
                                           obj=lambda_function_1_name),
                                SimpleLink(pred="runtime",
                                           obj=lambda_function_1_runtime),
                            ),
                            resource_links=(
                                ResourceLink(
                                    pred="account",
                                    obj=f"arn:aws::::account/{account_id}"),
                                ResourceLink(
                                    pred="region",
                                    obj=
                                    f"arn:aws:::{account_id}:region/{resource_region_name}",
                                ),
                            ),
                            transient_resource_links=(ResourceLink(
                                pred="role",
                                obj="arn:aws:iam::123456789012:role/test_role_1"
                            ), ),
                        ),
                    ),
                ]
                lambda_function_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:lambda:function"
                ]
                self.assertCountEqual(lambda_function_resources,
                                      expected_lambda_function_resources)
                ## EC2 VPCs
                expected_ec2_vpc_resources = [
                    Resource(
                        resource_id=vpc_1_arn,
                        type="aws:ec2:vpc",
                        link_collection=LinkCollection(
                            simple_links=(
                                SimpleLink(pred="is_default", obj=True),
                                SimpleLink(pred="cidr_block", obj=vpc_1_cidr),
                                SimpleLink(pred="state", obj="available"),
                            ),
                            resource_links=(
                                ResourceLink(
                                    pred="account",
                                    obj=f"arn:aws::::account/{account_id}"),
                                ResourceLink(
                                    pred="region",
                                    obj=
                                    f"arn:aws:::{account_id}:region/{resource_region_name}",
                                ),
                            ),
                        ),
                    )
                ]
                ec2_vpc_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:ec2:vpc"
                ]
                self.assertCountEqual(ec2_vpc_resources,
                                      expected_ec2_vpc_resources)
                ## EC2 VPC Flow Logs
                expected_ec2_vpc_flow_log_resources = [
                    Resource(
                        resource_id=flow_log_1_arn,
                        type="aws:ec2:flow-log",
                        link_collection=LinkCollection(
                            simple_links=(
                                SimpleLink(
                                    pred="creation_time",
                                    obj=flow_log_1_creation_time.replace(
                                        tzinfo=datetime.timezone.utc).
                                    isoformat(),
                                ),
                                SimpleLink(pred="deliver_logs_status",
                                           obj="SUCCESS"),
                                SimpleLink(pred="flow_log_status",
                                           obj="ACTIVE"),
                                SimpleLink(pred="traffic_type", obj="ALL"),
                                SimpleLink(pred="log_destination_type",
                                           obj="s3"),
                                SimpleLink(pred="log_destination",
                                           obj=fixed_bucket_1_arn),
                                SimpleLink(
                                    pred="log_format",
                                    obj=
                                    "${version} ${account-id} ${interface-id} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${start} ${end} ${action} ${log-status}",
                                ),
                            ),
                            resource_links=(
                                ResourceLink(
                                    pred="account",
                                    obj=f"arn:aws::::account/{account_id}"),
                                ResourceLink(
                                    pred="region",
                                    obj=
                                    f"arn:aws:::{account_id}:region/{resource_region_name}",
                                ),
                            ),
                            transient_resource_links=(TransientResourceLink(
                                pred="vpc",
                                obj=vpc_1_arn,
                            ), ),
                        ),
                    )
                ]
                ec2_vpc_flow_log_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:ec2:flow-log"
                ]
                self.assertCountEqual(ec2_vpc_flow_log_resources,
                                      expected_ec2_vpc_flow_log_resources)
                ## EC2 Subnets
                expected_ec2_subnet_resources = [
                    Resource(
                        resource_id=subnet_1_arn,
                        type="aws:ec2:subnet",
                        link_collection=LinkCollection(
                            simple_links=(
                                SimpleLink(pred="cidr_block",
                                           obj=subnet_1_cidr),
                                SimpleLink(pred="first_ip",
                                           obj=subnet_1_first_ip),
                                SimpleLink(pred="last_ip",
                                           obj=subnet_1_last_ip),
                                SimpleLink(pred="state", obj="available"),
                            ),
                            resource_links=(
                                ResourceLink(pred="vpc", obj=vpc_1_arn),
                                ResourceLink(
                                    pred="account",
                                    obj=f"arn:aws::::account/{account_id}"),
                                ResourceLink(
                                    pred="region",
                                    obj=
                                    f"arn:aws:::{account_id}:region/{resource_region_name}",
                                ),
                            ),
                        ),
                    )
                ]
                ec2_subnet_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:ec2:subnet"
                ]
                self.assertCountEqual(ec2_subnet_resources,
                                      expected_ec2_subnet_resources)
                ## EC2 EBS Volumes
                expected_ec2_ebs_volume_resources = [
                    Resource(
                        resource_id=ebs_volume_1_arn,
                        type="aws:ec2:volume",
                        link_collection=LinkCollection(
                            simple_links=(
                                SimpleLink(pred="availability_zone",
                                           obj=ebs_volume_1_az),
                                SimpleLink(
                                    pred="create_time",
                                    obj=ebs_volume_1_create_time.replace(
                                        tzinfo=datetime.timezone.utc).
                                    isoformat(),
                                ),
                                SimpleLink(pred="size", obj=ebs_volume_1_size),
                                SimpleLink(pred="state", obj="available"),
                                SimpleLink(pred="volume_type", obj="gp2"),
                                SimpleLink(pred="encrypted", obj=False),
                            ),
                            resource_links=(
                                ResourceLink(
                                    pred="account",
                                    obj=f"arn:aws::::account/{account_id}"),
                                ResourceLink(
                                    pred="region",
                                    obj=
                                    f"arn:aws:::{account_id}:region/{resource_region_name}",
                                ),
                            ),
                        ),
                    )
                ]
                ec2_ebs_volume_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:ec2:volume"
                ]
                self.assertCountEqual(ec2_ebs_volume_resources,
                                      expected_ec2_ebs_volume_resources)
                ## S3 Buckets
                expected_s3_bucket_resources = [
                    Resource(
                        resource_id=bucket_1_arn,
                        type="aws:s3:bucket",
                        link_collection=LinkCollection(
                            simple_links=(
                                SimpleLink(pred="name", obj=bucket_1_name),
                                SimpleLink(
                                    pred="creation_date",
                                    obj=bucket_1_creation_date.replace(
                                        tzinfo=datetime.timezone.utc).
                                    isoformat(),
                                ),
                            ),
                            resource_links=(
                                ResourceLink(
                                    pred="account",
                                    obj=f"arn:aws::::account/{account_id}"),
                                ResourceLink(
                                    pred="region",
                                    obj=
                                    f"arn:aws:::{account_id}:region/{resource_region_name}",
                                ),
                            ),
                        ),
                    )
                ]
                s3_bucket_resources = [
                    resource for resource in graph_set.resources
                    if resource.type == "aws:s3:bucket"
                ]
                self.assertCountEqual(s3_bucket_resources,
                                      expected_s3_bucket_resources)

                expected_num_graph_set_resources = (
                    0 + len(expected_account_resources) +
                    len(expected_region_resources) +
                    len(expected_iam_policy_resources) +
                    len(expected_iam_role_resources) +
                    len(expected_lambda_function_resources) +
                    len(expected_ec2_ebs_volume_resources) +
                    len(expected_ec2_subnet_resources) +
                    len(expected_ec2_vpc_resources) +
                    len(expected_ec2_vpc_flow_log_resources) +
                    len(expected_s3_bucket_resources))
                self.assertEqual(len(graph_set.resources),
                                 expected_num_graph_set_resources)
Ejemplo n.º 7
0
    def test(self):
        policy_doc = {
            "Version":
            "2012-10-17",
            "Statement": [
                {
                    "Sid":
                    "AllowViewAccountInfo",
                    "Effect":
                    "Allow",
                    "Action": [
                        "iam:GetAccountPasswordPolicy",
                        "iam:GetAccountSummary",
                        "iam:ListVirtualMFADevices",
                    ],
                    "Resource":
                    "*",
                },
                {
                    "Sid": "AllowManageOwnPasswords",
                    "Effect": "Allow",
                    "Action": ["iam:ChangePassword", "iam:GetUser"],
                    "Resource": "arn:aws:iam::*:user/${aws:username}",
                },
                {
                    "Sid":
                    "AllowManageOwnAccessKeys",
                    "Effect":
                    "Allow",
                    "Action": [
                        "iam:CreateAccessKey",
                        "iam:DeleteAccessKey",
                        "iam:ListAccessKeys",
                        "iam:UpdateAccessKey",
                    ],
                    "Resource":
                    "arn:aws:iam::*:user/${aws:username}",
                },
                {
                    "Sid":
                    "AllowManageOwnSigningCertificates",
                    "Effect":
                    "Allow",
                    "Action": [
                        "iam:DeleteSigningCertificate",
                        "iam:ListSigningCertificates",
                        "iam:UpdateSigningCertificate",
                        "iam:UploadSigningCertificate",
                    ],
                    "Resource":
                    "arn:aws:iam::*:user/${aws:username}",
                },
                {
                    "Sid":
                    "AllowManageOwnSSHPublicKeys",
                    "Effect":
                    "Allow",
                    "Action": [
                        "iam:DeleteSSHPublicKey",
                        "iam:GetSSHPublicKey",
                        "iam:ListSSHPublicKeys",
                        "iam:UpdateSSHPublicKey",
                        "iam:UploadSSHPublicKey",
                    ],
                    "Resource":
                    "arn:aws:iam::*:user/${aws:username}",
                },
                {
                    "Sid":
                    "AllowManageOwnGitCredentials",
                    "Effect":
                    "Allow",
                    "Action": [
                        "iam:CreateServiceSpecificCredential",
                        "iam:DeleteServiceSpecificCredential",
                        "iam:ListServiceSpecificCredentials",
                        "iam:ResetServiceSpecificCredential",
                        "iam:UpdateServiceSpecificCredential",
                    ],
                    "Resource":
                    "arn:aws:iam::*:user/${aws:username}",
                },
                {
                    "Sid":
                    "AllowManageOwnVirtualMFADevice",
                    "Effect":
                    "Allow",
                    "Action": [
                        "iam:CreateVirtualMFADevice",
                        "iam:DeleteVirtualMFADevice"
                    ],
                    "Resource":
                    "arn:aws:iam::*:mfa/${aws:username}",
                },
                {
                    "Sid":
                    "AllowManageOwnUserMFA",
                    "Effect":
                    "Allow",
                    "Action": [
                        "iam:DeactivateMFADevice",
                        "iam:EnableMFADevice",
                        "iam:ListMFADevices",
                        "iam:ResyncMFADevice",
                    ],
                    "Resource":
                    "arn:aws:iam::*:user/${aws:username}",
                },
                {
                    "Sid":
                    "DenyAllExceptListedIfNoMFA",
                    "Effect":
                    "Deny",
                    "NotAction": [
                        "iam:CreateVirtualMFADevice",
                        "iam:EnableMFADevice",
                        "iam:GetUser",
                        "iam:ListMFADevices",
                        "iam:ListVirtualMFADevices",
                        "iam:ResyncMFADevice",
                        "sts:GetSessionToken",
                    ],
                    "Resource":
                    "*",
                    "Condition": {
                        "BoolIfExists": {
                            "aws:MultiFactorAuthPresent": "false"
                        }
                    },
                },
            ],
        }

        expected_policy_doc_sorted_str = '{"Statement": [{"Action": ["iam:ChangePassword", "iam:GetUser"], "Effect": "Allow", "Resource": "arn:aws:iam::*:user/${aws:username}", "Sid": "AllowManageOwnPasswords"}, {"Action": ["iam:CreateAccessKey", "iam:DeleteAccessKey", "iam:ListAccessKeys", "iam:UpdateAccessKey"], "Effect": "Allow", "Resource": "arn:aws:iam::*:user/${aws:username}", "Sid": "AllowManageOwnAccessKeys"}, {"Action": ["iam:CreateServiceSpecificCredential", "iam:DeleteServiceSpecificCredential", "iam:ListServiceSpecificCredentials", "iam:ResetServiceSpecificCredential", "iam:UpdateServiceSpecificCredential"], "Effect": "Allow", "Resource": "arn:aws:iam::*:user/${aws:username}", "Sid": "AllowManageOwnGitCredentials"}, {"Action": ["iam:CreateVirtualMFADevice", "iam:DeleteVirtualMFADevice"], "Effect": "Allow", "Resource": "arn:aws:iam::*:mfa/${aws:username}", "Sid": "AllowManageOwnVirtualMFADevice"}, {"Action": ["iam:DeactivateMFADevice", "iam:EnableMFADevice", "iam:ListMFADevices", "iam:ResyncMFADevice"], "Effect": "Allow", "Resource": "arn:aws:iam::*:user/${aws:username}", "Sid": "AllowManageOwnUserMFA"}, {"Action": ["iam:DeleteSSHPublicKey", "iam:GetSSHPublicKey", "iam:ListSSHPublicKeys", "iam:UpdateSSHPublicKey", "iam:UploadSSHPublicKey"], "Effect": "Allow", "Resource": "arn:aws:iam::*:user/${aws:username}", "Sid": "AllowManageOwnSSHPublicKeys"}, {"Action": ["iam:DeleteSigningCertificate", "iam:ListSigningCertificates", "iam:UpdateSigningCertificate", "iam:UploadSigningCertificate"], "Effect": "Allow", "Resource": "arn:aws:iam::*:user/${aws:username}", "Sid": "AllowManageOwnSigningCertificates"}, {"Action": ["iam:GetAccountPasswordPolicy", "iam:GetAccountSummary", "iam:ListVirtualMFADevices"], "Effect": "Allow", "Resource": "*", "Sid": "AllowViewAccountInfo"}, {"Condition": {"BoolIfExists": {"aws:MultiFactorAuthPresent": "false"}}, "Effect": "Deny", "NotAction": ["iam:CreateVirtualMFADevice", "iam:EnableMFADevice", "iam:GetUser", "iam:ListMFADevices", "iam:ListVirtualMFADevices", "iam:ResyncMFADevice", "sts:GetSessionToken"], "Resource": "*", "Sid": "DenyAllExceptListedIfNoMFA"}], "Version": "2012-10-17"}'

        policy_doc_sorted_str = policy_doc_dict_to_sorted_str(policy_doc)

        self.assertEqual(policy_doc_sorted_str, expected_policy_doc_sorted_str)