mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
Merge branch 'master' into improve-error-handling
This commit is contained in:
commit
6c54275b02
@ -78,6 +78,8 @@ functions:
|
||||
|
||||
Sometimes you might want to attach Lambda functions to existing Cognito User Pools. In that case you just need to set the `existing` event configuration property to `true`. All the other config parameters can also be used on existing user pools:
|
||||
|
||||
**IMPORTANT:** You can only attach 1 existing Cognito User Pool per function.
|
||||
|
||||
**NOTE:** Using the `existing` config will add an additional Lambda function and IAM Role to your stack. The Lambda function backs-up the Custom Cognito User Pool Resource which is used to support existing user pools.
|
||||
|
||||
```yaml
|
||||
|
||||
@ -16,7 +16,7 @@ function handler(event, context) {
|
||||
}
|
||||
|
||||
function create(event, context) {
|
||||
const { FunctionName, UserPoolName, UserPoolConfig } = event.ResourceProperties;
|
||||
const { FunctionName, UserPoolName, UserPoolConfigs } = event.ResourceProperties;
|
||||
const { Region, AccountId } = getEnvironment(context);
|
||||
|
||||
const lambdaArn = getLambdaArn(Region, AccountId, FunctionName);
|
||||
@ -32,7 +32,7 @@ function create(event, context) {
|
||||
updateConfiguration({
|
||||
lambdaArn,
|
||||
userPoolName: UserPoolName,
|
||||
userPoolConfig: UserPoolConfig,
|
||||
userPoolConfigs: UserPoolConfigs,
|
||||
region: Region,
|
||||
})
|
||||
)
|
||||
@ -41,14 +41,14 @@ function create(event, context) {
|
||||
|
||||
function update(event, context) {
|
||||
const { Region, AccountId } = getEnvironment(context);
|
||||
const { FunctionName, UserPoolName, UserPoolConfig } = event.ResourceProperties;
|
||||
const { FunctionName, UserPoolName, UserPoolConfigs } = event.ResourceProperties;
|
||||
|
||||
const lambdaArn = getLambdaArn(Region, AccountId, FunctionName);
|
||||
|
||||
return updateConfiguration({
|
||||
lambdaArn,
|
||||
userPoolName: UserPoolName,
|
||||
userPoolConfig: UserPoolConfig,
|
||||
userPoolConfigs: UserPoolConfigs,
|
||||
region: Region,
|
||||
});
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ function getConfiguration(config) {
|
||||
}
|
||||
|
||||
function updateConfiguration(config) {
|
||||
const { lambdaArn, userPoolConfig, region } = config;
|
||||
const { lambdaArn, userPoolConfigs, region } = config;
|
||||
const cognito = new CognitoIdentityServiceProvider({ region });
|
||||
|
||||
return getConfiguration(config).then(res => {
|
||||
@ -54,7 +54,9 @@ function updateConfiguration(config) {
|
||||
return accum;
|
||||
}, LambdaConfig);
|
||||
|
||||
LambdaConfig[userPoolConfig.Trigger] = lambdaArn;
|
||||
userPoolConfigs.forEach(poolConfig => {
|
||||
LambdaConfig[poolConfig.Trigger] = lambdaArn;
|
||||
});
|
||||
|
||||
return cognito.updateUserPool({ UserPoolId, LambdaConfig }).promise();
|
||||
});
|
||||
|
||||
@ -485,8 +485,12 @@ module.exports = {
|
||||
)}`
|
||||
);
|
||||
},
|
||||
getCustomResourceCognitoUserPoolResourceLogicalId(functionName, idx) {
|
||||
return `${this.getNormalizedFunctionName(functionName)}CustomCognitoUserPool${idx}`;
|
||||
getCustomResourceCognitoUserPoolResourceLogicalId(functionName) {
|
||||
// NOTE: we have to keep the 1 at the end to ensure backwards compatibility
|
||||
// previously we've used an index to allow the creation of multiple custom
|
||||
// Cognito User Pool resources
|
||||
// we're now using one resource to handle multiple Cognito User Pool event definitions
|
||||
return `${this.getNormalizedFunctionName(functionName)}CustomCognitoUserPool1`;
|
||||
},
|
||||
// Event Bridge
|
||||
getCustomResourceEventBridgeHandlerFunctionName() {
|
||||
|
||||
@ -780,10 +780,9 @@ describe('#naming()', () => {
|
||||
describe('#getCustomResourceCognitoUserPoolResourceLogicalId()', () => {
|
||||
it('should return the logical id of the Cognito User Pool custom resouce', () => {
|
||||
const functionName = 'my-function';
|
||||
const index = 1;
|
||||
expect(
|
||||
sdk.naming.getCustomResourceCognitoUserPoolResourceLogicalId(functionName, index)
|
||||
).to.equal('MyDashfunctionCustomCognitoUserPool1');
|
||||
expect(sdk.naming.getCustomResourceCognitoUserPoolResourceLogicalId(functionName)).to.equal(
|
||||
'MyDashfunctionCustomCognitoUserPool1'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -108,56 +108,90 @@ class AwsCompileCognitoUserPoolEvents {
|
||||
const { service } = this.serverless;
|
||||
const { provider } = service;
|
||||
const { compiledCloudFormationTemplate } = provider;
|
||||
const { Resources } = compiledCloudFormationTemplate;
|
||||
const iamRoleStatements = [];
|
||||
|
||||
// used to keep track of the custom resources created for each Cognito User Pool
|
||||
const poolResources = {};
|
||||
|
||||
service.getAllFunctions().forEach(functionName => {
|
||||
let numEventsForFunc = 0;
|
||||
let currentPoolName = null;
|
||||
let funcUsesExistingCognitoUserPool = false;
|
||||
const functionObj = service.getFunction(functionName);
|
||||
const FunctionName = functionObj.name;
|
||||
|
||||
if (functionObj.events) {
|
||||
functionObj.events.forEach((event, idx) => {
|
||||
functionObj.events.forEach(event => {
|
||||
if (event.cognitoUserPool && event.cognitoUserPool.existing) {
|
||||
idx++;
|
||||
numEventsForFunc++;
|
||||
const { pool, trigger } = event.cognitoUserPool;
|
||||
funcUsesExistingCognitoUserPool = true;
|
||||
|
||||
if (!currentPoolName) {
|
||||
currentPoolName = pool;
|
||||
}
|
||||
if (pool !== currentPoolName) {
|
||||
const errorMessage = [
|
||||
'Only one Cognito User Pool can be configured per function.',
|
||||
` In "${FunctionName}" you're attempting to configure "${currentPoolName}" and "${pool}" at the same time.`,
|
||||
].join('');
|
||||
throw new this.serverless.classes.Error(errorMessage);
|
||||
}
|
||||
|
||||
const eventFunctionLogicalId = this.provider.naming.getLambdaLogicalId(functionName);
|
||||
const customResourceFunctionLogicalId = this.provider.naming.getCustomResourceCognitoUserPoolHandlerFunctionLogicalId();
|
||||
const customCognitoUserPoolResourceLogicalId = this.provider.naming.getCustomResourceCognitoUserPoolResourceLogicalId(
|
||||
functionName,
|
||||
idx
|
||||
const customPoolResourceLogicalId = this.provider.naming.getCustomResourceCognitoUserPoolResourceLogicalId(
|
||||
functionName
|
||||
);
|
||||
|
||||
const customCognitoUserPool = {
|
||||
[customCognitoUserPoolResourceLogicalId]: {
|
||||
Type: 'Custom::CognitoUserPool',
|
||||
Version: 1.0,
|
||||
DependsOn: [eventFunctionLogicalId, customResourceFunctionLogicalId],
|
||||
Properties: {
|
||||
ServiceToken: {
|
||||
'Fn::GetAtt': [customResourceFunctionLogicalId, 'Arn'],
|
||||
},
|
||||
FunctionName,
|
||||
UserPoolName: pool,
|
||||
UserPoolConfig: {
|
||||
Trigger: trigger,
|
||||
// store how often the custom Cognito User Pool resource is used
|
||||
if (poolResources[pool]) {
|
||||
poolResources[pool] = _.union(poolResources[pool], [customPoolResourceLogicalId]);
|
||||
} else {
|
||||
Object.assign(poolResources, {
|
||||
[pool]: [customPoolResourceLogicalId],
|
||||
});
|
||||
}
|
||||
|
||||
let customCognitoUserPoolResource;
|
||||
if (numEventsForFunc === 1) {
|
||||
customCognitoUserPoolResource = {
|
||||
[customPoolResourceLogicalId]: {
|
||||
Type: 'Custom::CognitoUserPool',
|
||||
Version: 1.0,
|
||||
DependsOn: [eventFunctionLogicalId, customResourceFunctionLogicalId],
|
||||
Properties: {
|
||||
ServiceToken: {
|
||||
'Fn::GetAtt': [customResourceFunctionLogicalId, 'Arn'],
|
||||
},
|
||||
FunctionName,
|
||||
UserPoolName: pool,
|
||||
UserPoolConfigs: [
|
||||
{
|
||||
Trigger: trigger,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
_.merge(compiledCloudFormationTemplate.Resources, customCognitoUserPool);
|
||||
iamRoleStatements.push({
|
||||
Effect: 'Allow',
|
||||
Resource: '*',
|
||||
Action: [
|
||||
'cognito-idp:ListUserPools',
|
||||
'cognito-idp:DescribeUserPool',
|
||||
'cognito-idp:UpdateUserPool',
|
||||
],
|
||||
});
|
||||
} else {
|
||||
Resources[customPoolResourceLogicalId].Properties.UserPoolConfigs.push({
|
||||
Trigger: trigger,
|
||||
});
|
||||
}
|
||||
|
||||
iamRoleStatements.push({
|
||||
Effect: 'Allow',
|
||||
Resource: '*',
|
||||
Action: [
|
||||
'cognito-idp:ListUserPools',
|
||||
'cognito-idp:DescribeUserPool',
|
||||
'cognito-idp:UpdateUserPool',
|
||||
],
|
||||
});
|
||||
_.merge(Resources, customCognitoUserPoolResource);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -171,6 +205,22 @@ class AwsCompileCognitoUserPoolEvents {
|
||||
}
|
||||
});
|
||||
|
||||
// check if we need to add DependsOn clauses in case more than 1
|
||||
// custom resources are created for one Cognito User Pool (to avoid race conditions)
|
||||
if (Object.keys(poolResources).length > 0) {
|
||||
Object.keys(poolResources).forEach(pool => {
|
||||
const resources = poolResources[pool];
|
||||
if (resources.length > 1) {
|
||||
resources.forEach((currResourceLogicalId, idx) => {
|
||||
if (idx > 0) {
|
||||
const prevResourceLogicalId = resources[idx - 1];
|
||||
Resources[currResourceLogicalId].DependsOn.push(prevResourceLogicalId);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (iamRoleStatements.length) {
|
||||
return addCustomResourceToService.call(this, 'cognitoUserPool', iamRoleStatements);
|
||||
}
|
||||
|
||||
@ -368,6 +368,22 @@ describe('AwsCompileCognitoUserPoolEvents', () => {
|
||||
|
||||
expect(addCustomResourceToServiceStub).to.have.been.calledOnce;
|
||||
expect(addCustomResourceToServiceStub.args[0][1]).to.equal('cognitoUserPool');
|
||||
expect(addCustomResourceToServiceStub.args[0][2]).to.deep.equal([
|
||||
{
|
||||
Action: [
|
||||
'cognito-idp:ListUserPools',
|
||||
'cognito-idp:DescribeUserPool',
|
||||
'cognito-idp:UpdateUserPool',
|
||||
],
|
||||
Effect: 'Allow',
|
||||
Resource: '*',
|
||||
},
|
||||
{
|
||||
Action: ['lambda:AddPermission', 'lambda:RemovePermission'],
|
||||
Effect: 'Allow',
|
||||
Resource: 'arn:aws:lambda:*:*:function:first',
|
||||
},
|
||||
]);
|
||||
expect(Resources.FirstCustomCognitoUserPool1).to.deep.equal({
|
||||
Type: 'Custom::CognitoUserPool',
|
||||
Version: 1,
|
||||
@ -378,13 +394,273 @@ describe('AwsCompileCognitoUserPoolEvents', () => {
|
||||
},
|
||||
FunctionName: 'first',
|
||||
UserPoolName: 'existing-cognito-user-pool',
|
||||
UserPoolConfig: {
|
||||
Trigger: 'CustomMessage',
|
||||
},
|
||||
UserPoolConfigs: [
|
||||
{
|
||||
Trigger: 'CustomMessage',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create the necessary resources for a service using multiple event definitions', () => {
|
||||
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
|
||||
first: {
|
||||
name: 'first',
|
||||
events: [
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'CustomMessage',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'PreSignUp',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'DefineAuthChallenge',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return expect(
|
||||
awsCompileCognitoUserPoolEvents.existingCognitoUserPools()
|
||||
).to.be.fulfilled.then(() => {
|
||||
const {
|
||||
Resources,
|
||||
} = awsCompileCognitoUserPoolEvents.serverless.service.provider.compiledCloudFormationTemplate;
|
||||
|
||||
expect(addCustomResourceToServiceStub).to.have.been.calledOnce;
|
||||
expect(addCustomResourceToServiceStub.args[0][1]).to.equal('cognitoUserPool');
|
||||
expect(addCustomResourceToServiceStub.args[0][2]).to.deep.equal([
|
||||
{
|
||||
Action: [
|
||||
'cognito-idp:ListUserPools',
|
||||
'cognito-idp:DescribeUserPool',
|
||||
'cognito-idp:UpdateUserPool',
|
||||
],
|
||||
Effect: 'Allow',
|
||||
Resource: '*',
|
||||
},
|
||||
{
|
||||
Action: ['lambda:AddPermission', 'lambda:RemovePermission'],
|
||||
Effect: 'Allow',
|
||||
Resource: 'arn:aws:lambda:*:*:function:first',
|
||||
},
|
||||
]);
|
||||
expect(Resources.FirstCustomCognitoUserPool1).to.deep.equal({
|
||||
Type: 'Custom::CognitoUserPool',
|
||||
Version: 1,
|
||||
DependsOn: ['FirstLambdaFunction', 'CustomDashresourceDashexistingDashcupLambdaFunction'],
|
||||
Properties: {
|
||||
ServiceToken: {
|
||||
'Fn::GetAtt': ['CustomDashresourceDashexistingDashcupLambdaFunction', 'Arn'],
|
||||
},
|
||||
FunctionName: 'first',
|
||||
UserPoolName: 'existing-cognito-user-pool',
|
||||
UserPoolConfigs: [
|
||||
{
|
||||
Trigger: 'CustomMessage',
|
||||
},
|
||||
{
|
||||
Trigger: 'PreSignUp',
|
||||
},
|
||||
{
|
||||
Trigger: 'DefineAuthChallenge',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create DependsOn clauses when one cognito user pool is used in more than 1 custom resources', () => {
|
||||
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
|
||||
first: {
|
||||
name: 'first',
|
||||
events: [
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'CustomMessage',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'PreSignUp',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'DefineAuthChallenge',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
second: {
|
||||
name: 'second',
|
||||
events: [
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'PostConfirmation',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'PreAuthentication',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'PostAuthentication',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return expect(
|
||||
awsCompileCognitoUserPoolEvents.existingCognitoUserPools()
|
||||
).to.be.fulfilled.then(() => {
|
||||
const {
|
||||
Resources,
|
||||
} = awsCompileCognitoUserPoolEvents.serverless.service.provider.compiledCloudFormationTemplate;
|
||||
|
||||
expect(addCustomResourceToServiceStub).to.have.been.calledOnce;
|
||||
expect(addCustomResourceToServiceStub.args[0][1]).to.equal('cognitoUserPool');
|
||||
expect(addCustomResourceToServiceStub.args[0][2]).to.deep.equal([
|
||||
{
|
||||
Action: [
|
||||
'cognito-idp:ListUserPools',
|
||||
'cognito-idp:DescribeUserPool',
|
||||
'cognito-idp:UpdateUserPool',
|
||||
],
|
||||
Effect: 'Allow',
|
||||
Resource: '*',
|
||||
},
|
||||
{
|
||||
Action: ['lambda:AddPermission', 'lambda:RemovePermission'],
|
||||
Effect: 'Allow',
|
||||
Resource: 'arn:aws:lambda:*:*:function:first',
|
||||
},
|
||||
{
|
||||
Action: [
|
||||
'cognito-idp:ListUserPools',
|
||||
'cognito-idp:DescribeUserPool',
|
||||
'cognito-idp:UpdateUserPool',
|
||||
],
|
||||
Effect: 'Allow',
|
||||
Resource: '*',
|
||||
},
|
||||
{
|
||||
Action: ['lambda:AddPermission', 'lambda:RemovePermission'],
|
||||
Effect: 'Allow',
|
||||
Resource: 'arn:aws:lambda:*:*:function:second',
|
||||
},
|
||||
]);
|
||||
expect(Object.keys(Resources)).to.have.length(2);
|
||||
expect(Resources.FirstCustomCognitoUserPool1).to.deep.equal({
|
||||
Type: 'Custom::CognitoUserPool',
|
||||
Version: 1,
|
||||
DependsOn: ['FirstLambdaFunction', 'CustomDashresourceDashexistingDashcupLambdaFunction'],
|
||||
Properties: {
|
||||
ServiceToken: {
|
||||
'Fn::GetAtt': ['CustomDashresourceDashexistingDashcupLambdaFunction', 'Arn'],
|
||||
},
|
||||
FunctionName: 'first',
|
||||
UserPoolName: 'existing-cognito-user-pool',
|
||||
UserPoolConfigs: [
|
||||
{
|
||||
Trigger: 'CustomMessage',
|
||||
},
|
||||
{
|
||||
Trigger: 'PreSignUp',
|
||||
},
|
||||
{
|
||||
Trigger: 'DefineAuthChallenge',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(Resources.SecondCustomCognitoUserPool1).to.deep.equal({
|
||||
Type: 'Custom::CognitoUserPool',
|
||||
Version: 1,
|
||||
DependsOn: [
|
||||
'SecondLambdaFunction',
|
||||
'CustomDashresourceDashexistingDashcupLambdaFunction',
|
||||
'FirstCustomCognitoUserPool1',
|
||||
],
|
||||
Properties: {
|
||||
ServiceToken: {
|
||||
'Fn::GetAtt': ['CustomDashresourceDashexistingDashcupLambdaFunction', 'Arn'],
|
||||
},
|
||||
FunctionName: 'second',
|
||||
UserPoolName: 'existing-cognito-user-pool',
|
||||
UserPoolConfigs: [
|
||||
{
|
||||
Trigger: 'PostConfirmation',
|
||||
},
|
||||
{
|
||||
Trigger: 'PreAuthentication',
|
||||
},
|
||||
{
|
||||
Trigger: 'PostAuthentication',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if more than 1 Cognito User Pool is configured per function', () => {
|
||||
awsCompileCognitoUserPoolEvents.serverless.service.functions = {
|
||||
first: {
|
||||
name: 'first',
|
||||
events: [
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool',
|
||||
trigger: 'CustomMessage',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
cognitoUserPool: {
|
||||
pool: 'existing-cognito-user-pool-2',
|
||||
trigger: 'PreSignUp',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return expect(() => awsCompileCognitoUserPoolEvents.existingCognitoUserPools()).to.throw(
|
||||
'Only one Cognito User Pool'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#mergeWithCustomResources()', () => {
|
||||
|
||||
@ -208,6 +208,7 @@ class AwsCompileS3Events {
|
||||
|
||||
service.getAllFunctions().forEach(functionName => {
|
||||
let numEventsForFunc = 0;
|
||||
let currentBucketName = null;
|
||||
let funcUsesExistingS3Bucket = false;
|
||||
const functionObj = service.getFunction(functionName);
|
||||
const FunctionName = functionObj.name;
|
||||
@ -221,6 +222,17 @@ class AwsCompileS3Events {
|
||||
const notificationEvent = event.s3.event || 's3:ObjectCreated:*';
|
||||
funcUsesExistingS3Bucket = true;
|
||||
|
||||
if (!currentBucketName) {
|
||||
currentBucketName = bucket;
|
||||
}
|
||||
if (bucket !== currentBucketName) {
|
||||
const errorMessage = [
|
||||
'Only one S3 Bucket can be configured per function.',
|
||||
` In "${FunctionName}" you're attempting to configure "${currentBucketName}" and "${bucket}" at the same time.`,
|
||||
].join('');
|
||||
throw new this.serverless.classes.Error(errorMessage);
|
||||
}
|
||||
|
||||
rules = _.map(event.s3.rules, rule => {
|
||||
const key = Object.keys(rule)[0];
|
||||
const value = rule[key];
|
||||
|
||||
@ -741,5 +741,29 @@ describe('AwsCompileS3Events', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if more than 1 S3 bucket is configured per function', () => {
|
||||
awsCompileS3Events.serverless.service.functions = {
|
||||
first: {
|
||||
name: 'second',
|
||||
events: [
|
||||
{
|
||||
s3: {
|
||||
bucket: 'existing-s3-bucket',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
s3: {
|
||||
bucket: 'existing-s3-bucket-2',
|
||||
existing: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return expect(() => awsCompileS3Events.existingS3Buckets()).to.throw('Only one S3 Bucket');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -13,8 +13,8 @@ function basic(event, context, callback) {
|
||||
return callback(null, nextEvent);
|
||||
}
|
||||
|
||||
function existing(event, context, callback) {
|
||||
const functionName = 'existing';
|
||||
function existingSimple(event, context, callback) {
|
||||
const functionName = 'existingSimple';
|
||||
const nextEvent = Object.assign({}, event);
|
||||
nextEvent.response.autoConfirmUser = true;
|
||||
|
||||
@ -22,4 +22,11 @@ function existing(event, context, callback) {
|
||||
return callback(null, nextEvent);
|
||||
}
|
||||
|
||||
module.exports = { basic, existing };
|
||||
function existingMulti(event, context, callback) {
|
||||
const functionName = 'existingMulti';
|
||||
|
||||
log(functionName, JSON.stringify(event));
|
||||
return callback(null, event);
|
||||
}
|
||||
|
||||
module.exports = { basic, existingSimple, existingMulti };
|
||||
|
||||
@ -12,10 +12,22 @@ functions:
|
||||
- cognitoUserPool:
|
||||
pool: CHANGE_TO_UNIQUE_PER_RUN
|
||||
trigger: PreSignUp
|
||||
existing:
|
||||
handler: core.existing
|
||||
existingSimple:
|
||||
handler: core.existingSimple
|
||||
events:
|
||||
- cognitoUserPool:
|
||||
pool: CHANGE_TO_UNIQUE_PER_RUN
|
||||
trigger: PreSignUp
|
||||
existing: true
|
||||
# testing if two functions share one cognito user pool with multiple configs
|
||||
existingMulti:
|
||||
handler: core.existingMulti
|
||||
events:
|
||||
- cognitoUserPool:
|
||||
pool: CHANGE_TO_UNIQUE_PER_RUN
|
||||
trigger: PreSignUp
|
||||
existing: true
|
||||
- cognitoUserPool:
|
||||
pool: CHANGE_TO_UNIQUE_PER_RUN
|
||||
trigger: PreAuthentication
|
||||
existing: true
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const BbPromise = require('bluebird');
|
||||
const { expect } = require('chai');
|
||||
|
||||
const { getTmpDirPath } = require('../../utils/fs');
|
||||
@ -9,6 +10,9 @@ const {
|
||||
deleteUserPool,
|
||||
findUserPoolByName,
|
||||
createUser,
|
||||
createUserPoolClient,
|
||||
setUserPassword,
|
||||
initiateAuth,
|
||||
} = require('../../utils/cognito');
|
||||
const {
|
||||
createTestService,
|
||||
@ -23,7 +27,8 @@ describe('AWS - Cognito User Pool Integration Test', () => {
|
||||
let stackName;
|
||||
let tmpDirPath;
|
||||
let poolBasicSetup;
|
||||
let poolExistingSetup;
|
||||
let poolExistingSimpleSetup;
|
||||
let poolExistingMultiSetup;
|
||||
const stage = 'dev';
|
||||
|
||||
beforeAll(() => {
|
||||
@ -36,17 +41,23 @@ describe('AWS - Cognito User Pool Integration Test', () => {
|
||||
// Ensure unique user pool names for each test (to avoid collision among concurrent CI runs)
|
||||
config => {
|
||||
poolBasicSetup = `${config.service} CUP Basic`;
|
||||
poolExistingSetup = `${config.service} CUP Existing`;
|
||||
poolExistingSimpleSetup = `${config.service} CUP Existing Simple`;
|
||||
poolExistingMultiSetup = `${config.service} CUP Existing Multi`;
|
||||
config.functions.basic.events[0].cognitoUserPool.pool = poolBasicSetup;
|
||||
config.functions.existing.events[0].cognitoUserPool.pool = poolExistingSetup;
|
||||
config.functions.existingSimple.events[0].cognitoUserPool.pool = poolExistingSimpleSetup;
|
||||
config.functions.existingMulti.events[0].cognitoUserPool.pool = poolExistingMultiSetup;
|
||||
config.functions.existingMulti.events[1].cognitoUserPool.pool = poolExistingMultiSetup;
|
||||
},
|
||||
});
|
||||
serviceName = serverlessConfig.service;
|
||||
stackName = `${serviceName}-${stage}`;
|
||||
// create an external Cognito User Pool
|
||||
// NOTE: deployment can only be done once the Cognito User Pool is created
|
||||
console.info(`Creating Cognito User Pool "${poolExistingSetup}"...`);
|
||||
return createUserPool(poolExistingSetup).then(() => {
|
||||
// create external Cognito User Pools
|
||||
// NOTE: deployment can only be done once the Cognito User Pools are created
|
||||
console.info('Creating Cognito User Pools');
|
||||
return BbPromise.all([
|
||||
createUserPool(poolExistingSimpleSetup),
|
||||
createUserPool(poolExistingMultiSetup),
|
||||
]).then(() => {
|
||||
console.info(`Deploying "${stackName}" service...`);
|
||||
deployService();
|
||||
});
|
||||
@ -55,8 +66,11 @@ describe('AWS - Cognito User Pool Integration Test', () => {
|
||||
afterAll(() => {
|
||||
console.info('Removing service...');
|
||||
removeService();
|
||||
console.info(`Deleting Cognito User Pool "${poolExistingSetup}"...`);
|
||||
return deleteUserPool(poolExistingSetup);
|
||||
console.info('Deleting Cognito User Pools');
|
||||
return BbPromise.all([
|
||||
deleteUserPool(poolExistingSimpleSetup),
|
||||
deleteUserPool(poolExistingMultiSetup),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('Basic Setup', () => {
|
||||
@ -80,22 +94,53 @@ describe('AWS - Cognito User Pool Integration Test', () => {
|
||||
});
|
||||
|
||||
describe('Existing Setup', () => {
|
||||
it('should invoke function when a user is created', () => {
|
||||
let userPoolId;
|
||||
const functionName = 'existing';
|
||||
const markers = getMarkers(functionName);
|
||||
describe('single function / single pool setup', () => {
|
||||
it('should invoke function when a user is created', () => {
|
||||
let userPoolId;
|
||||
const functionName = 'existingSimple';
|
||||
const markers = getMarkers(functionName);
|
||||
|
||||
return findUserPoolByName(poolExistingSetup)
|
||||
.then(pool => {
|
||||
userPoolId = pool.Id;
|
||||
return createUser(userPoolId, 'janedoe', '!!!wAsD123456wAsD!!!');
|
||||
})
|
||||
.then(() => waitForFunctionLogs(functionName, markers.start, markers.end))
|
||||
.then(logs => {
|
||||
expect(logs).to.include(`"userPoolId":"${userPoolId}"`);
|
||||
expect(logs).to.include('"userName":"janedoe"');
|
||||
expect(logs).to.include('"triggerSource":"PreSignUp_AdminCreateUser"');
|
||||
});
|
||||
return findUserPoolByName(poolExistingSimpleSetup)
|
||||
.then(pool => {
|
||||
userPoolId = pool.Id;
|
||||
return createUser(userPoolId, 'janedoe', '!!!wAsD123456wAsD!!!');
|
||||
})
|
||||
.then(() => waitForFunctionLogs(functionName, markers.start, markers.end))
|
||||
.then(logs => {
|
||||
expect(logs).to.include(`"userPoolId":"${userPoolId}"`);
|
||||
expect(logs).to.include('"userName":"janedoe"');
|
||||
expect(logs).to.include('"triggerSource":"PreSignUp_AdminCreateUser"');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('single function / multi pool setup', () => {
|
||||
it('should invoke function when a user inits auth after being created', () => {
|
||||
let userPoolId;
|
||||
let clientId;
|
||||
const functionName = 'existingMulti';
|
||||
const markers = getMarkers(functionName);
|
||||
const username = 'janedoe';
|
||||
const password = '!!!wAsD123456wAsD!!!';
|
||||
|
||||
return findUserPoolByName(poolExistingMultiSetup)
|
||||
.then(pool => {
|
||||
userPoolId = pool.Id;
|
||||
return createUserPoolClient('myClient', userPoolId).then(client => {
|
||||
clientId = client.UserPoolClient.ClientId;
|
||||
return createUser(userPoolId, username, password)
|
||||
.then(() => setUserPassword(userPoolId, username, password))
|
||||
.then(() => initiateAuth(clientId, username, password));
|
||||
});
|
||||
})
|
||||
.then(() => waitForFunctionLogs(functionName, markers.start, markers.end))
|
||||
.then(logs => {
|
||||
expect(logs).to.include(`"userPoolId":"${userPoolId}"`);
|
||||
expect(logs).to.include(`"userName":"${username}"`);
|
||||
expect(logs).to.include('"triggerSource":"PreSignUp_AdminCreateUser"');
|
||||
expect(logs).to.include('"triggerSource":"PreAuthentication_Authentication"');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -9,6 +9,17 @@ function createUserPool(name) {
|
||||
return cognito.createUserPool({ PoolName: name }).promise();
|
||||
}
|
||||
|
||||
function createUserPoolClient(name, userPoolId) {
|
||||
const cognito = new AWS.CognitoIdentityServiceProvider({ region });
|
||||
|
||||
const params = {
|
||||
ClientName: name,
|
||||
UserPoolId: userPoolId,
|
||||
ExplicitAuthFlows: ['USER_PASSWORD_AUTH'],
|
||||
};
|
||||
return cognito.createUserPoolClient(params).promise();
|
||||
}
|
||||
|
||||
function deleteUserPool(name) {
|
||||
const cognito = new AWS.CognitoIdentityServiceProvider({ region });
|
||||
|
||||
@ -53,9 +64,38 @@ function createUser(userPoolId, username, password) {
|
||||
return cognito.adminCreateUser(params).promise();
|
||||
}
|
||||
|
||||
function setUserPassword(userPoolId, username, password) {
|
||||
const cognito = new AWS.CognitoIdentityServiceProvider({ region });
|
||||
|
||||
const params = {
|
||||
UserPoolId: userPoolId,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Permanent: true,
|
||||
};
|
||||
return cognito.adminSetUserPassword(params).promise();
|
||||
}
|
||||
|
||||
function initiateAuth(clientId, username, password) {
|
||||
const cognito = new AWS.CognitoIdentityServiceProvider({ region });
|
||||
|
||||
const params = {
|
||||
ClientId: clientId,
|
||||
AuthFlow: 'USER_PASSWORD_AUTH',
|
||||
AuthParameters: {
|
||||
USERNAME: username,
|
||||
PASSWORD: password,
|
||||
},
|
||||
};
|
||||
return cognito.initiateAuth(params).promise();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createUserPool: persistentRequest.bind(this, createUserPool),
|
||||
deleteUserPool: persistentRequest.bind(this, deleteUserPool),
|
||||
findUserPoolByName: persistentRequest.bind(this, findUserPoolByName),
|
||||
createUserPoolClient: persistentRequest.bind(this, createUserPoolClient),
|
||||
createUser: persistentRequest.bind(this, createUser),
|
||||
setUserPassword: persistentRequest.bind(this, setUserPassword),
|
||||
initiateAuth: persistentRequest.bind(this, initiateAuth),
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user