From c1c3ed58b6f778f352c847d0cd17aa5c5b345f1f Mon Sep 17 00:00:00 2001 From: Edward Foyle Date: Thu, 10 Sep 2020 15:48:11 -0700 Subject: [PATCH 1/3] feat: headless update auth --- .../src/commands/auth/update.js | 44 +- packages/amplify-category-auth/src/index.js | 13 +- .../awscloudformation/constants.ts | 3 +- .../handlers/get-add-auth-handler.ts | 34 -- .../handlers/resource-handlers.ts | 55 ++ .../provider-utils/awscloudformation/index.js | 174 +------ .../service-walkthrough-types.ts | 14 +- ...eta-update.ts => amplify-meta-updaters.ts} | 48 +- .../utils/attach-prev-params-to-context.ts | 11 + ...s-applier.ts => auth-defaults-appliers.ts} | 24 + ...st-adaptor.ts => auth-request-adaptors.ts} | 64 ++- .../utils/message-printer.ts | 27 + .../utils/post-add-auth-message-printer.ts | 15 - .../utils/synthesize-resources.ts | 250 ++++++--- .../auth/1/UpdateAuthRequest.schema.json | 483 ++++++++++++++++++ .../scripts/generateSchemas.ts | 8 +- .../amplify-headless-interface/src/index.ts | 1 + .../src/interface/auth/update.ts | 36 ++ .../amplify-util-headless-input/src/index.ts | 7 +- .../src/schemaSuppliers.ts | 4 + 20 files changed, 971 insertions(+), 344 deletions(-) delete mode 100644 packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/get-add-auth-handler.ts create mode 100644 packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/resource-handlers.ts rename packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/{post-add-auth-meta-update.ts => amplify-meta-updaters.ts} (51%) create mode 100644 packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/attach-prev-params-to-context.ts rename packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/{add-auth-defaults-applier.ts => auth-defaults-appliers.ts} (58%) rename packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/{add-auth-request-adaptor.ts => auth-request-adaptors.ts} (79%) create mode 100644 packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/message-printer.ts delete mode 100644 packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/post-add-auth-message-printer.ts create mode 100644 packages/amplify-headless-interface/schemas/auth/1/UpdateAuthRequest.schema.json create mode 100644 packages/amplify-headless-interface/src/interface/auth/update.ts diff --git a/packages/amplify-category-auth/src/commands/auth/update.js b/packages/amplify-category-auth/src/commands/auth/update.js index fd599cbc558..312aee6f6cd 100644 --- a/packages/amplify-category-auth/src/commands/auth/update.js +++ b/packages/amplify-category-auth/src/commands/auth/update.js @@ -2,9 +2,10 @@ const { messages } = require('../../provider-utils/awscloudformation/assets/stri const { getAuthResourceName } = require('../../utils/getAuthResourceName'); const { transformUserPoolGroupSchema } = require('../../provider-utils/awscloudformation/utils/transform-user-pool-group'); const path = require('path'); +const { category } = require('../..'); +const { attachPrevParamsToContext } = require('../../provider-utils/awscloudformation/utils/attach-prev-params-to-context'); const subcommand = 'update'; -const category = 'auth'; let options; module.exports = { @@ -48,48 +49,9 @@ module.exports = { context.print.error('Provider not configured for this category'); return; } - return providerController.updateResource(context, category, options); + return providerController.updateResource(context, options); }) .then(async name => { - // eslint-disable-line no-shadow - const resourceDirPath = path.join(amplify.pathManager.getBackendDirPath(), 'auth', name, 'parameters.json'); - const authParameters = amplify.readJsonFile(resourceDirPath); - if (authParameters.dependsOn) { - amplify.updateamplifyMetaAfterResourceUpdate(category, name, 'dependsOn', authParameters.dependsOn); - } - - let customAuthConfigured = false; - if (authParameters.triggers) { - const triggers = JSON.parse(authParameters.triggers); - customAuthConfigured = - triggers.DefineAuthChallenge && - triggers.DefineAuthChallenge.length > 0 && - triggers.CreateAuthChallenge && - triggers.CreateAuthChallenge.length > 0 && - triggers.VerifyAuthChallengeResponse && - triggers.VerifyAuthChallengeResponse.length > 0; - } - amplify.updateamplifyMetaAfterResourceUpdate(category, resourceName, 'customAuth', customAuthConfigured); - - // Update Identity Pool dependency attributes on userpool groups - const allResources = context.amplify.getProjectMeta(); - if (allResources.auth && allResources.auth.userPoolGroups) { - let attributes = ['UserPoolId', 'AppClientIDWeb', 'AppClientID']; - if (authParameters.identityPoolName) { - attributes.push('IdentityPoolId'); - } - const userPoolGroupDependsOn = [ - { - category: 'auth', - resourceName, - attributes, - }, - ]; - - amplify.updateamplifyMetaAfterResourceUpdate('auth', 'userPoolGroups', 'dependsOn', userPoolGroupDependsOn); - await transformUserPoolGroupSchema(context); - } - const { print } = context; print.success(`Successfully updated resource ${name} locally`); print.info(''); diff --git a/packages/amplify-category-auth/src/index.js b/packages/amplify-category-auth/src/index.js index 25179cfa692..61afdc1c08f 100644 --- a/packages/amplify-category-auth/src/index.js +++ b/packages/amplify-category-auth/src/index.js @@ -17,10 +17,11 @@ const { ENV_SPECIFIC_PARAMS } = require('./provider-utils/awscloudformation/cons const { transformUserPoolGroupSchema } = require('./provider-utils/awscloudformation/utils/transform-user-pool-group'); const { uploadFiles } = require('./provider-utils/awscloudformation/utils/trigger-file-uploader'); -const { validateAddAuthRequest } = require('amplify-util-headless-input'); -const { getAddAuthRequestAdaptor } = require('./provider-utils/awscloudformation/utils/add-auth-request-adaptor'); -const { getAddAuthHandler } = require('./provider-utils/awscloudformation/handlers/get-add-auth-handler'); +const { validateAddAuthRequest, validateUpdateAuthRequest } = require('amplify-util-headless-input'); +const { getAddAuthRequestAdaptor, getUpdateAuthRequestAdaptor } = require('./provider-utils/awscloudformation/utils/auth-request-adaptors'); +const { getAddAuthHandler, getUpdateAuthHandler } = require('./provider-utils/awscloudformation/handlers/resource-handlers'); const { projectHasAuth } = require('./provider-utils/awscloudformation/utils/project-has-auth'); +const { attachPrevParamsToContext } = require('./provider-utils/awscloudformation/utils/attach-prev-params-to-context'); // this function is being kept for temporary compatability. async function add(context) { @@ -316,6 +317,12 @@ const executeAmplifyHeadlessCommand = async (context, headlessPayload) => { .then(getAddAuthRequestAdaptor(context.amplify.getProjectConfig().frontend)) .then(getAddAuthHandler(context)); return; + case 'update': + await attachPrevParamsToContext(context); + await validateUpdateAuthRequest(headlessPayload) + .then(getUpdateAuthRequestAdaptor(context.amplify.getProjectConfig().frontend, context.updatingAuth.requiredAttributes)) + .then(getUpdateAuthHandler(context)); + return; default: context.print.error(`Headless mode for ${context.input.command} auth is not implemented yet`); return; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/constants.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/constants.ts index d8a2093575b..912101ee63c 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/constants.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/constants.ts @@ -34,7 +34,8 @@ export const safeDefaults = [ 'userpoolClientRefreshTokenValidity', ]; -export const protectedValues = [ +// These attributes cannot be modified once the auth resource is created +export const immutableAttributes = [ 'resourceName', 'userPoolName', 'identityPoolName', diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/get-add-auth-handler.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/get-add-auth-handler.ts deleted file mode 100644 index 41e4c7acf14..00000000000 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/get-add-auth-handler.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { ServiceQuestionsResult } from '../service-walkthrough-types'; -import { getAddAuthDefaultsApplier } from '../utils/add-auth-defaults-applier'; -import { getResourceSynthesizer } from '../utils/synthesize-resources'; -import { getPostAddAuthMetaUpdater } from '../utils/post-add-auth-meta-update'; -import { getPostAddAuthMessagePrinter } from '../utils/post-add-auth-message-printer'; - -/** - * Factory function that returns a ServiceQuestionsResult consumer that handles all of the resource generation logic. - * The consumer returns the resourceName of the generated resource. - * @param context The amplify context - */ -export const getAddAuthHandler = (context: any) => async (request: ServiceQuestionsResult) => { - const serviceMetadata = require('../../supported-services').supportedServices[request.serviceName]; - const { cfnFilename, defaultValuesFilename, provider } = serviceMetadata; - let projectName = context.amplify.getProjectConfig().projectName.toLowerCase(); - const disallowedChars = /[^A-Za-z0-9]+/g; - projectName = projectName.replace(disallowedChars, ''); - const requestWithDefaults = await getAddAuthDefaultsApplier(context, defaultValuesFilename, projectName)(request); - await getResourceSynthesizer( - context, - cfnFilename, - provider, - )(requestWithDefaults) - .then(req => req.resourceName!) - .then(getPostAddAuthMetaUpdater(context, { service: requestWithDefaults.serviceName, providerName: provider })) - .then(getPostAddAuthMessagePrinter(context)) - .catch(err => { - context.print.info(err.stack); - context.print.error('There was an error adding the auth resource'); - context.usageData.emitError(err); - process.exitCode = 1; - }); - return requestWithDefaults.resourceName!; -}; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/resource-handlers.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/resource-handlers.ts new file mode 100644 index 00000000000..9a0f8914892 --- /dev/null +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/handlers/resource-handlers.ts @@ -0,0 +1,55 @@ +import { ServiceQuestionsResult } from '../service-walkthrough-types'; +import { getAddAuthDefaultsApplier, getUpdateAuthDefaultsApplier } from '../utils/auth-defaults-appliers'; +import { getResourceSynthesizer, getResourceUpdater } from '../utils/synthesize-resources'; +import { getPostAddAuthMetaUpdater, getPostUpdateAuthMetaUpdater } from '../utils/amplify-meta-updaters'; +import { getPostAddAuthMessagePrinter, getPostUpdateAuthMessagePrinter } from '../utils/message-printer'; +import { supportedServices } from '../../supported-services'; + +/** + * Factory function that returns a ServiceQuestionsResult consumer that handles all of the resource generation logic. + * The consumer returns the resourceName of the generated resource. + * @param context The amplify context + */ +export const getAddAuthHandler = (context: any) => async (request: ServiceQuestionsResult) => { + const serviceMetadata = supportedServices[request.serviceName]; + const { cfnFilename, defaultValuesFilename, provider } = serviceMetadata; + let projectName = context.amplify.getProjectConfig().projectName.toLowerCase(); + const disallowedChars = /[^A-Za-z0-9]+/g; + projectName = projectName.replace(disallowedChars, ''); + const requestWithDefaults = await getAddAuthDefaultsApplier(context, defaultValuesFilename, projectName)(request); + await getResourceSynthesizer( + context, + cfnFilename, + provider, + )(requestWithDefaults) + .then(req => req.resourceName!) + .then(getPostAddAuthMetaUpdater(context, { service: requestWithDefaults.serviceName, providerName: provider })) + .then(getPostAddAuthMessagePrinter(context.print)) + .catch(err => { + context.print.info(err.stack); + context.print.error('There was an error adding the auth resource'); + context.usageData.emitError(err); + process.exitCode = 1; + }); + return requestWithDefaults.resourceName!; +}; + +export const getUpdateAuthHandler = (context: any) => async (request: ServiceQuestionsResult) => { + const { cfnFilename, defaultValuesFilename, provider } = supportedServices[request.serviceName]; + const requestWithDefaults = await getUpdateAuthDefaultsApplier(defaultValuesFilename, context.updatingAuth)(request); + await getResourceUpdater( + context, + cfnFilename, + provider, + )(requestWithDefaults) + .then(req => req.resourceName!) + .then(getPostUpdateAuthMetaUpdater(context)) + .then(getPostUpdateAuthMessagePrinter(context.print)) + .catch(err => { + context.print.info(err.stack); + context.print.error('There was an error updating the auth resource'); + context.usageData.emitError(err); + process.exitCode = 1; + }); + return requestWithDefaults.resourceName!; +}; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js index 9d36e791d18..d32702754ce 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js @@ -1,21 +1,11 @@ const inquirer = require('inquirer'); const open = require('open'); -const path = require('path'); -const fs = require('fs-extra'); const _ = require('lodash'); const { getAuthResourceName } = require('../../utils/getAuthResourceName'); -const { verificationBucketName } = require('./utils/verification-bucket-name'); -const { - copyCfnTemplate, - removeDeprecatedProps, - createUserPoolGroups, - addAdminAuth, - saveResourceParameters, - lambdaTriggers, - copyS3Assets, -} = require('./utils/synthesize-resources'); -const { protectedValues, safeDefaults, ENV_SPECIFIC_PARAMS, privateKeys } = require('./constants'); -const { getAddAuthHandler } = require('./handlers/get-add-auth-handler'); +const { copyCfnTemplate, saveResourceParameters } = require('./utils/synthesize-resources'); +const { immutableAttributes: ENV_SPECIFIC_PARAMS, privateKeys } = require('./constants'); +const { getAddAuthHandler, getUpdateAuthHandler } = require('./handlers/resource-handlers'); +const { supportedServices } = require('../supported-services'); function serviceQuestions(context, defaultValuesFilename, stringMapFilename, serviceWalkthroughFilename, serviceMetadata) { const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`; @@ -24,156 +14,26 @@ function serviceQuestions(context, defaultValuesFilename, stringMapFilename, ser } async function addResource(context, service) { - const serviceMetadata = require('../supported-services').supportedServices[service]; + const serviceMetadata = supportedServices[service]; const { defaultValuesFilename, stringMapFilename, serviceWalkthroughFilename } = serviceMetadata; return getAddAuthHandler(context)( await serviceQuestions(context, defaultValuesFilename, stringMapFilename, serviceWalkthroughFilename, serviceMetadata), ); } -// may be able to consolidate this into just createUserPoolGroups -async function updateUserPoolGroups(context, resourceName, userPoolGroupList) { - if (userPoolGroupList && userPoolGroupList.length > 0) { - const userPoolGroupPrecedenceList = []; - - for (let i = 0; i < userPoolGroupList.length; i += 1) { - userPoolGroupPrecedenceList.push({ - groupName: userPoolGroupList[i], - precedence: i + 1, - }); - } - - const userPoolGroupFile = path.join( - context.amplify.pathManager.getBackendDirPath(), - 'auth', - 'userPoolGroups', - 'user-pool-group-precedence.json', - ); - - fs.outputFileSync(userPoolGroupFile, JSON.stringify(userPoolGroupPrecedenceList, null, 4)); - - context.amplify.updateamplifyMetaAfterResourceUpdate('auth', 'userPoolGroups', { - service: 'Cognito-UserPool-Groups', - providerPlugin: 'awscloudformation', - dependsOn: [ - { - category: 'auth', - resourceName, - attributes: ['UserPoolId', 'AppClientIDWeb', 'AppClientID', 'IdentityPoolId'], - }, - ], - }); - } -} - -async function updateResource(context, category, serviceResult) { - const { service, resourceName } = serviceResult; - let props = {}; - const serviceMetadata = require('../supported-services').supportedServices[service]; - const { cfnFilename, defaultValuesFilename, stringMapFilename, serviceWalkthroughFilename, provider } = serviceMetadata; - - return serviceQuestions(context, defaultValuesFilename, stringMapFilename, serviceWalkthroughFilename, serviceMetadata) - .then(async result => { - const defaultValuesSrc = `${__dirname}/assets/${defaultValuesFilename}`; - const { functionMap } = require(defaultValuesSrc); - const { authProviders } = require(`${__dirname}/assets/string-maps.js`); - - /* if user has used the default configuration, - * we populate base choices like authSelections and resourceName for them */ - if (!result.authSelections) { - result.authSelections = 'identityPoolAndUserPool'; - } - - const defaults = functionMap[result.authSelections](context.updatingAuth.resourceName); - - // removing protected values from results - for (let i = 0; i < protectedValues.length; i += 1) { - if (context.updatingAuth[protectedValues[i]]) { - delete result[protectedValues[i]]; - } - } - - if (result.useDefault && ['default', 'defaultSocial'].includes(result.useDefault)) { - for (let i = 0; i < safeDefaults.length; i += 1) { - delete context.updatingAuth[safeDefaults[i]]; - } - } - - await verificationBucketName(result, context.updatingAuth); - - props = Object.assign(defaults, removeDeprecatedProps(context.updatingAuth), result); - - const resources = context.amplify.getProjectMeta(); - - if (resources.auth.userPoolGroups) { - await updateUserPoolGroups(context, props.resourceName, result.userPoolGroupList); - } else { - await createUserPoolGroups(context, props.resourceName, result.userPoolGroupList); - } - - if (resources.api && resources.api.AdminQueries) { - // Find Existing functionName - let functionName; - if (resources.api.AdminQueries.dependsOn) { - const adminFunctionResource = resources.api.AdminQueries.dependsOn.find( - resource => resource.category === 'function' && resource.resourceName.includes('AdminQueries'), - ); - if (adminFunctionResource) { - functionName = adminFunctionResource.resourceName; - } - } - await addAdminAuth(context, props.resourceName, 'update', result.adminQueryGroup, functionName); - } else { - await addAdminAuth(context, props.resourceName, 'add', result.adminQueryGroup); - } - - const providerPlugin = context.amplify.getPluginInstance(context, provider); - const previouslySaved = providerPlugin.loadResourceParameters(context, 'auth', resourceName).triggers || '{}'; - await lambdaTriggers(props, context, JSON.parse(previouslySaved)); - - if ((!result.updateFlow && !result.thirdPartyAuth) || (result.updateFlow === 'manual' && !result.thirdPartyAuth)) { - delete props.selectedParties; - delete props.authProviders; - authProviders.forEach(a => { - if (props[a.answerHashKey]) { - delete props[a.answerHashKey]; - } - }); - if (props.googleIos) { - delete props.googleIos; - } - if (props.googleAndroid) { - delete props.googleAndroid; - } - if (props.audiences) { - delete props.audiences; - } - } - - if (props.useDefault === 'default' || props.hostedUI === false) { - delete props.oAuthMetadata; - delete props.hostedUIProviderMeta; - delete props.hostedUIProviderCreds; - delete props.hostedUIDomainName; - delete props.authProvidersUserPool; - } - - if (result.updateFlow !== 'updateUserPoolGroups' && result.updateFlow !== 'updateAdminQueries') { - await copyCfnTemplate(context, category, props, cfnFilename); - saveResourceParameters(context, provider, category, resourceName, props, ENV_SPECIFIC_PARAMS); - } - }) - .then(async () => { - await copyS3Assets(context, props); - return props.resourceName; - }); +async function updateResource(context, { service }) { + const serviceMetadata = supportedServices[service]; + const { defaultValuesFilename, stringMapFilename, serviceWalkthroughFilename } = serviceMetadata; + return getUpdateAuthHandler(context)( + await serviceQuestions(context, defaultValuesFilename, stringMapFilename, serviceWalkthroughFilename, serviceMetadata), + ); } async function updateConfigOnEnvInit(context, category, service) { - const srvcMetaData = require('../supported-services').supportedServices.Cognito; - const { defaultValuesFilename, stringMapFilename, serviceWalkthroughFilename } = srvcMetaData; + const srvcMetaData = supportedServices.Cognito; + const { defaultValuesFilename, stringMapFilename, serviceWalkthroughFilename, provider } = srvcMetaData; - const providerPlugin = context.amplify.getPluginInstance(context, srvcMetaData.provider); + const providerPlugin = context.amplify.getPluginInstance(context, provider); // previously selected answers const resourceParams = providerPlugin.loadResourceParameters(context, 'auth', service); // ask only env specific questions @@ -240,8 +100,7 @@ async function migrate(context) { if (!Object.keys(existingAuth).length > 0) { return; } - const servicesMetadata = require('../supported-services').supportedServices; - const { provider, cfnFilename, defaultValuesFilename } = servicesMetadata.Cognito; + const { provider, cfnFilename, defaultValuesFilename } = supportedServices.Cognito; const defaultValuesSrc = `${__dirname}/assets/${defaultValuesFilename}`; const { roles } = require(defaultValuesSrc); @@ -420,8 +279,7 @@ async function openIdentityPoolConsole(context, region, identityPoolId) { } function getPermissionPolicies(context, service, resourceName, crudOptions) { - const serviceMetadata = require('../supported-services').supportedServices[service]; - const { serviceWalkthroughFilename } = serviceMetadata; + const { serviceWalkthroughFilename } = supportedServices[service]; const serviceWalkthroughSrc = `${__dirname}/service-walkthroughs/${serviceWalkthroughFilename}`; const { getIAMPolicies } = require(serviceWalkthroughSrc); diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/service-walkthrough-types.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/service-walkthrough-types.ts index 06e25821683..d7a184b4cfd 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/service-walkthrough-types.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/service-walkthrough-types.ts @@ -7,12 +7,14 @@ export type ServiceQuestionsResult = ServiceQuestionsBaseResult & PasswordPolicyResult & PasswordRecoveryResult & MfaResult & - AdminQueriesResult; + AdminQueriesResult & + Triggers; export interface ServiceQuestionsBaseResult { - serviceName: string; + serviceName: 'Cognito'; resourceName?: string; - useDefault: 'manual'; + useDefault: 'default' | 'defaultSocial' | 'manual'; + updateFlow?: 'default' | 'defaultSocial' | 'manual' | 'callbacks' | 'providers' | 'updateUserPoolGroups' | 'updateAdminQueries'; requiredAttributes: string[]; authSelections: 'userPoolOnly' | 'identityPoolAndUserPool'; userPoolName?: string; @@ -25,7 +27,7 @@ export interface ServiceQuestionsBaseResult { } export interface OAuthResult { - hostedUI: boolean; + hostedUI?: boolean; hostedUIDomainName?: string; hostedUIProviderMeta?: any; hostedUIProviderCreds?: any; @@ -87,6 +89,10 @@ export type PasswordPolicy = 'Requires Lowercase' | 'Requires Numbers' | 'Requir export type UsernameAttributes = 'username' | 'email' | 'phone_number' | 'email, phone_number'; +export interface Triggers { + triggers?: any; // TODO create a type for this +} + export enum TriggerType { CreateAuthChallenge = 'CreateAuthChallenge', CustomMessage = 'CustomMessage', diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/post-add-auth-meta-update.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/amplify-meta-updaters.ts similarity index 51% rename from packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/post-add-auth-meta-update.ts rename to packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/amplify-meta-updaters.ts index 7b0d01d9c9e..636ed0493a2 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/post-add-auth-meta-update.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/amplify-meta-updaters.ts @@ -1,5 +1,6 @@ import * as path from 'path'; -import { JSONUtilities } from 'amplify-cli-core'; +import { JSONUtilities, $TSAny, pathManager } from 'amplify-cli-core'; +import { transformUserPoolGroupSchema } from './transform-user-pool-group'; /** * Factory function that returns a function that updates Amplify meta files after adding auth resource assets * @@ -54,3 +55,48 @@ export const getPostAddAuthMetaUpdater = (context: any, resultMetadata: { servic } return resourceName; }; + +/** + * Factory function that returns a function that updates Amplify meta files after updating auth resource assets + * @param context The amplify context + */ +export const getPostUpdateAuthMetaUpdater = (context: any) => async (resourceName: string) => { + const resourceDirPath = path.join(pathManager.getBackendDirPath(), 'auth', resourceName, 'parameters.json'); + const authParameters = JSONUtilities.readJson<$TSAny>(resourceDirPath); + if (authParameters.dependsOn) { + context.amplify.updateamplifyMetaAfterResourceUpdate('auth', resourceName, 'dependsOn', authParameters.dependsOn); + } + + let customAuthConfigured = false; + if (authParameters.triggers) { + const triggers = JSON.parse(authParameters.triggers); + customAuthConfigured = + triggers.DefineAuthChallenge && + triggers.DefineAuthChallenge.length > 0 && + triggers.CreateAuthChallenge && + triggers.CreateAuthChallenge.length > 0 && + triggers.VerifyAuthChallengeResponse && + triggers.VerifyAuthChallengeResponse.length > 0; + } + context.amplify.updateamplifyMetaAfterResourceUpdate('auth', resourceName, 'customAuth', customAuthConfigured); + + // Update Identity Pool dependency attributes on userpool groups + const allResources = context.amplify.getProjectMeta(); + if (allResources.auth && allResources.auth.userPoolGroups) { + let attributes = ['UserPoolId', 'AppClientIDWeb', 'AppClientID']; + if (authParameters.identityPoolName) { + attributes.push('IdentityPoolId'); + } + const userPoolGroupDependsOn = [ + { + category: 'auth', + resourceName, + attributes, + }, + ]; + + context.amplify.updateamplifyMetaAfterResourceUpdate('auth', 'userPoolGroups', 'dependsOn', userPoolGroupDependsOn); + await transformUserPoolGroupSchema(context); + } + return resourceName; +}; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/attach-prev-params-to-context.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/attach-prev-params-to-context.ts new file mode 100644 index 00000000000..20438b26992 --- /dev/null +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/attach-prev-params-to-context.ts @@ -0,0 +1,11 @@ +import { getAuthResourceName } from '../../../utils/getAuthResourceName'; +import { supportedServices } from '../../supported-services'; + +/** + * Some existing code relies on the existing auth parameters being attached to context.updatingAuth + */ +export const attachPrevParamsToContext = async (context: any) => { + const resourceName = await getAuthResourceName(context); + const providerPlugin = context.amplify.getPluginInstance(context, supportedServices.Cognito.provider); + context.updatingAuth = providerPlugin.loadResourceParameters(context, 'auth', resourceName); +}; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/add-auth-defaults-applier.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/auth-defaults-appliers.ts similarity index 58% rename from packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/add-auth-defaults-applier.ts rename to packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/auth-defaults-appliers.ts index 14f4e58c7c6..74be9716e8d 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/add-auth-defaults-applier.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/auth-defaults-appliers.ts @@ -2,6 +2,8 @@ import { ServiceQuestionsResult } from '../service-walkthrough-types'; import { verificationBucketName } from './verification-bucket-name'; import { merge } from 'lodash'; import { structureOAuthMetadata } from '../service-walkthroughs/auth-questions'; +import { removeDeprecatedProps } from './synthesize-resources'; +import { immutableAttributes, safeDefaults } from '../constants'; /** * Factory function that returns a function that applies default values to a ServiceQuestionsResult request. @@ -25,3 +27,25 @@ export const getAddAuthDefaultsApplier = (context: any, defaultValuesFilename: s * ensuring that manual entries override defaults */ return merge(functionMap[result.authSelections](result.resourceName), result, roles); }; + +export const getUpdateAuthDefaultsApplier = (defaultValuesFilename: string, previousResult: ServiceQuestionsResult) => async ( + result: ServiceQuestionsResult, +): Promise => { + const { functionMap } = await import(`../assets/${defaultValuesFilename}`); + if (!result.authSelections) { + result.authSelections = 'identityPoolAndUserPool'; + } + + const defaults = functionMap[result.authSelections](previousResult.resourceName); + + // ensure immutable attributes are removed from result + immutableAttributes.filter(pv => pv in previousResult).forEach(pv => delete (result as any)[pv]); + + if (['default', 'defaultSocial'].includes(result.useDefault)) { + safeDefaults.forEach(sd => delete (previousResult as any)[sd]); + } + + await verificationBucketName(result, previousResult); + + return merge(defaults, removeDeprecatedProps(previousResult), result); +}; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/add-auth-request-adaptor.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/auth-request-adaptors.ts similarity index 79% rename from packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/add-auth-request-adaptor.ts rename to packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/auth-request-adaptors.ts index f50fe36fa33..776f4605f22 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/add-auth-request-adaptor.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/auth-request-adaptors.ts @@ -9,6 +9,10 @@ import { CognitoIdentityPoolConfiguration, CognitoOAuthConfiguration, CognitoSocialProviderConfiguration, + UpdateAuthRequest, + CognitoUserPoolConfiguration, + CognitoUserPoolModification, + CognitoIdentityPoolModification, } from 'amplify-headless-interface'; import { identityPoolProviders, userPoolProviders } from '../service-walkthroughs/auth-questions'; import { merge } from 'lodash'; @@ -44,11 +48,48 @@ export const getAddAuthRequestAdaptor: AddAuthRequestAdaptorFactory = projectTyp return { serviceName: cognitoConfig.serviceName, resourceName, - useDefault: 'manual', requiredAttributes, - authSelections: cognitoConfig.includeIdentityPool ? 'identityPoolAndUserPool' : 'userPoolOnly', + ...immutableAttributeAdaptor(userPoolConfig, identityPoolConfig), + ...mutableAttributeAdaptor(projectType, requiredAttributes, userPoolConfig, cognitoConfig.includeIdentityPool, identityPoolConfig), + }; +}; + +export const getUpdateAuthRequestAdaptor = (projectType: string, requiredAttributes: string[]) => ({ + serviceModification, +}: UpdateAuthRequest): ServiceQuestionsResult => { + const idPoolModification = serviceModification.includeIdentityPool ? serviceModification.identityPoolModification : undefined; + return { + serviceName: serviceModification.serviceName, + requiredAttributes, + ...mutableAttributeAdaptor( + projectType, + requiredAttributes, + serviceModification.userPoolModification, + serviceModification.includeIdentityPool, + idPoolModification, + ), + }; +}; + +const immutableAttributeAdaptor = (userPoolConfig: CognitoUserPoolConfiguration, identityPoolConfig?: CognitoIdentityPoolConfiguration) => { + return { userPoolName: userPoolConfig.userPoolName, usernameAttributes: signinAttributeMap[userPoolConfig.signinMethod], + ...immutableIdentityPoolMap(identityPoolConfig), + }; +}; + +const mutableAttributeAdaptor = ( + projectType: string, + requiredAttributes: string[], + userPoolConfig: CognitoUserPoolConfiguration | CognitoUserPoolModification, + includeIdentityPool: boolean, + identityPoolConfig?: CognitoIdentityPoolConfiguration | CognitoIdentityPoolModification, +) => { + return { + useDefault: 'manual' as 'manual', + updateFlow: 'manual' as 'manual', + authSelections: includeIdentityPool ? 'identityPoolAndUserPool' : ('userPoolOnly' as 'userPoolOnly' | 'identityPoolAndUserPool'), userPoolGroups: (userPoolConfig.userPoolGroups?.length || 0) > 0, userPoolGroupList: (userPoolConfig.userPoolGroups || []).map(group => group.groupName), // TODO may need to map "customPolicy" userpoolClientRefreshTokenValidity: userPoolConfig.refreshTokenPeriod, @@ -58,14 +99,17 @@ export const getAddAuthRequestAdaptor: AddAuthRequestAdaptorFactory = projectTyp ...mfaMap(userPoolConfig.mfa), ...passwordRecoveryMap(userPoolConfig.passwordRecovery), ...passwordPolicyMap(userPoolConfig.passwordPolicy), - ...identityPoolMap(identityPoolConfig, projectType), + ...mutableIdentityPoolMap(projectType, identityPoolConfig), ...oauthMap(userPoolConfig.oAuth, requiredAttributes), }; }; // converts the oauth config to the existing format -const oauthMap = (oauthConfig?: CognitoOAuthConfiguration, requiredAttributes: string[] = []): OAuthResult & SocialProviderResult => { - if (!oauthConfig) return { hostedUI: false }; +const oauthMap = ( + oauthConfig?: CognitoOAuthConfiguration, + requiredAttributes: string[] = [], +): (OAuthResult & SocialProviderResult) | {} => { + if (!oauthConfig) return {}; return { hostedUI: true, hostedUIDomainName: oauthConfig.domainPrefix, @@ -109,7 +153,10 @@ const socialProviderMap = ( }; // converts the identity pool config to the existing format -const identityPoolMap = (idPoolConfig: CognitoIdentityPoolConfiguration | undefined, projectType: string): IdentityPoolResult => { +const mutableIdentityPoolMap = ( + projectType: string, + idPoolConfig?: CognitoIdentityPoolConfiguration | CognitoIdentityPoolModification, +): IdentityPoolResult => { if (!idPoolConfig) return { thirdPartyAuth: false, @@ -117,7 +164,6 @@ const identityPoolMap = (idPoolConfig: CognitoIdentityPoolConfiguration | undefi }; type AppIds = Pick; const result = { - identityPoolName: idPoolConfig.identityPoolName, allowUnauthenticatedIdentities: idPoolConfig.unauthenticatedLogin, thirdPartyAuth: !!idPoolConfig.identitySocialFederation, authProviders: (idPoolConfig.identitySocialFederation || []) @@ -136,6 +182,10 @@ const identityPoolMap = (idPoolConfig: CognitoIdentityPoolConfiguration | undefi return result; }; +const immutableIdentityPoolMap = (idPoolConfig?: CognitoIdentityPoolConfiguration) => ({ + identityPoolName: idPoolConfig?.identityPoolName, +}); + // converts the password policy to the existing format const passwordPolicyMap = (pwPolicy?: CognitoPasswordPolicy): PasswordPolicyResult => { if (!pwPolicy) return {}; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/message-printer.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/message-printer.ts new file mode 100644 index 00000000000..662bfc97f18 --- /dev/null +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/message-printer.ts @@ -0,0 +1,27 @@ +/** + * A factory function that returns a function that prints the "success message" after adding auth + * @param print The amplify print object + */ +export const getPostAddAuthMessagePrinter = (print: any) => (resourceName: string) => { + print.success(`Successfully added auth resource ${resourceName} locally`); + printCommonText(print); +}; + +/** + * A factory function that returns a function that prints the "success message" after updating auth + * @param context The amplify print object + */ +export const getPostUpdateAuthMessagePrinter = (print: any) => (resourceName: string) => { + print.success(`Successfully updated auth resource ${resourceName} locally`); + printCommonText(print); +}; + +const printCommonText = (print: any) => { + print.info(''); + print.success('Some next steps:'); + print.info('"amplify push" will build all your local backend resources and provision it in the cloud'); + print.info( + '"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud', + ); + print.info(''); +}; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/post-add-auth-message-printer.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/post-add-auth-message-printer.ts deleted file mode 100644 index f143359bf62..00000000000 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/post-add-auth-message-printer.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * A factory function that returns a function that prints the "success message" after adding auth - * @param context The amplify context - */ -export const getPostAddAuthMessagePrinter = (context: any) => (resourceName: string) => { - const { print } = context; - print.success(`Successfully added auth resource ${resourceName} locally`); - print.info(''); - print.success('Some next steps:'); - print.info('"amplify push" will build all your local backend resources and provision it in the cloud'); - print.info( - '"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud', - ); - print.info(''); -}; diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/synthesize-resources.ts b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/synthesize-resources.ts index f157fe1f72f..31597c2cf12 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/synthesize-resources.ts +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/utils/synthesize-resources.ts @@ -1,10 +1,12 @@ import { ServiceQuestionsResult } from '../service-walkthrough-types'; import * as path from 'path'; -import { existsSync, copySync } from 'fs-extra'; +import { existsSync, copySync, outputFileSync } from 'fs-extra'; import uuid from 'uuid'; import { cfnTemplateRoot, privateKeys, adminAuthAssetRoot, triggerRoot, ENV_SPECIFIC_PARAMS } from '../constants'; import { ServiceName as FunctionServiceName } from 'amplify-category-function'; import { pathManager, JSONUtilities } from 'amplify-cli-core'; +import { get } from 'lodash'; +import { authProviders } from '../assets/string-maps'; const category = 'auth'; @@ -12,29 +14,149 @@ const category = 'auth'; * Factory function that returns a function that synthesizes all resources based on a ServiceQuestionsResult request. * The function returns the request unchanged to enable .then() chaining * @param context The amplify context - * @param cfnFilename The template cfnFilename + * @param cfnFilename The template CFN filename * @param provider The cloud provider name */ export const getResourceSynthesizer = (context: any, cfnFilename: string, provider: string) => async ( request: Readonly, ) => { await lambdaTriggers(request, context, null); - await createUserPoolGroups(context, request.resourceName, request.userPoolGroupList); + await createUserPoolGroups(context, request.resourceName!, request.userPoolGroupList); await addAdminAuth(context, request.resourceName!, 'add', request.adminQueryGroup); await copyCfnTemplate(context, category, request, cfnFilename); saveResourceParameters(context, provider, category, request.resourceName!, request, ENV_SPECIFIC_PARAMS); - await copyS3Assets(context, request); + await copyS3Assets(request); return request; }; /** - * The functions below should not be exported, but they are for now because the update flow still uses them individually - * Once the update flow is also refactored, they will be internal to this file + * Factory function that returns a function that updates the auth resource based on a ServiceQuestionsResult request. + * The function retusn the request unchanged to enable .then() chaining * - * They are refactored with minimal modification from awscloudformation/indes.js + * The code is more-or-less refactored as-is from the existing update logic + * @param context The amplify context + * @param cfnFilename The template CFN filename + * @param provider The cloud provider name */ +export const getResourceUpdater = (context: any, cfnFilename: string, provider: string) => async (request: ServiceQuestionsResult) => { + const resources = context.amplify.getProjectMeta(); + if (resources.auth.userPoolGroups) { + await updateUserPoolGroups(context, request.resourceName!, request.userPoolGroupList); + } else { + await createUserPoolGroups(context, request.resourceName!, request.userPoolGroupList); + } + + const adminQueriesFunctionName = get<{ category: string; resourceName: string }[]>(resources, ['api', 'AdminQueries', 'dependsOn'], []) + .filter(resource => resource.category === 'function') + .map(resource => resource.resourceName) + .find(resourceName => resourceName.includes('AdminQueries')); + if (adminQueriesFunctionName) { + await addAdminAuth(context, request.resourceName!, 'update', request.adminQueryGroup, adminQueriesFunctionName); + } else { + await addAdminAuth(context, request.resourceName!, 'add', request.adminQueryGroup); + } + + const providerPlugin = context.amplify.getPluginInstance(context, provider); + const previouslySaved = providerPlugin.loadResourceParameters(context, 'auth', request.resourceName).triggers || '{}'; + await lambdaTriggers(request, context, JSON.parse(previouslySaved)); + + if ((!request.updateFlow && !request.thirdPartyAuth) || (request.updateFlow === 'manual' && !request.thirdPartyAuth)) { + delete request.selectedParties; + delete request.authProviders; + authProviders.forEach(a => delete (request as any)[a.answerHashKey]); + if (request.googleIos) { + delete request.googleIos; + } + if (request.googleAndroid) { + delete request.googleAndroid; + } + if (request.audiences) { + delete request.audiences; + } + } + + if (request.useDefault === 'default' || request.hostedUI === false) { + delete request.oAuthMetadata; + delete request.hostedUIProviderMeta; + delete request.hostedUIProviderCreds; + delete request.hostedUIDomainName; + delete request.authProvidersUserPool; + } -export const lambdaTriggers = async (coreAnswers: any, context: any, previouslySaved: any) => { + if (request.updateFlow !== 'updateUserPoolGroups' && request.updateFlow !== 'updateAdminQueries') { + await copyCfnTemplate(context, category, request, cfnFilename); + saveResourceParameters(context, provider, category, request.resourceName!, request, ENV_SPECIFIC_PARAMS); + } + await copyS3Assets(request); + return request; +}; + +/** + * The 3 functions below should not be exported, but they are for now because externalAuthEnable still uses them individually + */ +export const copyCfnTemplate = async (context: any, category: string, options: any, cfnFilename: string) => { + const targetDir = path.join(pathManager.getBackendDirPath(), category, options.resourceName); + + const copyJobs = [ + { + dir: cfnTemplateRoot, + template: cfnFilename, + target: path.join(targetDir, `${options.resourceName}-cloudformation-template.yml`), + paramsFile: path.join(targetDir, 'parameters.json'), + }, + ]; + + const privateParams = Object.assign({}, options); + privateKeys.forEach(p => delete privateParams[p]); + + return await context.amplify.copyBatch(context, copyJobs, privateParams, true); +}; + +export const saveResourceParameters = ( + context: any, + providerName: string, + category: string, + resource: string, + params: any, + envSpecificParams: any[] = [], +) => { + const provider = context.amplify.getPluginInstance(context, providerName); + let privateParams = Object.assign({}, params); + privateKeys.forEach(p => delete privateParams[p]); + privateParams = removeDeprecatedProps(privateParams); + provider.saveResourceParameters(context, category, resource, privateParams, envSpecificParams); +}; + +export const removeDeprecatedProps = (props: any) => { + [ + 'authRoleName', + 'unauthRoleName', + 'userpoolClientName', + 'roleName', + 'policyName', + 'mfaLambdaLogPolicy', + 'mfaPassRolePolicy', + 'mfaLambdaIAMPolicy', + 'userpoolClientLogPolicy', + 'userpoolClientLambdaPolicy', + 'lambdaLogPolicy', + 'openIdRolePolicy', + 'openIdLambdaIAMPolicy', + 'mfaLambdaRole', + 'openIdLambdaRoleName', + 'CreateAuthChallenge', + 'CustomMessage', + 'DefineAuthChallenge', + 'PostAuthentication', + 'PostConfirmation', + 'PreAuthentication', + 'PreSignup', + 'VerifyAuthChallengeResponse', + ].forEach(deprecatedField => delete props[deprecatedField]); + return props; +}; + +const lambdaTriggers = async (coreAnswers: any, context: any, previouslySaved: any) => { const { handleTriggers } = require('./trigger-flow-auth-helper'); let triggerKeyValues = {}; @@ -65,7 +187,7 @@ export const lambdaTriggers = async (coreAnswers: any, context: any, previouslyS coreAnswers.dependsOn = context.amplify.dependsOnBlock(context, dependsOnKeys, 'Cognito'); }; -export const createUserPoolGroups = async (context: any, resourceName: any, userPoolGroupList: any) => { +const createUserPoolGroups = async (context: any, resourceName: string, userPoolGroupList?: string[]) => { if (userPoolGroupList && userPoolGroupList.length > 0) { const userPoolGroupPrecedenceList = []; @@ -108,7 +230,41 @@ export const createUserPoolGroups = async (context: any, resourceName: any, user } }; -export const addAdminAuth = async ( +const updateUserPoolGroups = async (context: any, resourceName: string, userPoolGroupList?: string[]) => { + if (userPoolGroupList && userPoolGroupList.length > 0) { + const userPoolGroupPrecedenceList = []; + + for (let i = 0; i < userPoolGroupList.length; i += 1) { + userPoolGroupPrecedenceList.push({ + groupName: userPoolGroupList[i], + precedence: i + 1, + }); + } + + const userPoolGroupFile = path.join( + context.amplify.pathManager.getBackendDirPath(), + 'auth', + 'userPoolGroups', + 'user-pool-group-precedence.json', + ); + + outputFileSync(userPoolGroupFile, JSON.stringify(userPoolGroupPrecedenceList, null, 4)); + + context.amplify.updateamplifyMetaAfterResourceUpdate('auth', 'userPoolGroups', { + service: 'Cognito-UserPool-Groups', + providerPlugin: 'awscloudformation', + dependsOn: [ + { + category: 'auth', + resourceName, + attributes: ['UserPoolId', 'AppClientIDWeb', 'AppClientID', 'IdentityPoolId'], + }, + ], + }); + } +}; + +const addAdminAuth = async ( context: any, authResourceName: string, operation: 'update' | 'add', @@ -125,7 +281,7 @@ export const addAdminAuth = async ( } }; -export const createAdminAuthFunction = async ( +const createAdminAuthFunction = async ( context: any, authResourceName: string, functionName: string, @@ -202,7 +358,7 @@ export const createAdminAuthFunction = async ( } }; -export const createAdminAuthAPI = async (context: any, authResourceName: string, functionName: string, operation: 'update' | 'add') => { +const createAdminAuthAPI = async (context: any, authResourceName: string, functionName: string, operation: 'update' | 'add') => { const apiName = 'AdminQueries'; const targetDir = path.join(pathManager.getBackendDirPath(), 'api', apiName); const dependsOn = []; @@ -257,72 +413,10 @@ export const createAdminAuthAPI = async (context: any, authResourceName: string, } }; -export const copyCfnTemplate = async (context: any, category: string, options: any, cfnFilename: string) => { - const targetDir = path.join(pathManager.getBackendDirPath(), category, options.resourceName); - - const copyJobs = [ - { - dir: cfnTemplateRoot, - template: cfnFilename, - target: path.join(targetDir, `${options.resourceName}-cloudformation-template.yml`), - paramsFile: path.join(targetDir, 'parameters.json'), - }, - ]; - - const privateParams = Object.assign({}, options); - privateKeys.forEach(p => delete privateParams[p]); - - return await context.amplify.copyBatch(context, copyJobs, privateParams, true); -}; - -export const saveResourceParameters = ( - context: any, - providerName: string, - category: string, - resource: string, - params: any, - envSpecificParams: any[] = [], -) => { - const provider = context.amplify.getPluginInstance(context, providerName); - let privateParams = Object.assign({}, params); - privateKeys.forEach(p => delete privateParams[p]); - privateParams = removeDeprecatedProps(privateParams); - provider.saveResourceParameters(context, category, resource, privateParams, envSpecificParams); -}; - -export const removeDeprecatedProps = (props: any) => { - [ - 'authRoleName', - 'unauthRoleName', - 'userpoolClientName', - 'roleName', - 'policyName', - 'mfaLambdaLogPolicy', - 'mfaPassRolePolicy', - 'mfaLambdaIAMPolicy', - 'userpoolClientLogPolicy', - 'userpoolClientLambdaPolicy', - 'lambdaLogPolicy', - 'openIdRolePolicy', - 'openIdLambdaIAMPolicy', - 'mfaLambdaRole', - 'openIdLambdaRoleName', - 'CreateAuthChallenge', - 'CustomMessage', - 'DefineAuthChallenge', - 'PostAuthentication', - 'PostConfirmation', - 'PreAuthentication', - 'PreSignup', - 'VerifyAuthChallengeResponse', - ].forEach(deprecatedField => delete props[deprecatedField]); - return props; -}; - -export const copyS3Assets = async (context: any, props: any) => { - const targetDir = path.join(pathManager.getBackendDirPath(), 'auth', props.resourceName, 'assets'); - const triggers = props.triggers ? JSONUtilities.parse(props.triggers) : null; - const confirmationFileNeeded = props.triggers && triggers.CustomMessage && triggers.CustomMessage.includes('verification-link'); +const copyS3Assets = async (request: ServiceQuestionsResult) => { + const targetDir = path.join(pathManager.getBackendDirPath(), 'auth', request.resourceName!, 'assets'); + const triggers = request.triggers ? JSONUtilities.parse(request.triggers) : null; + const confirmationFileNeeded = request.triggers && triggers.CustomMessage && triggers.CustomMessage.includes('verification-link'); if (confirmationFileNeeded) { if (!existsSync(targetDir)) { const source = path.join(triggerRoot, 'CustomMessage/assets'); diff --git a/packages/amplify-headless-interface/schemas/auth/1/UpdateAuthRequest.schema.json b/packages/amplify-headless-interface/schemas/auth/1/UpdateAuthRequest.schema.json new file mode 100644 index 00000000000..e13caf6c4b0 --- /dev/null +++ b/packages/amplify-headless-interface/schemas/auth/1/UpdateAuthRequest.schema.json @@ -0,0 +1,483 @@ +{ + "type": "object", + "properties": { + "version": { + "type": "number", + "enum": [ + 1 + ] + }, + "serviceModification": { + "anyOf": [ + { + "allOf": [ + { + "$ref": "#/definitions/BaseCognitoServiceModification" + }, + { + "$ref": "#/definitions/NoCognitoIdentityPool" + } + ] + }, + { + "allOf": [ + { + "$ref": "#/definitions/BaseCognitoServiceModification" + }, + { + "$ref": "#/definitions/ModifyCognitoIdentityPool" + } + ] + } + ] + } + }, + "required": [ + "serviceModification", + "version" + ], + "definitions": { + "BaseCognitoServiceModification": { + "type": "object", + "properties": { + "serviceName": { + "type": "string", + "enum": [ + "Cognito" + ] + }, + "userPoolModification": { + "$ref": "#/definitions/Pick" + } + }, + "required": [ + "serviceName", + "userPoolModification" + ] + }, + "Pick": { + "type": "object", + "properties": { + "userPoolGroups": { + "type": "array", + "items": { + "$ref": "#/definitions/CognitoUserPoolGroup" + } + }, + "adminQueries": { + "$ref": "#/definitions/CognitoAdminQueries" + }, + "mfa": { + "anyOf": [ + { + "$ref": "#/definitions/CognitoMFAOff" + }, + { + "$ref": "#/definitions/CognitoMFASettings" + } + ] + }, + "passwordPolicy": { + "$ref": "#/definitions/CognitoPasswordPolicy" + }, + "passwordRecovery": { + "anyOf": [ + { + "$ref": "#/definitions/CognitoEmailPasswordRecoveryConfiguration" + }, + { + "$ref": "#/definitions/CognitoSMSPasswordRecoveryConfiguration" + } + ] + }, + "refreshTokenPeriod": { + "type": "number" + }, + "readAttributes": { + "type": "array", + "items": { + "enum": [ + "ADDRESS", + "BIRTHDATE", + "EMAIL", + "EMAIL_VERIFIED", + "FAMILY_NAME", + "GENDER", + "GIVEN_NAME", + "LOCALE", + "MIDDLE_NAME", + "NAME", + "NICKNAME", + "PHONE_NUMBER", + "PHONE_NUMBER_VERIFIED", + "PICTURE", + "PREFERRED_USERNAME", + "PROFILE", + "UPDATED_AT", + "WEBSITE", + "ZONE_INFO" + ], + "type": "string" + } + }, + "writeAttributes": { + "type": "array", + "items": { + "enum": [ + "ADDRESS", + "BIRTHDATE", + "EMAIL", + "FAMILY_NAME", + "GENDER", + "GIVEN_NAME", + "LOCALE", + "MIDDLE_NAME", + "NAME", + "NICKNAME", + "PHONE_NUMBER", + "PICTURE", + "PREFERRED_USERNAME", + "PROFILE", + "UPDATED_AT", + "WEBSITE", + "ZONE_INFO" + ], + "type": "string" + } + }, + "oAuth": { + "$ref": "#/definitions/CognitoOAuthConfiguration" + }, + "addUserToGroup": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + } + }, + "required": [ + "groupName" + ] + }, + "emailBlacklist": { + "type": "array", + "items": { + "type": "string" + } + }, + "emailWhitelist": { + "type": "array", + "items": { + "type": "string" + } + }, + "customAuthScaffolding": { + "type": "object", + "properties": { + "customChallengeAnswer": { + "type": "string" + } + }, + "required": [ + "customChallengeAnswer" + ] + } + } + }, + "CognitoUserPoolGroup": { + "type": "object", + "properties": { + "customPolicy": { + "type": "string" + }, + "groupName": { + "type": "string" + } + }, + "required": [ + "groupName" + ] + }, + "CognitoAdminQueries": { + "type": "object", + "properties": { + "permissions": { + "type": "object", + "properties": { + "restrictAccess": { + "type": "boolean" + }, + "groupName": { + "type": "string" + } + }, + "required": [ + "restrictAccess" + ] + } + }, + "required": [ + "permissions" + ] + }, + "CognitoMFAOff": { + "type": "object", + "properties": { + "mode": { + "type": "string", + "enum": [ + "OFF" + ] + } + }, + "required": [ + "mode" + ] + }, + "CognitoMFASettings": { + "type": "object", + "properties": { + "mode": { + "enum": [ + "ON", + "OPTIONAL" + ], + "type": "string" + }, + "mfaTypes": { + "type": "array", + "items": { + "enum": [ + "SMS", + "TOTP" + ], + "type": "string" + } + }, + "smsMessage": { + "type": "string" + }, + "mfaWithCaptcha": { + "type": "object", + "properties": { + "googleRecaptchaSecret": { + "type": "string" + } + }, + "required": [ + "googleRecaptchaSecret" + ] + } + }, + "required": [ + "mfaTypes", + "mode", + "smsMessage" + ] + }, + "CognitoPasswordPolicy": { + "type": "object", + "properties": { + "minimumLength": { + "type": "number" + }, + "additionalConstraints": { + "type": "array", + "items": { + "enum": [ + "REQUIRE_DIGIT", + "REQUIRE_LOWERCASE", + "REQUIRE_SYMBOL", + "REQUIRE_UPPERCASE" + ], + "type": "string" + } + } + } + }, + "CognitoEmailPasswordRecoveryConfiguration": { + "type": "object", + "properties": { + "deliveryMethod": { + "type": "string", + "enum": [ + "EMAIL" + ] + }, + "emailMessage": { + "type": "string" + }, + "emailSubject": { + "type": "string" + } + }, + "required": [ + "deliveryMethod", + "emailMessage", + "emailSubject" + ] + }, + "CognitoSMSPasswordRecoveryConfiguration": { + "type": "object", + "properties": { + "deliveryMethod": { + "type": "string", + "enum": [ + "SMS" + ] + }, + "smsMessage": { + "type": "string" + } + }, + "required": [ + "deliveryMethod", + "smsMessage" + ] + }, + "CognitoOAuthConfiguration": { + "type": "object", + "properties": { + "domainPrefix": { + "type": "string" + }, + "redirectSigninURIs": { + "type": "array", + "items": { + "type": "string" + } + }, + "redirectSignoutURIs": { + "type": "array", + "items": { + "type": "string" + } + }, + "oAuthGrantType": { + "enum": [ + "CODE", + "IMPLICIT" + ], + "type": "string" + }, + "oAuthScopes": { + "type": "array", + "items": { + "enum": [ + "AWS.COGNITO.SIGNIN.USER.ADMIN", + "EMAIL", + "OPENID", + "PHONE", + "PROFILE" + ], + "type": "string" + } + }, + "socialProviderConfigurations": { + "type": "array", + "items": { + "$ref": "#/definitions/CognitoSocialProviderConfiguration" + } + } + }, + "required": [ + "oAuthGrantType", + "oAuthScopes", + "redirectSigninURIs", + "redirectSignoutURIs" + ] + }, + "CognitoSocialProviderConfiguration": { + "type": "object", + "properties": { + "provider": { + "enum": [ + "FACEBOOK", + "GOOGLE", + "LOGIN_WITH_AMAZON" + ], + "type": "string" + }, + "clientId": { + "type": "string" + }, + "clientSecret": { + "type": "string" + } + }, + "required": [ + "clientId", + "clientSecret", + "provider" + ] + }, + "NoCognitoIdentityPool": { + "description": "Specifies that the Cognito configuration should not include an identity pool", + "type": "object", + "properties": { + "includeIdentityPool": { + "type": "boolean", + "enum": [ + false + ] + } + }, + "required": [ + "includeIdentityPool" + ] + }, + "ModifyCognitoIdentityPool": { + "type": "object", + "properties": { + "includeIdentityPool": { + "type": "boolean", + "enum": [ + true + ] + }, + "identityPoolModification": { + "$ref": "#/definitions/Pick" + } + }, + "required": [ + "identityPoolModification", + "includeIdentityPool" + ] + }, + "Pick": { + "type": "object", + "properties": { + "unauthenticatedLogin": { + "type": "boolean" + }, + "identitySocialFederation": { + "type": "array", + "items": { + "$ref": "#/definitions/CognitoIdentitySocialFederation" + } + } + } + }, + "CognitoIdentitySocialFederation": { + "type": "object", + "properties": { + "provider": { + "enum": [ + "AMAZON", + "FACEBOOK", + "GOOGLE" + ], + "type": "string" + }, + "clientId": { + "type": "string" + } + }, + "required": [ + "clientId", + "provider" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/amplify-headless-interface/scripts/generateSchemas.ts b/packages/amplify-headless-interface/scripts/generateSchemas.ts index c1c9550b6cf..a0f7fcb0f71 100644 --- a/packages/amplify-headless-interface/scripts/generateSchemas.ts +++ b/packages/amplify-headless-interface/scripts/generateSchemas.ts @@ -13,6 +13,11 @@ const typeDefs: TypeDef[] = [ category: 'auth', relativeSourcePaths: [path.join('auth', 'add.ts')], }, + { + typeName: 'UpdateAuthRequest', + category: 'auth', + relativeSourcePaths: ['add.ts', 'update.ts'].map(file => path.join('auth', file)), + }, { typeName: 'AddApiRequest', category: 'api', @@ -27,7 +32,7 @@ const typeDefs: TypeDef[] = [ typeName: 'UpdateApiRequest', category: 'api', relativeSourcePaths: ['add.ts', 'update.ts'].map(file => path.join('api', file)), - } + }, ]; const schemaFileName = (typeName: string) => `${typeName}.schema.json`; @@ -53,6 +58,7 @@ typeDefs.forEach(typeDef => { } fs.ensureFileSync(schemaFilePath); fs.writeFileSync(schemaFilePath, JSON.stringify(typeSchema, undefined, 4)); + console.log(`Schema version ${version} written for type ${typeDef.typeName}.`); }); // Interface types are expected to be exported as "typeName" in the file diff --git a/packages/amplify-headless-interface/src/index.ts b/packages/amplify-headless-interface/src/index.ts index 63cebf731d8..9df27325c23 100644 --- a/packages/amplify-headless-interface/src/index.ts +++ b/packages/amplify-headless-interface/src/index.ts @@ -1,4 +1,5 @@ export * from './interface/api/add'; export * from './interface/api/update'; export * from './interface/auth/add'; +export * from './interface/auth/update'; export * from './interface/storage/add'; diff --git a/packages/amplify-headless-interface/src/interface/auth/update.ts b/packages/amplify-headless-interface/src/interface/auth/update.ts new file mode 100644 index 00000000000..c7a0c0aeb97 --- /dev/null +++ b/packages/amplify-headless-interface/src/interface/auth/update.ts @@ -0,0 +1,36 @@ +import { CognitoUserPoolConfiguration, CognitoIdentityPoolConfiguration, NoCognitoIdentityPool } from './add'; + +export interface UpdateAuthRequest { + version: 1; + serviceModification: CognitoServiceModification; +} + +export type CognitoServiceModification = BaseCognitoServiceModification & (NoCognitoIdentityPool | ModifyCognitoIdentityPool); + +export interface BaseCognitoServiceModification { + serviceName: 'Cognito'; + userPoolModification: CognitoUserPoolModification; +} + +export interface ModifyCognitoIdentityPool { + includeIdentityPool: true; + identityPoolModification: CognitoIdentityPoolModification; +} + +export type CognitoUserPoolModification = Pick< + CognitoUserPoolConfiguration, + | 'userPoolGroups' + | 'adminQueries' + | 'mfa' + | 'passwordPolicy' + | 'passwordRecovery' + | 'refreshTokenPeriod' + | 'readAttributes' + | 'writeAttributes' + | 'oAuth' + | 'addUserToGroup' + | 'emailBlacklist' + | 'emailWhitelist' + | 'customAuthScaffolding' +>; +export type CognitoIdentityPoolModification = Pick; diff --git a/packages/amplify-util-headless-input/src/index.ts b/packages/amplify-util-headless-input/src/index.ts index 7349f582002..b9c8af504cd 100644 --- a/packages/amplify-util-headless-input/src/index.ts +++ b/packages/amplify-util-headless-input/src/index.ts @@ -4,9 +4,10 @@ import { addApiRequestSchemaSupplier, addAuthRequestSchemaSupplier, updateApiRequestSchemaSupplier, + updateAuthRequestSchemaSupplier, } from './schemaSuppliers'; import { noopUpgradePipeline } from './upgradePipelines'; -import { AddStorageRequest, AddApiRequest, AddAuthRequest, UpdateApiRequest } from 'amplify-headless-interface'; +import { AddStorageRequest, AddApiRequest, AddAuthRequest, UpdateAuthRequest, UpdateApiRequest } from 'amplify-headless-interface'; export const validateAddStorageRequest = (raw: string) => { return new HeadlessInputValidator(addStorageRequestSchemaSupplier, noopUpgradePipeline).validate(raw); @@ -23,3 +24,7 @@ export const validateUpdateApiRequest = (raw: string) => { export const validateAddAuthRequest = (raw: string) => { return new HeadlessInputValidator(addAuthRequestSchemaSupplier, noopUpgradePipeline).validate(raw); }; + +export const validateUpdateAuthRequest = (raw: string) => { + return new HeadlessInputValidator(updateAuthRequestSchemaSupplier, noopUpgradePipeline).validate(raw); +}; diff --git a/packages/amplify-util-headless-input/src/schemaSuppliers.ts b/packages/amplify-util-headless-input/src/schemaSuppliers.ts index 70cf71265cf..f4e21116f45 100644 --- a/packages/amplify-util-headless-input/src/schemaSuppliers.ts +++ b/packages/amplify-util-headless-input/src/schemaSuppliers.ts @@ -8,6 +8,10 @@ export const addAuthRequestSchemaSupplier: VersionedSchemaSupplier = version => return getSchema('AddAuthRequest', 'auth', version); }; +export const updateAuthRequestSchemaSupplier: VersionedSchemaSupplier = version => { + return getSchema('UpdateAuthRequest', 'auth', version); +}; + export const addApiRequestSchemaSupplier: VersionedSchemaSupplier = version => { return getSchema('AddApiRequest', 'api', version); }; From a924a481ea3abbc09870b106b1c43756f8084e07 Mon Sep 17 00:00:00 2001 From: Edward Foyle Date: Tue, 22 Sep 2020 17:22:55 -0700 Subject: [PATCH 2/3] test: add headless auth e2e tests --- .../amplify-e2e-core/src/utils/headless.ts | 27 ++++-- .../src/__tests__/auth_2.test.ts | 86 ++++++++++++++++++- 2 files changed, 103 insertions(+), 10 deletions(-) diff --git a/packages/amplify-e2e-core/src/utils/headless.ts b/packages/amplify-e2e-core/src/utils/headless.ts index 7514798fc90..6c46913e96c 100644 --- a/packages/amplify-e2e-core/src/utils/headless.ts +++ b/packages/amplify-e2e-core/src/utils/headless.ts @@ -1,21 +1,36 @@ -import { AddApiRequest, UpdateApiRequest } from 'amplify-headless-interface'; +import { AddApiRequest, AddAuthRequest, UpdateApiRequest, UpdateAuthRequest } from 'amplify-headless-interface'; import execa from 'execa'; import { getCLIPath } from '..'; -export const addHeadlessApi = (cwd: string, request: AddApiRequest) => { - return executeHeadlessCommand(cwd, 'api', 'add', request); +export const addHeadlessApi = async (cwd: string, request: AddApiRequest) => { + await executeHeadlessCommand(cwd, 'api', 'add', request); }; export const updateHeadlessApi = async (cwd: string, request: UpdateApiRequest) => { - return executeHeadlessCommand(cwd, 'api', 'update', request); + await executeHeadlessCommand(cwd, 'api', 'update', request); }; export const removeHeadlessApi = async (cwd: string, apiName: string) => { - await execa(getCLIPath(), ['remove', 'api', apiName, '--yes'], { cwd }); + await headlessRemoveResource(cwd, 'api', apiName); }; +export const addHeadlessAuth = async (cwd: string, request: AddAuthRequest) => { + await executeHeadlessCommand(cwd, 'auth', 'add', request); +}; + +export const updateHeadlessAuth = async (cwd: string, request: UpdateAuthRequest) => { + await executeHeadlessCommand(cwd, 'auth', 'update', request); +}; + +export const removeHeadlessAuth = async (cwd: string, authName: string) => { + await headlessRemoveResource(cwd, 'auth', authName); +}; + +const headlessRemoveResource = async (cwd: string, category: string, resourceName: string) => { + await execa(getCLIPath(), ['remove', category, resourceName, '--yes'], { cwd }); +}; const executeHeadlessCommand = async (cwd: string, category: string, operation: string, request: AnyHeadlessRequest) => { await execa(getCLIPath(), [operation, category, '--headless'], { input: JSON.stringify(request), cwd }); }; -type AnyHeadlessRequest = AddApiRequest | UpdateApiRequest; +type AnyHeadlessRequest = AddApiRequest | UpdateApiRequest | AddAuthRequest | UpdateAuthRequest; diff --git a/packages/amplify-e2e-tests/src/__tests__/auth_2.test.ts b/packages/amplify-e2e-tests/src/__tests__/auth_2.test.ts index b7d37ee2ff2..a7a9df82c73 100644 --- a/packages/amplify-e2e-tests/src/__tests__/auth_2.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/auth_2.test.ts @@ -5,20 +5,19 @@ import { initIosProjectWithProfile, deleteProject, amplifyPushAuth, - amplifyPush, + addHeadlessAuth, + updateHeadlessAuth, + removeHeadlessAuth, } from 'amplify-e2e-core'; import { addAuthWithDefault, removeAuthWithDefault, - addAuthWithDefaultSocial, - addAuthWithGroupTrigger, addAuthWithRecaptchaTrigger, addAuthWithCustomTrigger, addAuthWithSignInSignOutUrl, updateAuthWithoutCustomTrigger, updateAuthRemoveRecaptchaTrigger, updateAuthSignInSignOutUrl, - addAuthViaAPIWithTrigger, addAuthWithMaxOptions, addAuthUserPoolOnly, getBackendAmplifyMeta, @@ -33,6 +32,8 @@ import { getUserPoolClients, getLambdaFunction, } from 'amplify-e2e-core'; +import { AddAuthRequest, CognitoUserPoolSigninMethod, CognitoUserProperty, UpdateAuthRequest } from 'amplify-headless-interface'; +import _ from 'lodash'; const defaultsSettings = { name: 'authTest', @@ -192,3 +193,80 @@ describe('amplify updating auth...', () => { expect(meta.Auth.Default.authenticationFlowType).toEqual('USER_SRP_AUTH'); }); }); + +describe('headless auth', () => { + let projRoot: string; + beforeEach(async () => { + projRoot = await createNewProjectDir('auth-update'); + }); + + afterEach(async () => { + await deleteProject(projRoot); + deleteProjectDir(projRoot); + }); + it('adds auth resource', async () => { + const addAuthRequest: AddAuthRequest = { + version: 1, + resourceName: 'myAuthResource', + serviceConfiguration: { + serviceName: 'Cognito', + includeIdentityPool: false, + userPoolConfiguration: { + requiredSignupAttributes: [CognitoUserProperty.EMAIL, CognitoUserProperty.PHONE_NUMBER], + signinMethod: CognitoUserPoolSigninMethod.USERNAME, + }, + }, + }; + + await initJSProjectWithProfile(projRoot, defaultsSettings); + await addHeadlessAuth(projRoot, addAuthRequest); + await amplifyPushAuth(projRoot); + const meta = getProjectMeta(projRoot); + const id = Object.keys(meta.auth).map(key => meta.auth[key])[0].output.UserPoolId; + const userPool = await getUserPool(id, meta.providers.awscloudformation.Region); + expect(userPool.UserPool).toBeDefined(); + }); + + it('updates existing auth resource', async () => { + const updateAuthRequest: UpdateAuthRequest = { + version: 1, + serviceModification: { + serviceName: 'Cognito', + userPoolModification: { + userPoolGroups: [ + { + groupName: 'group1', + }, + { + groupName: 'group2', + }, + ], + }, + includeIdentityPool: true, + identityPoolModification: { + unauthenticatedLogin: true, + }, + }, + }; + + await initJSProjectWithProfile(projRoot, defaultsSettings); + await addAuthWithDefault(projRoot, {}); + await updateHeadlessAuth(projRoot, updateAuthRequest); + await amplifyPushAuth(projRoot); + const meta = getProjectMeta(projRoot); + const id = Object.keys(meta.auth).map(key => meta.auth[key])[0].output.UserPoolId; + const userPool = await getUserPool(id, meta.providers.awscloudformation.Region); + expect(userPool.UserPool).toBeDefined(); + expect(_.get(meta, ['auth', 'userPoolGroups'])).toBeDefined(); + }); + + it('removes auth resource', async () => { + await initJSProjectWithProfile(projRoot, defaultsSettings); + await addAuthWithDefault(projRoot, {}); + const { auth: authBefore } = getBackendAmplifyMeta(projRoot); + const authResourceName = _.keys(authBefore).find(() => true); // first element or undefined + await removeHeadlessAuth(projRoot, authResourceName); + const { auth: authAfter } = getBackendAmplifyMeta(projRoot); + expect(_.isEmpty(authAfter)).toBe(true); + }); +}); From 88feff044180fb2e62c1b7a0bed7eab7d7679773 Mon Sep 17 00:00:00 2001 From: Edward Foyle Date: Wed, 23 Sep 2020 14:57:13 -0700 Subject: [PATCH 3/3] fix: ask correct questions on init env --- .../src/provider-utils/awscloudformation/index.js | 2 +- packages/amplify-e2e-tests/src/__tests__/env.test.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js index d32702754ce..7dfae061d30 100644 --- a/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js +++ b/packages/amplify-category-auth/src/provider-utils/awscloudformation/index.js @@ -3,7 +3,7 @@ const open = require('open'); const _ = require('lodash'); const { getAuthResourceName } = require('../../utils/getAuthResourceName'); const { copyCfnTemplate, saveResourceParameters } = require('./utils/synthesize-resources'); -const { immutableAttributes: ENV_SPECIFIC_PARAMS, privateKeys } = require('./constants'); +const { ENV_SPECIFIC_PARAMS, privateKeys } = require('./constants'); const { getAddAuthHandler, getUpdateAuthHandler } = require('./handlers/resource-handlers'); const { supportedServices } = require('../supported-services'); diff --git a/packages/amplify-e2e-tests/src/__tests__/env.test.ts b/packages/amplify-e2e-tests/src/__tests__/env.test.ts index 672847e456b..d205be68646 100644 --- a/packages/amplify-e2e-tests/src/__tests__/env.test.ts +++ b/packages/amplify-e2e-tests/src/__tests__/env.test.ts @@ -127,7 +127,7 @@ describe('environment commands with Cognito Triggers', () => { }); it('init a project, pull environment', async () => { - await pullEnvironment(projRoot, {}); + await pullEnvironment(projRoot); const meta = getProjectMeta(projRoot); await validate(meta); }); @@ -155,7 +155,7 @@ describe('environment commands with recaptcha trigger', () => { await validate(meta); }); it('init a project, pull environment', async () => { - await pullEnvironment(projRoot, {}); + await pullEnvironment(projRoot); const meta = getProjectMeta(projRoot); await validate(meta); }); @@ -183,7 +183,7 @@ describe('environment commands with HostedUI params', () => { }); it('init a project, pull environment', async () => { - await pullEnvironment(projRoot, {}); + await pullEnvironment(projRoot); const meta = getProjectMeta(projRoot); await validate(meta); });