Merge pull request #3657 from hassankhan/add-cognito-user-pool-triggers

Add Cognito User Pool Triggers
This commit is contained in:
Philipp Muens 2017-06-06 09:47:19 +02:00 committed by GitHub
commit f1df11abd2
30 changed files with 992 additions and 1 deletions

View File

@ -90,6 +90,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
<li><a href="./events/iot.md">IoT</a></li>
<li><a href="./events/cloudwatch-event.md">CloudWatch Event</a></li>
<li><a href="./events/cloudwatch-log.md">CloudWatch Log</a></li>
<li><a href="./events/cognito-user-pool.md">Cognito User Pool</a></li>
</ul>
</div>
</div>

View File

@ -0,0 +1,116 @@
<!--
title: Serverless Framework - AWS Lambda Events - Cognito User Pool
menuText: Cognito User Pool
menuOrder: 10
description: Setting up AWS Cognito User Pool Triggers with AWS Lambda via the Serverless Framework
layout: Doc
-->
<!-- DOCS-SITE-LINK:START automatically generated -->
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/cognito-user-pool)
<!-- DOCS-SITE-LINK:END -->
# Cognito User Pool
## Valid Triggers
Serverless supports all Cognito User Pool Triggers as specified [here][aws-triggers-list].
## Simple event definition
This will create a Cognito User Pool with the specified name. You can reference the same pool multiple times.
```yml
functions:
preSignUp:
handler: preSignUp.handler
events:
- cognitoUserPool:
pool: MyUserPool
trigger: PreSignUp
customMessage:
handler: customMessage.handler
events:
- cognitoUserPool:
pool: MyUserPool
trigger: CustomMessage
```
## Multiple pools event definitions
This will create multiple Cognito User Pools with their specified names:
```yml
functions:
preSignUpForPool1:
handler: preSignUp.handler
events:
- cognitoUserPool:
pool: MyUserPool1
trigger: PreSignUp
preSignUpForPool2:
handler: preSignUp.handler
events:
- cognitoUserPool:
pool: MyUserPool2
trigger: PreSignUp
```
You can also deploy the same function for different user pools:
```yml
functions:
preSignUp:
handler: preSignUp.handler
events:
- cognitoUserPool:
pool: MyUserPool1
trigger: PreSignUp
- cognitoUserPool:
pool: MyUserPool2
trigger: PreSignUp
```
## Custom message trigger handlers
For custom messages, you will need to check `event.triggerSource` type inside your handler function:
```js
// customMessage.js
function handler(event, context, callback) {
if (event.triggerSource === 'CustomMessage_AdminCreateUser') {
// ...
}
if (event.triggerSource === 'CustomMessage_ResendCode') {
// ...
}
}
```
## Overriding a generated User Pool
A Cognito User Pool created by an event can be overridden by using the [logical resource name][logical-resource-names] in `Resources`:
```yml
functions:
preSignUp:
handler: preSignUpForPool1.handler
events:
- cognitoUserPool:
pool: MyUserPool
trigger: PreSignUp
postConfirmation:
handler: postConfirmation.handler
events:
- cognitoUserPool:
pool: MyUserPool
trigger: PostConfirmation
resources:
Resources:
CognitoUserPoolMyUserPool:
Type: AWS::Cognito::UserPool
```
[aws-triggers-list]: http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html#cognito-user-pools-lambda-trigger-syntax-shared
[logical-resource-names]: ../guide/resources#aws-cloudformation-resource-reference

View File

