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

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 (43) 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 +2 -1
  5. package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +32 -14
  6. package/src/emit/crud/crudActionPlan.js +1 -1
  7. package/src/emit/crud/emitCrudApplicationArtifacts.js +10 -4
  8. package/src/emit/crud/emitDynamicCrudFlowSchemas.js +16 -8
  9. package/src/emit/crud/templates/CreateEndpoint_Main.ejs +12 -3
  10. package/src/emit/crud/templates/CreateLogicalLib.ejs +85 -20
  11. package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +48 -7
  12. package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +47 -6
  13. package/src/emit/crud/templates/UpdateLogicalLib.ejs +754 -353
  14. package/src/emit/function/functionYamlArtifact.js +4 -2
  15. package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +5 -5
  16. package/src/emit/rbac/emitRbacApplicationArtifacts.js +70 -89
  17. package/src/emit/rbac/templates/CancelUsage_Main.ejs +14 -10
  18. package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +14 -10
  19. package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +56 -16
  20. package/src/emit/rbac/templates/DeleteTargetRole_Main.ejs +96 -0
  21. package/src/emit/rbac/templates/GetMyPermissions_Main.ejs +110 -0
  22. package/src/emit/rbac/templates/GetTargetRole_Main.ejs +62 -0
  23. package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +76 -34
  24. package/src/emit/rbac/templates/UpdateTargetRole_Main.ejs +107 -0
  25. package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +24 -21
  26. package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +12 -0
  27. package/src/emit/resource/emitResourceArtifacts.js +75 -8
  28. package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +4 -2
  29. package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +14 -18
  30. package/src/emit/system/templates/WebSocketComplete_Yaml.ejs +4 -2
  31. package/src/emit/systemText/emitSystemTextSchemas.js +5 -2
  32. package/src/generate.js +18 -12
  33. package/src/generateResources/IntTest/generateResources/generateResources.js +1 -1
  34. package/src/generateResources/IntTest/generateTests/generateTemplate.js +1 -1
  35. package/src/generateResources/IntTest/upload/uploadIntTest.js +8 -2
  36. package/test/createTargetRoleHandler.test.js +42 -15
  37. package/test/crudApplicationArtifacts.test.js +62 -3
  38. package/test/customFlowApplicationArtifacts.test.js +3 -5
  39. package/test/dynamicCrudFlowSchemas.test.js +5 -17
  40. package/test/dynamicRbacFlowSchemas.test.js +5 -5
  41. package/test/functionYamlArtifact.test.js +4 -2
  42. package/test/publicApi.test.js +18 -14
  43. package/test/resourceArtifacts.test.js +7 -3
