def get_identity_mappings(stack_manager, stack_arn, updated_resources={}):
    # Collect a list of stacks to search for resource metadata.
    stacks_to_search = []
    stack = stack_manager.get_stack_info(stack_arn)
    if stack.stack_type == stack_info.StackInfo.STACK_TYPE_DEPLOYMENT_ACCESS or stack.stack_type == stack_info.StackInfo.STACK_TYPE_RESOURCE_GROUP:
        # Pools can be linked between resource groups and the deployment access stack.
        stacks_to_search.extend(stack.deployment.resource_groups)
        if stack.deployment.deployment_access:
            stacks_to_search.append(stack.deployment.deployment_access)
    elif stack.stack_type == stack.STACK_TYPE_PROJECT:
        # The project stack can have pools that aren't linked to a deployment.
        stacks_to_search.append(stack)

    # Fetch the stack descriptions and collect information from Custom::CognitoIdentityPool and Custom::CognitoUserPool resources.
    identity_pool_mappings = []
    idp_by_pool_name = {}
    for stack in stacks_to_search:
        for resource in stack.resources:
            if resource.type == 'Custom::CognitoIdentityPool':
                identity_pool_mappings.append({
                    'identity_pool_resource': resource,
                })
                print 'Found CognitoIdentityPool {}.{}'.format(stack.stack_name, resource.logical_id)
            elif resource.type == 'Custom::CognitoUserPool':
                identities = resource.metadata.get('CloudCanvas', {}).get('Identities', [])
                updated_resource = updated_resources.get(stack.stack_arn, {}).get(resource.logical_id, {})
                physical_id = updated_resource.get('physical_id', resource.physical_id)
                # Skip user pools that haven't been created yet, they will be linked later on creation.
                if physical_id:
                    for identity in identities:
                        pool_name = identity.get('IdentityPoolLogicalName')
                        client_app = identity.get('ClientApp')
                        if not client_app:
                            raise RuntimeError('Missing ClientApp in Identities metadata for stack {} resource {}'.format(
                                stack.stack_name, resource.logical_id))

                        # Get the client id from the updated_resources parameter if possible, otherwise get it from the pool.
                        client_id = updated_resource.get('client_apps', {}).get('client_app', {}).get('client_id')
                        if not client_id:
                            client_id = user_pool.get_client_id(physical_id, client_app)

                        if not client_id:
                            # A client may be missing if there's more than one pool updating at the same time and the list of clients is changing.
                            print 'Unable to find client named {} in user pool with physical id {} defined in stack {} resource {}'.format(
                                client_app, physical_id, stack.stack_name, resource.logical_id)
                        else:
                            pools = idp_by_pool_name.get(pool_name, [])
                            pools.append({
                                'ClientId': client_id,
                                'ProviderName': user_pool.get_provider_name(physical_id),
                                'ServerSideTokenCheck': True
                            })
                            idp_by_pool_name[pool_name] = pools
                            print 'Found CognitoUserPool {}.{} mapped to {} with client id {}'.format(stack.stack_name, resource.logical_id, pool_name, client_id)

    # Combine the user pool mappings with the identity pool mappings.
    for mapping in identity_pool_mappings:
        mapping['providers'] = idp_by_pool_name.get(mapping['identity_pool_resource'].logical_id, [])

    return identity_pool_mappings
Example #2
0
def update_cognito_identity_providers(stack_arn,
                                      user_pool_id,
                                      updated_resources={}):
    provider_to_update = user_pool.get_provider_name(user_pool_id)
    mappings = get_identity_mappings(stack_arn, updated_resources)

    for mapping in mappings:
        identity_pool_id = mapping['identity_pool_resource'].physical_id
        if identity_pool_id:
            identity_pool = get_identity_client().describe_identity_pool(
                IdentityPoolId=identity_pool_id)
            is_linked = bool([
                provider for provider in identity_pool.get(
                    'CognitoIdentityProviders', [])
                if provider.get('ProviderName') == provider_to_update
            ])
            should_be_linked = bool([
                provider for provider in mapping['providers']
                if provider.get('ProviderName') == provider_to_update
            ])
            # If is_linked, the link either needs to be updated or removed.
            # If should_be_linked, the link either needs to be created or updated.
            if is_linked or should_be_linked:
                # Create an update request based on the current pool description.
                update_request = {}
                for field in IDENTITY_POOL_FIELDS:
                    if field in identity_pool:
                        update_request[field] = identity_pool[field]

                # Replace the current list of providers.  This handles linking, updating, and unlinking pools.
                update_request['CognitoIdentityProviders'] = mapping[
                    'providers']

                # Update the pool.
                get_identity_client().update_identity_pool(**update_request)