diff --git a/lib/deploy/stepFunctions/compileIamRole.js b/lib/deploy/stepFunctions/compileIamRole.js index bcdc94c4..c5f56760 100644 --- a/lib/deploy/stepFunctions/compileIamRole.js +++ b/lib/deploy/stepFunctions/compileIamRole.js @@ -588,6 +588,25 @@ function getS3ObjectPermissions(action, state) { const prefix = state.Parameters.Prefix; let arn; + if (action === 's3:listObjectsV2') { + return [ + { + action: 's3:Get*', + resource: [ + `arn:aws:s3:::${bucket}`, + `arn:aws:s3:::${bucket}/*`, + ], + }, + { + action: 's3:List*', + resource: [ + `arn:aws:s3:::${bucket}`, + `arn:aws:s3:::${bucket}/*`, + ], + }, + ]; + } + if (prefix) { arn = `arn:aws:s3:::${bucket}/${prefix}/${key}`; } else if (bucket === '*' && key === '*') { @@ -737,6 +756,9 @@ function getIamPermissions(taskStates) { case 'arn:aws:states:::s3:putObject': case 'arn:aws:states:::aws-sdk:s3:putObject': return getS3ObjectPermissions('s3:PutObject', state); + case 'arn:aws:states:::s3:listObjectsV2': + case 'arn:aws:states:::aws-sdk:s3:listObjectsV2': + return getS3ObjectPermissions('s3:listObjectsV2', state); default: if (isIntrinsic(state.Resource) || !!state.Resource.match(/arn:aws(-[a-z]+)*:lambda/)) { diff --git a/lib/deploy/stepFunctions/compileIamRole.test.js b/lib/deploy/stepFunctions/compileIamRole.test.js index 16eb6449..ab17a4a7 100644 --- a/lib/deploy/stepFunctions/compileIamRole.test.js +++ b/lib/deploy/stepFunctions/compileIamRole.test.js @@ -3804,4 +3804,57 @@ describe('#compileIamRole', () => { .PermissionsBoundary; expect(boundary).to.equal('arn:aws:iam::myAccount:policy/permission_boundary'); }); + + + it('should handle permissions listObjectsV2', () => { + const myBucket = 'myBucket'; + serverless.service.stepFunctions = { + stateMachines: { + myStateMachine1: { + id: 'StateMachine1', + definition: { + StartAt: 'A', + States: { + A: { + Type: 'Map', + ItemProcessor: { + ProcessorConfig: { + Mode: 'DISTRIBUTED', + }, + }, + StartAt: 'B', + States: { + B: { + Type: 'Task', + Resource: 'arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:hello', + End: true, + }, + }, + ItemReader: { + Resource: 'arn:aws:states:::s3:listObjectsV2', + Parameters: { + Bucket: myBucket, + Prefix: 'hello', + }, + }, + End: true, + }, + }, + }, + }, + }, + }; + + serverlessStepFunctions.compileIamRole(); + const statements = serverlessStepFunctions.serverless.service.provider + .compiledCloudFormationTemplate.Resources.StateMachine1Role.Properties.Policies[0] + .PolicyDocument.Statement; + + expect(statements).to.have.lengthOf(4); + expect(statements[3].Effect).to.equal('Allow'); + expect(statements[3].Action[0]).to.equal('s3:Get*'); + expect(statements[3].Action[1]).to.equal('s3:List*'); + expect(statements[3].Resource[0]).to.equal(`arn:aws:s3:::${myBucket}`); + expect(statements[3].Resource[1]).to.equal(`arn:aws:s3:::${myBucket}/*`); + }); });