@@ -1240,6 +1240,194 @@ func (s *ServiceTestSuite) TestCreateAppeal__WithExistingAppealAndWithAutoApprov
12401240 s .Equal (expectedResult , appeals )
12411241}
12421242
1243+ func (s * ServiceTestSuite ) TestCreateAppeal__WithAdditionalAppeals () {
1244+ s .setup ()
1245+ providerType := "test-provider-type"
1246+ providerURN := "test-provider-urn"
1247+ resourceType := "test-resource-type"
1248+ targetResource := & domain.ResourceIdentifier {
1249+ ID : "test-resource-id-2" ,
1250+ }
1251+ targetRole := "test-role-1"
1252+
1253+ resources := []* domain.Resource {
1254+ {
1255+ ID : "test-resource-id-1" ,
1256+ URN : "test-resource-urn-1" ,
1257+ Type : resourceType ,
1258+ ProviderType : providerType ,
1259+ ProviderURN : providerURN ,
1260+ },
1261+ {
1262+ ID : "test-resource-id-2" ,
1263+ URN : "test-resource-urn-2" ,
1264+ Type : resourceType ,
1265+ ProviderType : providerType ,
1266+ ProviderURN : providerURN ,
1267+ },
1268+ }
1269+ policies := []* domain.Policy {
1270+ {
1271+ ID : "test-policy-id-1" ,
1272+ Version : 1 ,
1273+ Steps : []* domain.Step {
1274+ {
1275+ Name : "test-step-1" ,
1276+ Strategy : domain .ApprovalStepStrategyAuto ,
1277+ ApproveIf : `true` ,
1278+ },
1279+ },
1280+ Requirements : []* domain.Requirement {
1281+ {
1282+ On : & domain.RequirementTrigger {
1283+ Expression : `$appeal.resource.urn == "test-resource-urn-1"` ,
1284+ },
1285+ Appeals : []* domain.AdditionalAppeal {
1286+ {
1287+ Resource : targetResource ,
1288+ Role : targetRole ,
1289+ },
1290+ },
1291+ },
1292+ },
1293+ },
1294+ }
1295+ providers := []* domain.Provider {
1296+ {
1297+ ID : "test-provider-id" ,
1298+ Type : providerType ,
1299+ URN : providerURN ,
1300+ Config : & domain.ProviderConfig {
1301+ Resources : []* domain.ResourceConfig {
1302+ {
1303+ Type : resourceType ,
1304+ Policy : & domain.PolicyConfig {
1305+ ID : policies [0 ].ID ,
1306+ Version : int (policies [0 ].Version ),
1307+ },
1308+ Roles : []* domain.Role {
1309+ {
1310+ ID : "test-role-1" ,
1311+ Permissions : []interface {}{"test-permission-1" },
1312+ },
1313+ },
1314+ },
1315+ },
1316+ },
1317+ },
1318+ }
1319+
1320+ appealsPayload := []* domain.Appeal {
1321+ {
1322+ 1323+ 1324+ ResourceID : "test-resource-id-1" ,
1325+ Resource : & domain.Resource {
1326+ ID : "test-resource-id-1" ,
1327+ URN : "test-resource-urn-1" ,
1328+ Type : resourceType ,
1329+ ProviderType : providerType ,
1330+ ProviderURN : providerURN ,
1331+ },
1332+ Role : "test-role-1" ,
1333+ },
1334+ }
1335+
1336+ // 1.a main appeal creation
1337+ expectedResourceFilters := domain.ListResourcesFilter {IDs : []string {appealsPayload [0 ].Resource .ID }}
1338+ s .mockResourceService .EXPECT ().Find (mock .AnythingOfType ("*context.emptyCtx" ), expectedResourceFilters ).Return ([]* domain.Resource {resources [0 ]}, nil ).Once ()
1339+ s .mockProviderService .EXPECT ().Find (mock .AnythingOfType ("*context.emptyCtx" )).Return (providers , nil ).Once ()
1340+ s .mockPolicyService .EXPECT ().Find (mock .AnythingOfType ("*context.emptyCtx" )).Return (policies , nil ).Once ()
1341+ s .mockGrantService .EXPECT ().List (mock .AnythingOfType ("*context.emptyCtx" ), mock .AnythingOfType ("domain.ListGrantsFilter" )).Return ([]domain.Grant {}, nil ).Once ().Run (func (args mock.Arguments ) {
1342+ filter := args .Get (1 ).(domain.ListGrantsFilter )
1343+ s .Equal ([]string {appealsPayload [0 ].AccountID }, filter .AccountIDs )
1344+ s .Equal ([]string {appealsPayload [0 ].Resource .ID }, filter .ResourceIDs )
1345+ s .Equal ([]string {appealsPayload [0 ].Role }, filter .Roles )
1346+ })
1347+ s .mockRepository .EXPECT ().Find (mock .AnythingOfType ("*context.emptyCtx" ), mock .AnythingOfType ("*domain.ListAppealsFilter" )).Return ([]* domain.Appeal {}, nil ).Once ()
1348+ s .mockProviderService .EXPECT ().ValidateAppeal (mock .AnythingOfType ("*context.emptyCtx" ), appealsPayload [0 ], providers [0 ], policies [0 ]).Return (nil ).Once ()
1349+ s .mockProviderService .EXPECT ().GetPermissions (mock .AnythingOfType ("*context.emptyCtx" ), providers [0 ].Config , appealsPayload [0 ].Resource .Type , appealsPayload [0 ].Role ).Return ([]interface {}{"test-permission-1" }, nil ).Once ()
1350+ s .mockGrantService .EXPECT ().List (mock .AnythingOfType ("*context.emptyCtx" ), mock .AnythingOfType ("domain.ListGrantsFilter" )).Return ([]domain.Grant {}, nil ).Once ().Run (func (args mock.Arguments ) {
1351+ filter := args .Get (1 ).(domain.ListGrantsFilter )
1352+ s .Equal ([]string {appealsPayload [0 ].AccountID }, filter .AccountIDs )
1353+ s .Equal ([]string {appealsPayload [0 ].Resource .ID }, filter .ResourceIDs )
1354+ })
1355+ expectedGrant := & domain.Grant {ID : "main-grant" }
1356+ s .mockGrantService .EXPECT ().Prepare (mock .AnythingOfType ("*context.emptyCtx" ), mock .AnythingOfType ("domain.Appeal" )).Return (expectedGrant , nil ).Once ().Run (func (args mock.Arguments ) {
1357+ appeal := args .Get (1 ).(domain.Appeal )
1358+ s .Equal (appealsPayload [0 ].AccountID , appeal .AccountID )
1359+ s .Equal (appealsPayload [0 ].Role , appeal .Role )
1360+ s .Equal (appealsPayload [0 ].ResourceID , appeal .ResourceID )
1361+ s .Equal (len (policies [0 ].Steps ), len (appeal .Approvals ))
1362+ })
1363+ s .mockPolicyService .EXPECT ().GetOne (mock .AnythingOfType ("*context.emptyCtx" ), policies [0 ].ID , policies [0 ].Version ).Return (policies [0 ], nil ).Once ()
1364+
1365+ // 2.a additional appeal creation
1366+ s .mockResourceService .EXPECT ().Get (mock .AnythingOfType ("*context.cancelCtx" ), targetResource ).Return (resources [1 ], nil ).Once ()
1367+ expectedResourceFilters = domain.ListResourcesFilter {IDs : []string {resources [1 ].ID }}
1368+ s .mockResourceService .EXPECT ().Find (mock .AnythingOfType ("*context.cancelCtx" ), expectedResourceFilters ).Return ([]* domain.Resource {resources [1 ]}, nil ).Once ()
1369+ s .mockProviderService .EXPECT ().Find (mock .AnythingOfType ("*context.cancelCtx" )).Return (providers , nil ).Once ()
1370+ s .mockPolicyService .EXPECT ().Find (mock .AnythingOfType ("*context.cancelCtx" )).Return (policies , nil ).Once ()
1371+ s .mockGrantService .EXPECT ().List (mock .AnythingOfType ("*context.cancelCtx" ), mock .AnythingOfType ("domain.ListGrantsFilter" )).Return ([]domain.Grant {}, nil ).Once ().Run (func (args mock.Arguments ) {
1372+ filter := args .Get (1 ).(domain.ListGrantsFilter )
1373+ s .Equal ([]string {appealsPayload [0 ].AccountID }, filter .AccountIDs )
1374+ s .Equal ([]string {targetResource .ID }, filter .ResourceIDs )
1375+ s .Equal ([]string {targetRole }, filter .Roles )
1376+ })
1377+ s .mockRepository .EXPECT ().Find (mock .AnythingOfType ("*context.cancelCtx" ), mock .AnythingOfType ("*domain.ListAppealsFilter" )).Return ([]* domain.Appeal {}, nil ).Once ()
1378+ s .mockProviderService .EXPECT ().ValidateAppeal (mock .AnythingOfType ("*context.cancelCtx" ), mock .AnythingOfType ("*domain.Appeal" ), providers [0 ], policies [0 ]).Return (nil ).Once ().Run (func (args mock.Arguments ) {
1379+ appeal := args .Get (1 ).(* domain.Appeal )
1380+ s .Equal (appealsPayload [0 ].AccountID , appeal .AccountID )
1381+ s .Equal (targetRole , appeal .Role )
1382+ s .Equal (targetResource .ID , appeal .ResourceID )
1383+ })
1384+ s .mockProviderService .EXPECT ().GetPermissions (mock .AnythingOfType ("*context.cancelCtx" ), providers [0 ].Config , resourceType , targetRole ).Return ([]interface {}{"test-permission-1" }, nil ).Once ()
1385+ s .mockGrantService .EXPECT ().List (mock .AnythingOfType ("*context.cancelCtx" ), mock .AnythingOfType ("domain.ListGrantsFilter" )).Return ([]domain.Grant {}, nil ).Once ().Run (func (args mock.Arguments ) {
1386+ filter := args .Get (1 ).(domain.ListGrantsFilter )
1387+ s .Equal ([]string {appealsPayload [0 ].AccountID }, filter .AccountIDs )
1388+ s .Equal ([]string {targetResource .ID }, filter .ResourceIDs )
1389+ })
1390+ expectedAdditionalGrant := & domain.Grant {ID : "additional-grant" }
1391+ s .mockGrantService .EXPECT ().Prepare (mock .AnythingOfType ("*context.cancelCtx" ), mock .AnythingOfType ("domain.Appeal" )).Return (expectedAdditionalGrant , nil ).Once ().Run (func (args mock.Arguments ) {
1392+ appeal := args .Get (1 ).(domain.Appeal )
1393+ s .Equal (appealsPayload [0 ].AccountID , appeal .AccountID )
1394+ s .Equal (targetRole , appeal .Role )
1395+ s .Equal (targetResource .ID , appeal .ResourceID )
1396+ s .Equal (len (policies [0 ].Steps ), len (appeal .Approvals ))
1397+ })
1398+ s .mockPolicyService .EXPECT ().GetOne (mock .AnythingOfType ("*context.cancelCtx" ), policies [0 ].ID , policies [0 ].Version ).Return (policies [0 ], nil ).Once ()
1399+
1400+ // 2.b grant access for the additional appeal
1401+ s .mockProviderService .EXPECT ().GrantAccess (mock .AnythingOfType ("*context.cancelCtx" ), mock .AnythingOfType ("domain.Grant" )).Return (nil ).Once ().Run (func (args mock.Arguments ) {
1402+ grant := args .Get (1 ).(domain.Grant )
1403+ s .Equal (expectedAdditionalGrant .ID , grant .ID )
1404+ })
1405+ s .mockRepository .EXPECT ().BulkUpsert (mock .AnythingOfType ("*context.cancelCtx" ), mock .AnythingOfType ("[]*domain.Appeal" )).Return (nil ).Once ().Run (func (args mock.Arguments ) {
1406+ appeals := args .Get (1 ).([]* domain.Appeal )
1407+ appeal := appeals [0 ]
1408+ s .Equal (targetResource .ID , appeal .Resource .ID )
1409+ })
1410+ s .mockAuditLogger .EXPECT ().Log (mock .AnythingOfType ("*context.cancelCtx" ), appeal .AuditKeyBulkInsert , mock .Anything ).Return (nil ).Once ()
1411+ s .mockNotifier .EXPECT ().Notify (mock .Anything ).Return (nil ).Once ()
1412+
1413+ // 1.b grant access for the main appeal
1414+ s .mockProviderService .EXPECT ().GrantAccess (mock .AnythingOfType ("*context.emptyCtx" ), mock .AnythingOfType ("domain.Grant" )).Return (nil ).Once ().Run (func (args mock.Arguments ) {
1415+ grant := args .Get (1 ).(domain.Grant )
1416+ s .Equal (expectedGrant .ID , grant .ID )
1417+ })
1418+ s .mockRepository .EXPECT ().BulkUpsert (mock .AnythingOfType ("*context.emptyCtx" ), mock .AnythingOfType ("[]*domain.Appeal" )).Return (nil ).Once ().Run (func (args mock.Arguments ) {
1419+ appeals := args .Get (1 ).([]* domain.Appeal )
1420+ appeal := appeals [0 ]
1421+ s .Equal (appealsPayload [0 ].Resource .ID , appeal .Resource .ID )
1422+ })
1423+ s .mockAuditLogger .EXPECT ().Log (mock .AnythingOfType ("*context.emptyCtx" ), appeal .AuditKeyBulkInsert , mock .Anything ).Return (nil ).Once ()
1424+ s .mockNotifier .EXPECT ().Notify (mock .Anything ).Return (nil ).Once ()
1425+
1426+ err := s .service .Create (context .Background (), appealsPayload )
1427+
1428+ s .NoError (err )
1429+ }
1430+
12431431func (s * ServiceTestSuite ) TestUpdateApproval () {
12441432 timeNow := time .Now ()
12451433 appeal .TimeNow = func () time.Time {
0 commit comments