Skip to content

Commit

Permalink
feat(cognito): add mutable property in cognito user pool custom attri…
Browse files Browse the repository at this point in the history
…bute (#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 <t.panozzo@miriade.it>
Co-authored-by: Niranjan Jayakar <nija@amazon.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
4 people committed Apr 17, 2020
1 parent 340c9e4 commit 16e85df
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 8 deletions.
9 changes: 6 additions & 3 deletions packages/@aws-cdk/aws-cognito/README.md
Expand Up @@ -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(),
},
});
Expand All @@ -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
Expand Down
53 changes: 49 additions & 4 deletions packages/@aws-cdk/aws-cognito/lib/user-pool-attr.ts
Expand Up @@ -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
}

/**
Expand All @@ -163,7 +188,7 @@ export interface StringAttributeConstraints {
/**
* Props for constructing a StringAttr
*/
export interface StringAttributeProps extends StringAttributeConstraints {
export interface StringAttributeProps extends StringAttributeConstraints, CustomAttributeProps {
}

/**
Expand All @@ -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) {
Expand All @@ -182,6 +208,7 @@ export class StringAttribute implements ICustomAttribute {
}
this.minLen = props?.minLen;
this.maxLen = props?.maxLen;
this.mutable = props?.mutable;
}

public bind(): CustomAttributeConfig {
Expand All @@ -196,6 +223,7 @@ export class StringAttribute implements ICustomAttribute {
return {
dataType: 'String',
stringConstraints,
mutable: this.mutable,
};
}
}
Expand All @@ -220,7 +248,7 @@ export interface NumberAttributeConstraints {
/**
* Props for NumberAttr
*/
export interface NumberAttributeProps extends NumberAttributeConstraints {
export interface NumberAttributeProps extends NumberAttributeConstraints, CustomAttributeProps {
}

/**
Expand All @@ -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 {
Expand All @@ -247,6 +277,7 @@ export class NumberAttribute implements ICustomAttribute {
return {
dataType: 'Number',
numberConstraints,
mutable: this.mutable,
};
}
}
Expand All @@ -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,
};
}
}
Expand All @@ -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,
};
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cognito/lib/user-pool.ts
Expand Up @@ -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);
Expand Down
69 changes: 68 additions & 1 deletion 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
Expand Down

0 comments on commit 16e85df

Please sign in to comment.