@ -70,7 +70,7 @@ We're also using the term `normalizedName` or similar terms in this guide. This
|Lambda::Function | {normalizedFunctionName}LambdaFunction | HelloLambdaFunction |
|Lambda::Version | {normalizedFunctionName}LambdaVersion{sha256} | HelloLambdaVersionr3pgoTvv1xT4E4NiCL6JG02fl6vIyi7OS1aW0FwAI |
|Logs::LogGroup | {normalizedFunctionName}LogGroup | HelloLogGroup |
|Lambda::Permission | <ul><li>**Schedule**: {normalizedFunctionName}LambdaPermissionEventsRuleSchedule{index}</li><li>**CloudWatch Event**: {normalizedFunctionName}LambdaPermissionEventsRuleCloudWatchEvent{index}</li><li>**CloudWatch Log**: {normalizedFunctionName}LambdaPermissionLogsSubscriptionFilterCloudWatchLog{index}</li><li>**IoT**: {normalizedFunctionName}LambdaPermissionIotTopicRule{index} </li><li>**S3**: {normalizedFunctionName}LambdaPermission{normalizedBucketName}S3</li><li>**APIG**: {normalizedFunctionName}LambdaPermissionApiGateway</li><li>**SNS**: {normalizedFunctionName}LambdaPermission{normalizedTopicName}SNS</li><li>**Alexa Skill**: {normalizedFunctionName}LambdaPermissionAlexaSkill</li> </ul> | <ul><li>**Schedule**: HelloLambdaPermissionEventsRuleSchedule1</li><li>**CloudWatch Event**: HelloLambdaPermissionEventsRuleCloudWatchEvent1</li><li>**CloudWatch Log**: HelloLambdaPermissionLogsSubscriptionFilterCloudWatchLog1</li><li>**IoT**: HelloLambdaPermissionIotTopicRule1 </li><li>**S3**: HelloLambdaPermissionBucketS3</li><li>**APIG**: HelloLambdaPermissionApiGateway</li><li>**SNS**: HelloLambdaPermissionTopicSNS</li><li>**Alexa Skill**: HelloLambdaPermissionAlexaSkill</li> </ul>|
|Lambda::Permission | <ul><li>**Schedule**: {normalizedFunctionName}LambdaPermissionEventsRuleSchedule{index}</li><li>**CloudWatch Event**: {normalizedFunctionName}LambdaPermissionEventsRuleCloudWatchEvent{index}</li><li>**CloudWatch Log**: {normalizedFunctionName}LambdaPermissionLogsSubscriptionFilterCloudWatchLog{index}</li><li>**IoT**: {normalizedFunctionName}LambdaPermissionIotTopicRule{index} </li><li>**S3**: {normalizedFunctionName}LambdaPermission{normalizedBucketName}S3</li><li>**APIG**: {normalizedFunctionName}LambdaPermissionApiGateway</li><li>**SNS**: {normalizedFunctionName}LambdaPermission{normalizedTopicName}SNS</li><li>**Alexa Skill**: {normalizedFunctionName}LambdaPermissionAlexaSkill</li><li>**Cognito User Pool Trigger Source**: {normalizedFunctionName}LambdaPermissionCognitoUserPool{normalizedPoolId}TriggerSource{triggerSource}</li> </ul> | <ul><li>**Schedule**: HelloLambdaPermissionEventsRuleSchedule1</li><li>**CloudWatch Event**: HelloLambdaPermissionEventsRuleCloudWatchEvent1</li><li>**CloudWatch Log**: HelloLambdaPermissionLogsSubscriptionFilterCloudWatchLog1</li><li>**IoT**: HelloLambdaPermissionIotTopicRule1 </li><li>**S3**: HelloLambdaPermissionBucketS3</li><li>**APIG**: HelloLambdaPermissionApiGateway</li><li>**SNS**: HelloLambdaPermissionTopicSNS</li><li>**Alexa Skill**: HelloLambdaPermissionAlexaSkill</li><li>**Cognito User Pool Trigger Source**: HelloLambdaPermissionCognitoUserPoolMyPoolTriggerSourceCustomMessage</li> </ul>|
|Events::Rule | <ul><li>**Schedule**: {normalizedFuntionName}EventsRuleSchedule{SequentialID}</li><li>**CloudWatch Event**: {normalizedFuntionName}EventsRuleCloudWatchEvent{SequentialID}</li> </ul> | <ul><li>**Schedule**: HelloEventsRuleSchedule1</li><li>**CloudWatch Event**: HelloEventsRuleCloudWatchEvent1</li></ul> |
|AWS::Logs::SubscriptionFilter | {normalizedFuntionName}LogsSubscriptionFilterCloudWatchLog{SequentialID} | HelloLogsSubscriptionFilterCloudWatchLog1 |
|AWS::IoT::TopicRule | {normalizedFuntionName}IotTopicRule{SequentialID} | HelloIotTopicRule1 |
@ -83,6 +83,7 @@ We're also using the term `normalizedName` or similar terms in this guide. This
|SNS::Topic | SNSTopic{normalizedTopicName} | SNSTopicSometopic |
|SNS::Subscription | {normalizedFunctionName}SnsSubscription{normalizedTopicName} | HelloSnsSubscriptionSomeTopic |
|AWS::Lambda::EventSourceMapping | <ul><li>**DynamoDB:** {normalizedFunctionName}EventSourceMappingDynamodb{tableName}</li><li>**Kinesis:** {normalizedFunctionName}EventSourceMappingKinesis{streamName}</li></ul> | <ul><li>**DynamoDB:** HelloLambdaEventSourceMappingDynamodbUsers</li><li>**Kinesis:** HelloLambdaEventSourceMappingKinesisMystream</li></ul> |
|Cognito::UserPool | CognitoUserPool{normalizedPoolId} | CognitoUserPoolPoolId |
## Override AWS CloudFormation Resource

View File

@ -137,6 +137,9 @@ functions:
- cloudwatchLog:
logGroup: '/aws/lambda/hello'
filter: '{$.userIdentity.type = Root}'
- cognitoUserPool:
pool: MyUserPool
trigger: PreSignUp
# The "Resources" your "Functions" use. Raw AWS CloudFormation goes in here.
resources:

