From 16e85df5c3077496d3ebe7c4fa8230514756c027 Mon Sep 17 00:00:00 2001 From: tom139 Date: Fri, 17 Apr 2020 22:31:55 +0200 Subject: [PATCH] feat(cognito): add mutable property in cognito user pool custom attribute (#7190) * feat(cognito): add properties to custom attribute Add properties `mutable` and `developerOnly` to all CustomAttribute types. fixes: #7011 * docs(cognito): remove reference to required property * refactor(cognito): remove Base prefix in CustomAttribute class fixes #7190 * fix(cognito): export CustomAttribute abstract class fixes #7190 * docs(cognito): restore README line about required fields fixes #7011 * fix(cognito): remove developerOnly property in CustomAttribute fixes #7011 * refactor(cognito): remove BaseClass for custom attributes closes #7011 * adjustments to test statements Co-authored-by: Tommaso Panozzo Co-authored-by: Niranjan Jayakar Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- packages/@aws-cdk/aws-cognito/README.md | 9 ++- .../aws-cognito/lib/user-pool-attr.ts | 53 ++++++++++++-- .../@aws-cdk/aws-cognito/lib/user-pool.ts | 1 + .../aws-cognito/test/user-pool-attr.test.ts | 69 ++++++++++++++++++- 4 files changed, 124 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-cognito/README.md b/packages/@aws-cdk/aws-cognito/README.md index e40b2cfd1a384..ee9a2b93ace4b 100644 --- a/packages/@aws-cdk/aws-cognito/README.md +++ b/packages/@aws-cdk/aws-cognito/README.md @@ -168,9 +168,9 @@ new UserPool(this, 'myuserpool', { address: true, }, customAttributes: { - 'myappid': new StringAttribute({ minLen: 5, maxLen: 15 }), - 'callingcode': new NumberAttribute({ min: 1, max: 3 }), - 'isEmployee': new BooleanAttribute(), + 'myappid': new StringAttribute({ minLen: 5, maxLen: 15, mutable: false }), + 'callingcode': new NumberAttribute({ min: 1, max: 3, mutable: true }), + 'isEmployee': new BooleanAttribute({ mutable: true }), 'joinedOn': new DateTimeAttribute(), }, }); @@ -181,6 +181,9 @@ data types allow for further constraints on their length and values, respectivel Custom attributes cannot be marked as required. +All custom attributes share the property `mutable` that specifies whether the value of the attribute can be changed. +The default value is `false`. + ### Security Cognito sends various messages to its users via SMS, for different actions, ranging from account verification to diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts index aecb817890599..971385badda6a 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts @@ -141,6 +141,31 @@ export interface CustomAttributeConfig { * @default - None. */ readonly numberConstraints?: NumberAttributeConstraints; + + /** + * Specifies whether the value of the attribute can be changed. + * For any user pool attribute that's mapped to an identity provider attribute, you must set this parameter to true. + * Amazon Cognito updates mapped attributes when users sign in to your application through an identity provider. + * If an attribute is immutable, Amazon Cognito throws an error when it attempts to update the attribute. + * + * @default false + */ + readonly mutable?: boolean +} + +/** + * Constraints that can be applied to a custom attribute of any type. + */ +export interface CustomAttributeProps { + /** + * Specifies whether the value of the attribute can be changed. + * For any user pool attribute that's mapped to an identity provider attribute, you must set this parameter to true. + * Amazon Cognito updates mapped attributes when users sign in to your application through an identity provider. + * If an attribute is immutable, Amazon Cognito throws an error when it attempts to update the attribute. + * + * @default false + */ + readonly mutable?: boolean } /** @@ -163,7 +188,7 @@ export interface StringAttributeConstraints { /** * Props for constructing a StringAttr */ -export interface StringAttributeProps extends StringAttributeConstraints { +export interface StringAttributeProps extends StringAttributeConstraints, CustomAttributeProps { } /** @@ -172,6 +197,7 @@ export interface StringAttributeProps extends StringAttributeConstraints { export class StringAttribute implements ICustomAttribute { private readonly minLen?: number; private readonly maxLen?: number; + private readonly mutable?: boolean; constructor(props: StringAttributeProps = {}) { if (props.minLen && props.minLen < 0) { @@ -182,6 +208,7 @@ export class StringAttribute implements ICustomAttribute { } this.minLen = props?.minLen; this.maxLen = props?.maxLen; + this.mutable = props?.mutable; } public bind(): CustomAttributeConfig { @@ -196,6 +223,7 @@ export class StringAttribute implements ICustomAttribute { return { dataType: 'String', stringConstraints, + mutable: this.mutable, }; } } @@ -220,7 +248,7 @@ export interface NumberAttributeConstraints { /** * Props for NumberAttr */ -export interface NumberAttributeProps extends NumberAttributeConstraints { +export interface NumberAttributeProps extends NumberAttributeConstraints, CustomAttributeProps { } /** @@ -229,10 +257,12 @@ export interface NumberAttributeProps extends NumberAttributeConstraints { export class NumberAttribute implements ICustomAttribute { private readonly min?: number; private readonly max?: number; + private readonly mutable?: boolean; constructor(props: NumberAttributeProps = {}) { this.min = props?.min; this.max = props?.max; + this.mutable = props?.mutable; } public bind(): CustomAttributeConfig { @@ -247,6 +277,7 @@ export class NumberAttribute implements ICustomAttribute { return { dataType: 'Number', numberConstraints, + mutable: this.mutable, }; } } @@ -255,9 +286,16 @@ export class NumberAttribute implements ICustomAttribute { * The Boolean custom attribute type. */ export class BooleanAttribute implements ICustomAttribute { + private readonly mutable?: boolean; + + constructor(props: CustomAttributeProps = {}) { + this.mutable = props?.mutable; + } + public bind(): CustomAttributeConfig { return { - dataType: 'Boolean' + dataType: 'Boolean', + mutable: this.mutable, }; } } @@ -266,9 +304,16 @@ export class BooleanAttribute implements ICustomAttribute { * The DateTime custom attribute type. */ export class DateTimeAttribute implements ICustomAttribute { + private readonly mutable?: boolean; + + constructor(props: CustomAttributeProps = {}) { + this.mutable = props?.mutable; + } + public bind(): CustomAttributeConfig { return { - dataType: 'DateTime' + dataType: 'DateTime', + mutable: this.mutable, }; } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index d206cd8c91156..f74c200423ddb 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -868,6 +868,7 @@ export class UserPool extends Resource implements IUserPool { attributeDataType: attrConfig.dataType, numberAttributeConstraints: (attrConfig.numberConstraints) ? numberConstraints : undefined, stringAttributeConstraints: (attrConfig.stringConstraints) ? stringConstraints : undefined, + mutable: attrConfig.mutable, }; }); schema.push(...customAttrs); diff --git a/packages/@aws-cdk/aws-cognito/test/user-pool-attr.test.ts b/packages/@aws-cdk/aws-cognito/test/user-pool-attr.test.ts index e420571cc47dd..f001712a802a7 100644 --- a/packages/@aws-cdk/aws-cognito/test/user-pool-attr.test.ts +++ b/packages/@aws-cdk/aws-cognito/test/user-pool-attr.test.ts @@ -1,7 +1,74 @@ import '@aws-cdk/assert/jest'; -import { BooleanAttribute, DateTimeAttribute, NumberAttribute, StringAttribute } from '../lib'; +import { BooleanAttribute, CustomAttributeConfig, DateTimeAttribute, ICustomAttribute, NumberAttribute, StringAttribute } from '../lib'; describe('User Pool Attributes', () => { + + describe('mutable', () => { + test('default', () => { + // GIVEN + const allAttributes: ICustomAttribute[] = [ + new StringAttribute(), + new NumberAttribute(), + new BooleanAttribute(), + new DateTimeAttribute(), + ]; + + // WHEN + const bounds: CustomAttributeConfig[] = allAttributes.map((attr) => attr.bind() ); + + // THEN + bounds.forEach((bound) => { + expect(bound.mutable).toBeUndefined(); + }); + }); + + describe('mutable is set to true when specified', () => { + // GIVEN + const allTrueProps = { + mutable: true, + }; + const allAttributeTypes: ICustomAttribute[] = [ + new StringAttribute(allTrueProps), + new NumberAttribute(allTrueProps), + new BooleanAttribute(allTrueProps), + new DateTimeAttribute(allTrueProps), + ]; + + // WHEN + const bounds: CustomAttributeConfig[] = allAttributeTypes.map((attr) => attr.bind() ); + + // THEN + bounds.forEach((bound) => { + test(`in attribute of type ${bound.dataType}:`, () => { + expect(bound.mutable).toEqual(true); + }); + }); + }); + + describe('mutable is set to false', () => { + // GIVEN + const allFalseProps = { + mutable: false, + }; + const allAttributeTypes: ICustomAttribute[] = [ + new StringAttribute(allFalseProps), + new NumberAttribute(allFalseProps), + new BooleanAttribute(allFalseProps), + new DateTimeAttribute(allFalseProps), + ]; + + // WHEN + const bounds: CustomAttributeConfig[] = allAttributeTypes.map((attr) => attr.bind() ); + + // THEN + bounds.forEach((bound) => { + test(`in attribute of type ${bound.dataType}`, () => { + expect(bound.mutable).toEqual(false); + }); + }); + }); + }); + describe('StringAttribute', () => { test('default', () => { // GIVEN