Beispiel #1
0
    def list_config_service_resources(self,
                                      resource_ids,
                                      resource_name,
                                      limit,
                                      next_token,
                                      backend_region=None,
                                      resource_region=None):
        # S3 need not care about "backend_region" as S3 is global. The resource_region only matters for aggregated queries as you can
        # filter on bucket regions for them. For other resource types, you would need to iterate appropriately for the backend_region.

        # Resource IDs are the same as S3 bucket names
        # For aggregation -- did we get both a resource ID and a resource name?
        if resource_ids and resource_name:
            # If the values are different, then return an empty list:
            if resource_name not in resource_ids:
                return [], None

        # If no filter was passed in for resource names/ids then return them all:
        if not resource_ids and not resource_name:
            bucket_list = list(self.backends['global'].buckets.keys())

        else:
            # Match the resource name / ID:
            bucket_list = []
            filter_buckets = [resource_name] if resource_name else resource_ids

            for bucket in self.backends['global'].buckets.keys():
                if bucket in filter_buckets:
                    bucket_list.append(bucket)

        # If a resource_region was supplied (aggregated only), then filter on bucket region too:
        if resource_region:
            region_buckets = []

            for bucket in bucket_list:
                if self.backends['global'].buckets[
                        bucket].region_name == resource_region:
                    region_buckets.append(bucket)

            bucket_list = region_buckets

        if not bucket_list:
            return [], None

        # Pagination logic:
        sorted_buckets = sorted(bucket_list)
        new_token = None

        # Get the start:
        if not next_token:
            start = 0
        else:
            # Tokens for this moto feature is just the bucket name:
            # For OTHER non-global resource types, it's the region concatenated with the resource ID.
            if next_token not in sorted_buckets:
                raise InvalidNextTokenException()

            start = sorted_buckets.index(next_token)

        # Get the list of items to collect:
        bucket_list = sorted_buckets[start:(start + limit)]

        if len(sorted_buckets) > (start + limit):
            new_token = sorted_buckets[start + limit]

        return [{
            'type': 'AWS::S3::Bucket',
            'id': bucket,
            'name': bucket,
            'region': self.backends['global'].buckets[bucket].region_name
        } for bucket in bucket_list], new_token