@@ -319,8 +319,10 @@ function _sqsFilterPatterns(
319
319
  - body:
320
320
  MessageAttributes:
321
321
  callingFlow:
322
- Value:
323
- - exists: false
322
+ - exists: false
323
+ - body:
324
+ MessageAttributes:
325
+ - exists: false
324
326
  #<#/${hookFunctionName}FilterPatterns#>
325
327
  `;
326
328
  }
@@ -4,20 +4,20 @@ 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 = [
18
- { flowTag: 'CreateRoleLimit', event: ['ownTopic'] },
19
- { flowTag: 'ListRoleLimits', event: ['lambdaSyncApi'] },
20
- { flowTag: 'DeleteRoleLimit', event: ['ownTopic'] },
21
21
  { flowTag: 'ReservedDynamicUsage', event: ['ownTopic'] },
22
22
  { flowTag: 'ConfirmUsage', event: ['ownTopic'] },
23
23
  { flowTag: 'CancelUsage', event: ['ownTopic'] }
@@ -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)
@@ -39,11 +52,7 @@ export async function emitRbacApplicationArtifacts(objects) {
39
52
 
40
53
  const limitHandlers = await Promise.all(
41
54
  hasSelfRbac
42
- ? [
43
- ...LIMIT_LIST_DEFINITIONS.map(createLimitListHandler),
44
- ...LIMIT_MUTATION_DEFINITIONS.map(createLimitMutationHandler),
45
- ...ACCOUNT_LIMIT_HANDLERS.map(createAccountLimitHandler)
46
- ]
55
+ ? ACCOUNT_LIMIT_HANDLERS.map(createAccountLimitHandler)
47
56
  : []
48
57
  );
49
58
 
@@ -91,14 +100,6 @@ const LIST_DEFINITIONS = [
91
100
  }
92
101
  ];
93
102
 
94
- const LIMIT_LIST_DEFINITIONS = [
95
- {
96
- flowTag: 'ListRoleLimits',
97
- tableName: 'RoleLimit',
98
- requestProperty: 'roleLimitId'
99
- }
100
- ];
101
-
102
103
  const MUTATION_DEFINITIONS = [
103
104
  {
104
105
  flowTag: 'CreateRolePermissions',
@@ -111,27 +112,6 @@ const MUTATION_DEFINITIONS = [
111
112
  tableName: 'RolePermissions',
112
113
  method: 'deleteItem',
113
114
  fields: ['roleIdKey', 'flowTypeConcat']
114
- },
115
- {
116
- flowTag: 'DeleteTargetRole',
117
- tableName: 'TargetRoles',
118
- method: 'deleteItem',
119
- fields: ['identifiersConcat', 'roleIdKey']
120
- }
121
- ];
122
-
123
- const LIMIT_MUTATION_DEFINITIONS = [
124
- {
125
- flowTag: 'CreateRoleLimit',
126
- tableName: 'RoleLimit',
127
- method: 'putItem',
128
- fields: ['roleLimitId', 'limitType', 'limitValue', 'actionType']
129
- },
130
- {
131
- flowTag: 'DeleteRoleLimit',
132
- tableName: 'RoleLimit',
133
- method: 'deleteItem',
134
- fields: ['roleLimitId', 'limitType']
135
115
  }
136
116
  ];
137
117
 
@@ -151,6 +131,20 @@ const STATIC_RBAC_APPLICATION_PATHS = [
151
131
  'app/src/generatedCode/FlowRbac/CreateTargetRole/source/CreateTargetRole_HdrSqs.js',
152
132
  'app/src/generatedCode/FlowRbac/CreateTargetRole/source/CreateTargetRole_WbsMain.js',
153
133
  'app/src/generatedCode/FlowRbac/CreateTargetRole/source/CreateTargetRole_HdrWbs.js',
134
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_Main.js',
135
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_HdrSqs.js',
136
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_WbsMain.js',
137
+ 'app/src/generatedCode/FlowRbac/UpdateTargetRole/source/UpdateTargetRole_HdrWbs.js',
138
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_Main.js',
139
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_HdrSqs.js',
140
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_WbsMain.js',
141
+ 'app/src/generatedCode/FlowRbac/DeleteTargetRole/source/DeleteTargetRole_HdrWbs.js',
142
+ 'app/src/generatedCode/FlowRbac/GetTargetRole/source/GetTargetRole_Main.js',
143
+ 'app/src/generatedCode/FlowRbac/GetTargetRole/source/GetTargetRole_HdrSqs.js',
144
+ 'app/src/generatedCode/FlowRbac/GetTargetRole/source/GetTargetRole_HdrApi.js',
145
+ 'app/src/generatedCode/FlowRbac/GetMyPermissions/source/GetMyPermissions_Main.js',
146
+ 'app/src/generatedCode/FlowRbac/GetMyPermissions/source/GetMyPermissions_HdrSqs.js',
147
+ 'app/src/generatedCode/FlowRbac/GetMyPermissions/source/GetMyPermissions_HdrApi.js',
154
148
  'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_Main.js',
155
149
  'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_HdrSqs.js',
156
150
  'app/src/generatedCode/FlowRbac/ValidatorRbac/source/ValidatorRbac_WbsMain.js',
@@ -176,17 +170,6 @@ const STATIC_RBAC_APPLICATION_PATHS = [
176
170
  ];
177
171
 
178
172
  const STATIC_LIMIT_APPLICATION_PATHS = [
179
- ...LIMIT_LIST_DEFINITIONS.flatMap(({ flowTag }) => [
180
- `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_Main.js`,
181
- `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrSqs.js`,
182
- `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrApi.js`
183
- ]),
184
- ...LIMIT_MUTATION_DEFINITIONS.flatMap(({ flowTag }) => [
185
- `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_Main.js`,
186
- `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrSqs.js`,
187
- `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_WbsMain.js`,
188
- `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrWbs.js`
189
- ]),
190
173
  ...ACCOUNT_LIMIT_HANDLERS.flatMap((flowTag) => [
191
174
  `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_Main.js`,
192
175
  `app/src/generatedCode/FlowLimit/${flowTag}/source/${flowTag}_HdrSqs.js`,
@@ -253,80 +236,78 @@ async function createAccountLimitHandler(flowTag) {
253
236
  };
254
237
  }
255
238
 
256
- async function createLimitListHandler(definition) {
257
- const sourcePath = `app/src/generatedCode/FlowLimit/${definition.flowTag}/source`;
258
- const handlerPath = `src/generatedCode/FlowLimit/${definition.flowTag}/source`;
239
+
240
+ async function createCustomRbacOwnTopicHandler(flowTag, mainTemplateName) {
241
+ const sourcePath = `app/src/generatedCode/FlowRbac/${flowTag}/source`;
242
+ const handlerPath = `src/generatedCode/FlowRbac/${flowTag}/source`;
243
+ const webSocket = await createWebSocketEntry(
244
+ flowTag,
245
+ sourcePath,
246
+ handlerPath
247
+ );
259
248
  return {
260
249
  artifacts: [
261
250
  {
262
- path: `${sourcePath}/${definition.flowTag}_Main.js`,
263
- content: await renderRbacTemplate('ListRbac_Main.ejs', {
264
- ...definition,
265
- useContextUserId: definition.useContextUserId ?? false
266
- })
251
+ path: `${sourcePath}/${flowTag}_Main.js`,
252
+ content: await renderRbacTemplate(mainTemplateName, { flowTag })
267
253
  },
268
254
  {
269
- path: `${sourcePath}/${definition.flowTag}_HdrSqs.js`,
270
- content: await renderRbacTemplate('ListRbac_HdrSqs.ejs', definition)
255
+ path: `${sourcePath}/${flowTag}_HdrSqs.js`,
256
+ content: await renderRbacTemplate('ListRbac_HdrSqs.ejs', { flowTag })
271
257
  },
272
- {
273
- path: `${sourcePath}/${definition.flowTag}_HdrApi.js`,
274
- content: await renderRbacTemplate('ListRbac_HdrApi.ejs', definition)
275
- }
258
+ ...webSocket.artifacts
276
259
  ],
277
260
  functions: [
278
261
  {
279
262
  type: 'sqs',
280
- functionName: `${definition.flowTag}HdrSqs`,
281
- handlerPath: `${handlerPath}/${definition.flowTag}_HdrSqs.main`,
263
+ functionName: `${flowTag}HdrSqs`,
264
+ handlerPath: `${handlerPath}/${flowTag}_HdrSqs.main`,
282
265
  roleName: 'FlowRbacRole',
283
- queueName: `${definition.flowTag}HdrSqs`,
284
- flowTag: definition.flowTag,
266
+ queueName: `${flowTag}HdrSqs`,
267
+ flowTag: flowTag,
285
268
  flowStepName: ''
286
269
  },
287
- {
288
- type: 'api',
289
- functionName: `${definition.flowTag}HdrApi`,
290
- handlerPath: `${handlerPath}/${definition.flowTag}_HdrApi.main`,
291
- roleName: 'FlowRbacRole',
292
- flowTag: definition.flowTag,
293
- flowStepName: ''
294
- }
270
+ ...webSocket.functions
295
271
  ]
296
272
  };
297
273
  }
298
274
 
299
- async function createLimitMutationHandler(definition) {
300
- const sourcePath = `app/src/generatedCode/FlowLimit/${definition.flowTag}/source`;
301
- const handlerPath = `src/generatedCode/FlowLimit/${definition.flowTag}/source`;
302
- const webSocket = await createWebSocketEntry(
303
- definition.flowTag,
304
- sourcePath,
305
- handlerPath
306
- );
275
+ async function createCustomRbacApiHandler(flowTag, mainTemplateName) {
276
+ const sourcePath = `app/src/generatedCode/FlowRbac/${flowTag}/source`;
277
+ const handlerPath = `src/generatedCode/FlowRbac/${flowTag}/source`;
307
278
  return {
308
279
  artifacts: [
309
280
  {
310
- path: `${sourcePath}/${definition.flowTag}_Main.js`,
311
- content: await renderRbacTemplate('RbacMutation_Main.ejs', definition)
281
+ path: `${sourcePath}/${flowTag}_Main.js`,
282
+ content: await renderRbacTemplate(mainTemplateName, { flowTag })
312
283
  },
313
284
  {
314
- path: `${sourcePath}/${definition.flowTag}_HdrSqs.js`,
315
- content: await renderRbacTemplate('ListRbac_HdrSqs.ejs', definition)
285
+ path: `${sourcePath}/${flowTag}_HdrSqs.js`,
286
+ content: await renderRbacTemplate('ListRbac_HdrSqs.ejs', { flowTag })
316
287
  },
317
- ...webSocket.artifacts
288
+ {
289
+ path: `${sourcePath}/${flowTag}_HdrApi.js`,
290
+ content: await renderRbacTemplate('ListRbac_HdrApi.ejs', { flowTag })
291
+ }
318
292
  ],
319
293
  functions: [
320
294
  {
321
295
  type: 'sqs',
322
- functionName: `${definition.flowTag}HdrSqs`,
323
- handlerPath: `${handlerPath}/${definition.flowTag}_HdrSqs.main`,
296
+ functionName: `${flowTag}HdrSqs`,
297
+ handlerPath: `${handlerPath}/${flowTag}_HdrSqs.main`,
324
298
  roleName: 'FlowRbacRole',
325
- queueName: `${definition.flowTag}HdrSqs`,
326
- flowTag: definition.flowTag,
299
+ queueName: `${flowTag}HdrSqs`,
300
+ flowTag: flowTag,
327
301
  flowStepName: ''
328
302
  },
329
- ...webSocket.functions
303
+ {
304
+ type: 'api',
305
+ functionName: `${flowTag}HdrApi`,
306
+ handlerPath: `${handlerPath}/${flowTag}_HdrApi.main`,
307
+ roleName: 'FlowRbacRole',
308
+ flowTag: flowTag,
309
+ flowStepName: ''
310
+ }
330
311
  ]
331
312
  };
332
313
  }
@@ -39,21 +39,25 @@ export default async function CancelUsage(_izContext, requestParams) {
39
39
  requestParams?.limitTag;
40
40
 
41
41
  const targetIdentifiers = requestParams?.targetIdentifiers;
42
- let identifiersConcat = requestParams?.identifiersConcat;
42
+ let identifiersConcat =
43
+ requestParams?.identifiersConcat || requestParams?.identifierConcat;
43
44
  if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
44
- if (targetIdentifiers.identifierConcat) {
45
- identifiersConcat = targetIdentifiers.identifierConcat;
45
+ if (targetIdentifiers.identifiersConcat || targetIdentifiers.identifierConcat) {
46
+ identifiersConcat =
47
+ targetIdentifiers.identifiersConcat || targetIdentifiers.identifierConcat;
46
48
  } else {
47
- const sortedKeys = Object.keys(targetIdentifiers).sort();
49
+ const sortedKeys = Object.keys(targetIdentifiers)
50
+ .filter(
51
+ (k) =>
52
+ k !== 'submittedByUserId' &&
53
+ k !== 'identifierConcat' &&
54
+ k !== 'identifiersConcat'
55
+ )
56
+ .sort();
48
57
  const parts = [];
49
58
  for (const key of sortedKeys) {
50
59
  const value = targetIdentifiers[key];
51
- if (
52
- key !== 'submittedByUserId' &&
53
- key !== 'identifierConcat' &&
54
- value !== undefined &&
55
- value !== null
56
- ) {
60
+ if (value !== undefined && value !== null) {
57
61
  parts.push(`${key}#${value}`);
58
62
  }
