@@ -2148,6 +2148,285 @@ describe('#compileIamRole', () => {
2148
2148
. to . be . deep . equal ( '*' ) ;
2149
2149
} ) ;
2150
2150
2151
+ it ( 'should resolve literal bucket names in S3 permissions' , ( ) => {
2152
+ const literalBucket = 'my-test-bucket' ;
2153
+ const literalKey = 'test-key.txt' ;
2154
+
2155
+ serverless . service . stepFunctions = {
2156
+ stateMachines : {
2157
+ myStateMachine1 : {
2158
+ id : 'StateMachine1' ,
2159
+ definition : {
2160
+ StartAt : 'A' ,
2161
+ States : {
2162
+ A : {
2163
+ Type : 'Task' ,
2164
+ Resource : 'arn:aws:states:::aws-sdk:s3:getObject' ,
2165
+ Parameters : {
2166
+ Bucket : literalBucket ,
2167
+ Key : literalKey ,
2168
+ } ,
2169
+ End : true ,
2170
+ } ,
2171
+ } ,
2172
+ } ,
2173
+ } ,
2174
+ } ,
2175
+ } ;
2176
+
2177
+ serverlessStepFunctions . compileIamRole ( ) ;
2178
+ const statements = serverlessStepFunctions . serverless . service . provider
2179
+ . compiledCloudFormationTemplate . Resources . StateMachine1Role . Properties . Policies [ 0 ]
2180
+ . PolicyDocument . Statement ;
2181
+
2182
+ expect ( statements [ 0 ] . Resource [ 0 ] ) . to . equal ( `arn:aws:s3:::${ literalBucket } /${ literalKey } ` ) ;
2183
+ } ) ;
2184
+
2185
+ it ( 'should resolve CloudFormation reference buckets in S3 permissions' , ( ) => {
2186
+ const bucketRef = { Ref : 'MyS3Bucket' } ;
2187
+
2188
+ serverless . service . stepFunctions = {
2189
+ stateMachines : {
2190
+ myStateMachine1 : {
2191
+ id : 'StateMachine1' ,
2192
+ definition : {
2193
+ StartAt : 'A' ,
2194
+ States : {
2195
+ A : {
2196
+ Type : 'Task' ,
2197
+ Resource : 'arn:aws:states:::aws-sdk:s3:getObject' ,
2198
+ Parameters : {
2199
+ Bucket : bucketRef ,
2200
+ Key : 'test-key.txt' ,
2201
+ } ,
2202
+ End : true ,
2203
+ } ,
2204
+ } ,
2205
+ } ,
2206
+ } ,
2207
+ } ,
2208
+ } ;
2209
+
2210
+ serverlessStepFunctions . compileIamRole ( ) ;
2211
+ const statements = serverlessStepFunctions . serverless . service . provider
2212
+ . compiledCloudFormationTemplate . Resources . StateMachine1Role . Properties . Policies [ 0 ]
2213
+ . PolicyDocument . Statement ;
2214
+
2215
+ expect ( statements [ 0 ] . Resource [ 0 ] ) . to . deep . equal ( {
2216
+ 'Fn::Sub' : [
2217
+ 'arn:aws:s3:::${bucket}/test-key.txt' ,
2218
+ { bucket : bucketRef } ,
2219
+ ] ,
2220
+ } ) ;
2221
+ } ) ;
2222
+
2223
+ it ( 'should resolve Fn::GetAtt bucket references in S3 permissions' , ( ) => {
2224
+ const bucketRef = { 'Fn::GetAtt' : [ 'MyS3Bucket' , 'Arn' ] } ;
2225
+
2226
+ serverless . service . stepFunctions = {
2227
+ stateMachines : {
2228
+ myStateMachine1 : {
2229
+ id : 'StateMachine1' ,
2230
+ definition : {
2231
+ StartAt : 'A' ,
2232
+ States : {
2233
+ A : {
2234
+ Type : 'Task' ,
2235
+ Resource : 'arn:aws:states:::aws-sdk:s3:putObject' ,
2236
+ Parameters : {
2237
+ Bucket : bucketRef ,
2238
+ Key : 'output.json' ,
2239
+ Body : { result : 'success' } ,
2240
+ } ,
2241
+ End : true ,
2242
+ } ,
2243
+ } ,
2244
+ } ,
2245
+ } ,
2246
+ } ,
2247
+ } ;
2248
+
2249
+ serverlessStepFunctions . compileIamRole ( ) ;
2250
+ const statements = serverlessStepFunctions . serverless . service . provider
2251
+ . compiledCloudFormationTemplate . Resources . StateMachine1Role . Properties . Policies [ 0 ]
2252
+ . PolicyDocument . Statement ;
2253
+
2254
+ expect ( statements [ 0 ] . Resource [ 0 ] ) . to . deep . equal ( {
2255
+ 'Fn::Sub' : [
2256
+ 'arn:aws:s3:::${bucket}/output.json' ,
2257
+ { bucket : bucketRef } ,
2258
+ ] ,
2259
+ } ) ;
2260
+ } ) ;
2261
+
2262
+ it ( 'should resolve bucket references in S3 listObjectsV2 permissions' , ( ) => {
2263
+ const bucketRef = { Ref : 'MyS3Bucket' } ;
2264
+
2265
+ serverless . service . stepFunctions = {
2266
+ stateMachines : {
2267
+ myStateMachine1 : {
2268
+ id : 'StateMachine1' ,
2269
+ definition : {
2270
+ StartAt : 'A' ,
2271
+ States : {
2272
+ A : {
2273
+ Type : 'Map' ,
2274
+ ItemProcessor : {
2275
+ ProcessorConfig : {
2276
+ Mode : 'DISTRIBUTED' ,
2277
+ } ,
2278
+ } ,
2279
+ StartAt : 'B' ,
2280
+ States : {
2281
+ B : {
2282
+ Type : 'Task' ,
2283
+ Resource : 'arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:hello' ,
2284
+ End : true ,
2285
+ } ,
2286
+ } ,
2287
+ ItemReader : {
2288
+ Resource : 'arn:aws:states:::s3:listObjectsV2' ,
2289
+ Parameters : {
2290
+ Bucket : bucketRef ,
2291
+ Prefix : 'data' ,
2292
+ } ,
2293
+ } ,
2294
+ End : true ,
2295
+ } ,
2296
+ } ,
2297
+ } ,
2298
+ } ,
2299
+ } ,
2300
+ } ;
2301
+
2302
+ serverlessStepFunctions . compileIamRole ( ) ;
2303
+ const statements = serverlessStepFunctions . serverless . service . provider
2304
+ . compiledCloudFormationTemplate . Resources . StateMachine1Role . Properties . Policies [ 0 ]
2305
+ . PolicyDocument . Statement ;
2306
+
2307
+ expect ( statements [ 3 ] . Resource [ 0 ] ) . to . deep . equal ( {
2308
+ 'Fn::Sub' : [
2309
+ 'arn:aws:s3:::${bucket}' ,
2310
+ { bucket : bucketRef } ,
2311
+ ] ,
2312
+ } ) ;
2313
+ expect ( statements [ 3 ] . Resource [ 1 ] ) . to . deep . equal ( {
2314
+ 'Fn::Sub' : [
2315
+ 'arn:aws:s3:::${bucket}/*' ,
2316
+ { bucket : bucketRef } ,
2317
+ ] ,
2318
+ } ) ;
2319
+ } ) ;
2320
+
2321
+ it ( 'should handle mixed literal and reference buckets correctly' , ( ) => {
2322
+ const literalBucket = 'literal-bucket' ;
2323
+ const bucketRef = { Ref : 'ReferenceBucket' } ;
2324
+ const literalFile = 'file1.txt' ;
2325
+
2326
+ serverless . service . stepFunctions = {
2327
+ stateMachines : {
2328
+ myStateMachine1 : {
2329
+ id : 'StateMachine1' ,
2330
+ definition : {
2331
+ StartAt : 'A' ,
2332
+ States : {
2333
+ A : {
2334
+ Type : 'Task' ,
2335
+ Resource : 'arn:aws:states:::aws-sdk:s3:getObject' ,
2336
+ Parameters : {
2337
+ Bucket : literalBucket ,
2338
+ Key : literalFile ,
2339
+ } ,
2340
+ Next : 'B' ,
2341
+ } ,
2342
+ B : {
2343
+ Type : 'Task' ,
2344
+ Resource : 'arn:aws:states:::aws-sdk:s3:putObject' ,
2345
+ Parameters : {
2346
+ Bucket : bucketRef ,
2347
+ Key : 'file2.txt' ,
2348
+ Body : { } ,
2349
+ } ,
2350
+ End : true ,
2351
+ } ,
2352
+ } ,
2353
+ } ,
2354
+ } ,
2355
+ } ,
2356
+ } ;
2357
+
2358
+ serverlessStepFunctions . compileIamRole ( ) ;
2359
+ const statements = serverlessStepFunctions . serverless . service . provider
2360
+ . compiledCloudFormationTemplate . Resources . StateMachine1Role . Properties . Policies [ 0 ]
2361
+ . PolicyDocument . Statement ;
2362
+
2363
+ // Check that we have consolidated permissions
2364
+ expect ( statements ) . to . have . lengthOf ( 2 ) ;
2365
+
2366
+ // First statement for s3:GetObject with literal bucket
2367
+ expect ( statements [ 0 ] . Action ) . to . deep . equal ( [ 's3:GetObject' ] ) ;
2368
+ expect ( statements [ 0 ] . Resource [ 0 ] ) . to . equal ( `arn:aws:s3:::${ literalBucket } /${ literalFile } ` ) ;
2369
+
2370
+ // Second statement for s3:PutObject with reference bucket
2371
+ expect ( statements [ 1 ] . Action ) . to . deep . equal ( [ 's3:PutObject' ] ) ;
2372
+ expect ( statements [ 1 ] . Resource [ 0 ] ) . to . deep . equal ( {
2373
+ 'Fn::Sub' : [
2374
+ 'arn:aws:s3:::${bucket}/file2.txt' ,
2375
+ { bucket : bucketRef } ,
2376
+ ] ,
2377
+ } ) ;
2378
+ } ) ;
2379
+
2380
+ it ( 'should handle bucket references with prefixes correctly' , ( ) => {
2381
+ const bucketRef = { Ref : 'MyS3Bucket' } ;
2382
+
2383
+ serverless . service . stepFunctions = {
2384
+ stateMachines : {
2385
+ myStateMachine1 : {
2386
+ id : 'StateMachine1' ,
2387
+ definition : {
2388
+ StartAt : 'A' ,
2389
+ States : {
2390
+ A : {
2391
+ Type : 'Map' ,
2392
+ ItemProcessor : {
2393
+ StartAt : 'B' ,
2394
+ States : {
2395
+ B : {
2396
+ Type : 'Task' ,
2397
+ Resource : 'arn:aws:lambda:us-west-2:1234567890:function:foo' ,
2398
+ End : true ,
2399
+ } ,
2400
+ } ,
2401
+ } ,
2402
+ ResultWriter : {
2403
+ Resource : 'arn:aws:states:::s3:putObject' ,
2404
+ Parameters : {
2405
+ Bucket : bucketRef ,
2406
+ Prefix : 'results' ,
2407
+ } ,
2408
+ } ,
2409
+ End : true ,
2410
+ } ,
2411
+ } ,
2412
+ } ,
2413
+ } ,
2414
+ } ,
2415
+ } ;
2416
+
2417
+ serverlessStepFunctions . compileIamRole ( ) ;
2418
+ const statements = serverlessStepFunctions . serverless . service . provider
2419
+ . compiledCloudFormationTemplate . Resources . StateMachine1Role . Properties . Policies [ 0 ]
2420
+ . PolicyDocument . Statement ;
2421
+
2422
+ expect ( statements [ 1 ] . Resource [ 0 ] ) . to . deep . equal ( {
2423
+ 'Fn::Sub' : [
2424
+ 'arn:aws:s3:::${bucket}/results/*' ,
2425
+ { bucket : bucketRef } ,
2426
+ ] ,
2427
+ } ) ;
2428
+ } ) ;
2429
+
2151
2430
it ( 'should not generate any permissions for Task states not yet supported' , ( ) => {
2152
2431
const genStateMachine = id => ( {
2153
2432
id,
0 commit comments