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
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)