From 01a58b69ef1f44b1621189fc2a513a5b61b4974e Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Fri, 7 Aug 2020 15:47:58 +0100 Subject: [PATCH 1/6] feat(cognito): better control sms role creation - Introduce a property `enableSmsRole` that can be used to override CDK logic and explicitly enable or disable automatic creation of an IAM role for SMS. - Instead of creating the SMS role by default, all of the time, be smart about determining when the role is actually needed. Create the role only if (a) SMS is configured as MFA second factor, (b) sign in via phone number is enabled, or (c) phone verification is required. BREAKING CHANGE: CDK may now remove a previously created IAM role for SMS. The role will be removed only because it's not actually required by the user pool based on its configuration, so this should have no impact. This behaviour can be explicitly overridden by setting `enableSmsRole` property. closes #6943 --- packages/@aws-cdk/aws-cognito/README.md | 9 +- .../@aws-cdk/aws-cognito/lib/user-pool.ts | 81 ++- ...r-pool-client-explicit-props.expected.json | 58 +- ...nteg.user-pool-domain-cfdist.expected.json | 78 +-- ...g.user-pool-domain-signinurl.expected.json | 58 +- .../test/integ.user-pool-idp.expected.json | 58 +- .../integ.user-pool-signup-code.expected.json | 12 +- .../integ.user-pool-signup-link.expected.json | 12 +- .../test/integ.user-pool.expected.json | 58 +- .../aws-cognito/test/user-pool.test.ts | 574 ++++++++++++------ 10 files changed, 511 insertions(+), 487 deletions(-) diff --git a/packages/@aws-cdk/aws-cognito/README.md b/packages/@aws-cdk/aws-cognito/README.md index 4674fb7570ef8..b4f5a86dc5c26 100644 --- a/packages/@aws-cdk/aws-cognito/README.md +++ b/packages/@aws-cdk/aws-cognito/README.md @@ -201,8 +201,13 @@ The default value is `false`. Cognito sends various messages to its users via SMS, for different actions, ranging from account verification to marketing. In order to send SMS messages, Cognito needs an IAM role that it can assume, with permissions that allow it -to send SMS messages. By default, CDK will create this IAM role but can also be explicily specified to an existing IAM -role using the `smsRole` property. +to send SMS messages. + +By default, the CDK looks at all of the specified properties (and their defaults when not explicitly specified) and +automatically creates an SMS role, when needed. For example, if MFA second factor by SMS is enabled, the CDK will +create a new role. The `smsRole` property can be used to specify the user supplied role that should be used instead. +Additionally, the property `enableSmsRole` can be used to override the CDK's default behaviour to either enable or +suppress automatic role creation. ```ts import { Role } from '@aws-cdk/aws-iam'; diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index 6ff23e96dde5d..3ffe26e1c4a01 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -471,6 +471,13 @@ export interface UserPoolProps { */ readonly smsRoleExternalId?: string; + /** + * Setting this would explicitly enable or disable SMS role creation. + * When left unspecified, CDK will determine based on other properties if a role is needed or not. + * @default - CDK will determine based on other properties of the user pool if an SMS role should be created or not. + */ + readonly enableSmsRole?: boolean; + /** * Methods in which a user registers or signs in to a user pool. * Allows either username with aliases OR sign in with email, phone, or both. @@ -835,41 +842,57 @@ export class UserPool extends UserPoolBase { return { usernameAttrs, aliasAttrs, autoVerifyAttrs }; } - private smsConfiguration(props: UserPoolProps): CfnUserPool.SmsConfigurationProperty { + private smsConfiguration(props: UserPoolProps): CfnUserPool.SmsConfigurationProperty | undefined { + if (props.enableSmsRole === false && props.smsRole) { + throw new Error('enableSmsRole cannot be disabled when smsRole is specified'); + } + if (props.smsRole) { return { snsCallerArn: props.smsRole.roleArn, externalId: props.smsRoleExternalId, }; - } else { - const smsRoleExternalId = this.node.uniqueId.substr(0, 1223); // sts:ExternalId max length of 1224 - const smsRole = props.smsRole ?? new Role(this, 'smsRole', { - assumedBy: new ServicePrincipal('cognito-idp.amazonaws.com', { - conditions: { - StringEquals: { 'sts:ExternalId': smsRoleExternalId }, - }, - }), - inlinePolicies: { - /* - * The UserPool is very particular that it must contain an 'sns:Publish' action as an inline policy. - * Ideally, a conditional that restricts this action to 'sms' protocol needs to be attached, but the UserPool deployment fails validation. - * Seems like a case of being excessively strict. - */ - 'sns-publish': new PolicyDocument({ - statements: [ - new PolicyStatement({ - actions: [ 'sns:Publish' ], - resources: [ '*' ], - }), - ], - }), - }, - }); - return { - externalId: smsRoleExternalId, - snsCallerArn: smsRole.roleArn, - }; } + + if (props.enableSmsRole === false) { + return undefined; + } + + const mfaEnabled = props.mfa && props.mfa !== Mfa.OFF; + const mfaSms = !props.mfaSecondFactor || props.mfaSecondFactor.sms; // mfaSecondFactor.sms is true, by default if MFA is 'on' + const phoneVerification = props.signInAliases?.phone === true || props.autoVerify?.phone === true; + const roleRequired = (mfaEnabled && mfaSms) || phoneVerification; + if (!roleRequired && props.enableSmsRole === undefined) { + return undefined; + } + + const smsRoleExternalId = this.node.uniqueId.substr(0, 1223); // sts:ExternalId max length of 1224 + const smsRole = props.smsRole ?? new Role(this, 'smsRole', { + assumedBy: new ServicePrincipal('cognito-idp.amazonaws.com', { + conditions: { + StringEquals: { 'sts:ExternalId': smsRoleExternalId }, + }, + }), + inlinePolicies: { + /* + * The UserPool is very particular that it must contain an 'sns:Publish' action as an inline policy. + * Ideally, a conditional that restricts this action to 'sms' protocol needs to be attached, but the UserPool deployment fails validation. + * Seems like a case of being excessively strict. + */ + 'sns-publish': new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: [ 'sns:Publish' ], + resources: [ '*' ], + }), + ], + }), + }, + }); + return { + externalId: smsRoleExternalId, + snsCallerArn: smsRole.roleArn, + }; } private mfaConfiguration(props: UserPoolProps): string[] | undefined { diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-client-explicit-props.expected.json b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-client-explicit-props.expected.json index 8b5246dfedf58..be2e268a29eac 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-client-explicit-props.expected.json +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-client-explicit-props.expected.json @@ -1,49 +1,18 @@ { "Resources": { - "myuserpoolsmsRole0E16FDD9": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Condition": { - "StringEquals": { - "sts:ExternalId": "integuserpoolclientexplicitpropsmyuserpoolFC6541FF" - } - }, - "Effect": "Allow", - "Principal": { - "Service": "cognito-idp.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": "sns:Publish", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "sns-publish" - } - ] - } - }, "myuserpool01998219": { "Type": "AWS::Cognito::UserPool", "Properties": { - "AccountRecoverySetting": { + "AccountRecoverySetting": { "RecoveryMechanisms": [ - { "Name": "verified_phone_number", "Priority": 1 }, - { "Name": "verified_email", "Priority": 2 } + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } ] }, "AdminCreateUserConfig": { @@ -51,15 +20,6 @@ }, "EmailVerificationMessage": "The verification code to your new account is {####}", "EmailVerificationSubject": "Verify your new account", - "SmsConfiguration": { - "ExternalId": "integuserpoolclientexplicitpropsmyuserpoolFC6541FF", - "SnsCallerArn": { - "Fn::GetAtt": [ - "myuserpoolsmsRole0E16FDD9", - "Arn" - ] - } - }, "SmsVerificationMessage": "The verification code to your new account is {####}", "VerificationMessageTemplate": { "DefaultEmailOption": "CONFIRM_WITH_CODE", diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-cfdist.expected.json b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-cfdist.expected.json index 511d200ed5d90..368a5945b361b 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-cfdist.expected.json +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-cfdist.expected.json @@ -1,49 +1,18 @@ { "Resources": { - "UserPoolsmsRole4EA729DD": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Condition": { - "StringEquals": { - "sts:ExternalId": "integuserpooldomaincfdistUserPool17475E8A" - } - }, - "Effect": "Allow", - "Principal": { - "Service": "cognito-idp.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": "sns:Publish", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "sns-publish" - } - ] - } - }, "UserPool6BA7E5F2": { "Type": "AWS::Cognito::UserPool", "Properties": { - "AccountRecoverySetting": { + "AccountRecoverySetting": { "RecoveryMechanisms": [ - { "Name": "verified_phone_number", "Priority": 1 }, - { "Name": "verified_email", "Priority": 2 } + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } ] }, "AdminCreateUserConfig": { @@ -51,15 +20,6 @@ }, "EmailVerificationMessage": "The verification code to your new account is {####}", "EmailVerificationSubject": "Verify your new account", - "SmsConfiguration": { - "ExternalId": "integuserpooldomaincfdistUserPool17475E8A", - "SnsCallerArn": { - "Fn::GetAtt": [ - "UserPoolsmsRole4EA729DD", - "Arn" - ] - } - }, "SmsVerificationMessage": "The verification code to your new account is {####}", "VerificationMessageTemplate": { "DefaultEmailOption": "CONFIRM_WITH_CODE", @@ -176,7 +136,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3BucketC6CBC09E" + "Ref": "AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3Bucket67234880" }, "S3Key": { "Fn::Join": [ @@ -189,7 +149,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3VersionKeyB194AB23" + "Ref": "AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3VersionKey9802AE96" } ] } @@ -202,7 +162,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3VersionKeyB194AB23" + "Ref": "AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3VersionKey9802AE96" } ] } @@ -244,17 +204,17 @@ } }, "Parameters": { - "AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3BucketC6CBC09E": { + "AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3Bucket67234880": { "Type": "String", - "Description": "S3 bucket for asset \"a75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4\"" + "Description": "S3 bucket for asset \"8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061\"" }, - "AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4S3VersionKeyB194AB23": { + "AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061S3VersionKey9802AE96": { "Type": "String", - "Description": "S3 key for asset version \"a75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4\"" + "Description": "S3 key for asset version \"8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061\"" }, - "AssetParametersa75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4ArtifactHashBE5BD63C": { + "AssetParameters8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061ArtifactHash9212BF97": { "Type": "String", - "Description": "Artifact hash for asset \"a75563f489fb6bc4064bc85b91ef607f671326e647bcd9d9bcab0731de62edd4\"" + "Description": "Artifact hash for asset \"8ae75ec4aaae0510b0918d3a69fac5c978d780ae0d60bb94c65c7f5b4c498061\"" } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-signinurl.expected.json b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-signinurl.expected.json index 6bb3d7edab140..694cf43b5f5ea 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-signinurl.expected.json +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-domain-signinurl.expected.json @@ -1,49 +1,18 @@ { "Resources": { - "UserPoolsmsRole4EA729DD": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Condition": { - "StringEquals": { - "sts:ExternalId": "integuserpooldomainsigninurlUserPool1325E89F" - } - }, - "Effect": "Allow", - "Principal": { - "Service": "cognito-idp.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": "sns:Publish", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "sns-publish" - } - ] - } - }, "UserPool6BA7E5F2": { "Type": "AWS::Cognito::UserPool", "Properties": { - "AccountRecoverySetting": { + "AccountRecoverySetting": { "RecoveryMechanisms": [ - { "Name": "verified_phone_number", "Priority": 1 }, - { "Name": "verified_email", "Priority": 2 } + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } ] }, "AdminCreateUserConfig": { @@ -51,15 +20,6 @@ }, "EmailVerificationMessage": "The verification code to your new account is {####}", "EmailVerificationSubject": "Verify your new account", - "SmsConfiguration": { - "ExternalId": "integuserpooldomainsigninurlUserPool1325E89F", - "SnsCallerArn": { - "Fn::GetAtt": [ - "UserPoolsmsRole4EA729DD", - "Arn" - ] - } - }, "SmsVerificationMessage": "The verification code to your new account is {####}", "VerificationMessageTemplate": { "DefaultEmailOption": "CONFIRM_WITH_CODE", diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-idp.expected.json b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-idp.expected.json index 3fa00974541cf..e68a262eb7ee3 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-idp.expected.json +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-idp.expected.json @@ -1,49 +1,18 @@ { "Resources": { - "poolsmsRole04048F13": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Condition": { - "StringEquals": { - "sts:ExternalId": "integuserpoolidppoolAE0BD80C" - } - }, - "Effect": "Allow", - "Principal": { - "Service": "cognito-idp.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": "sns:Publish", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "sns-publish" - } - ] - } - }, "pool056F3F7E": { "Type": "AWS::Cognito::UserPool", "Properties": { - "AccountRecoverySetting": { + "AccountRecoverySetting": { "RecoveryMechanisms": [ - { "Name": "verified_phone_number", "Priority": 1 }, - { "Name": "verified_email", "Priority": 2 } + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } ] }, "AdminCreateUserConfig": { @@ -51,15 +20,6 @@ }, "EmailVerificationMessage": "The verification code to your new account is {####}", "EmailVerificationSubject": "Verify your new account", - "SmsConfiguration": { - "ExternalId": "integuserpoolidppoolAE0BD80C", - "SnsCallerArn": { - "Fn::GetAtt": [ - "poolsmsRole04048F13", - "Arn" - ] - } - }, "SmsVerificationMessage": "The verification code to your new account is {####}", "VerificationMessageTemplate": { "DefaultEmailOption": "CONFIRM_WITH_CODE", diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-signup-code.expected.json b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-signup-code.expected.json index 90d858978f043..5cc13052434f2 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-signup-code.expected.json +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-signup-code.expected.json @@ -40,10 +40,16 @@ "myuserpool01998219": { "Type": "AWS::Cognito::UserPool", "Properties": { - "AccountRecoverySetting": { + "AccountRecoverySetting": { "RecoveryMechanisms": [ - { "Name": "verified_phone_number", "Priority": 1 }, - { "Name": "verified_email", "Priority": 2 } + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } ] }, "AdminCreateUserConfig": { diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-signup-link.expected.json b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-signup-link.expected.json index 45661be1e0766..53c9f89ed8031 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool-signup-link.expected.json +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool-signup-link.expected.json @@ -40,10 +40,16 @@ "myuserpool01998219": { "Type": "AWS::Cognito::UserPool", "Properties": { - "AccountRecoverySetting": { + "AccountRecoverySetting": { "RecoveryMechanisms": [ - { "Name": "verified_phone_number", "Priority": 1 }, - { "Name": "verified_email", "Priority": 2 } + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } ] }, "AdminCreateUserConfig": { diff --git a/packages/@aws-cdk/aws-cognito/test/integ.user-pool.expected.json b/packages/@aws-cdk/aws-cognito/test/integ.user-pool.expected.json index f2beef72d6eb4..85214615c050d 100644 --- a/packages/@aws-cdk/aws-cognito/test/integ.user-pool.expected.json +++ b/packages/@aws-cdk/aws-cognito/test/integ.user-pool.expected.json @@ -1,49 +1,18 @@ { "Resources": { - "myuserpoolsmsRole0E16FDD9": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Condition": { - "StringEquals": { - "sts:ExternalId": "integuserpoolmyuserpoolDA38443C" - } - }, - "Effect": "Allow", - "Principal": { - "Service": "cognito-idp.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": "sns:Publish", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "sns-publish" - } - ] - } - }, "myuserpool01998219": { "Type": "AWS::Cognito::UserPool", "Properties": { - "AccountRecoverySetting": { + "AccountRecoverySetting": { "RecoveryMechanisms": [ - { "Name": "verified_phone_number", "Priority": 1 }, - { "Name": "verified_email", "Priority": 2 } + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } ] }, "AdminCreateUserConfig": { @@ -51,15 +20,6 @@ }, "EmailVerificationMessage": "The verification code to your new account is {####}", "EmailVerificationSubject": "Verify your new account", - "SmsConfiguration": { - "ExternalId": "integuserpoolmyuserpoolDA38443C", - "SnsCallerArn": { - "Fn::GetAtt": [ - "myuserpoolsmsRole0E16FDD9", - "Arn" - ] - } - }, "SmsVerificationMessage": "The verification code to your new account is {####}", "UserPoolName": "MyUserPool", "VerificationMessageTemplate": { diff --git a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts index 801879f6ea55a..17a09ff9863b6 100644 --- a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts +++ b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts @@ -1,6 +1,6 @@ import '@aws-cdk/assert/jest'; import { ABSENT } from '@aws-cdk/assert/lib/assertions/have-resource'; -import { Role } from '@aws-cdk/aws-iam'; +import { Role, ServicePrincipal } from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import { CfnParameter, Construct, Duration, Stack, Tag } from '@aws-cdk/core'; import { AccountRecovery, Mfa, NumberAttribute, StringAttribute, UserPool, UserPoolIdentityProvider, UserPoolOperation, VerificationEmailStyle } from '../lib'; @@ -28,49 +28,8 @@ describe('User Pool', () => { EmailSubject: 'Verify your new account', SmsMessage: 'The verification code to your new account is {####}', }, - SmsConfiguration: { - SnsCallerArn: { - 'Fn::GetAtt': [ 'PoolsmsRoleC3352CE6', 'Arn' ], - }, - ExternalId: 'Pool', - }, lambdaTriggers: ABSENT, }); - - expect(stack).toHaveResource('AWS::IAM::Role', { - AssumeRolePolicyDocument: { - Statement: [ - { - Action: 'sts:AssumeRole', - Condition: { - StringEquals: { - 'sts:ExternalId': 'Pool', - }, - }, - Effect: 'Allow', - Principal: { - Service: 'cognito-idp.amazonaws.com', - }, - }, - ], - Version: '2012-10-17', - }, - Policies: [ - { - PolicyDocument: { - Statement: [ - { - Action: 'sns:Publish', - Effect: 'Allow', - Resource: '*', - }, - ], - Version: '2012-10-17', - }, - PolicyName: 'sns-publish', - }, - ], - }); }); test('self sign up option is correctly configured', () => { @@ -896,202 +855,427 @@ describe('User Pool', () => { }, }); }); -}); - -test('addClient', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - const userpool = new UserPool(stack, 'Pool'); - userpool.addClient('UserPoolClient', { - userPoolClientName: 'userpoolclient', - }); - const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); - imported.addClient('UserPoolImportedClient', { - userPoolClientName: 'userpoolimportedclient', - }); - - // THEN - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { - ClientName: 'userpoolclient', - UserPoolId: stack.resolve(userpool.userPoolId), - }); - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { - ClientName: 'userpoolimportedclient', - UserPoolId: stack.resolve(imported.userPoolId), - }); -}); -test('addDomain', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - const userpool = new UserPool(stack, 'Pool'); - userpool.addDomain('UserPoolDomain', { - cognitoDomain: { - domainPrefix: 'userpooldomain', - }, - }); - const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); - imported.addDomain('UserPoolImportedDomain', { - cognitoDomain: { - domainPrefix: 'userpoolimporteddomain', - }, - }); - - // THEN - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { - Domain: 'userpooldomain', - UserPoolId: stack.resolve(userpool.userPoolId), - }); - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { - Domain: 'userpoolimporteddomain', - UserPoolId: stack.resolve(imported.userPoolId), - }); -}); - -test('registered identity providers', () => { - // GIVEN - const stack = new Stack(); - const userPool = new UserPool(stack, 'pool'); - const provider1 = UserPoolIdentityProvider.fromProviderName(stack, 'provider1', 'provider1'); - const provider2 = UserPoolIdentityProvider.fromProviderName(stack, 'provider2', 'provider2'); - - // WHEN - userPool.registerIdentityProvider(provider1); - userPool.registerIdentityProvider(provider2); - - // THEN - expect(userPool.identityProviders).toEqual([provider1, provider2]); -}); - -function fooFunction(scope: Construct, name: string): lambda.IFunction { - return new lambda.Function(scope, name, { - functionName: name, - code: lambda.Code.inline('foo'), - runtime: lambda.Runtime.NODEJS_12_X, - handler: 'index.handler', - }); -} - -describe('AccountRecoverySetting should be configured correctly', () => { - test('EMAIL_AND_PHONE_WITHOUT_MFA', () => { + test('addClient', () => { // GIVEN const stack = new Stack(); // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_AND_PHONE_WITHOUT_MFA }); + const userpool = new UserPool(stack, 'Pool'); + userpool.addClient('UserPoolClient', { + userPoolClientName: 'userpoolclient', + }); + const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); + imported.addClient('UserPoolImportedClient', { + userPoolClientName: 'userpoolimportedclient', + }); // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_email', Priority: 1 }, - { Name: 'verified_phone_number', Priority: 2 }, - ], - }, + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { + ClientName: 'userpoolclient', + UserPoolId: stack.resolve(userpool.userPoolId), + }); + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { + ClientName: 'userpoolimportedclient', + UserPoolId: stack.resolve(imported.userPoolId), }); }); - test('PHONE_WITHOUT_MFA_AND_EMAIL', () => { + test('addDomain', () => { // GIVEN const stack = new Stack(); // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_WITHOUT_MFA_AND_EMAIL }); + const userpool = new UserPool(stack, 'Pool'); + userpool.addDomain('UserPoolDomain', { + cognitoDomain: { + domainPrefix: 'userpooldomain', + }, + }); + const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); + imported.addDomain('UserPoolImportedDomain', { + cognitoDomain: { + domainPrefix: 'userpoolimporteddomain', + }, + }); // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_phone_number', Priority: 1 }, - { Name: 'verified_email', Priority: 2 }, - ], - }, + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { + Domain: 'userpooldomain', + UserPoolId: stack.resolve(userpool.userPoolId), + }); + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { + Domain: 'userpoolimporteddomain', + UserPoolId: stack.resolve(imported.userPoolId), }); }); - test('EMAIL_ONLY', () => { + test('registered identity providers', () => { // GIVEN const stack = new Stack(); + const userPool = new UserPool(stack, 'pool'); + const provider1 = UserPoolIdentityProvider.fromProviderName(stack, 'provider1', 'provider1'); + const provider2 = UserPoolIdentityProvider.fromProviderName(stack, 'provider2', 'provider2'); // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_ONLY }); + userPool.registerIdentityProvider(provider1); + userPool.registerIdentityProvider(provider2); // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_email', Priority: 1 }, - ], - }, - }); + expect(userPool.identityProviders).toEqual([provider1, provider2]); }); - test('PHONE_ONLY_WITHOUT_MFA', () => { - // GIVEN - const stack = new Stack(); + describe('AccountRecoverySetting should be configured correctly', () => { + test('EMAIL_AND_PHONE_WITHOUT_MFA', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_AND_PHONE_WITHOUT_MFA }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_email', Priority: 1 }, + { Name: 'verified_phone_number', Priority: 2 }, + ], + }, + }); + }); - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_ONLY_WITHOUT_MFA }); + test('PHONE_WITHOUT_MFA_AND_EMAIL', () => { + // GIVEN + const stack = new Stack(); - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_phone_number', Priority: 1 }, - ], - }, + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_WITHOUT_MFA_AND_EMAIL }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_phone_number', Priority: 1 }, + { Name: 'verified_email', Priority: 2 }, + ], + }, + }); }); - }); - test('NONE', () => { - // GIVEN - const stack = new Stack(); + test('EMAIL_ONLY', () => { + // GIVEN + const stack = new Stack(); - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.NONE }); + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_ONLY }); - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'admin_only', Priority: 1 }, - ], - }, + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_email', Priority: 1 }, + ], + }, + }); }); - }); - test('PHONE_AND_EMAIL', () => { - // GIVEN - const stack = new Stack(); + test('PHONE_ONLY_WITHOUT_MFA', () => { + // GIVEN + const stack = new Stack(); - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_AND_EMAIL }); + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_ONLY_WITHOUT_MFA }); - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: ABSENT, + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_phone_number', Priority: 1 }, + ], + }, + }); + }); + + test('NONE', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.NONE }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'admin_only', Priority: 1 }, + ], + }, + }); + }); + + test('PHONE_AND_EMAIL', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_AND_EMAIL }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: ABSENT, + }); + }); + + test('default', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool'); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_phone_number', Priority: 1 }, + { Name: 'verified_email', Priority: 2 }, + ], + }, + }); }); }); - test('default', () => { - // GIVEN - const stack = new Stack(); + describe('sms roles', () => { + test('default', () => { + // GIVEN + const stack = new Stack(); - // WHEN - new UserPool(stack, 'pool'); + // WHEN + new UserPool(stack, 'pool'); - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_phone_number', Priority: 1 }, - { Name: 'verified_email', Priority: 2 }, + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + SmsConfiguration: ABSENT, + }); + }); + + test('smsRole and smsExternalId is set', () => { + // GIVEN + const stack = new Stack(); + const smsRole = new Role(stack, 'smsRole', { + assumedBy: new ServicePrincipal('service.amazonaws.com'), + }); + + // WHEN + new UserPool(stack, 'pool', { + smsRole, + smsRoleExternalId: 'role-external-id', + }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + SmsConfiguration: { + ExternalId: 'role-external-id', + SnsCallerArn: { 'Fn::GetAtt': [ 'smsRoleA4587CE8', 'Arn' ] }, + }, + }); + }); + + test('setting enableSmsRole creates an sms role', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { + enableSmsRole: true, + }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + SmsConfiguration: { + ExternalId: 'pool', + SnsCallerArn: { 'Fn::GetAtt': [ 'poolsmsRole04048F13', 'Arn' ] }, + }, + }); + expect(stack).toHaveResource('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Condition: { + StringEquals: { + 'sts:ExternalId': 'pool', + }, + }, + Effect: 'Allow', + Principal: { + Service: 'cognito-idp.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + Policies: [ + { + PolicyDocument: { + Statement: [ + { + Action: 'sns:Publish', + Effect: 'Allow', + Resource: '*', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'sns-publish', + }, ], - }, + }); + }); + + test('auto sms role is not created when MFA and phoneVerification is off', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { + mfa: Mfa.OFF, + signInAliases: { + phone: false, + }, + }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + SmsConfiguration: ABSENT, + }); + }); + + test('auto sms role is not created when OTP-based MFA is enabled and phoneVerification is off', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { + mfa: Mfa.REQUIRED, + mfaSecondFactor: { + otp: true, + sms: false, + }, + signInAliases: { + phone: false, + }, + }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + SmsConfiguration: ABSENT, + }); + }); + + test('auto sms role is created when phone verification is turned on', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { + mfa: Mfa.OFF, + signInAliases: { phone: true }, + }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + SmsConfiguration: { + ExternalId: 'pool', + SnsCallerArn: { 'Fn::GetAtt': [ 'poolsmsRole04048F13', 'Arn' ] }, + }, + }); + }); + + test('auto sms role is created when phone auto-verification is set', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { + mfa: Mfa.OFF, + signInAliases: { phone: false }, + autoVerify: { phone: true }, + }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + SmsConfiguration: { + ExternalId: 'pool', + SnsCallerArn: { 'Fn::GetAtt': [ 'poolsmsRole04048F13', 'Arn' ] }, + }, + }); + }); + + test('auto sms role is created when MFA is turned on', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { + mfa: Mfa.REQUIRED, + mfaSecondFactor: { + sms: true, + otp: false, + }, + signInAliases: { + phone: false, + }, + }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + SmsConfiguration: { + ExternalId: 'pool', + SnsCallerArn: { 'Fn::GetAtt': [ 'poolsmsRole04048F13', 'Arn' ] }, + }, + }); + }); + + test('auto sms role is not created when enableSmsRole is unset, even when MFA is configured', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { + mfa: Mfa.REQUIRED, + mfaSecondFactor: { + sms: true, + otp: false, + }, + enableSmsRole: false, + }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + SmsConfiguration: ABSENT, + }); + }); + + test('throws an error when smsRole is specified but enableSmsRole is unset', () => { + const stack = new Stack(); + const smsRole = new Role(stack, 'smsRole', { + assumedBy: new ServicePrincipal('service.amazonaws.com'), + }); + + expect(() => new UserPool(stack, 'pool', { + smsRole, + enableSmsRole: false, + })).toThrow(/enableSmsRole cannot be disabled/); }); }); }); + +function fooFunction(scope: Construct, name: string): lambda.IFunction { + return new lambda.Function(scope, name, { + functionName: name, + code: lambda.Code.inline('foo'), + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + }); +} \ No newline at end of file From 6a8b14c39632d6bde8d2beafc5af00a9bb8d960c Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Fri, 7 Aug 2020 16:15:06 +0100 Subject: [PATCH 2/6] avoid refactoring tests --- .../aws-cognito/test/user-pool.test.ts | 380 +++++++++--------- 1 file changed, 190 insertions(+), 190 deletions(-) diff --git a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts index 17a09ff9863b6..204fe5134cfe9 100644 --- a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts +++ b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts @@ -856,195 +856,6 @@ describe('User Pool', () => { }); }); - test('addClient', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - const userpool = new UserPool(stack, 'Pool'); - userpool.addClient('UserPoolClient', { - userPoolClientName: 'userpoolclient', - }); - const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); - imported.addClient('UserPoolImportedClient', { - userPoolClientName: 'userpoolimportedclient', - }); - - // THEN - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { - ClientName: 'userpoolclient', - UserPoolId: stack.resolve(userpool.userPoolId), - }); - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { - ClientName: 'userpoolimportedclient', - UserPoolId: stack.resolve(imported.userPoolId), - }); - }); - - test('addDomain', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - const userpool = new UserPool(stack, 'Pool'); - userpool.addDomain('UserPoolDomain', { - cognitoDomain: { - domainPrefix: 'userpooldomain', - }, - }); - const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); - imported.addDomain('UserPoolImportedDomain', { - cognitoDomain: { - domainPrefix: 'userpoolimporteddomain', - }, - }); - - // THEN - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { - Domain: 'userpooldomain', - UserPoolId: stack.resolve(userpool.userPoolId), - }); - expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { - Domain: 'userpoolimporteddomain', - UserPoolId: stack.resolve(imported.userPoolId), - }); - }); - - test('registered identity providers', () => { - // GIVEN - const stack = new Stack(); - const userPool = new UserPool(stack, 'pool'); - const provider1 = UserPoolIdentityProvider.fromProviderName(stack, 'provider1', 'provider1'); - const provider2 = UserPoolIdentityProvider.fromProviderName(stack, 'provider2', 'provider2'); - - // WHEN - userPool.registerIdentityProvider(provider1); - userPool.registerIdentityProvider(provider2); - - // THEN - expect(userPool.identityProviders).toEqual([provider1, provider2]); - }); - - describe('AccountRecoverySetting should be configured correctly', () => { - test('EMAIL_AND_PHONE_WITHOUT_MFA', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_AND_PHONE_WITHOUT_MFA }); - - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_email', Priority: 1 }, - { Name: 'verified_phone_number', Priority: 2 }, - ], - }, - }); - }); - - test('PHONE_WITHOUT_MFA_AND_EMAIL', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_WITHOUT_MFA_AND_EMAIL }); - - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_phone_number', Priority: 1 }, - { Name: 'verified_email', Priority: 2 }, - ], - }, - }); - }); - - test('EMAIL_ONLY', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_ONLY }); - - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_email', Priority: 1 }, - ], - }, - }); - }); - - test('PHONE_ONLY_WITHOUT_MFA', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_ONLY_WITHOUT_MFA }); - - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_phone_number', Priority: 1 }, - ], - }, - }); - }); - - test('NONE', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.NONE }); - - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'admin_only', Priority: 1 }, - ], - }, - }); - }); - - test('PHONE_AND_EMAIL', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_AND_EMAIL }); - - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: ABSENT, - }); - }); - - test('default', () => { - // GIVEN - const stack = new Stack(); - - // WHEN - new UserPool(stack, 'pool'); - - // THEN - expect(stack).toHaveResource('AWS::Cognito::UserPool', { - AccountRecoverySetting: { - RecoveryMechanisms: [ - { Name: 'verified_phone_number', Priority: 1 }, - { Name: 'verified_email', Priority: 2 }, - ], - }, - }); - }); - }); - describe('sms roles', () => { test('default', () => { // GIVEN @@ -1271,6 +1082,75 @@ describe('User Pool', () => { }); }); +test('addClient', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const userpool = new UserPool(stack, 'Pool'); + userpool.addClient('UserPoolClient', { + userPoolClientName: 'userpoolclient', + }); + const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); + imported.addClient('UserPoolImportedClient', { + userPoolClientName: 'userpoolimportedclient', + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { + ClientName: 'userpoolclient', + UserPoolId: stack.resolve(userpool.userPoolId), + }); + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolClient', { + ClientName: 'userpoolimportedclient', + UserPoolId: stack.resolve(imported.userPoolId), + }); +}); + +test('addDomain', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + const userpool = new UserPool(stack, 'Pool'); + userpool.addDomain('UserPoolDomain', { + cognitoDomain: { + domainPrefix: 'userpooldomain', + }, + }); + const imported = UserPool.fromUserPoolId(stack, 'imported', 'imported-userpool-id'); + imported.addDomain('UserPoolImportedDomain', { + cognitoDomain: { + domainPrefix: 'userpoolimporteddomain', + }, + }); + + // THEN + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { + Domain: 'userpooldomain', + UserPoolId: stack.resolve(userpool.userPoolId), + }); + expect(stack).toHaveResourceLike('AWS::Cognito::UserPoolDomain', { + Domain: 'userpoolimporteddomain', + UserPoolId: stack.resolve(imported.userPoolId), + }); +}); + +test('registered identity providers', () => { + // GIVEN + const stack = new Stack(); + const userPool = new UserPool(stack, 'pool'); + const provider1 = UserPoolIdentityProvider.fromProviderName(stack, 'provider1', 'provider1'); + const provider2 = UserPoolIdentityProvider.fromProviderName(stack, 'provider2', 'provider2'); + + // WHEN + userPool.registerIdentityProvider(provider1); + userPool.registerIdentityProvider(provider2); + + // THEN + expect(userPool.identityProviders).toEqual([provider1, provider2]); +}); + function fooFunction(scope: Construct, name: string): lambda.IFunction { return new lambda.Function(scope, name, { functionName: name, @@ -1278,4 +1158,124 @@ function fooFunction(scope: Construct, name: string): lambda.IFunction { runtime: lambda.Runtime.NODEJS_12_X, handler: 'index.handler', }); -} \ No newline at end of file +} + +describe('AccountRecoverySetting should be configured correctly', () => { + test('EMAIL_AND_PHONE_WITHOUT_MFA', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_AND_PHONE_WITHOUT_MFA }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_email', Priority: 1 }, + { Name: 'verified_phone_number', Priority: 2 }, + ], + }, + }); + }); + + test('PHONE_WITHOUT_MFA_AND_EMAIL', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_WITHOUT_MFA_AND_EMAIL }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_phone_number', Priority: 1 }, + { Name: 'verified_email', Priority: 2 }, + ], + }, + }); + }); + + test('EMAIL_ONLY', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.EMAIL_ONLY }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_email', Priority: 1 }, + ], + }, + }); + }); + + test('PHONE_ONLY_WITHOUT_MFA', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_ONLY_WITHOUT_MFA }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_phone_number', Priority: 1 }, + ], + }, + }); + }); + + test('NONE', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.NONE }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'admin_only', Priority: 1 }, + ], + }, + }); + }); + + test('PHONE_AND_EMAIL', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool', { accountRecovery: AccountRecovery.PHONE_AND_EMAIL }); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: ABSENT, + }); + }); + + test('default', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new UserPool(stack, 'pool'); + + // THEN + expect(stack).toHaveResource('AWS::Cognito::UserPool', { + AccountRecoverySetting: { + RecoveryMechanisms: [ + { Name: 'verified_phone_number', Priority: 1 }, + { Name: 'verified_email', Priority: 2 }, + ], + }, + }); + }); +}); \ No newline at end of file From 208de895bbce9039fdf4908c4d9c6fe6b97b784e Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Mon, 10 Aug 2020 10:04:48 +0100 Subject: [PATCH 3/6] PR feedback --- packages/@aws-cdk/aws-cognito/lib/user-pool.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index 3ffe26e1c4a01..80a65675a0856 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -858,10 +858,9 @@ export class UserPool extends UserPoolBase { return undefined; } - const mfaEnabled = props.mfa && props.mfa !== Mfa.OFF; - const mfaSms = !props.mfaSecondFactor || props.mfaSecondFactor.sms; // mfaSecondFactor.sms is true, by default if MFA is 'on' + const mfaConfiguration = this.mfaConfiguration(props); const phoneVerification = props.signInAliases?.phone === true || props.autoVerify?.phone === true; - const roleRequired = (mfaEnabled && mfaSms) || phoneVerification; + const roleRequired = mfaConfiguration?.includes('SMS_MFA') || phoneVerification; if (!roleRequired && props.enableSmsRole === undefined) { return undefined; } From 5399a3cd63a23311b9078421771f80877de8e020 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Mon, 10 Aug 2020 10:24:40 +0100 Subject: [PATCH 4/6] fix test --- .../aws-cognito/test/user-pool.test.ts | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts index 960713be132cc..1be015f803baa 100644 --- a/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts +++ b/packages/@aws-cdk/aws-cognito/test/user-pool.test.ts @@ -28,49 +28,9 @@ describe('User Pool', () => { EmailSubject: 'Verify your new account', SmsMessage: 'The verification code to your new account is {####}', }, - SmsConfiguration: { - SnsCallerArn: { - 'Fn::GetAtt': [ 'PoolsmsRoleC3352CE6', 'Arn' ], - }, - ExternalId: 'Pool', - }, + SmsConfiguration: ABSENT, lambdaTriggers: ABSENT, }); - - expect(stack).toHaveResource('AWS::IAM::Role', { - AssumeRolePolicyDocument: { - Statement: [ - { - Action: 'sts:AssumeRole', - Condition: { - StringEquals: { - 'sts:ExternalId': 'Pool', - }, - }, - Effect: 'Allow', - Principal: { - Service: 'cognito-idp.amazonaws.com', - }, - }, - ], - Version: '2012-10-17', - }, - Policies: [ - { - PolicyDocument: { - Statement: [ - { - Action: 'sns:Publish', - Effect: 'Allow', - Resource: '*', - }, - ], - Version: '2012-10-17', - }, - PolicyName: 'sns-publish', - }, - ], - }); }); test('self sign up option is correctly configured', () => { From 23411befb9ace48847dd7455bfc43bfbe8dc705d Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Mon, 10 Aug 2020 13:48:10 +0100 Subject: [PATCH 5/6] fix elbv2-actions --- .../test/integ.cognito.lit.expected.json | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/test/integ.cognito.lit.expected.json b/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/test/integ.cognito.lit.expected.json index e35271c92c173..be3c5a7fe88d8 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/test/integ.cognito.lit.expected.json +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2-actions/test/integ.cognito.lit.expected.json @@ -453,43 +453,6 @@ ] } }, - "UserPoolsmsRole4EA729DD": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Condition": { - "StringEquals": { - "sts:ExternalId": "integcognitoUserPool7BB79D76" - } - }, - "Effect": "Allow", - "Principal": { - "Service": "cognito-idp.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": "sns:Publish", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "sns-publish" - } - ] - } - }, "UserPool6BA7E5F2": { "Type": "AWS::Cognito::UserPool", "Properties": { @@ -504,15 +467,6 @@ }, "EmailVerificationMessage": "The verification code to your new account is {####}", "EmailVerificationSubject": "Verify your new account", - "SmsConfiguration": { - "ExternalId": "integcognitoUserPool7BB79D76", - "SnsCallerArn": { - "Fn::GetAtt": [ - "UserPoolsmsRole4EA729DD", - "Arn" - ] - } - }, "SmsVerificationMessage": "The verification code to your new account is {####}", "VerificationMessageTemplate": { "DefaultEmailOption": "CONFIRM_WITH_CODE", From ed67091c99e994c0afb65615ec2c830efa1fc27b Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Mon, 10 Aug 2020 16:59:00 +0100 Subject: [PATCH 6/6] fix appsync --- .../test/integ.graphql-iam.expected.json | 46 ------------------- .../test/integ.graphql.expected.json | 46 ------------------- 2 files changed, 92 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql-iam.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.graphql-iam.expected.json index e83e633d67676..1d90ae0c15d4b 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql-iam.expected.json +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql-iam.expected.json @@ -1,42 +1,5 @@ { "Resources": { - "PoolsmsRoleC3352CE6": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Condition": { - "StringEquals": { - "sts:ExternalId": "awsappsyncintegPool5D14B05B" - } - }, - "Effect": "Allow", - "Principal": { - "Service": "cognito-idp.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": "sns:Publish", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "sns-publish" - } - ] - } - }, "PoolD3F588B8": { "Type": "AWS::Cognito::UserPool", "Properties": { @@ -57,15 +20,6 @@ }, "EmailVerificationMessage": "The verification code to your new account is {####}", "EmailVerificationSubject": "Verify your new account", - "SmsConfiguration": { - "ExternalId": "awsappsyncintegPool5D14B05B", - "SnsCallerArn": { - "Fn::GetAtt": [ - "PoolsmsRoleC3352CE6", - "Arn" - ] - } - }, "SmsVerificationMessage": "The verification code to your new account is {####}", "UserPoolName": "myPool", "VerificationMessageTemplate": { diff --git a/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json b/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json index dc19df3e4c395..80d03e19f7767 100644 --- a/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json +++ b/packages/@aws-cdk/aws-appsync/test/integ.graphql.expected.json @@ -1,42 +1,5 @@ { "Resources": { - "PoolsmsRoleC3352CE6": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Statement": [ - { - "Action": "sts:AssumeRole", - "Condition": { - "StringEquals": { - "sts:ExternalId": "awsappsyncintegPool5D14B05B" - } - }, - "Effect": "Allow", - "Principal": { - "Service": "cognito-idp.amazonaws.com" - } - } - ], - "Version": "2012-10-17" - }, - "Policies": [ - { - "PolicyDocument": { - "Statement": [ - { - "Action": "sns:Publish", - "Effect": "Allow", - "Resource": "*" - } - ], - "Version": "2012-10-17" - }, - "PolicyName": "sns-publish" - } - ] - } - }, "PoolD3F588B8": { "Type": "AWS::Cognito::UserPool", "Properties": { @@ -57,15 +20,6 @@ }, "EmailVerificationMessage": "The verification code to your new account is {####}", "EmailVerificationSubject": "Verify your new account", - "SmsConfiguration": { - "ExternalId": "awsappsyncintegPool5D14B05B", - "SnsCallerArn": { - "Fn::GetAtt": [ - "PoolsmsRoleC3352CE6", - "Arn" - ] - } - }, "SmsVerificationMessage": "The verification code to your new account is {####}", "UserPoolName": "myPool", "VerificationMessageTemplate": {