From 0bf576eb55dabfd4d8f51072bcb3608c601895ea Mon Sep 17 00:00:00 2001 From: Antti Kojo Date: Fri, 17 Mar 2023 09:44:25 +0200 Subject: [PATCH 1/2] add sqs event support --- index.js | 57 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index 406e1c1..14065f8 100644 --- a/index.js +++ b/index.js @@ -95,21 +95,22 @@ class CustomRoles { return this.getPolicyFromStatements('logging', statements); } - getStreamsPolicy(functionName, functionObj) { + getEventPolicy(functionName, functionObj, eventType) { if (!functionObj.events) { return null; } const resources = functionObj.events.reduce((acc, event) => { - if (!event.stream) { + if (!event[eventType]) { return acc; } + let e = event[eventType]; let eventSourceArn; - if (typeof event.stream === 'string') { - eventSourceArn = event.stream; - } else if (typeof event.stream === 'object' && event.stream.arn) { - eventSourceArn = event.stream.arn; + if (typeof e === 'string') { + eventSourceArn = e; + } else if (typeof e === 'object' && e.arn) { + eventSourceArn = e.arn; } if (!eventSourceArn) { @@ -117,17 +118,19 @@ class CustomRoles { return acc; } - const streamType = event.stream.type || eventSourceArn.split(':')[2]; - if (streamType === 'dynamodb') { + const eventSubType = eventType === 'stream' ? e.type || eventSourceArn.split(':')[2] : undefined; + if (eventType === 'stream' && eventSubType === 'dynamodb') { acc.dynamodb.push(eventSourceArn); - } else if (streamType === 'kinesis') { + } else if (eventType === 'stream' && eventSubType === 'kinesis') { acc.kinesis.push(eventSourceArn); + } else if (eventType === 'sqs') { + acc.sqs.push(eventSourceArn); } else { - this.log(`WARNING: Stream event type for function '${functionName}' is not configured properly. IAM permissions will not be set properly.`); + this.log(`WARNING: event type for function '${functionName}' is not configured properly. IAM permissions will not be set properly.`); } return acc; - }, { dynamodb: [], kinesis: [] }); + }, { dynamodb: [], kinesis: [], sqs: [] }); const statements = []; if (resources.dynamodb.length) { @@ -137,7 +140,7 @@ class CustomRoles { 'dynamodb:GetRecords', 'dynamodb:GetShardIterator', 'dynamodb:DescribeStream', - 'dynamodb:ListStreams' + 'dynamodb:ListStreams', ], Resource: resources.dynamodb }); @@ -149,13 +152,32 @@ class CustomRoles { 'kinesis:GetRecords', 'kinesis:GetShardIterator', 'kinesis:DescribeStream', - 'kinesis:ListStreams' + 'kinesis:ListStreams', ], Resource: resources.kinesis }); } + if (resources.sqs.length) { + statements.push({ + Effect: 'Allow', + Action: [ + 'sqs:ReceiveMessage', + 'sqs:DeleteMessage', + 'sqs:GetQueueAttributes' + ], + Resource: resources.sqs + }); + } - return this.getPolicyFromStatements('streams', statements); + return this.getPolicyFromStatements(eventType, statements); + } + + getEventPolicyName(eventType) { + if (eventType === 'stream') { return 'streams' } + else if (eventType === 'sqs') { return 'queues' } + else { + this.log(`WARNING: event type for function '${functionName}' is not configured properly. IAM permissions will not be set properly.`); + } } getRole(stackName, functionName, policies, managedPolicies, permissionsBoundary) { @@ -239,11 +261,16 @@ class CustomRoles { policies.push(customPolicy); } - const streamsPolicy = this.getStreamsPolicy(functionName, functionObj); + const streamsPolicy = this.getEventPolicy(functionName, functionObj, 'stream'); if (streamsPolicy) { policies.push(streamsPolicy); } + const sqsPolicy = this.getEventPolicy(functionName, functionObj, 'sqs'); + if (sqsPolicy) { + policies.push(sqsPolicy); + } + if (service.provider.vpc || functionObj.vpc) { managedPolicies.push(VPC_POLICY); } From 99fb73451173c0ee5b6ab5c0ef1d0e8587f22bea Mon Sep 17 00:00:00 2001 From: Antti Kojo Date: Fri, 17 Mar 2023 14:09:34 +0200 Subject: [PATCH 2/2] update tests and cleanup --- index.js | 26 +++++++++++++++----------- test/index-tests.js | 26 +++++++++++++------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/index.js b/index.js index 14065f8..c34d3c9 100644 --- a/index.js +++ b/index.js @@ -95,6 +95,17 @@ class CustomRoles { return this.getPolicyFromStatements('logging', statements); } + getEventPolicyName(eventType, functionName) { + if (eventType === 'stream') { + return 'streams'; + } + if (eventType === 'sqs') { + return 'queues'; + } + this.log(`WARNING: event type for function '${functionName}' is not configured properly. IAM permissions will not be set properly.`); + return undefined; + } + getEventPolicy(functionName, functionObj, eventType) { if (!functionObj.events) { return null; @@ -105,7 +116,7 @@ class CustomRoles { return acc; } - let e = event[eventType]; + const e = event[eventType]; let eventSourceArn; if (typeof e === 'string') { eventSourceArn = e; @@ -114,7 +125,7 @@ class CustomRoles { } if (!eventSourceArn) { - this.log(`WARNING: Stream event source for function '${functionName}' is not configured properly. IAM permissions will not be set properly.`); + this.log(`WARNING: event source for function '${functionName}' is not configured properly. IAM permissions will not be set properly.`); return acc; } @@ -169,15 +180,8 @@ class CustomRoles { }); } - return this.getPolicyFromStatements(eventType, statements); - } - - getEventPolicyName(eventType) { - if (eventType === 'stream') { return 'streams' } - else if (eventType === 'sqs') { return 'queues' } - else { - this.log(`WARNING: event type for function '${functionName}' is not configured properly. IAM permissions will not be set properly.`); - } + return this.getPolicyFromStatements(this + .getEventPolicyName(eventType, functionName), statements); } getRole(stackName, functionName, policies, managedPolicies, permissionsBoundary) { diff --git a/test/index-tests.js b/test/index-tests.js index f50119b..0fed666 100644 --- a/test/index-tests.js +++ b/test/index-tests.js @@ -98,7 +98,7 @@ describe('serverless-plugin-custom-roles', function() { }); }); - describe('#getStreamsPolicy', function() { + describe('#getEventPolicy for stream', function() { beforeEach(function() { this.functionName = 'testFunction'; this.instance = createTestInstance(); @@ -107,7 +107,7 @@ describe('serverless-plugin-custom-roles', function() { it('should do nothing if function does not have events defined', function() { const functionObj = {}; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result).to.be.null; sinon.assert.notCalled(this.instance.serverless.cli.log); @@ -120,7 +120,7 @@ describe('serverless-plugin-custom-roles', function() { }] }; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result).to.be.null; sinon.assert.notCalled(this.instance.serverless.cli.log); @@ -134,7 +134,7 @@ describe('serverless-plugin-custom-roles', function() { }] }; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result) .to.containSubset({ @@ -167,7 +167,7 @@ describe('serverless-plugin-custom-roles', function() { }] }; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result) .to.containSubset({ @@ -196,12 +196,12 @@ describe('serverless-plugin-custom-roles', function() { }] }; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result).to.be.null; sinon.assert.calledWithExactly( this.instance.serverless.cli.log, - `[serverless-plugin-custom-roles]: WARNING: Stream event source for function '${this.functionName}' is not configured properly. IAM permissions will not be set properly.` + `[serverless-plugin-custom-roles]: WARNING: event source for function '${this.functionName}' is not configured properly. IAM permissions will not be set properly.` ); }); @@ -216,7 +216,7 @@ describe('serverless-plugin-custom-roles', function() { }] }; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result) .to.containSubset({ @@ -248,7 +248,7 @@ describe('serverless-plugin-custom-roles', function() { }] }; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result) .to.containSubset({ @@ -281,7 +281,7 @@ describe('serverless-plugin-custom-roles', function() { }] }; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result) .to.containSubset({ @@ -314,7 +314,7 @@ describe('serverless-plugin-custom-roles', function() { }] }; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result) .to.containSubset({ @@ -347,12 +347,12 @@ describe('serverless-plugin-custom-roles', function() { }] }; - const result = this.instance.getStreamsPolicy(this.functionName, functionObj); + const result = this.instance.getEventPolicy(this.functionName, functionObj, 'stream'); expect(result).to.be.null; sinon.assert.calledWithExactly( this.instance.serverless.cli.log, - `[serverless-plugin-custom-roles]: WARNING: Stream event type for function '${this.functionName}' is not configured properly. IAM permissions will not be set properly.` + `[serverless-plugin-custom-roles]: WARNING: event type for function '${this.functionName}' is not configured properly. IAM permissions will not be set properly.` ); }); });