@izara_project/izara-core-generate-service-code 1.0.85 → 1.0.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/src/compile/compileProject.js +1 -2
  3. package/src/compile/loadSchemas.js +29 -17
  4. package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +1 -0
  5. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +15 -4
  6. package/src/emit/crud/emitCrudApplicationArtifacts.js +10 -4
  7. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +10 -4
  8. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +36 -4
  9. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +35 -3
  10. package/src/emit/crud/templates/UpdateLogicalLib.ejs +754 -353
  11. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +5 -2
  12. package/src/emit/rbac/emitRbacApplicationArtifacts.js +102 -6
  13. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +34 -4
  14. package/src/emit/rbac/templates/DeleteTargetRole_Main.ejs +96 -0
  15. package/src/emit/rbac/templates/GetMyPermissions_Main.ejs +106 -0
  16. package/src/emit/rbac/templates/GetTargetRole_Main.ejs +62 -0
  17. package/src/emit/rbac/templates/UpdateTargetRole_Main.ejs +107 -0
  18. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +12 -12
  19. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +12 -0
  20. package/src/emit/resource/emitResourceArtifacts.js +43 -2
  21. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +4 -2
  22. package/src/emit/systemText/emitSystemTextSchemas.js +5 -2
  23. package/src/generate.js +18 -12
  24. package/src/generateResources/IntTest/generateResources/generateResources.js +1 -1
  25. package/src/generateResources/IntTest/generateTests/generateTemplate.js +1 -1
  26. package/src/generateResources/IntTest/upload/uploadIntTest.js +8 -2
  27. package/test/createTargetRoleHandler.test.js +42 -1
  28. package/test/crudApplicationArtifacts.test.js +53 -0
  29. package/test/dynamicRbacFlowSchemas.test.js +5 -2
  30. package/test/publicApi.test.js +18 -14
  31. package/test/resourceArtifacts.test.js +3 -0
@@ -4,14 +4,17 @@ import { rbacFlowTag } from './rbacFlowTag.js';
4
4
 
5
5
  const RBAC_CORE_FLOW_DEFINITIONS = [
6
6
  { flowTag: 'CreateTargetRole', event: ['ownTopic'] },
7
- { flowTag: 'ListTargetRole', event: ['lambdaSyncApi'] },
7
+ { flowTag: 'UpdateTargetRole', event: ['ownTopic'] },
8
8
  { flowTag: 'DeleteTargetRole', event: ['ownTopic'] },
9
+ { flowTag: 'GetTargetRole', event: ['lambdaSyncApi'] },
10
+ { flowTag: 'ListTargetRole', event: ['lambdaSyncApi'] },
9
11
  { flowTag: 'CreateRolePermissions', event: ['ownTopic'] },
10
12
  { flowTag: 'ListRolePermissions', event: ['lambdaSyncApi'] },
11
13
  { flowTag: 'DeleteRolePermissions', event: ['ownTopic'] },
12
14
  { flowTag: 'CreateUserRole', event: ['ownTopic'] },
13
15
  { flowTag: 'ListUserInRoles', event: ['lambdaSyncApi'] },
14
- { flowTag: 'DeleteUserFromRole', event: ['ownTopic'] }
16
+ { flowTag: 'DeleteUserFromRole', event: ['ownTopic'] },
17
+ { flowTag: 'GetMyPermissions', event: ['lambdaSyncApi'] }
15
18
  ];
16
19
 
