@aws-amplify/cli-internal
Version:
Amplify CLI
235 lines • 12.6 kB
JavaScript
;
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