Skip to content

Commit 07f4623

Browse files
authored
Merge pull request #246 from horike37/feature/nested_workflows
feat: add support for nested workflows
2 parents 2c9a17c + abe26fb commit 07f4623

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

lib/deploy/stepFunctions/compileIamRole.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,31 @@ function getLambdaPermissions(state) {
202202
}];
203203
}
204204

205+
function getStepFunctionsPermissions(state) {
206+
const stateMachineArn = state.Parameters['StateMachineArn.$']
207+
? '*'
208+
: state.Parameters.StateMachineArn;
209+
210+
return [{
211+
action: 'states:StartExecution',
212+
resource: stateMachineArn,
213+
}, {
214+
action: 'states:DescribeExecution,states:StopExecution',
215+
// this is excessive but mirrors behaviour in the console
216+
// also, DescribeExecution supports executions as resources but StopExecution
217+
// doesn't support resources
218+
resource: '*',
219+
}, {
220+
action: 'events:PutTargets,events:PutRule,events:DescribeRule',
221+
resource: {
222+
'Fn::Sub': [
223+
'arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/StepFunctionsGetEventsForStepFunctionsExecutionRule',
224+
{},
225+
],
226+
},
227+
}];
228+
}
229+
205230
// if there are multiple permissions with the same action, then collapsed them into one
206231
// permission instead, and collect the resources into an array
207232
function consolidatePermissionsByAction(permissions) {
@@ -276,6 +301,11 @@ function getIamPermissions(taskStates) {
276301
case 'arn:aws:states:::lambda:invoke.waitForTaskToken':
277302
return getLambdaPermissions.bind(this)(state);
278303

304+
case 'arn:aws:states:::states:startExecution':
305+
case 'arn:aws:states:::states:startExecution.sync':
306+
case 'arn:aws:states:::states:startExecution.waitForTaskToken':
307+
return getStepFunctionsPermissions(state);
308+
279309
default:
280310
if (isIntrinsic(state.Resource) || state.Resource.startsWith('arn:aws:lambda')) {
281311
return [{

lib/deploy/stepFunctions/compileIamRole.test.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,4 +1369,127 @@ describe('#compileIamRole', () => {
13691369
];
13701370
expect(lambdaPermissions[0].Resource).to.deep.eq(lambdaArns);
13711371
});
1372+
1373+
it('should give step functions permissions (too permissive, but mirrors console behaviour)', () => {
1374+
const stateMachineArn = 'arn:aws:states:us-east-1:123456789:stateMachine:HelloStateMachine';
1375+
const genStateMachine = name => ({
1376+
name,
1377+
definition: {
1378+
StartAt: 'A',
1379+
States: {
1380+
A: {
1381+
Type: 'Task',
1382+
Resource: 'arn:aws:states:::states:startExecution',
1383+
Parameters: {
1384+
StateMachineArn: stateMachineArn,
1385+
Input: {},
1386+
},
1387+
Next: 'B',
1388+
},
1389+
B: {
1390+
Type: 'Task',
1391+
Resource: 'arn:aws:states:::states:startExecution.sync',
1392+
Parameters: {
1393+
StateMachineArn: stateMachineArn,
1394+
Input: {},
1395+
},
1396+
Next: 'C',
1397+
},
1398+
C: {
1399+
Type: 'Task',
1400+
Resource: 'arn:aws:states:::states:startExecution.waitForTaskToken',
1401+
Parameters: {
1402+
StateMachineArn: stateMachineArn,
1403+
Input: {},
1404+
},
1405+
End: true,
1406+
},
1407+
},
1408+
},
1409+
});
1410+
1411+
serverless.service.stepFunctions = {
1412+
stateMachines: {
1413+
myStateMachine1: genStateMachine('stateMachineBeta1'),
1414+
myStateMachine2: genStateMachine('stateMachineBeta2'),
1415+
},
1416+
};
1417+
1418+
serverlessStepFunctions.compileIamRole();
1419+
const statements = serverlessStepFunctions.serverless.service
1420+
.provider.compiledCloudFormationTemplate.Resources.IamRoleStateMachineExecution
1421+
.Properties.Policies[0].PolicyDocument.Statement;
1422+
1423+
const stateMachinePermissions = statements.filter(s => _.isEqual(s.Action, ['states:StartExecution']));
1424+
expect(stateMachinePermissions).to.have.lengthOf(1);
1425+
expect(stateMachinePermissions[0].Resource).to.deep.eq([stateMachineArn]);
1426+
1427+
const executionPermissions = statements.filter(s => _.isEqual(s.Action, ['states:DescribeExecution', 'states:StopExecution']));
1428+
expect(executionPermissions).to.have.lengthOf(1);
1429+
expect(executionPermissions[0].Resource).to.equal('*');
1430+
1431+
const eventPermissions = statements.filter(s => _.isEqual(s.Action, ['events:PutTargets', 'events:PutRule', 'events:DescribeRule']));
1432+
expect(eventPermissions).to.have.lengthOf(1);
1433+
expect(eventPermissions[0].Resource).to.deep.eq([{
1434+
'Fn::Sub': [
1435+
'arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/StepFunctionsGetEventsForStepFunctionsExecutionRule',
1436+
{},
1437+
],
1438+
}]);
1439+
});
1440+
1441+
it('should give step functions permission to * whenever StateMachineArn.$ is seen', () => {
1442+
const stateMachineArn = 'arn:aws:states:us-east-1:123456789:stateMachine:HelloStateMachine';
1443+
const genStateMachine = name => ({
1444+
name,
1445+
definition: {
1446+
StartAt: 'A',
1447+
States: {
1448+
A: {
1449+
Type: 'Task',
1450+
Resource: 'arn:aws:states:::states:startExecution',
1451+
Parameters: {
1452+
'StateMachineArn.$': '$.arn',
1453+
Input: {},
1454+
},
1455+
Next: 'B',
1456+
},
1457+
B: {
1458+
Type: 'Task',
1459+
Resource: 'arn:aws:states:::states:startExecution.sync',
1460+
Parameters: {
1461+
StateMachineArn: stateMachineArn,
1462+
Input: {},
1463+
},
1464+
Next: 'C',
1465+
},
1466+
C: {
1467+
Type: 'Task',
1468+
Resource: 'arn:aws:states:::states:startExecution.waitForTaskToken',
1469+
Parameters: {
1470+
StateMachineArn: stateMachineArn,
1471+
Input: {},
1472+
},
1473+
End: true,
1474+
},
1475+
},
1476+
},
1477+
});
1478+
1479+
serverless.service.stepFunctions = {
1480+
stateMachines: {
1481+
myStateMachine1: genStateMachine('stateMachineBeta1'),
1482+
myStateMachine2: genStateMachine('stateMachineBeta2'),
1483+
},
1484+
};
1485+
1486+
serverlessStepFunctions.compileIamRole();
1487+
const statements = serverlessStepFunctions.serverless.service
1488+
.provider.compiledCloudFormationTemplate.Resources.IamRoleStateMachineExecution
1489+
.Properties.Policies[0].PolicyDocument.Statement;
1490+
1491+
const stateMachinePermissions = statements.filter(s => _.includes(s.Action, 'states:StartExecution'));
1492+
expect(stateMachinePermissions).to.have.lengthOf(1);
1493+
expect(stateMachinePermissions[0].Resource).to.equal('*');
1494+
});
13721495
});

0 commit comments

Comments
 (0)