View File

@ -36,6 +36,7 @@
"./aws/package/compile/events/iot/index.js",
"./aws/package/compile/events/cloudWatchEvent/index.js",
"./aws/package/compile/events/cloudWatchLog/index.js",
"./aws/package/compile/events/cognitoUserPool/index.js",
"./aws/deployFunction/index.js",
"./aws/deployList/index.js",
"./aws/invokeLocal/index.js"

View File

@ -246,6 +246,11 @@ module.exports = {
.getNormalizedFunctionName(functionName)}LogsSubscriptionFilterCloudWatchLog${logsIndex}`;
},
// Cognito User Pool
getCognitoUserPoolLogicalId(poolId) {
return `CognitoUserPool${this.normalizeNameToAlphaNumericOnly(poolId)}`;
},
// Permissions
getLambdaS3PermissionLogicalId(functionName, bucketName) {
return `${this.getNormalizedFunctionName(functionName)}LambdaPermission${this
@ -278,4 +283,9 @@ module.exports = {
return `${this.getNormalizedFunctionName(functionName)
}LambdaPermissionLogsSubscriptionFilterCloudWatchLog${logsIndex}`;
},
getLambdaCognitoUserPoolPermissionLogicalId(functionName, poolId, triggerSource) {
return `${this
.getNormalizedFunctionName(functionName)}LambdaPermissionCognitoUserPool${
this.normalizeNameToAlphaNumericOnly(poolId)}TriggerSource${triggerSource}`;
},
};

View File

@ -388,6 +388,13 @@ describe('#naming()', () => {
});
});
describe('#getCognitoUserPoolLogicalId()', () => {
it('should normalize the user pool name and add the standard prefix', () => {
expect(sdk.naming.getCognitoUserPoolLogicalId('us-east-1_v123sDAS1'))
.to.equal('CognitoUserPoolUseast1v123sDAS1');
});
});
describe('#getLambdaS3PermissionLogicalId()', () => {
it('should normalize the function name and add the standard suffix', () => {
expect(sdk.naming.getLambdaS3PermissionLogicalId('functionName', 'bucket'))
@ -463,4 +470,14 @@ describe('#naming()', () => {
.to.equal('FunctionNameLambdaPermissionLogsSubscriptionFilterCloudWatchLog0');
});
});
describe('#getLambdaCognitoUserPoolPermissionLogicalId()', () => {
it('should normalize the function name and add the standard suffix', () => {
expect(sdk.naming.getLambdaCognitoUserPoolPermissionLogicalId(
'functionName',
'Pool1',
'CustomMessage'
)).to.equal('FunctionNameLambdaPermissionCognitoUserPoolPool1TriggerSourceCustomMessage');
});
});
});

View File

