UNPKG

@aws-amplify/cli-internal

Version:
235 lines • 12.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthCognitoForwardRefactorer = exports.renderOrphanTable = exports.renderImportTable = exports.extractSocialAuthLogicalIds = exports.buildImportSpec = exports.RESOURCE_TYPES = exports.USER_POOL_IDENTITY_PROVIDER_TYPE = exports.USER_POOL_DOMAIN_TYPE = exports.IDENTITY_POOL_ROLE_ATTACHMENT_TYPE = exports.IDENTITY_POOL_TYPE = exports.USER_POOL_TYPE = exports.USER_POOL_CLIENT_TYPE = exports.GEN2_WEB_CLIENT_LOGICAL_ID = exports.GEN2_NATIVE_APP_CLIENT_LOGICAL_ID = exports.GEN1_WEB_CLIENT_LOGICAL_ID = exports.GEN1_NATIVE_APP_CLIENT_LOGICAL_ID = void 0; const amplify_cli_core_1 = require("@aws-amplify/amplify-cli-core"); const forward_category_refactorer_1 = require("../workflow/forward-category-refactorer"); const category_refactorer_1 = require("../workflow/category-refactorer"); const utils_1 = require("../../_common/utils"); const cfn_1 = require("../../_common/cfn"); const cli_table3_1 = __importDefault(require("cli-table3")); exports.GEN1_NATIVE_APP_CLIENT_LOGICAL_ID = 'UserPoolClient'; exports.GEN1_WEB_CLIENT_LOGICAL_ID = 'UserPoolClientWeb'; exports.GEN2_NATIVE_APP_CLIENT_LOGICAL_ID = 'UserPoolNativeAppClient'; exports.GEN2_WEB_CLIENT_LOGICAL_ID = 'UserPoolAppClient'; exports.USER_POOL_CLIENT_TYPE = 'AWS::Cognito::UserPoolClient'; exports.USER_POOL_TYPE = 'AWS::Cognito::UserPool'; exports.IDENTITY_POOL_TYPE = 'AWS::Cognito::IdentityPool'; exports.IDENTITY_POOL_ROLE_ATTACHMENT_TYPE = 'AWS::Cognito::IdentityPoolRoleAttachment'; exports.USER_POOL_DOMAIN_TYPE = 'AWS::Cognito::UserPoolDomain'; exports.USER_POOL_IDENTITY_PROVIDER_TYPE = 'AWS::Cognito::UserPoolIdentityProvider'; exports.RESOURCE_TYPES = [exports.USER_POOL_TYPE, exports.USER_POOL_CLIENT_TYPE, exports.IDENTITY_POOL_TYPE, exports.IDENTITY_POOL_ROLE_ATTACHMENT_TYPE]; function buildImportSpec(config, domainLogicalId, idpLogicalIds) { const resourcesToImport = []; const templateAdditions = {}; templateAdditions[domainLogicalId] = { Type: exports.USER_POOL_DOMAIN_TYPE, DeletionPolicy: 'Retain', UpdateReplacePolicy: 'Retain', Properties: { Domain: config.domain, UserPoolId: config.userPoolId, }, }; resourcesToImport.push({ ResourceType: exports.USER_POOL_DOMAIN_TYPE, LogicalResourceId: domainLogicalId, ResourceIdentifier: { UserPoolId: config.userPoolId, Domain: config.domain, }, }); for (const provider of config.providers) { const logicalId = idpLogicalIds.get(provider.providerName); if (!logicalId) { throw new amplify_cli_core_1.AmplifyError('MigrationError', { message: `Identity provider '${provider.providerName}' exists on the UserPool but has no matching UserPoolIdentityProvider resource in the Gen2 template.`, }); } templateAdditions[logicalId] = { Type: exports.USER_POOL_IDENTITY_PROVIDER_TYPE, DeletionPolicy: 'Retain', UpdateReplacePolicy: 'Retain', Properties: { UserPoolId: config.userPoolId, ProviderName: provider.providerName, ProviderType: provider.providerType, ProviderDetails: {}, AttributeMapping: {}, }, }; resourcesToImport.push({ ResourceType: exports.USER_POOL_IDENTITY_PROVIDER_TYPE, LogicalResourceId: logicalId, ResourceIdentifier: { UserPoolId: config.userPoolId, ProviderName: provider.providerName, }, }); } return { resourcesToImport, templateAdditions }; } exports.buildImportSpec = buildImportSpec; function extractSocialAuthLogicalIds(template) { const idpLogicalIds = new Map(); let domainLogicalId; for (const [logicalId, resource] of Object.entries(template.Resources)) { if (resource.Type === exports.USER_POOL_DOMAIN_TYPE) { domainLogicalId = logicalId; } else if (resource.Type === exports.USER_POOL_IDENTITY_PROVIDER_TYPE) { const providerName = resource.Properties.ProviderName; if (providerName) idpLogicalIds.set(providerName, logicalId); } } return { domainLogicalId, idpLogicalIds }; } exports.extractSocialAuthLogicalIds = extractSocialAuthLogicalIds; function renderImportTable(resourcesToImport, gen2StackName) { var _a, _b; const table = new cli_table3_1.default({ head: ['Physical ID', 'Target Logical ID'], style: { head: [] } }); for (const r of resourcesToImport) { const physicalId = Object.values((_a = r.ResourceIdentifier) !== null && _a !== void 0 ? _a : {}).join('/'); table.push([physicalId, (_b = r.LogicalResourceId) !== null && _b !== void 0 ? _b : '']); } return `Import social auth resources into '${gen2StackName}'\n\n${table.toString()}`; } exports.renderImportTable = renderImportTable; async function renderOrphanTable(stackFacade, logicalIds, template, stackName, variant) { const deployedResources = await stackFacade.fetchStackResources(stackName); const userPool = deployedResources.find((r) => r.ResourceType === exports.USER_POOL_TYPE); if (!userPool) { throw new amplify_cli_core_1.AmplifyError('MigrationError', { message: `Unable to find user pool in stack ${stackName}` }); } const table = new cli_table3_1.default({ head: ['PhysicalId', 'Logical ID', 'Type'], style: { head: [] } }); for (const id of logicalIds) { const deployedResource = deployedResources.find((r) => r.LogicalResourceId === id); const templateResource = template.Resources[id]; if (!deployedResource || !templateResource) { throw new amplify_cli_core_1.AmplifyError('MigrationError', { message: `Unable to find resource with id ${id} in stack ${stackName}` }); } table.push([`${userPool.PhysicalResourceId}/${deployedResource.PhysicalResourceId}`, id, templateResource.Type]); } const header = variant === 'forward' ? `Orphan ${logicalIds.length} social auth resource(s) from '${stackName}'` : `Orphan ${logicalIds.length} imported social auth resource(s) from '${stackName}'`; return `${header}\n\n${table.toString()}`; } exports.renderOrphanTable = renderOrphanTable; class AuthCognitoForwardRefactorer extends forward_category_refactorer_1.ForwardCategoryRefactorer { resourceTypes() { return exports.RESOURCE_TYPES; } async beforeMove(blueprint) { const baseOps = await super.beforeMove(blueprint); const gen2StackId = blueprint.targetStackId; const gen2StackName = (0, utils_1.extractStackNameFromId)(gen2StackId); const holdingStackName = this.getHoldingStackName(gen2StackName); const holdingStack = await this.cfn.findStack(holdingStackName); if (holdingStack && !cfn_1.VALID_HOLDING_STACK_STATUSES.includes(holdingStack.StackStatus)) { throw new amplify_cli_core_1.AmplifyError('StackStateError', { message: `Unexpected state of stack ${holdingStackName}: ${holdingStack.StackStatus} (expected ${cfn_1.VALID_HOLDING_STACK_STATUSES.join(', ')})`, }); } if (holdingStack) return baseOps; const template = await this.cfn.fetchTemplate(gen2StackId); const { domainLogicalId, idpLogicalIds } = extractSocialAuthLogicalIds(template); if (domainLogicalId || idpLogicalIds.size > 0) { const socialProvidersResourceIds = [...(domainLogicalId ? [domainLogicalId] : []), ...idpLogicalIds.values()]; const gen2StackName = (0, utils_1.extractStackNameFromId)(gen2StackId); const description = await renderOrphanTable(this.gen2Branch, socialProvidersResourceIds, template, gen2StackName, 'forward'); baseOps.push({ resource: this.resource, validate: () => ({ description: `Deletion Protection (social auth): ${gen2StackName}`, run: async () => (0, category_refactorer_1.checkRetainPolicies)(template, socialProvidersResourceIds), }), describe: async () => [description], execute: () => this.cfn.orphan({ stackName: gen2StackId, logicalIds: socialProvidersResourceIds, resource: this.resource, }), }); } return baseOps; } async move(blueprint) { const baseOps = await super.move(blueprint); const gen2StackId = blueprint.targetStackId; const gen2StackName = (0, utils_1.extractStackNameFromId)(gen2StackId); const holdingStackName = this.getHoldingStackName(gen2StackName); const holdingStack = await this.cfn.findStack(holdingStackName); if (holdingStack && !cfn_1.VALID_HOLDING_STACK_STATUSES.includes(holdingStack.StackStatus)) { throw new amplify_cli_core_1.AmplifyError('StackStateError', { message: `Unexpected state of stack ${holdingStackName}: ${holdingStack.StackStatus} (expected ${cfn_1.VALID_HOLDING_STACK_STATUSES.join(', ')})`, }); } if (holdingStack) return baseOps; const gen1UserPoolId = this.gen1App.resourceMetaOutput(this.resource, 'UserPoolId'); const socialAuthConfig = await this.gen2Branch.fetchSocialAuthConfig(gen1UserPoolId); if (socialAuthConfig) { const template = await this.cfn.fetchTemplate(gen2StackId); const gen2StackName = (0, utils_1.extractStackNameFromId)(gen2StackId); const { domainLogicalId, idpLogicalIds } = extractSocialAuthLogicalIds(template); if (!domainLogicalId) { throw new amplify_cli_core_1.AmplifyError('MigrationError', { message: `Gen2 template '${gen2StackName}' has no UserPoolDomain resource for social auth import.`, }); } const { resourcesToImport, templateAdditions } = buildImportSpec(socialAuthConfig, domainLogicalId, idpLogicalIds); baseOps.push({ resource: this.resource, validate: () => undefined, describe: async () => [renderImportTable(resourcesToImport, gen2StackName)], execute: () => this.cfn.importResources({ stackName: gen2StackId, templateAdditions, resourcesToImport, resource: this.resource, }), }); } return baseOps; } async gen2LogicalId(sourceId, sourceResource, targetResources) { if (sourceResource.Type !== exports.USER_POOL_CLIENT_TYPE) { return await super.gen2LogicalId(sourceId, sourceResource, targetResources); } let candidates; const targetResourceIds = Array.from(targetResources.keys()); switch (sourceId) { case exports.GEN1_WEB_CLIENT_LOGICAL_ID: { candidates = targetResourceIds.filter((r) => r.includes(exports.GEN2_WEB_CLIENT_LOGICAL_ID)); break; } case exports.GEN1_NATIVE_APP_CLIENT_LOGICAL_ID: { candidates = targetResourceIds.filter((r) => r.includes(exports.GEN2_NATIVE_APP_CLIENT_LOGICAL_ID)); break; } default: throw new amplify_cli_core_1.AmplifyError('MigrationError', { message: `Unexpected source logical id ${sourceId} for resource of type ${exports.USER_POOL_CLIENT_TYPE}`, }); } if (candidates.length !== 1) { throw new amplify_cli_core_1.AmplifyError('MigrationError', { message: `Unable to map Gen1 resource ${sourceId} (${sourceResource.Type}) to Gen2 resource`, }); } return candidates[0]; } async fetchSourceStackId() { return this.findNestedStack(this.gen1Env, `auth${this.resource.resourceName}`); } async fetchDestStackId() { return this.findNestedStack(this.gen2Branch, 'auth'); } } exports.AuthCognitoForwardRefactorer = AuthCognitoForwardRefactorer; //# sourceMappingURL=auth-cognito-forward.js.map