Beispiel #2
0
    def list_config_service_resources(
        self,
        resource_ids,
        resource_name,
        limit,
        next_token,
        backend_region=None,
        resource_region=None,
        aggregator=None,
    ):
        # IAM roles are "global" and aren't assigned into any availability zone
        # The resource ID is a AWS-assigned random string like "AROA0BSVNSZKXVHS00SBJ"
        # The resource name is a user-assigned string like "MyDevelopmentAdminRole"
        # Stored in moto backend with the AWS-assigned random string like "AROA0BSVNSZKXVHS00SBJ"

        # Grab roles from backend; need the full values since names and id's are different
        role_list = list(self.backends["global"].roles.values())

        if not role_list:
            return [], None

        # Filter by resource name or ids
        if resource_name or resource_ids:
            filtered_roles = []
            # resource_name takes precedence over resource_ids
            if resource_name:
                for role in role_list:
                    if role.name == resource_name:
                        filtered_roles = [role]
                        break
                # but if both are passed, it must be a subset
                if filtered_roles and resource_ids:
                    if filtered_roles[0].id not in resource_ids:
                        return [], None
            else:
                for role in role_list:
                    if role.id in resource_ids:
                        filtered_roles.append(role)

            # Filtered roles are now the subject for the listing
            role_list = filtered_roles

        if aggregator:
            # IAM is a little special; Roles are created in us-east-1 (which AWS calls the "global" region)
            # However, the resource will return in the aggregator (in duplicate) for each region in the aggregator
            # Therefore, we'll need to find out the regions where the aggregators are running, and then duplicate the resource there

            # In practice, it looks like AWS will only duplicate these resources if you've "used" any roles in the region, but since
            # we can't really tell if this has happened in moto, we'll just bind this to the regions in your aggregator
            aggregated_regions = []
            aggregator_sources = aggregator.get(
                "account_aggregation_sources") or aggregator.get(
                    "organization_aggregation_source")
            for source in aggregator_sources:
                source_dict = source.__dict__
                if source_dict.get("all_aws_regions", False):
                    aggregated_regions = boto3.Session().get_available_regions(
                        "config")
                    break
                for region in source_dict.get("aws_regions", []):
                    aggregated_regions.append(region)

            duplicate_role_list = []
            for region in list(set(aggregated_regions)):
                for role in role_list:
                    duplicate_role_list.append({
                        "_id":
                        "{}{}".format(
                            role.id, region
                        ),  # this is only for sorting, isn't returned outside of this functin
                        "type":
                        "AWS::IAM::Role",
                        "id":
                        role.id,
                        "name":
                        role.name,
                        "region":
                        region,
                    })

            # Pagination logic, sort by role id
            sorted_roles = sorted(duplicate_role_list,
                                  key=lambda role: role["_id"])
        else:
            # Non-aggregated queries are in the else block, and we can treat these like a normal config resource
            # Pagination logic, sort by role id
            sorted_roles = sorted(role_list, key=lambda role: role.id)

        new_token = None

        # Get the start:
        if not next_token:
            start = 0
        else:
            try:
                # Find the index of the next
                start = next(
                    index for (index, r) in enumerate(sorted_roles)
                    if next_token == (r["_id"] if aggregator else r.id))
            except StopIteration:
                raise InvalidNextTokenException()

        # Get the list of items to collect:
        role_list = sorted_roles[start:(start + limit)]

        if len(sorted_roles) > (start + limit):
            record = sorted_roles[start + limit]
            new_token = record["_id"] if aggregator else record.id

        return (
            [{
                "type": "AWS::IAM::Role",
                "id": role["id"] if aggregator else role.id,
                "name": role["name"] if aggregator else role.name,
                "region": role["region"] if aggregator else "global",
            } for role in role_list],
            new_token,
        )
Beispiel #3
0
    def list_config_service_resources(
        self,
        resource_ids,
        resource_name,
        limit,
        next_token,
        backend_region=None,
        resource_region=None,
        aggregator=None,
    ):
        # For the Account Public Access Block, they are the same for all regions. The resource ID is the AWS account ID
        # There is no resource name -- it should be a blank string "" if provided.

        # The resource name can only ever be None or an empty string:
        if resource_name is not None and resource_name != "":
            return [], None

        pab = None
        account_id = get_moto_s3_account_id()
        regions = [
            region for region in Session().get_available_regions("config")
        ]

        # If a resource ID was passed in, then filter accordingly:
        if resource_ids:
            for resource_id in resource_ids:
                if account_id == resource_id:
                    pab = self.backends["global"].public_access_block
                    break

        # Otherwise, just grab the one from the backend:
        if not resource_ids:
            pab = self.backends["global"].public_access_block

        # If it's not present, then return nothing
        if not pab:
            return [], None

        # Filter on regions (and paginate on them as well):
        if backend_region:
            pab_list = [backend_region]
        elif resource_region:
            # Invalid region?
            if resource_region not in regions:
                return [], None

            pab_list = [resource_region]

        # Aggregated query where no regions were supplied so return them all:
        else:
            pab_list = regions

        # Pagination logic:
        sorted_regions = sorted(pab_list)
        new_token = None

        # Get the start:
        if not next_token:
            start = 0
        else:
            # Tokens for this moto feature is just the region-name:
            # For OTHER non-global resource types, it's the region concatenated with the resource ID.
            if next_token not in sorted_regions:
                raise InvalidNextTokenException()

            start = sorted_regions.index(next_token)

        # Get the list of items to collect:
        pab_list = sorted_regions[start:(start + limit)]

        if len(sorted_regions) > (start + limit):
            new_token = sorted_regions[start + limit]

        return (
            [{
                "type": "AWS::S3::AccountPublicAccessBlock",
                "id": account_id,
                "region": region,
            } for region in pab_list],
            new_token,
        )