@ -0,0 +1,164 @@
'use strict';
const _ = require('lodash');
const validTriggerSources = [
'PreSignUp',
'PostConfirmation',
'PreAuthentication',
'PostAuthentication',
'CustomMessage',
'DefineAuthChallenge',
'CreateAuthChallenge',
'VerifyAuthChallengeResponse',
];
class AwsCompileCognitoUserPoolEvents {
constructor(serverless) {
this.serverless = serverless;
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'package:compileEvents': this.compileCognitoUserPoolEvents.bind(this),
};
}
compileCognitoUserPoolEvents() {
const userPools = [];
const cognitoUserPoolTriggerFunctions = [];
// Iterate through all functions declared in `serverless.yml`
this.serverless.service.getAllFunctions().forEach((functionName) => {
const functionObj = this.serverless.service.getFunction(functionName);
if (functionObj.events) {
functionObj.events.forEach(event => {
if (event.cognitoUserPool) {
// Check event definition for `cognitoUserPool` object
if (typeof event.cognitoUserPool === 'object') {
// Check `cognitoUserPool` object has required properties
if (!event.cognitoUserPool.pool || !event.cognitoUserPool.trigger) {
throw new this.serverless.classes
.Error([
`Cognito User Pool event of function "${functionName}" is not an object.`,
'The correct syntax is an object with the "pool" and "trigger" properties.',
'Please check the docs for more info.',
].join(' '));
}
// Check `cognitoUserPool` trigger is valid
if (!_.includes(validTriggerSources, event.cognitoUserPool.trigger)) {
throw new this.serverless.classes
.Error([
'Cognito User Pool trigger source is invalid, must be one of:',
`${validTriggerSources.join(', ')}.`,
'Please check the docs for more info.',
].join(' '));
}
// Save trigger functions so we can use them to generate
// IAM permissions later
cognitoUserPoolTriggerFunctions.push({
functionName,
poolName: event.cognitoUserPool.pool,
triggerSource: event.cognitoUserPool.trigger,
});
// Save user pools so we can use them to generate
// CloudFormation resources later
userPools.push(event.cognitoUserPool.pool);
} else {
throw new this.serverless.classes
.Error([
`Cognito User Pool event of function "${functionName}" is not an object.`,
'The correct syntax is an object with the "pool" and "trigger" properties.',
'Please check the docs for more info.',
].join(' '));
}
}
});
}
});
// Generate CloudFormation templates for Cognito User Pool changes
_.forEach(userPools, (poolName) => {
// Create a `LambdaConfig` object for the CloudFormation template
const currentPoolTriggerFunctions = _.filter(cognitoUserPoolTriggerFunctions, {
poolName,
});
const lambdaConfig = _.reduce(currentPoolTriggerFunctions, (result, value) => {
const lambdaLogicalId = this.provider.naming.getLambdaLogicalId(value.functionName);
// Return a new object to avoid lint errors
return Object.assign({}, result, {
[value.triggerSource]: {
'Fn::GetAtt': [
lambdaLogicalId,
'Arn',
],
},
});
}, {});
const userPoolLogicalId = this.provider.naming.getCognitoUserPoolLogicalId(poolName);
const DependsOn = _.map(currentPoolTriggerFunctions, (value) => this
.provider.naming.getLambdaLogicalId(value.functionName));
const userPoolTemplate = {
Type: 'AWS::Cognito::UserPool',
Properties: {
UserPoolName: poolName,
LambdaConfig: lambdaConfig,
},
DependsOn,
};
const userPoolCFResource = {
[userPoolLogicalId]: userPoolTemplate,
};
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
userPoolCFResource);
});
// Generate CloudFormation templates for IAM permissions to allow Cognito to trigger Lambda
cognitoUserPoolTriggerFunctions.forEach((cognitoUserPoolTriggerFunction) => {
const userPoolLogicalId = this.provider.naming
.getCognitoUserPoolLogicalId(cognitoUserPoolTriggerFunction.poolName);
const lambdaLogicalId = this.provider.naming
.getLambdaLogicalId(cognitoUserPoolTriggerFunction.functionName);
const permissionTemplate = {
Type: 'AWS::Lambda::Permission',
Properties: {
FunctionName: {
'Fn::GetAtt': [
lambdaLogicalId,
'Arn',
],
},
Action: 'lambda:InvokeFunction',
Principal: 'cognito-idp.amazonaws.com',
SourceArn: {
'Fn::GetAtt': [
userPoolLogicalId,
'Arn',
],
},
},
};
const lambdaPermissionLogicalId = this.provider.naming
.getLambdaCognitoUserPoolPermissionLogicalId(cognitoUserPoolTriggerFunction.functionName,
cognitoUserPoolTriggerFunction.poolName, cognitoUserPoolTriggerFunction.triggerSource);
const permissionCFResource = {
[lambdaPermissionLogicalId]: permissionTemplate,
};
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
permissionCFResource);
});
}
}
module.exports = AwsCompileCognitoUserPoolEvents;

View File