59
63
  }
@@ -39,21 +39,25 @@ export default async function ConfirmUsage(_izContext, requestParams) {
39
39
  requestParams?.limitTag;
40
40
 
41
41
  const targetIdentifiers = requestParams?.targetIdentifiers;
42
- let identifiersConcat = requestParams?.identifiersConcat;
42
+ let identifiersConcat =
43
+ requestParams?.identifiersConcat || requestParams?.identifierConcat;
43
44
  if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
44
- if (targetIdentifiers.identifierConcat) {
45
- identifiersConcat = targetIdentifiers.identifierConcat;
45
+ if (targetIdentifiers.identifiersConcat || targetIdentifiers.identifierConcat) {
46
+ identifiersConcat =
47
+ targetIdentifiers.identifiersConcat || targetIdentifiers.identifierConcat;
46
48
  } else {
47
- const sortedKeys = Object.keys(targetIdentifiers).sort();
49
+ const sortedKeys = Object.keys(targetIdentifiers)
50
+ .filter(
51
+ (k) =>
52
+ k !== 'submittedByUserId' &&
53
+ k !== 'identifierConcat' &&
54
+ k !== 'identifiersConcat'
55
+ )
56
+ .sort();
48
57
  const parts = [];
49
58
  for (const key of sortedKeys) {
50
59
  const value = targetIdentifiers[key];
51
- if (
52
- key !== 'submittedByUserId' &&
53
- key !== 'identifierConcat' &&
54
- value !== undefined &&
55
- value !== null
56
- ) {
60
+ if (value !== undefined && value !== null) {
57
61
  parts.push(`${key}#${value}`);
58
62
  }
59
63
  }
@@ -28,21 +28,25 @@ export default async function CreateTargetRole(_izContext, requestParams) {
28
28
  _izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
29
29
 
30
30
  const targetIdentifiers = requestParams.targetIdentifiers;
31
- let identifiersConcat = requestParams.identifiersConcat;
31
+ let identifiersConcat =
32
+ requestParams.identifiersConcat || requestParams.identifierConcat;
32
33
  if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
33
- if (targetIdentifiers.identifierConcat) {
34
- identifiersConcat = targetIdentifiers.identifierConcat;
34
+ if (targetIdentifiers.identifiersConcat || targetIdentifiers.identifierConcat) {
35
+ identifiersConcat =
36
+ targetIdentifiers.identifiersConcat || targetIdentifiers.identifierConcat;
35
37
  } else {
36
- const sortedKeys = Object.keys(targetIdentifiers).sort();
38
+ const sortedKeys = Object.keys(targetIdentifiers)
39
+ .filter(
40
+ (k) =>
41
+ k !== 'submittedByUserId' &&
42
+ k !== 'identifierConcat' &&
43
+ k !== 'identifiersConcat'
44
+ )
45
+ .sort();
37
46
  const parts = [];
38
47
  for (const key of sortedKeys) {
39
48
  const value = targetIdentifiers[key];
40
- if (
41
- key !== 'submittedByUserId' &&
42
- key !== 'identifierConcat' &&
43
- value !== undefined &&
44
- value !== null
45
- ) {
49
+ if (value !== undefined && value !== null) {
46
50
  parts.push(`${key}#${value}`);
47
51
  }
48
52
  }
@@ -55,8 +59,17 @@ export default async function CreateTargetRole(_izContext, requestParams) {
55
59
  identifiersConcat = submittedByUserId;
56
60
  }
57
61
 
58
- const { roleName = 'owner', roleId: inputRoleId } = requestParams;
59
- const roleId = inputRoleId || roleName || identifierUuid();
62
+ const {
63
+ roleName = 'owner',
64
+ roleId: inputRoleId,
65
+ permissions = [],
66
+ assignUserIds = []
67
+ } = requestParams;
68
+ let defaultRoleId = identifierUuid();
69
+ if (roleName === 'owner') {
70
+ defaultRoleId = 'owner';
71
+ }
72
+ const roleId = inputRoleId || defaultRoleId;
60
73
  const roleIdKey = `${identifiersConcat}_${roleId}`;
61
74
 
62
75
  await dynamodbSharedLib.putItem(
@@ -65,26 +78,53 @@ export default async function CreateTargetRole(_izContext, requestParams) {
65
78
  {
66
79
  identifiersConcat,
67
80
  roleIdKey,
81
+ roleType: 'tenant_role',
68
82
  roleName,
69
- submittedByUserId
83
+ submittedByUserId,
84
+ createdAt: new Date().toISOString()
70
85
  }
71
86
  );
72
87
 
73
- if (submittedByUserId) {
88
+ if (Array.isArray(permissions) && permissions.length > 0) {
89
+ const rolePermissionsTable = dynamodbSharedLib.tableName(_izContext, 'RolePermissions');
90
+ for (const perm of permissions) {
91
+ const flowTypeConcat = typeof perm === 'string' ? perm : perm.flowTypeConcat;
92
+ if (flowTypeConcat) {
93
+ const permission =
94
+ typeof perm === 'object' && perm.permission !== undefined
95
+ ? perm.permission
96
+ : true;
97
+ const parameterGroups = (typeof perm === 'object' && perm.parameterGroups) || {};
98
+ await dynamodbSharedLib.putItem(_izContext, rolePermissionsTable, {
99
+ roleIdKey,
100
+ flowTypeConcat,
101
+ permission,
102
+ parameterGroups
103
+ });
104
+ }
105
+ }
106
+ }
107
+
108
+ const usersToAssign =
109
+ Array.isArray(assignUserIds) && assignUserIds.length > 0
110
+ ? assignUserIds
111
+ : (roleName === 'owner' && submittedByUserId ? [submittedByUserId] : []);
112
+
113
+ for (const userId of usersToAssign) {
74
114
  try {
75
115
  await dynamodbSharedLib.transactWriteItems(_izContext, [
76
116
  {
77
117
  action: 'put',
78
118
  params: {
79
119
  tableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
80
- attributes: { roleIdKey, submittedByUserId }
120
+ attributes: { roleIdKey, userId, submittedByUserId }
81
121
  }
82
122
  },
83
123
  {
84
124
  action: 'put',
85
125
  params: {
86
126
  tableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
87
- attributes: { submittedByUserId, roleIdKey }
127
+ attributes: { userId, roleIdKey, submittedByUserId }
88
128
  }
89
129
  }
90
130
  ]);
@@ -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,110 @@
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 =
35
+ requestParams.identifiersConcat || requestParams.identifierConcat;
36
+ if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
37
+ if (targetIdentifiers.identifiersConcat || targetIdentifiers.identifierConcat) {
38
+ identifiersConcat =
39
+ targetIdentifiers.identifiersConcat || targetIdentifiers.identifierConcat;
40
+ } else {
41
+ const sortedKeys = Object.keys(targetIdentifiers)
42
+ .filter(
43
+ (k) =>
44
+ k !== 'submittedByUserId' &&
45
+ k !== 'identifierConcat' &&
46
+ k !== 'identifiersConcat'
47
+ )
48
+ .sort();
49
+ const parts = [];
50
+ for (const key of sortedKeys) {
51
+ const value = targetIdentifiers[key];
52
+ if (value !== undefined && value !== null) {
53
+ parts.push(`${key}#${value}`);
54
+ }
55
+ }
56
+ if (parts.length > 0) {
57
+ identifiersConcat = parts.join('|');
58
+ }
59
+ }
60
+ }
61
+
62
+ const userRolesTable = dynamodbSharedLib.tableName(_izContext, 'UserRoles');
63
+ const rolePermissionsTable = dynamodbSharedLib.tableName(_izContext, 'RolePermissions');
64
+
65
+ const userRolesResult = await dynamodbSharedLib.query(_izContext, userRolesTable, {
66
+ userId: submittedByUserId
67
+ });
68
+ const userRoleItems = userRolesResult?.Items || [];
69
+
70
+ const matchingRoles = identifiersConcat
71
+ ? userRoleItems.filter((item) => item.roleIdKey?.startsWith(identifiersConcat))
72
+ : userRoleItems;
73
+
74
+ const permissionsMap = {};
75
+ const rolesList = [];
76
+
77
+ for (const roleItem of matchingRoles) {
78
+ rolesList.push(roleItem.roleIdKey);
79
+ const permsResult = await dynamodbSharedLib.query(_izContext, rolePermissionsTable, {
80
+ roleIdKey: roleItem.roleIdKey
81
+ });
82
+ for (const p of (permsResult?.Items || [])) {
83
+ if (p.permission === true) {
84
+ permissionsMap[p.flowTypeConcat] = true;
85
+ } else if (permissionsMap[p.flowTypeConcat] === undefined) {
86
+ permissionsMap[p.flowTypeConcat] = false;
87
+ }
88
+ }
89
+ }
90
+
91
+ const result = {
92
+ userId: submittedByUserId,
93
+ identifiersConcat: identifiersConcat || null,
94
+ roles: rolesList,
95
+ permissions: permissionsMap
96
+ };
97
+
98
+ await sns.publishMessage(_izContext, {
99
+ message: { returnResult: result },
100
+ TopicArn: snsSharedLib.snsTopicArnByFlowSchema(
101
+ _izContext,
102
+ 'GetMyPermissions_Out'
103
+ )
104
+ });
105
+ return result;
106
+ } catch (error) {
107
+ _izContext.logger.error(`[${process.env.iz_serviceTag}:GetMyPermissions] error:`, error);
108
+ throw error;
109
+ }
110
+ }