Beispiel #4
0
    def list_config_service_resources(
        self,
        resource_ids,
        resource_name,
        limit,
        next_token,
        backend_region=None,
        resource_region=None,
        aggregator=None,
    ):
        # IAM policies are "global" and aren't assigned into any availability zone
        # The resource ID is a AWS-assigned random string like "ANPA0BSVNSZK00SJSPVUJ"
        # The resource name is a user-assigned string like "my-development-policy"
        # Stored in moto backend with the arn like "arn:aws:iam::123456789012:policy/my-development-policy"

        policy_list = list(self.backends["global"].managed_policies.values())

        # We don't want to include AWS Managed Policies. This technically needs to
        # respect the configuration recorder's 'includeGlobalResourceTypes' setting,
        # but it's default set be default, and moto's config doesn't yet support
        # custom configuration recorders, we'll just behave as default.
        policy_list = list(
            filter(
                lambda policy: not policy.arn.startswith("arn:aws:iam::aws"),
                policy_list,
            ))

        if not policy_list:
            return [], None

        # Filter by resource name or ids
        if resource_name or resource_ids:
            filtered_policies = []
            # resource_name takes precedence over resource_ids
            if resource_name:
                for policy in policy_list:
                    if policy.name == resource_name:
                        filtered_policies = [policy]
                        break
                # but if both are passed, it must be a subset
                if filtered_policies and resource_ids:
                    if filtered_policies[0].id not in resource_ids:
                        return [], None

            else:
                for policy in policy_list:
                    if policy.id in resource_ids:
                        filtered_policies.append(policy)

            # Filtered roles are now the subject for the listing
            policy_list = filtered_policies

        if aggregator:
            # IAM is a little special; Policies are created in us-east-1 (which AWS calls the "global" region)
            # However, the resource will return in the aggregator (in duplicate) for each region in the aggregator
            # Therefore, we'll need to find out the regions where the aggregators are running, and then duplicate the resource there

            # In practice, it looks like AWS will only duplicate these resources if you've "used" any policies in the region, but since
            # we can't really tell if this has happened in moto, we'll just bind this to the regions in your aggregator
            aggregated_regions = []
            aggregator_sources = aggregator.get(
                "account_aggregation_sources") or aggregator.get(
                    "organization_aggregation_source")
            for source in aggregator_sources:
                source_dict = source.__dict__
                if source_dict.get("all_aws_regions", False):
                    aggregated_regions = boto3.Session().get_available_regions(
                        "config")
                    break
                for region in source_dict.get("aws_regions", []):
                    aggregated_regions.append(region)

            duplicate_policy_list = []
            for region in list(set(aggregated_regions)):
                for policy in policy_list:
                    duplicate_policy_list.append({
                        "_id":
                        "{}{}".format(
                            policy.id, region
                        ),  # this is only for sorting, isn't returned outside of this functin
                        "type":
                        "AWS::IAM::Policy",
                        "id":
                        policy.id,
                        "name":
                        policy.name,
                        "region":
                        region,
                    })

            # Pagination logic, sort by role id
            sorted_policies = sorted(duplicate_policy_list,
                                     key=lambda policy: policy["_id"])

        else:
            # Non-aggregated queries are in the else block, and we can treat these like a normal config resource
            # Pagination logic, sort by role id
            sorted_policies = sorted(policy_list, key=lambda role: role.id)

        new_token = None

        # Get the start:
        if not next_token:
            start = 0
        else:
            try:
                # Find the index of the next
                start = next(
                    index for (index, p) in enumerate(sorted_policies)
                    if next_token == (p["_id"] if aggregator else p.id))
            except StopIteration:
                raise InvalidNextTokenException()

        # Get the list of items to collect:
        policy_list = sorted_policies[start:(start + limit)]

        if len(sorted_policies) > (start + limit):
            record = sorted_policies[start + limit]
            new_token = record["_id"] if aggregator else record.id

        return (
            [{
                "type": "AWS::IAM::Policy",
                "id": policy["id"] if aggregator else policy.id,
                "name": policy["name"] if aggregator else policy.name,
                "region": policy["region"] if aggregator else "global",
            } for policy in policy_list],
            new_token,
        )