@ -0,0 +1,282 @@
'use strict';
const expect = require('chai').expect;
const AwsProvider = require('../../../../provider/awsProvider');
const AwsCompileCognitoUserPoolEvents = require('./index');
const Serverless = require('../../../../../../Serverless');
describe('AwsCompileCognitoUserPoolEvents', () => {
let serverless;
let awsCompileCognitoUserPoolEvents;
beforeEach(() => {
serverless = new Serverless();
serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} };
serverless.setProvider('aws', new AwsProvider(serverless));
awsCompileCognitoUserPoolEvents = new AwsCompileCognitoUserPoolEvents(serverless);
awsCompileCognitoUserPoolEvents.serverless.service.service = 'new-service';
});
describe('#constructor()', () => {
it('should set the provider variable to an instance of AwsProvider', () =>
expect(awsCompileCognitoUserPoolEvents.provider).to.be.instanceof(AwsProvider));
});
describe('#compileCognitoUserPoolEvents()', () => {
it('should throw an error if cognitoUserPool event type is not an object', () => {
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
first: {
events: [
{
cognitoUserPool: 42,
},
],
},
};
expect(() => awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents()).to.throw(Error);
});
it('should throw an error if the "pool" property is not given', () => {
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
first: {
events: [
{
cognitoUserPool: {
pool: null,
},
},
],
},
};
expect(() => awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents()).to.throw(Error);
});
it('should throw an error if the "trigger" property is not given', () => {
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
first: {
events: [
{
cognitoUserPool: {
trigger: null,
},
},
],
},
};
expect(() => awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents()).to.throw(Error);
});
it('should throw an error if the "trigger" property is invalid', () => {
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
first: {
events: [
{
cognitoUserPool: {
pool: 'MyUserPool',
trigger: 'invalidTrigger',
},
},
],
},
};
expect(() => awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents()).to.throw(Error);
});
it('should create resources when CUP events are given as separate functions', () => {
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
first: {
events: [
{
cognitoUserPool: {
pool: 'MyUserPool1',
trigger: 'PreSignUp',
},
},
],
},
second: {
events: [
{
cognitoUserPool: {
pool: 'MyUserPool2',
trigger: 'PostConfirmation',
},
},
],
},
};
awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents();
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1.Type
).to.equal('AWS::Cognito::UserPool');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2.Type
).to.equal('AWS::Cognito::UserPool');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.FirstLambdaPermissionCognitoUserPoolMyUserPool1TriggerSourcePreSignUp.Type
).to.equal('AWS::Lambda::Permission');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.SecondLambdaPermissionCognitoUserPoolMyUserPool2TriggerSourcePostConfirmation.Type
).to.equal('AWS::Lambda::Permission');
});
it('should create resources when CUP events are given with the same function', () => {
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
first: {
events: [
{
cognitoUserPool: {
pool: 'MyUserPool1',
trigger: 'PreSignUp',
},
},
{
cognitoUserPool: {
pool: 'MyUserPool2',
trigger: 'PostConfirmation',
},
},
],
},
};
awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents();
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1.Type
).to.equal('AWS::Cognito::UserPool');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2.Type
).to.equal('AWS::Cognito::UserPool');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.FirstLambdaPermissionCognitoUserPoolMyUserPool1TriggerSourcePreSignUp.Type
).to.equal('AWS::Lambda::Permission');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.FirstLambdaPermissionCognitoUserPoolMyUserPool2TriggerSourcePostConfirmation.Type
).to.equal('AWS::Lambda::Permission');
});
it('should create resources when CUP events are given with diff funcs and single event', () => {
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
first: {
events: [
{
cognitoUserPool: {
pool: 'MyUserPool1',
trigger: 'PreSignUp',
},
},
],
},
second: {
events: [
{
cognitoUserPool: {
pool: 'MyUserPool2',
trigger: 'PreSignUp',
},
},
],
},
};
awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents();
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1.Type
).to.equal('AWS::Cognito::UserPool');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1
.Properties.LambdaConfig.PreSignUp['Fn::GetAtt'][0]
).to.equal(serverless.service.serverless.getProvider('aws')
.naming.getLambdaLogicalId('first'));
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2.Type
).to.equal('AWS::Cognito::UserPool');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2
.Properties.LambdaConfig.PreSignUp['Fn::GetAtt'][0]
).to.equal(serverless.service.serverless.getProvider('aws')
.naming.getLambdaLogicalId('second'));
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.FirstLambdaPermissionCognitoUserPoolMyUserPool1TriggerSourcePreSignUp.Type
).to.equal('AWS::Lambda::Permission');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.SecondLambdaPermissionCognitoUserPoolMyUserPool2TriggerSourcePreSignUp.Type
).to.equal('AWS::Lambda::Permission');
});
it('should create single user pool resource when the same pool referenced repeatedly', () => {
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
first: {
events: [
{
cognitoUserPool: {
pool: 'MyUserPool',
trigger: 'PreSignUp',
},
},
],
},
second: {
events: [
{
cognitoUserPool: {
pool: 'MyUserPool',
trigger: 'PostConfirmation',
},
},
],
},
};
awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents();
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.CognitoUserPoolMyUserPool.Type
).to.equal('AWS::Cognito::UserPool');
expect(Object.keys(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.CognitoUserPoolMyUserPool.Properties.LambdaConfig).length
).to.equal(2);
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.FirstLambdaPermissionCognitoUserPoolMyUserPoolTriggerSourcePreSignUp.Type
).to.equal('AWS::Lambda::Permission');
expect(awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
.SecondLambdaPermissionCognitoUserPoolMyUserPoolTriggerSourcePostConfirmation.Type
).to.equal('AWS::Lambda::Permission');
});
it('should not create resources when CUP events are not given', () => {
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
first: {
events: [],
},
};
awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents();
expect(
awsCompileCognitoUserPoolEvents.serverless.service.provider
.compiledCloudFormationTemplate.Resources
).to.deep.equal({});
});
});
});

View File

@ -79,6 +79,9 @@ functions:
# state:
# - pending
# - cloudwatchLog: '/aws/lambda/hello'
# - cognitoUserPool:
# pool: MyUserPool
# trigger: PreSignUp
# Define function environment variables here
# environment:

View File

@ -77,6 +77,9 @@ functions:
# state:
# - pending
# - cloudwatchLog: '/aws/lambda/hello'
# - cognitoUserPool:
# pool: MyUserPool
# trigger: PreSignUp
# Define function environment variables here
# environment:

View File

@ -77,6 +77,9 @@ functions:
# state:
# - pending
# - cloudwatchLog: '/aws/lambda/hello'
# - cognitoUserPool:
# pool: MyUserPool
# trigger: PreSignUp
# Define function environment variables here
# environment:

View File

@ -77,6 +77,9 @@ functions:
# state:
# - pending
# - cloudwatchLog: '/aws/lambda/hello'
# - cognitoUserPool:
# pool: MyUserPool
# trigger: PreSignUp
# Define function environment variables here
# environment:

View File

@ -82,6 +82,9 @@ functions:
# state:
# - pending
# - cloudwatchLog: '/aws/lambda/hello'
# - cognitoUserPool:
# pool: MyUserPool
# trigger: PreSignUp
# Define function environment variables here
# environment:

View File

@ -82,6 +82,9 @@ functions:
# state:
# - pending
# - cloudwatchLog: '/aws/lambda/hello'
# - cognitoUserPool:
# pool: MyUserPool
# trigger: PreSignUp
# Define function environment variables here
# environment:

View File

@ -82,6 +82,9 @@ functions:
# state:
# - pending
# - cloudwatchLog: '/aws/lambda/hello'
# - cognitoUserPool:
# pool: MyUserPool
# trigger: PreSignUp
# Define function environment variables here
# environment:

View File

@ -79,6 +79,9 @@ functions:
# state:
# - pending
# - cloudwatchLog: '/aws/lambda/hello'
# - cognitoUserPool:
# pool: MyUserPool
# trigger: PreSignUp
# Define function environment variables here
# environment:

View File

@ -0,0 +1,27 @@
'use strict';
const preSignUp = (event, context, callback) => {
const nextEvent = Object.assign({}, event);
nextEvent.response.autoConfirmUser = true;
process.stdout.write(JSON.stringify(nextEvent));
callback(null, nextEvent);
};
const customMessage = (event, context, callback) => {
const nextEvent = Object.assign({}, event);
if (event.triggerSource === 'CustomMessage_SignUp') {
nextEvent.response.smsMessage = `Welcome to the service. Your confirmation code is ${
event.request.codeParameter}`;
nextEvent.response.emailSubject = 'Welcome to the service';
nextEvent.response.emailMessage = `Thank you for signing up. ${
event.request.codeParameter} is your verification code`;
}
process.stdout.write(JSON.stringify(nextEvent));
callback(null, nextEvent);
};
module.exports.preSignUp1 = preSignUp;
module.exports.preSignUp2 = preSignUp;
module.exports.customMessage1 = customMessage;
module.exports.customMessage2 = customMessage;

View File

@ -0,0 +1,31 @@
service: aws-nodejs
provider:
name: aws
runtime: nodejs6.10
functions:
preSignUp1:
handler: handler.preSignUp1
events:
- cognitoUserPool:
pool: ${env:COGNITO_USER_POOL_1}
trigger: PreSignUp
customMessage1:
handler: handler.customMessage1
events:
- cognitoUserPool:
pool: ${env:COGNITO_USER_POOL_1}
trigger: CustomMessage
preSignUp2:
handler: handler.preSignUp2
events:
- cognitoUserPool:
pool: ${env:COGNITO_USER_POOL_2}
trigger: PreSignUp
customMessage2:
handler: handler.customMessage2
events:
- cognitoUserPool:
pool: ${env:COGNITO_USER_POOL_2}
trigger: CustomMessage

View File

@ -0,0 +1,77 @@
'use strict';
const path = require('path');
const expect = require('chai').expect;
const Utils = require('../../../../utils/index');
describe('AWS - Cognito User Pool: Multiple User Pools with multiple ' +
'events with multiple functions', () => {
beforeAll(() => {
Utils.createTestService('aws-nodejs', path.join(__dirname, 'service'));
Utils.deployService();
});
it('should call the specified function on the first User Pool when PreSignUp ' +
'event is triggered', () => Utils
.getCognitoUserPoolId(process.env.COGNITO_USER_POOL_1)
.then((poolId) =>
Promise.all([
poolId,
Utils.createCognitoUser(poolId, 'test@test.com', 'Password123!'),
])
)
.delay(60000)
.then((promiseResponse) => {
const poolId = promiseResponse[0];
const logs = Utils.getFunctionLogs('preSignUp1');
expect(RegExp(`"userPoolId":"${poolId}"`, 'g').test(logs)).to.equal(true);
expect(/"triggerSource":"PreSignUp_\w+"/g.test(logs)).to.equal(true);
})
);
it('should call the specified function on the first User Pool when CustomMessage ' +
'event is triggered', () => Utils
.getCognitoUserPoolId(process.env.COGNITO_USER_POOL_1)
.then((poolId) => {
const logs = Utils.getFunctionLogs('customMessage1');
expect(RegExp(`"userPoolId":"${poolId}"`, 'g').test(logs)).to.equal(true);
expect(/"triggerSource":"CustomMessage_AdminCreateUser"/g.test(logs)).to.equal(true);
})
);
it('should call the specified function on the second User Pool when PreSignUp ' +
'event is triggered', () => Utils
.getCognitoUserPoolId(process.env.COGNITO_USER_POOL_2)
.then((poolId) =>
Promise.all([
poolId,
Utils.createCognitoUser(poolId, 'test@test.com', 'Password123!'),
])
)
.delay(60000)
.then((promiseResponse) => {
const poolId = promiseResponse[0];
const logs = Utils.getFunctionLogs('preSignUp2');
expect(RegExp(`"userPoolId":"${poolId}"`, 'g').test(logs)).to.equal(true);
expect(/"triggerSource":"PreSignUp_\w+"/g.test(logs)).to.equal(true);
})
);
it('should call the specified function on the second User Pool when CustomMessage ' +
'event is triggered', () => Utils
.getCognitoUserPoolId(process.env.COGNITO_USER_POOL_2)
.then((poolId) => {
const logs = Utils.getFunctionLogs('customMessage2');
expect(RegExp(`"userPoolId":"${poolId}"`, 'g').test(logs)).to.equal(true);
expect(/"triggerSource":"CustomMessage_AdminCreateUser"/g.test(logs)).to.equal(true);
})
);
afterAll(() => {
Utils.removeService();
});
});