17
20
  const LIMIT_FLOW_DEFINITIONS = [
@@ -29,6 +29,19 @@ export async function emitRbacApplicationArtifacts(objects) {
29
29
  ...(hasSelfRbac
30
30
  ? [
31
31
  createTargetRoleHandler(),
32
+ createCustomRbacOwnTopicHandler(
33
+ 'UpdateTargetRole',
34
+ 'UpdateTargetRole_Main.ejs'
35
+ ),
36
+ createCustomRbacOwnTopicHandler(
37
+ 'DeleteTargetRole',
38
+ 'DeleteTargetRole_Main.ejs'
39
+ ),
40
+ createCustomRbacApiHandler('GetTargetRole', 'GetTargetRole_Main.ejs'),
41
+ createCustomRbacApiHandler(
42
+ 'GetMyPermissions',
43
+ 'GetMyPermissions_Main.ejs'
44
+ ),
32
45
  ...LIST_DEFINITIONS.map(createListHandler),
33
46
  ...MUTATION_DEFINITIONS.map(createMutationHandler),
34
47
  ...USER_ROLE_MUTATIONS.map(createUserRoleMutationHandler)
@@ -111,12 +124,6 @@ const MUTATION_DEFINITIONS = [
111
124
  tableName: 'RolePermissions',
112
125
  method: 'deleteItem',
113
126
  fields: ['roleIdKey', 'flowTypeConcat']
114
- },
115
- {
116
- flowTag: 'DeleteTargetRole',
117
- tableName: 'TargetRoles',
118
- method: 'deleteItem',
119
- fields: ['identifiersConcat', 'roleIdKey']
120
127
  }
121
128
  ];
122
129
 
@@ -151,6 +158,20 @@ const STATIC_RBAC_APPLICATION_PATHS = [
151
158
  'app/src/generatedCode/FlowRbac/CreateTargetRole/source/CreateTargetRole_HdrSqs.js',
152
159
  'app/src/generatedCode/FlowRbac/CreateTargetRole/source/CreateTargetRole_WbsMain.js',
153
160
  'app/src/generatedCode/FlowRbac/CreateTargetRole/source/CreateTargetRole_HdrWbs.js',
161
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_Main.js',
162
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_HdrSqs.js',
163
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_WbsMain.js',
164
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_HdrWbs.js',
165
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_Main.js',
166
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_HdrSqs.js',
167
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_WbsMain.js',
168
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_HdrWbs.js',
169
+ 'app/src/generatedCode/FlowRbac/GetTargetRole/source/GetTargetRole_Main.js',
170
+ 'app/src/generatedCode/FlowRbac/GetTargetRole/source/GetTargetRole_HdrSqs.js',
171
+ 'app/src/generatedCode/FlowRbac/GetTargetRole/source/GetTargetRole_HdrApi.js',
172
+ 'app/src/generatedCode/FlowRbac/GetMyPermissions/source/GetMyPermissions_Main.js',
173
+ 'app/src/generatedCode/FlowRbac/GetMyPermissions/source/GetMyPermissions_HdrSqs.js',
174
+ 'app/src/generatedCode/FlowRbac/GetMyPermissions/source/GetMyPermissions_HdrApi.js',
154
175
  'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_Main.js',
155
176
  'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_HdrSqs.js',
156
177
  'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_WbsMain.js',
@@ -331,6 +352,81 @@ async function createLimitMutationHandler(definition) {
331
352
  };
332
353
  }
333
354
 
355
+ async function createCustomRbacOwnTopicHandler(flowTag, mainTemplateName) {
356
+ const sourcePath = `app/src/generatedCode/FlowRbac/${flowTag}/source`;
357
+ const handlerPath = `src/generatedCode/FlowRbac/${flowTag}/source`;
358
+ const webSocket = await createWebSocketEntry(
359
+ flowTag,
360
+ sourcePath,
361
+ handlerPath
362
+ );
363
+ return {
364
+ artifacts: [
365
+ {
366
+ path: `${sourcePath}/${flowTag}_Main.js`,
367
+ content: await renderRbacTemplate(mainTemplateName, { flowTag })
368
+ },
369
+ {
370
+ path: `${sourcePath}/${flowTag}_HdrSqs.js`,
371
+ content: await renderRbacTemplate('ListRbac_HdrSqs.ejs', { flowTag })
372
+ },
373
+ ...webSocket.artifacts
374
+ ],
375
+ functions: [
376
+ {
377
+ type: 'sqs',
378
+ functionName: `${flowTag}HdrSqs`,
379
+ handlerPath: `${handlerPath}/${flowTag}_HdrSqs.main`,
380
+ roleName: 'FlowRbacRole',
381
+ queueName: `${flowTag}HdrSqs`,
382
+ flowTag: flowTag,
383
+ flowStepName: ''
384
+ },
385
+ ...webSocket.functions
386
+ ]
387
+ };
388
+ }
389
+
390
+ async function createCustomRbacApiHandler(flowTag, mainTemplateName) {
391
+ const sourcePath = `app/src/generatedCode/FlowRbac/${flowTag}/source`;
392
+ const handlerPath = `src/generatedCode/FlowRbac/${flowTag}/source`;
393
+ return {
394
+ artifacts: [
395
+ {
396
+ path: `${sourcePath}/${flowTag}_Main.js`,
397
+ content: await renderRbacTemplate(mainTemplateName, { flowTag })
398
+ },
399
+ {
400
+ path: `${sourcePath}/${flowTag}_HdrSqs.js`,
401
+ content: await renderRbacTemplate('ListRbac_HdrSqs.ejs', { flowTag })
402
+ },
403
+ {
404
+ path: `${sourcePath}/${flowTag}_HdrApi.js`,
405
+ content: await renderRbacTemplate('ListRbac_HdrApi.ejs', { flowTag })
406
+ }
407
+ ],
408
+ functions: [
409
+ {
410
+ type: 'sqs',
411
+ functionName: `${flowTag}HdrSqs`,
412
+ handlerPath: `${handlerPath}/${flowTag}_HdrSqs.main`,
413
+ roleName: 'FlowRbacRole',
414
+ queueName: `${flowTag}HdrSqs`,
415
+ flowTag: flowTag,
416
+ flowStepName: ''
417
+ },
418
+ {
419
+ type: 'api',
420
+ functionName: `${flowTag}HdrApi`,
421
+ handlerPath: `${handlerPath}/${flowTag}_HdrApi.main`,
422
+ roleName: 'FlowRbacRole',
423
+ flowTag: flowTag,
424
+ flowStepName: ''
425
+ }
426
+ ]
427
+ };
428
+ }
429
+
334
430
  async function createTargetRoleHandler() {
335
431
  const sourcePath = 'app/src/generatedCode/FlowRbac/CreateTargetRole/source';
336
432
  const webSocket = await createWebSocketEntry(
@@ -55,7 +55,12 @@ export default async function CreateTargetRole(_izContext, requestParams) {
55
55
  identifiersConcat = submittedByUserId;
56
56
  }
57
57
 
58
- const { roleName = 'owner', roleId: inputRoleId } = requestParams;
58
+ const {
59
+ roleName = 'owner',
60
+ roleId: inputRoleId,
61
+ permissions = [],
62
+ assignUserIds = []
63
+ } = requestParams;
59
64
  const roleId = inputRoleId || roleName || identifierUuid();
60
65
  const roleIdKey = `${identifiersConcat}_${roleId}`;
61
66
 
@@ -70,21 +75,46 @@ export default async function CreateTargetRole(_izContext, requestParams) {
70
75
  }
71
76
  );
72
77
 
73
- if (submittedByUserId) {
78
+ if (Array.isArray(permissions) && permissions.length > 0) {
79
+ const rolePermissionsTable = dynamodbSharedLib.tableName(_izContext, 'RolePermissions');
80
+ for (const perm of permissions) {
81
+ const flowTypeConcat = typeof perm === 'string' ? perm : perm.flowTypeConcat;
82
+ if (flowTypeConcat) {
83
+ const permission =
84
+ typeof perm === 'object' && perm.permission !== undefined
85
+ ? perm.permission
86
+ : true;
87
+ const parameterGroups = (typeof perm === 'object' && perm.parameterGroups) || {};
88
+ await dynamodbSharedLib.putItem(_izContext, rolePermissionsTable, {
89
+ roleIdKey,
90
+ flowTypeConcat,
91
+ permission,
92
+ parameterGroups
93
+ });
94
+ }
95
+ }
96
+ }
97
+
98
+ const usersToAssign =
99
+ Array.isArray(assignUserIds) && assignUserIds.length > 0
100
+ ? assignUserIds
101
+ : (roleName === 'owner' && submittedByUserId ? [submittedByUserId] : []);
102
+
103
+ for (const userId of usersToAssign) {
74
104
  try {
75
105
  await dynamodbSharedLib.transactWriteItems(_izContext, [
76
106
  {
77
107
  action: 'put',
78
108
  params: {
79
109
  tableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
80
- attributes: { roleIdKey, submittedByUserId }
110
+ attributes: { roleIdKey, userId, submittedByUserId }
81
111
  }
82
112
  },
83
113
  {
84
114
  action: 'put',
85
115
  params: {
86
116
  tableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
87
- attributes: { submittedByUserId, roleIdKey }
117
+ attributes: { userId, roleIdKey, submittedByUserId }
88
118
  }
89
119
  }
90
120
  ]);
@@ -0,0 +1,96 @@
1
+ /*
2
+ Copyright (C) 2020 Sven Mason <http://izara.io>
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as
6
+ published by the Free Software Foundation, either version 3 of the
7
+ License, or (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+
18
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
19
+ import snsSharedLib from '@izara_project/izara-core-library-sns';
20
+ import { sns } from '@izara_project/izara-core-library-external-request';
21
+ import { NoRetryError } from '@izara_project/izara-core-library-core';
22
+
23
+ export default async function DeleteTargetRole(_izContext, requestParams) {
24
+ try {
25
+ const { identifiersConcat, roleIdKey } = requestParams;
26
+ if (!roleIdKey) {
27
+ throw new NoRetryError('roleIdKey is required');
28
+ }
29
+
30
+ if (roleIdKey.endsWith('_owner')) {
31
+ throw new NoRetryError('Cannot delete system owner role');
32
+ }
33
+
34
+ const targetRolesTable = dynamodbSharedLib.tableName(_izContext, 'TargetRoles');
35
+ const rolePermissionsTable = dynamodbSharedLib.tableName(_izContext, 'RolePermissions');
36
+ const roleUsersTable = dynamodbSharedLib.tableName(_izContext, 'RoleUsers');
37
+ const userRolesTable = dynamodbSharedLib.tableName(_izContext, 'UserRoles');
38
+
39
+ // 1. Delete from TargetRoles
40
+ await dynamodbSharedLib.deleteItem(
41
+ _izContext,
42
+ targetRolesTable,
43
+ { identifiersConcat, roleIdKey }
44
+ );
45
+
46
+ // 2. Cascade delete all permissions for this roleIdKey
47
+ const permsResult = await dynamodbSharedLib.query(_izContext, rolePermissionsTable, {
48
+ roleIdKey
49
+ });
50
+ for (const perm of (permsResult?.Items || [])) {
51
+ await dynamodbSharedLib.deleteItem(_izContext, rolePermissionsTable, {
52
+ roleIdKey,
53
+ flowTypeConcat: perm.flowTypeConcat
54
+ });
55
+ }
56
+
57
+ // 3. Cascade delete user assignments in RoleUsers and UserRoles
58
+ const usersResult = await dynamodbSharedLib.query(_izContext, roleUsersTable, {
59
+ roleIdKey
60
+ });
61
+ for (const userItem of (usersResult?.Items || [])) {
62
+ const userId = userItem.userId;
63
+ try {
64
+ await dynamodbSharedLib.transactWriteItems(_izContext, [
65
+ {
66
+ action: 'delete',
67
+ params: {
68
+ tableName: roleUsersTable,
69
+ keyValues: { roleIdKey, userId }
70
+ }
71
+ },
72
+ {
73
+ action: 'delete',
74
+ params: {
75
+ tableName: userRolesTable,
76
+ keyValues: { userId, roleIdKey }
77
+ }
78
+ }
79
+ ]);
80
+ } catch (err) {
81
+ _izContext.logger.debug(`[${process.env.iz_serviceTag}:DeleteTargetRole] Cascade user cleanup ignored:`, err);
82
+ }
83
+ }
84
+
85
+ await sns.publishMessage(_izContext, {
86
+ message: { returnResult: { identifiersConcat, roleIdKey, status: 'deleted' } },
87
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
88
+ _izContext,
89
+ 'DeleteTargetRole_Out'
90
+ )
91
+ });
92
+ } catch (error) {
93
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:DeleteTargetRole] error:`, error);
94
+ throw error;
95
+ }
96
+ }
@@ -0,0 +1,106 @@
1
+ /*
2
+ Copyright (C) 2020 Sven Mason <http://izara.io>
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as
6
+ published by the Free Software Foundation, either version 3 of the
7
+ License, or (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+
18
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
19
+ import snsSharedLib from '@izara_project/izara-core-library-sns';
20
+ import { sns } from '@izara_project/izara-core-library-external-request';
21
+ import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
22
+
23
+ export default async function GetMyPermissions(_izContext, requestParams) {
24
+ try {
25
+ const submittedByUserId =
26
+ requestParams.submittedByUserId ||
27
+ _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
28
+
29
+ if (!submittedByUserId) {
30
+ throw new NoRetryError('User identity not found in context.');
31
+ }
32
+
33
+ const targetIdentifiers = requestParams.targetIdentifiers;
34
+ let identifiersConcat = requestParams.identifiersConcat;
35
+ if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
36
+ if (targetIdentifiers.identifierConcat) {
37
+ identifiersConcat = targetIdentifiers.identifierConcat;
38
+ } else {
39
+ const sortedKeys = Object.keys(targetIdentifiers).sort();
40
+ const parts = [];
41
+ for (const key of sortedKeys) {
42
+ const value = targetIdentifiers[key];
43
+ if (
44
+ key !== 'submittedByUserId' &&
45
+ key !== 'identifierConcat' &&
46
+ value !== undefined &&
47
+ value !== null
48
+ ) {
49
+ parts.push(`${key}#${value}`);
50
+ }
51
+ }
52
+ if (parts.length > 0) {
53
+ identifiersConcat = parts.join('|');
54
+ }
55
+ }
56
+ }
57
+
58
+ const userRolesTable = dynamodbSharedLib.tableName(_izContext, 'UserRoles');
59
+ const rolePermissionsTable = dynamodbSharedLib.tableName(_izContext, 'RolePermissions');
60
+
61
+ const userRolesResult = await dynamodbSharedLib.query(_izContext, userRolesTable, {
62
+ userId: submittedByUserId
63
+ });
64
+ const userRoleItems = userRolesResult?.Items || [];
65
+
66
+ const matchingRoles = identifiersConcat
67
+ ? userRoleItems.filter((item) => item.roleIdKey?.startsWith(identifiersConcat))
68
+ : userRoleItems;
69
+
70
+ const permissionsMap = {};
71
+ const rolesList = [];
72
+
73
+ for (const roleItem of matchingRoles) {
74
+ rolesList.push(roleItem.roleIdKey);
75
+ const permsResult = await dynamodbSharedLib.query(_izContext, rolePermissionsTable, {
76
+ roleIdKey: roleItem.roleIdKey
77
+ });
78
+ for (const p of (permsResult?.Items || [])) {
79
+ if (p.permission === true) {
80
+ permissionsMap[p.flowTypeConcat] = true;
81
+ } else if (permissionsMap[p.flowTypeConcat] === undefined) {
82
+ permissionsMap[p.flowTypeConcat] = false;
83
+ }
84
+ }
85
+ }
86
+
87
+ const result = {
88
+ userId: submittedByUserId,
89
+ identifiersConcat: identifiersConcat || null,
90
+ roles: rolesList,
91
+ permissions: permissionsMap
92
+ };
93
+
94
+ await sns.publishMessage(_izContext, {
95
+ message: { returnResult: result },
96
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
97
+ _izContext,
98
+ 'GetMyPermissions_Out'
99
+ )
100
+ });
101
+ return result;
102
+ } catch (error) {
103
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:GetMyPermissions] error:`, error);
104
+ throw error;
105
+ }
106
+ }
@@ -0,0 +1,62 @@
1
+ /*
2
+ Copyright (C) 2020 Sven Mason <http://izara.io>
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as
6
+ published by the Free Software Foundation, either version 3 of the
7
+ License, or (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+
18
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
19
+ import snsSharedLib from '@izara_project/izara-core-library-sns';
20
+ import { sns } from '@izara_project/izara-core-library-external-request';
21
+ import { NoRetryError } from '@izara_project/izara-core-library-core';
22
+
23
+ export default async function GetTargetRole(_izContext, requestParams) {
24
+ try {
25
+ const { identifiersConcat, roleIdKey } = requestParams;
26
+ if (!roleIdKey) {
27
+ throw new NoRetryError('roleIdKey is required');
28
+ }
29
+
30
+ const targetRolesTable = dynamodbSharedLib.tableName(_izContext, 'TargetRoles');
31
+ const rolePermissionsTable = dynamodbSharedLib.tableName(_izContext, 'RolePermissions');
32
+ const roleUsersTable = dynamodbSharedLib.tableName(_izContext, 'RoleUsers');
33
+
34
+ const role = identifiersConcat
35
+ ? await dynamodbSharedLib.getItem(_izContext, targetRolesTable, { identifiersConcat, roleIdKey })
36
+ : null;
37
+
38
+ const [permsResult, usersResult] = await Promise.all([
39
+ dynamodbSharedLib.query(_izContext, rolePermissionsTable, { roleIdKey }),
40
+ dynamodbSharedLib.query(_izContext, roleUsersTable, { roleIdKey })
41
+ ]);
42
+
43
+ const result = {
44
+ role,
45
+ roleIdKey,
46
+ permissions: permsResult?.Items || [],
47
+ users: usersResult?.Items || []
48
+ };
49
+
50
+ await sns.publishMessage(_izContext, {
51
+ message: { returnResult: result },
52
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
53
+ _izContext,
54
+ 'GetTargetRole_Out'
55
+ )
56
+ });
57
+ return result;
58
+ } catch (error) {
59
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:GetTargetRole] error:`, error);
60
+ throw error;
61
+ }
62
+ }
@@ -0,0 +1,107 @@
1
+ /*
2
+ Copyright (C) 2020 Sven Mason <http://izara.io>
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as
6
+ published by the Free Software Foundation, either version 3 of the
7
+ License, or (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+
18
+ import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
19
+ import snsSharedLib from '@izara_project/izara-core-library-sns';
20
+ import { sns } from '@izara_project/izara-core-library-external-request';
21
+ import { consts, NoRetryError } from '@izara_project/izara-core-library-core';
22
+
23
+ export default async function UpdateTargetRole(_izContext, requestParams) {
24
+ try {
25
+ const submittedByUserId =
26
+ requestParams.submittedByUserId ||
27
+ _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
28
+
29
+ const {
30
+ identifiersConcat,
31
+ roleIdKey,
32
+ roleName,
33
+ permissions
34
+ } = requestParams;
35
+
36
+ if (!roleIdKey) {
37
+ throw new NoRetryError('roleIdKey is required');
38
+ }
39
+
40
+ if (roleIdKey.endsWith('_owner')) {
41
+ throw new NoRetryError('Cannot modify system owner role');
42
+ }
43
+
44
+ const targetRolesTable = dynamodbSharedLib.tableName(_izContext, 'TargetRoles');
45
+ const rolePermissionsTable = dynamodbSharedLib.tableName(_izContext, 'RolePermissions');
46
+
47
+ if (roleName && identifiersConcat) {
48
+ const existingRole = await dynamodbSharedLib.getItem(_izContext, targetRolesTable, {
49
+ identifiersConcat,
50
+ roleIdKey
51
+ });
52
+ if (existingRole) {
53
+ await dynamodbSharedLib.putItem(_izContext, targetRolesTable, {
54
+ ...existingRole,
55
+ roleName
56
+ });
57
+ }
58
+ }
59
+
60
+ if (Array.isArray(permissions)) {
61
+ const currentPermsQuery = await dynamodbSharedLib.query(_izContext, rolePermissionsTable, {
62
+ roleIdKey
63
+ });
64
+ const currentPerms = currentPermsQuery?.Items || [];
65
+ const currentFlowMap = new Map(currentPerms.map((p) => [p.flowTypeConcat, p]));
66
+
67
+ const newFlowSet = new Set();
68
+ for (const perm of permissions) {
69
+ const flowTypeConcat = typeof perm === 'string' ? perm : perm.flowTypeConcat;
70
+ if (flowTypeConcat) {
71
+ newFlowSet.add(flowTypeConcat);
72
+ const permission =
73
+ typeof perm === 'object' && perm.permission !== undefined
74
+ ? perm.permission
75
+ : true;
76
+ const parameterGroups = (typeof perm === 'object' && perm.parameterGroups) || {};
77
+ await dynamodbSharedLib.putItem(_izContext, rolePermissionsTable, {
78
+ roleIdKey,
79
+ flowTypeConcat,
80
+ permission,
81
+ parameterGroups
82
+ });
83
+ }
84
+ }
85
+
86
+ for (const [oldFlow] of currentFlowMap.entries()) {
87
+ if (!newFlowSet.has(oldFlow)) {
88
+ await dynamodbSharedLib.deleteItem(_izContext, rolePermissionsTable, {
89
+ roleIdKey,
90
+ flowTypeConcat: oldFlow
91
+ });
92
+ }
93
+ }
94
+ }
95
+
96
+ await sns.publishMessage(_izContext, {
97
+ message: { returnResult: { roleIdKey, status: 'updated' } },
98
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
99
+ _izContext,
100
+ 'UpdateTargetRole_Out'
101
+ )
102
+ });
103
+ } catch (error) {
104
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:UpdateTargetRole] error:`, error);
105
+ throw error;
106
+ }
107
+ }