@izara_project/izara-core-generate-service-code 1.0.84 → 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.
- package/package.json +1 -1
- package/src/compile/compileProject.js +1 -2
- package/src/compile/loadSchemas.js +29 -17
- package/src/emit/coreFunction/emitCoreFunctionArtifacts.js +6 -0
- package/src/emit/coreFunction/templates/InitialSetupConfig.ejs +26 -6
- package/src/emit/crud/crudActionPlan.js +3 -1
- package/src/emit/crud/emitCrudApplicationArtifacts.js +47 -8
- package/src/emit/crud/emitDynamicCrudFlowSchemas.js +136 -25
- package/src/emit/crud/templates/CreateConfirmComplete_Main.ejs +54 -0
- package/src/emit/crud/templates/CreateEndpoint_Main.ejs +248 -181
- package/src/emit/crud/templates/CreateLogicalLib.ejs +1155 -293
- package/src/emit/crud/templates/CreateNodeCallback_Main.ejs +221 -0
- package/src/emit/crud/templates/DeleteEndpoint_Main.ejs +36 -4
- package/src/emit/crud/templates/FlowEndpointRbacCallback_Main.ejs +20 -120
- package/src/emit/crud/templates/FlowEndpoint_HdrWbs.ejs +76 -14
- package/src/emit/crud/templates/FlowMain_Wbs.ejs +31 -35
- package/src/emit/crud/templates/UpdateEndpoint_Main.ejs +35 -3
- package/src/emit/crud/templates/UpdateLogicalLib.ejs +754 -353
- package/src/emit/rbac/emitDynamicRbacFlowSchemas.js +5 -2
- package/src/emit/rbac/emitRbacApplicationArtifacts.js +102 -6
- package/src/emit/rbac/templates/CancelUsage_Main.ejs +82 -22
- package/src/emit/rbac/templates/ConfirmUsage_Main.ejs +130 -14
- package/src/emit/rbac/templates/CreateTargetRole_Main.ejs +61 -12
- package/src/emit/rbac/templates/DeleteTargetRole_Main.ejs +96 -0
- package/src/emit/rbac/templates/GetMyPermissions_Main.ejs +106 -0
- package/src/emit/rbac/templates/GetTargetRole_Main.ejs +62 -0
- package/src/emit/rbac/templates/ReservedDynamicUsage_Main.ejs +30 -11
- package/src/emit/rbac/templates/UpdateTargetRole_Main.ejs +107 -0
- package/src/emit/rbac/templates/UserRbacFlow_Main.ejs +40 -15
- package/src/emit/rbac/templates/UserRoleMutation_Main.ejs +22 -7
- package/src/emit/resource/emitResourceArtifacts.js +52 -2
- package/src/emit/resource/templates/GeneratedLambdaRoleServiceConfig.ejs +6 -2
- package/src/emit/resource/templates/SystemDynamoDB_Yaml.ejs +2 -0
- package/src/emit/shared/templates/FlowHandler_HdrSqs.ejs +1 -1
- package/src/emit/system/templates/WebSocketConnect_Yaml.ejs +0 -1
- package/src/emit/systemText/emitSystemTextSchemas.js +5 -2
- package/src/generate.js +18 -12
- package/src/generateResources/IntTest/generateResources/generateResources.js +1 -1
- package/src/generateResources/IntTest/generateTests/generateTemplate.js +1 -1
- package/src/generateResources/IntTest/upload/uploadIntTest.js +8 -2
- package/test/createTargetRoleHandler.test.js +42 -1
- package/test/crudApplicationArtifacts.test.js +53 -0
- package/test/dynamicCrudFlowSchemas.test.js +16 -2
- package/test/dynamicRbacFlowSchemas.test.js +5 -2
- package/test/publicApi.test.js +18 -14
- package/test/resourceArtifacts.test.js +24 -0
|
@@ -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
|
+
}
|
|
@@ -25,8 +25,10 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
25
25
|
const submittedByUserId =
|
|
26
26
|
requestParams.submittedByUserId ||
|
|
27
27
|
_izContext.correlationIds?.get(consts.SUBMITTED_BY_USER_ID);
|
|
28
|
+
const flowTypeConcat =
|
|
29
|
+
requestParams.flowTypeConcat ||
|
|
30
|
+
requestParams.limitTag;
|
|
28
31
|
const {
|
|
29
|
-
limitTag,
|
|
30
32
|
value = 1,
|
|
31
33
|
usedTimestamp = new Date().toISOString(),
|
|
32
34
|
roleLimitId
|
|
@@ -35,9 +37,26 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
35
37
|
const targetIdentifiers = requestParams.targetIdentifiers;
|
|
36
38
|
let identifiersConcat = requestParams.identifiersConcat;
|
|
37
39
|
if (!identifiersConcat && targetIdentifiers && typeof targetIdentifiers === 'object') {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
if (targetIdentifiers.identifierConcat) {
|
|
41
|
+
identifiersConcat = targetIdentifiers.identifierConcat;
|
|
42
|
+
} else {
|
|
43
|
+
const sortedKeys = Object.keys(targetIdentifiers).sort();
|
|
44
|
+
const parts = [];
|
|
45
|
+
for (const key of sortedKeys) {
|
|
46
|
+
const value = targetIdentifiers[key];
|
|
47
|
+
if (
|
|
48
|
+
key !== 'submittedByUserId' &&
|
|
49
|
+
key !== 'identifierConcat' &&
|
|
50
|
+
value !== undefined &&
|
|
51
|
+
value !== null
|
|
52
|
+
) {
|
|
53
|
+
parts.push(`${key}#${value}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (parts.length > 0) {
|
|
57
|
+
identifiersConcat = parts.join('|');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
41
60
|
}
|
|
42
61
|
if (!identifiersConcat) {
|
|
43
62
|
identifiersConcat = submittedByUserId;
|
|
@@ -46,14 +65,14 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
46
65
|
if (!identifiersConcat) {
|
|
47
66
|
throw new NoRetryError('Identifier not found.');
|
|
48
67
|
}
|
|
49
|
-
if (!
|
|
50
|
-
throw new NoRetryError('limitTag is required.');
|
|
68
|
+
if (!flowTypeConcat) {
|
|
69
|
+
throw new NoRetryError('flowTypeConcat (or limitTag) is required.');
|
|
51
70
|
}
|
|
52
71
|
|
|
53
72
|
const uniqueIdentify = `${usedTimestamp}_${_izContext.uniqueRequestId || Date.now()}`;
|
|
54
73
|
const roleLimitKeys = Array.isArray(requestParams.roleLimitIds)
|
|
55
74
|
? requestParams.roleLimitIds
|
|
56
|
-
: [roleLimitId || `${identifiersConcat}_${
|
|
75
|
+
: [roleLimitId || `${identifiersConcat}_${flowTypeConcat}`];
|
|
57
76
|
|
|
58
77
|
let maxLimit = Infinity;
|
|
59
78
|
const limitRecords = [];
|
|
@@ -93,7 +112,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
93
112
|
const usageRecord = await dynamodbSharedLib.getItem(
|
|
94
113
|
_izContext,
|
|
95
114
|
dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
|
|
96
|
-
{ identifiersConcat,
|
|
115
|
+
{ identifiersConcat, flowTypeConcat }
|
|
97
116
|
);
|
|
98
117
|
const currentTotal = usageRecord?.totalCount || 0;
|
|
99
118
|
|
|
@@ -106,7 +125,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
106
125
|
_izContext,
|
|
107
126
|
dynamodbSharedLib.tableName(_izContext, 'RunningTotalReservedUsage'),
|
|
108
127
|
{
|
|
109
|
-
runningTotalReservedId: `${identifiersConcat}_${
|
|
128
|
+
runningTotalReservedId: `${identifiersConcat}_${flowTypeConcat}`,
|
|
110
129
|
usedTimestampId: uniqueIdentify,
|
|
111
130
|
usageCount: value,
|
|
112
131
|
runningTotalUsageStatus: 'reserved',
|
|
@@ -119,7 +138,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
119
138
|
dynamodbSharedLib.tableName(_izContext, 'RunningTotalUsage'),
|
|
120
139
|
{
|
|
121
140
|
identifiersConcat,
|
|
122
|
-
|
|
141
|
+
flowTypeConcat,
|
|
123
142
|
totalCount: currentTotal + value,
|
|
124
143
|
updateUuid: _izContext.uniqueRequestId || `${Date.now()}`
|
|
125
144
|
}
|
|
@@ -129,7 +148,7 @@ export default async function ReservedDynamicUsage(_izContext, requestParams) {
|
|
|
129
148
|
const returnResult = {
|
|
130
149
|
passStatus,
|
|
131
150
|
uniqueIdentify,
|
|
132
|
-
|
|
151
|
+
flowTypeConcat,
|
|
133
152
|
identifiersConcat
|
|
134
153
|
};
|
|
135
154
|
|
|
@@ -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
|
+
}
|
|
@@ -52,7 +52,7 @@ async function _getL2Cache(_izContext, submittedByUserId, cacheSubKey) {
|
|
|
52
52
|
try {
|
|
53
53
|
const tableName = dynamodbSharedLib.tableName(_izContext, 'RbacPermissionCaches');
|
|
54
54
|
const result = await dynamodbSharedLib.getItem(_izContext, tableName, {
|
|
55
|
-
submittedByUserId,
|
|
55
|
+
userId: submittedByUserId,
|
|
56
56
|
cacheKey: cacheSubKey
|
|
57
57
|
});
|
|
58
58
|
if (result && result.ttlTimestamp && result.ttlTimestamp > Math.floor(Date.now() / 1000)) {
|
|
@@ -77,7 +77,7 @@ async function _setL2Cache(_izContext, submittedByUserId, cacheSubKey, value) {
|
|
|
77
77
|
const tableName = dynamodbSharedLib.tableName(_izContext, 'RbacPermissionCaches');
|
|
78
78
|
const ttlTimestamp = Math.floor(Date.now() / 1000) + L2_TTL_SECONDS;
|
|
79
79
|
await dynamodbSharedLib.putItem(_izContext, tableName, {
|
|
80
|
-
submittedByUserId,
|
|
80
|
+
userId: submittedByUserId,
|
|
81
81
|
cacheKey: cacheSubKey,
|
|
82
82
|
permission: value.returnValue ? value.returnValue.permission : value.permission,
|
|
83
83
|
roleIdKey: value.returnValue ? value.returnValue.roleIdKey : value.roleIdKey,
|
|
@@ -92,12 +92,37 @@ async function _setL2Cache(_izContext, submittedByUserId, cacheSubKey, value) {
|
|
|
92
92
|
export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
93
93
|
const submittedByUserId =
|
|
94
94
|
requestParams.submittedByUserId ||
|
|
95
|
+
requestParams.targetIdentifiers?.submittedByUserId ||
|
|
95
96
|
_izContext.correlationIds.get(consts.SUBMITTED_BY_USER_ID);
|
|
96
97
|
|
|
97
98
|
const targetIdentifiers = requestParams.targetIdentifiers;
|
|
98
|
-
let targetId =
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
let targetId = '';
|
|
100
|
+
if (targetIdentifiers) {
|
|
101
|
+
if (typeof targetIdentifiers === 'string') {
|
|
102
|
+
targetId = targetIdentifiers;
|
|
103
|
+
} else if (typeof targetIdentifiers === 'object') {
|
|
104
|
+
if (targetIdentifiers.identifierConcat) {
|
|
105
|
+
targetId = targetIdentifiers.identifierConcat;
|
|
106
|
+
} else {
|
|
107
|
+
const sortedKeys = Object.keys(targetIdentifiers).sort();
|
|
108
|
+
const parts = [];
|
|
109
|
+
for (const key of sortedKeys) {
|
|
110
|
+
const value = targetIdentifiers[key];
|
|
111
|
+
if (
|
|
112
|
+
key !== 'submittedByUserId' &&
|
|
113
|
+
key !== 'identifierConcat' &&
|
|
114
|
+
value !== undefined &&
|
|
115
|
+
value !== null
|
|
116
|
+
) {
|
|
117
|
+
parts.push(`${key}#${value}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (parts.length > 0) {
|
|
121
|
+
targetId = parts.join('|');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
101
126
|
|
|
102
127
|
if (!submittedByUserId || !targetIdentifiers) {
|
|
103
128
|
throw new Error('Missing required request parameters: submittedByUserId or targetIdentifiers');
|
|
@@ -108,7 +133,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
108
133
|
throw new Error('Missing flowTypeConcat in requestParams');
|
|
109
134
|
}
|
|
110
135
|
|
|
111
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] requestParams
|
|
136
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] requestParams:`, {
|
|
112
137
|
submittedByUserId,
|
|
113
138
|
targetId,
|
|
114
139
|
flowTypeConcat,
|
|
@@ -150,7 +175,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
150
175
|
// 1. Check L1 In-Memory Cache
|
|
151
176
|
const l1Hit = _getL1Cache(fullCacheKey);
|
|
152
177
|
if (l1Hit) {
|
|
153
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L1 Cache hit
|
|
178
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L1 Cache hit:`, l1Hit);
|
|
154
179
|
await _publicMessageOut(_izContext, { ...l1Hit, parentFlowId });
|
|
155
180
|
return;
|
|
156
181
|
}
|
|
@@ -158,19 +183,19 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
158
183
|
// 2. Check L2 Shared DynamoDB Cache
|
|
159
184
|
const l2Hit = await _getL2Cache(_izContext, submittedByUserId, cacheSubKey);
|
|
160
185
|
if (l2Hit) {
|
|
161
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L2 Cache hit
|
|
186
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] L2 Cache hit:`, l2Hit);
|
|
162
187
|
_setL1Cache(fullCacheKey, l2Hit);
|
|
163
188
|
await _publicMessageOut(_izContext, { ...l2Hit, parentFlowId });
|
|
164
189
|
return;
|
|
165
190
|
}
|
|
166
191
|
|
|
167
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Cache miss — querying DynamoDB
|
|
192
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] Cache miss — querying DynamoDB`);
|
|
168
193
|
|
|
169
194
|
// 3. Fallback to DB Query
|
|
170
195
|
const userRolesResult = await dynamodbSharedLib.query(
|
|
171
196
|
_izContext,
|
|
172
197
|
dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
|
|
173
|
-
{ submittedByUserId }
|
|
198
|
+
{ userId: submittedByUserId }
|
|
174
199
|
);
|
|
175
200
|
|
|
176
201
|
let userRoles = [];
|
|
@@ -180,7 +205,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
180
205
|
userRoles = userRolesResult.Items;
|
|
181
206
|
}
|
|
182
207
|
|
|
183
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] userRoles fetched
|
|
208
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] userRoles fetched:`, { userRoles });
|
|
184
209
|
|
|
185
210
|
const targetIdPrefix = `${targetId}_`;
|
|
186
211
|
const targetRoles = [];
|
|
@@ -190,13 +215,13 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
190
215
|
}
|
|
191
216
|
}
|
|
192
217
|
|
|
193
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] targetRoles filtered
|
|
218
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] targetRoles filtered:`, {
|
|
194
219
|
targetIdPrefix,
|
|
195
220
|
targetRoles
|
|
196
221
|
});
|
|
197
222
|
|
|
198
223
|
if (targetRoles.length === 0) {
|
|
199
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] no targetRoles found, permission denied
|
|
224
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] no targetRoles found, permission denied`);
|
|
200
225
|
const result = {
|
|
201
226
|
returnValue: {
|
|
202
227
|
permission: false,
|
|
@@ -225,7 +250,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
225
250
|
})
|
|
226
251
|
);
|
|
227
252
|
|
|
228
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] rolePermissions fetched
|
|
253
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] rolePermissions fetched:`, {
|
|
229
254
|
rolePermissions
|
|
230
255
|
});
|
|
231
256
|
|
|
@@ -253,7 +278,7 @@ export default async function <%- flowTag %>(_izContext, requestParams = {}) {
|
|
|
253
278
|
status: 'Completed'
|
|
254
279
|
};
|
|
255
280
|
|
|
256
|
-
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] validation result
|
|
281
|
+
_izContext.logger.debug(`[${process.env.iz_serviceTag}:<%- flowTag %>] validation result:`, cacheResult);
|
|
257
282
|
|
|
258
283
|
// Store in L1 & L2 Cache
|
|
259
284
|
_setL1Cache(fullCacheKey, cacheResult);
|
|
@@ -16,7 +16,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
<%
|
|
19
|
-
const
|
|
19
|
+
const action = operation === 'Put' ? 'put' : 'delete';
|
|
20
|
+
const propName = operation === 'Put' ? 'attributes' : 'keyValues';
|
|
20
21
|
const roleUsersKey = operation === 'Put' ? 'userId, roleIdKey' : 'roleIdKey, userId';
|
|
21
22
|
-%> import dynamodbSharedLib from '@izara_project/izara-core-library-dynamodb';
|
|
22
23
|
import snsSharedLib from '@izara_project/izara-core-library-sns';
|
|
@@ -25,17 +26,31 @@ import { sns } from '@izara_project/izara-core-library-external-request';
|
|
|
25
26
|
export default async function <%- flowTag %>(_izContext, requestParams) {
|
|
26
27
|
try {
|
|
27
28
|
const { roleIdKey, userId } = requestParams;
|
|
29
|
+
<% if (operation === 'Delete') { -%>
|
|
30
|
+
if (roleIdKey && roleIdKey.endsWith('_owner')) {
|
|
31
|
+
const remainingUsers = await dynamodbSharedLib.query(
|
|
32
|
+
_izContext,
|
|
33
|
+
dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
|
|
34
|
+
{ roleIdKey }
|
|
35
|
+
);
|
|
36
|
+
if (remainingUsers?.Items?.length <= 1) {
|
|
37
|
+
throw new Error('Cannot remove the last remaining owner');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
<% } -%>
|
|
28
41
|
await dynamodbSharedLib.transactWriteItems(_izContext, [
|
|
29
42
|
{
|
|
30
|
-
<%-
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
action: '<%- action %>',
|
|
44
|
+
params: {
|
|
45
|
+
tableName: dynamodbSharedLib.tableName(_izContext, 'RoleUsers'),
|
|
46
|
+
<%- propName %>: { <%- roleUsersKey %> }
|
|
33
47
|
}
|
|
34
48
|
},
|
|
35
49
|
{
|
|
36
|
-
<%-
|
|
37
|
-
|
|
38
|
-
|
|
50
|
+
action: '<%- action %>',
|
|
51
|
+
params: {
|
|
52
|
+
tableName: dynamodbSharedLib.tableName(_izContext, 'UserRoles'),
|
|
53
|
+
<%- propName %>: { userId, roleIdKey }
|
|
39
54
|
}
|
|
40
55
|
}
|
|
41
56
|
]);
|
|
@@ -31,14 +31,17 @@ const RELATIONSHIP_ACTIONS = [
|
|
|
31
31
|
];
|
|
32
32
|
const RBAC_ACTIONS = [
|
|
33
33
|
'CreateTargetRole',
|
|
34
|
-
'
|
|
34
|
+
'UpdateTargetRole',
|
|
35
35
|
'DeleteTargetRole',
|
|
36
|
+
'GetTargetRole',
|
|
37
|
+
'ListTargetRole',
|
|
36
38
|
'CreateRolePermissions',
|
|
37
39
|
'ListRolePermissions',
|
|
38
40
|
'DeleteRolePermissions',
|
|
39
41
|
'CreateUserRole',
|
|
40
42
|
'ListUserInRoles',
|
|
41
43
|
'DeleteUserFromRole',
|
|
44
|
+
'GetMyPermissions',
|
|
42
45
|
'ValidatorRbac',
|
|
43
46
|
'CreateRoleLimit',
|
|
44
47
|
'ListRoleLimits',
|
|
@@ -215,6 +218,9 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
215
218
|
if (crud) {
|
|
216
219
|
for (const action of ['Create', 'Update', 'Delete']) {
|
|
217
220
|
const plan = crudActionPlans[action];
|
|
221
|
+
if (plan.hasCreateNodeCallback) {
|
|
222
|
+
crudSqsQueues.push(`${action}NodeCallbackHdrSqs`);
|
|
223
|
+
}
|
|
218
224
|
if (plan.hasComplete) {
|
|
219
225
|
crudSqsQueues.push(`${action}CompleteHdrSqs`);
|
|
220
226
|
}
|
|
@@ -474,6 +480,9 @@ function createResourceState({ objects, relationships, flows }) {
|
|
|
474
480
|
flow.initiateBy === 'systemWithUser'))) &&
|
|
475
481
|
upperFirst(flow.flowTag) === topic
|
|
476
482
|
) ||
|
|
483
|
+
(objects.length > 0 &&
|
|
484
|
+
topic !== 'Get' &&
|
|
485
|
+
CRUD_ACTIONS.includes(topic)) ||
|
|
477
486
|
(relationships.length > 0 &&
|
|
478
487
|
topic !== 'GetRelationship' &&
|
|
479
488
|
RELATIONSHIP_ACTIONS.includes(topic))
|
|
@@ -722,7 +731,10 @@ function roleStatements(state, roleName) {
|
|
|
722
731
|
addStatement(
|
|
723
732
|
statements,
|
|
724
733
|
['sns:Publish'],
|
|
725
|
-
|
|
734
|
+
[
|
|
735
|
+
...topicArns(roleTopicNames(state, roleName)),
|
|
736
|
+
...roleExternalTopicArns(state, roleName)
|
|
737
|
+
]
|
|
726
738
|
);
|
|
727
739
|
if (state.webSocketRoles.has(roleName)) {
|
|
728
740
|
statements.push({
|
|
@@ -760,6 +772,7 @@ function roleTableNames(state, roleName) {
|
|
|
760
772
|
...(state.rbac
|
|
761
773
|
? [
|
|
762
774
|
'TargetRoles',
|
|
775
|
+
'RolePermissions',
|
|
763
776
|
'RoleUsers',
|
|
764
777
|
'UserRoles',
|
|
765
778
|
'RoleLimit',
|
|
@@ -832,6 +845,9 @@ function roleQueueNames(state, roleName) {
|
|
|
832
845
|
if (!plan.outputTopic) return [];
|
|
833
846
|
return [
|
|
834
847
|
`${action}HdrSqs`,
|
|
848
|
+
...(plan.hasCreateNodeCallback
|
|
849
|
+
? [`${action}NodeCallbackHdrSqs`]
|
|
850
|
+
: []),
|
|
835
851
|
...(plan.hasRbacCallback
|
|
836
852
|
? [`${action}RbacCallbackHdrSqs`]
|
|
837
853
|
: []),
|
|
@@ -916,6 +932,9 @@ function roleTopicNames(state, roleName) {
|
|
|
916
932
|
topics.push(`${action}_In`, `${action}_Out`);
|
|
917
933
|
}
|
|
918
934
|
}
|
|
935
|
+
if (state.rbac) {
|
|
936
|
+
topics.push('ValidatorRbac_In');
|
|
937
|
+
}
|
|
919
938
|
return topics;
|
|
920
939
|
}
|
|
921
940
|
if (roleName === 'FlowRelationshipRole') {
|
|
@@ -928,6 +947,9 @@ function roleTopicNames(state, roleName) {
|
|
|
928
947
|
topics.push(`${action}_In`, `${action}_Out`);
|
|
929
948
|
}
|
|
930
949
|
}
|
|
950
|
+
if (state.rbac) {
|
|
951
|
+
topics.push('ValidatorRbac_In');
|
|
952
|
+
}
|
|
931
953
|
return topics;
|
|
932
954
|
}
|
|
933
955
|
if (roleName === 'FlowRbacRole') {
|
|
@@ -1014,6 +1036,34 @@ function roleExternalDynamoTables(state, roleName) {
|
|
|
1014
1036
|
return [];
|
|
1015
1037
|
}
|
|
1016
1038
|
|
|
1039
|
+
function roleExternalTopicArns(state, roleName) {
|
|
1040
|
+
if (!state.rbac) {
|
|
1041
|
+
return [];
|
|
1042
|
+
}
|
|
1043
|
+
const extraTags = getRbacValidatorServiceTags(state).filter(
|
|
1044
|
+
(tag) => tag && tag !== 'Rbac'
|
|
1045
|
+
);
|
|
1046
|
+
const extraTopics = extraTags.map(
|
|
1047
|
+
(tag) =>
|
|
1048
|
+
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:${tag}_\${self:custom.iz_stage}_ValidatorRbac_In`
|
|
1049
|
+
);
|
|
1050
|
+
if (roleName === 'FlowObjectsRole') {
|
|
1051
|
+
return [
|
|
1052
|
+
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_ValidatorRbac_In`,
|
|
1053
|
+
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_ConfirmUsage_In`,
|
|
1054
|
+
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_CancelUsage_In`,
|
|
1055
|
+
...extraTopics
|
|
1056
|
+
];
|
|
1057
|
+
}
|
|
1058
|
+
if (roleName === 'FlowRbacRole' || roleName === 'FlowRelationshipRole') {
|
|
1059
|
+
return [
|
|
1060
|
+
`arn:aws:sns:\${self:custom.iz_region}:\${self:custom.iz_accountId}:Rbac_\${self:custom.iz_stage}_ValidatorRbac_In`,
|
|
1061
|
+
...extraTopics
|
|
1062
|
+
];
|
|
1063
|
+
}
|
|
1064
|
+
return [];
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1017
1067
|
function queueArns(queueNames) {
|
|
1018
1068
|
return [...new Set(queueNames)]
|
|
1019
1069
|
.sort()
|