View File

@ -0,0 +1,9 @@
'use strict';
module.exports.preSignUp = (event, context, callback) => {
const nextEvent = Object.assign({}, event);
nextEvent.response.autoConfirmUser = true;
process.stdout.write(JSON.stringify(nextEvent));
callback(null, nextEvent);
};

View File

@ -0,0 +1,16 @@
service: aws-nodejs
provider:
name: aws
runtime: nodejs6.10
functions:
preSignUp:
handler: handler.preSignUp
events:
- cognitoUserPool:
pool: ${env:COGNITO_USER_POOL_1}
trigger: PreSignUp
- cognitoUserPool:
pool: ${env:COGNITO_USER_POOL_2}
trigger: PreSignUp

View File

@ -0,0 +1,53 @@
'use strict';
const path = require('path');
const expect = require('chai').expect;
const Utils = require('../../../../utils/index');
describe('AWS - Cognito User Pool: Multiple User Pools with single ' +
'event with single function', () => {
beforeAll(() => {
Utils.createTestService('aws-nodejs', path.join(__dirname, 'service'));
Utils.deployService();
});
it('should call the specified function on the first User Pool when PreSignUp ' +
'event is triggered', () => Utils
.getCognitoUserPoolId(process.env.COGNITO_USER_POOL_1)
.then((poolId) =>
Promise.all([
poolId,
Utils.createCognitoUser(poolId, 'test@test.com', 'Password123!'),
])
)
.delay(60000)
.then((promiseResponse) => {
const poolId = promiseResponse[0];
const logs = Utils.getFunctionLogs('preSignUp');
expect(RegExp(`"userPoolId":"${poolId}"`, 'g').test(logs)).to.equal(true);
expect(/"triggerSource":"PreSignUp_\w+"/g.test(logs)).to.equal(true);
})
);
it('should call the specified function on the second User Pool when PreSignUp ' +
'event is triggered', () => Utils
.getCognitoUserPoolId(process.env.COGNITO_USER_POOL_2)
.then((poolId) =>
Promise.all([
poolId,
Utils.createCognitoUser(poolId, 'test@test.com', 'Password123!'),
])
)
.delay(60000)
.then((promiseResponse) => {
const poolId = promiseResponse[0];
const logs = Utils.getFunctionLogs('preSignUp');
expect(RegExp(`"userPoolId":"${poolId}"`, 'g').test(logs)).to.equal(true);
expect(/"triggerSource":"PreSignUp_\w+"/g.test(logs)).to.equal(true);
})
);
afterAll(() => {
Utils.removeService();
});
});

View File

@ -0,0 +1,22 @@
'use strict';
module.exports.preSignUp = (event, context, callback) => {
const nextEvent = Object.assign({}, event);
nextEvent.response.autoConfirmUser = true;
process.stdout.write(JSON.stringify(nextEvent));
callback(null, nextEvent);
};
module.exports.customMessage = (event, context, callback) => {
const nextEvent = Object.assign({}, event);
if (event.triggerSource === 'CustomMessage_SignUp') {
nextEvent.response.smsMessage = `Welcome to the service. Your confirmation code is ${
event.request.codeParameter}`;
nextEvent.response.emailSubject = 'Welcome to the service';
nextEvent.response.emailMessage = `Thank you for signing up. ${
event.request.codeParameter} is your verification code`;
}
process.stdout.write(JSON.stringify(nextEvent));
callback(null, nextEvent);
};

View File

@ -0,0 +1,19 @@
service: aws-nodejs
provider:
name: aws
runtime: nodejs6.10
functions:
preSignUp:
handler: handler.preSignUp
events:
- cognitoUserPool:
pool: ${env:COGNITO_USER_POOL_1}
trigger: PreSignUp
customMessage:
handler: handler.customMessage
events:
- cognitoUserPool:
pool: ${env:COGNITO_USER_POOL_1}
trigger: CustomMessage

View File

@ -0,0 +1,39 @@
'use strict';
const path = require('path');
const expect = require('chai').expect;
const Utils = require('../../../../utils/index');
describe('AWS - Cognito User Pool: Single User Pool with multiple ' +
'events with multiple functions', () => {
beforeAll(() => {
Utils.createTestService('aws-nodejs', path.join(__dirname, 'service'));
Utils.deployService();
});
it('should call the specified function when PreSignUp event is triggered', () => Utils
.getCognitoUserPoolId(process.env.COGNITO_USER_POOL_1)
.then((poolId) =>
Utils.createCognitoUser(poolId, 'test@test.com', 'Password123!')
)
.delay(60000)
.then(() => {
const logs = Utils.getFunctionLogs('preSignUp');
expect(/"triggerSource":"PreSignUp_\w+"/g.test(logs)).to.equal(true);
})
);
it('should call the specified function when CustomMessage event is triggered', () => Utils
.getCognitoUserPoolId(process.env.COGNITO_USER_POOL_1)
.then((poolId) => {
const logs = Utils.getFunctionLogs('customMessage');
expect(RegExp(`"userPoolId":"${poolId}"`, 'g').test(logs)).to.equal(true);
expect(/"triggerSource":"CustomMessage_AdminCreateUser"/g.test(logs)).to.equal(true);
})
);
afterAll(() => {
Utils.removeService();
});
});

View File

@ -0,0 +1,9 @@
'use strict';
module.exports.preSignUp = (event, context, callback) => {
const nextEvent = Object.assign({}, event);
nextEvent.response.autoConfirmUser = true;
process.stdout.write(JSON.stringify(nextEvent));
callback(null, nextEvent);
};

View File

@ -0,0 +1,13 @@
service: aws-nodejs
provider:
name: aws
runtime: nodejs6.10
functions:
preSignUp:
handler: handler.preSignUp
events:
- cognitoUserPool:
pool: ${env:COGNITO_USER_POOL_1}
trigger: PreSignUp

View File

@ -0,0 +1,29 @@
'use strict';
const path = require('path');
const expect = require('chai').expect;
const Utils = require('../../../../utils/index');
describe('AWS - Cognito User Pool: Single User Pool with single ' +
'event with single function', () => {
beforeAll(() => {
Utils.createTestService('aws-nodejs', path.join(__dirname, 'service'));
Utils.deployService();
});
it('should call the specified function when PreSignUp event is triggered', () => Utils
.getCognitoUserPoolId(process.env.COGNITO_USER_POOL_1)
.then((poolId) =>
Utils.createCognitoUser(poolId, 'test@test.com', 'Password123!')
)
.delay(60000)
.then(() => {
const logs = Utils.getFunctionLogs('preSignUp');
expect(/"triggerSource":"PreSignUp_\w+"/g.test(logs)).to.equal(true);
})
);
afterAll(() => {
Utils.removeService();
});
});

View File

@ -51,6 +51,8 @@ module.exports = {
process.env.TOPIC_2 = `${serviceName}-1`;
process.env.BUCKET_1 = `${serviceName}-1`;
process.env.BUCKET_2 = `${serviceName}-2`;
process.env.COGNITO_USER_POOL_1 = `${serviceName}-1`;
process.env.COGNITO_USER_POOL_2 = `${serviceName}-2`;
// return the name of the CloudFormation stack
return `${serviceName}-dev`;
@ -163,6 +165,32 @@ module.exports = {
return cwe.putEventsPromised(params);
},
getCognitoUserPoolId(userPoolName) {
const cisp = new AWS.CognitoIdentityServiceProvider({ region: 'us-east-1' });
BbPromise.promisifyAll(cisp, { suffix: 'Promised' });
const params = {
MaxResults: 50,
};
return cisp.listUserPoolsPromised(params)
.then((data) => data.UserPools.find((userPool) =>
RegExp(userPoolName, 'g').test(userPool.Name)).Id
);
},
createCognitoUser(userPoolId, username, password) {
const cisp = new AWS.CognitoIdentityServiceProvider({ region: 'us-east-1' });
BbPromise.promisifyAll(cisp, { suffix: 'Promised' });
const params = {
UserPoolId: userPoolId,
Username: username,
TemporaryPassword: password,
};
return cisp.adminCreateUserPromised(params);
},
getFunctionLogs(functionName) {
const logs = execSync(`${serverlessExec} logs --function ${functionName} --noGreeting true`);
const logsString = new Buffer(logs, 'base64').toString();