@backstage-community/plugin-rbac-backend 5.5.1 → 5.5.3
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/CHANGELOG.md +14 -0
- package/dist/audit-log/audit-logger.cjs.js.map +1 -1
- package/dist/file-permissions/csv-file-watcher.cjs.js +226 -151
- package/dist/file-permissions/csv-file-watcher.cjs.js.map +1 -1
- package/dist/providers/connect-providers.cjs.js +3 -4
- package/dist/providers/connect-providers.cjs.js.map +1 -1
- package/dist/validation/policies-validation.cjs.js +1 -2
- package/dist/validation/policies-validation.cjs.js.map +1 -1
- package/package.json +2 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
### Dependencies
|
|
2
2
|
|
|
3
|
+
## 5.5.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 973a5ef: remove prettier from devDevpendencies
|
|
8
|
+
- Updated dependencies [973a5ef]
|
|
9
|
+
- @backstage-community/plugin-rbac-node@1.9.1
|
|
10
|
+
|
|
11
|
+
## 5.5.2
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 9aa839a: Fixes two issues that were impact the performance, the first was that we were individually adding and removing roles and the second was we were removing all policies and roles regardless of whether they should actually be removed.
|
|
16
|
+
|
|
3
17
|
## 5.5.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-logger.cjs.js","sources":["../../src/audit-log/audit-logger.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n AuthorizeResult,\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport type { PolicyQuery } from '@backstage/plugin-permission-node';\n\nimport type { AuditLogOptions } from '@janus-idp/backstage-plugin-audit-log-node';\n\nimport {\n PermissionAction,\n RoleConditionalPolicyDecision,\n Source,\n toPermissionAction,\n} from '@backstage-community/plugin-rbac-common';\n\nexport const RoleEvents = {\n CREATE_ROLE: 'CreateRole',\n UPDATE_ROLE: 'UpdateRole',\n DELETE_ROLE: 'DeleteRole',\n CREATE_OR_UPDATE_ROLE: 'CreateOrUpdateRole',\n GET_ROLE: 'GetRole',\n\n CREATE_ROLE_ERROR: 'CreateRoleError',\n UPDATE_ROLE_ERROR: 'UpdateRoleError',\n DELETE_ROLE_ERROR: 'DeleteRoleError',\n GET_ROLE_ERROR: 'GetRoleError',\n} as const;\n\nexport const PermissionEvents = {\n CREATE_POLICY: 'CreatePolicy',\n UPDATE_POLICY: 'UpdatePolicy',\n DELETE_POLICY: 'DeletePolicy',\n GET_POLICY: 'GetPolicy',\n\n CREATE_POLICY_ERROR: 'CreatePolicyError',\n UPDATE_POLICY_ERROR: 'UpdatePolicyError',\n DELETE_POLICY_ERROR: 'DeletePolicyError',\n GET_POLICY_ERROR: 'GetPolicyError',\n} as const;\n\nexport type RoleAuditInfo = {\n roleEntityRef: string;\n description?: string;\n source: Source;\n\n members: string[];\n};\n\nexport type PermissionAuditInfo = {\n policies: string[][];\n source: Source;\n};\n\nexport const EvaluationEvents = {\n PERMISSION_EVALUATION_STARTED: 'PermissionEvaluationStarted',\n PERMISSION_EVALUATION_COMPLETED: 'PermissionEvaluationCompleted',\n CONDITION_EVALUATION_COMPLETED: 'ConditionEvaluationCompleted',\n PERMISSION_EVALUATION_FAILED: 'PermissionEvaluationFailed',\n} as const;\n\nexport const ListPluginPoliciesEvents = {\n GET_PLUGINS_POLICIES: 'GetPluginsPolicies',\n GET_PLUGINS_POLICIES_ERROR: 'GetPluginsPoliciesError',\n};\n\nexport const ListConditionEvents = {\n GET_CONDITION_RULES: 'GetConditionRules',\n GET_CONDITION_RULES_ERROR: 'GetConditionRulesError',\n};\n\nexport type EvaluationAuditInfo = {\n userEntityRef: string;\n permissionName: string;\n action: PermissionAction;\n resourceType?: string;\n decision?: PolicyDecision;\n};\n\nexport const PoliciesData = {\n FAILED_TO_FETCH_NEWER_PERMISSIONS: 'FailedToFetchNewerPermissions',\n};\n\nexport const ConditionEvents = {\n CREATE_CONDITION: 'CreateCondition',\n UPDATE_CONDITION: 'UpdateCondition',\n DELETE_CONDITION: 'DeleteCondition',\n GET_CONDITION: 'GetCondition',\n\n CREATE_CONDITION_ERROR: 'CreateConditionError',\n UPDATE_CONDITION_ERROR: 'UpdateConditionError',\n DELETE_CONDITION_ERROR: 'DeleteConditionError',\n GET_CONDITION_ERROR: 'GetConditionError',\n PARSE_CONDITION_ERROR: 'ParseConditionError',\n CHANGE_CONDITIONAL_POLICIES_FILE_ERROR: 'ChangeConditionalPoliciesError',\n CONDITIONAL_POLICIES_FILE_NOT_FOUND: 'ConditionalPoliciesFileNotFound',\n};\n\nexport type ConditionAuditInfo = {\n condition: RoleConditionalPolicyDecision<PermissionAction>;\n};\n\nexport const RBAC_BACKEND = 'rbac-backend';\n\n// Audit log stage for processing Role-Based Access Control (RBAC) data\nexport const HANDLE_RBAC_DATA_STAGE = 'handleRBACData';\n\n// Audit log stage to refresh Role-Based Access Control (RBAC) data\nexport const FETCH_NEWER_PERMISSIONS_STAGE = 'fetchNewerPermissions';\n\n// Audit log stage for determining access rights based on user permissions and resource information\nexport const EVALUATE_PERMISSION_ACCESS_STAGE = 'evaluatePermissionAccess';\n\n// Audit log stage for sending the response to the client about handled permission policies, roles, and condition policies\nexport const SEND_RESPONSE_STAGE = 'sendResponse';\nexport const RESPONSE_ERROR = 'responseError';\n\nexport function createPermissionEvaluationOptions(\n message: string,\n userEntityRef: string,\n request: PolicyQuery,\n policyDecision?: PolicyDecision,\n): AuditLogOptions<EvaluationAuditInfo> {\n const auditInfo: EvaluationAuditInfo = {\n userEntityRef,\n permissionName: request.permission.name,\n action: toPermissionAction(request.permission.attributes),\n };\n\n const resourceType = (request.permission as ResourcePermission).resourceType;\n if (resourceType) {\n auditInfo.resourceType = resourceType;\n }\n\n let eventName;\n if (!policyDecision) {\n eventName = EvaluationEvents.PERMISSION_EVALUATION_STARTED;\n } else {\n auditInfo.decision = policyDecision;\n\n switch (policyDecision.result) {\n case AuthorizeResult.DENY:\n case AuthorizeResult.ALLOW:\n eventName = EvaluationEvents.PERMISSION_EVALUATION_COMPLETED;\n break;\n case AuthorizeResult.CONDITIONAL:\n eventName = EvaluationEvents.CONDITION_EVALUATION_COMPLETED;\n break;\n default:\n throw new Error('Unknown policy decision result');\n }\n }\n\n return {\n actorId: userEntityRef,\n message,\n eventName,\n metadata: auditInfo,\n stage: EVALUATE_PERMISSION_ACCESS_STAGE,\n status: 'succeeded',\n };\n}\n"],"names":["toPermissionAction","AuthorizeResult"],"mappings":";;;;;AA+BO,MAAM,UAAa,GAAA;AAAA,EACxB,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA,YAAA;AAAA,EACb,WAAa,EAAA,YAAA;AAAA,EACb,qBAAuB,EAAA,oBAAA;AAAA,EACvB,QAAU,EAAA,SAAA;AAAA,EAEV,iBAAmB,EAAA,iBAAA;AAAA,EACnB,iBAAmB,EAAA,iBAAA;AAAA,EACnB,iBAAmB,EAAA,iBAAA;AAAA,EACnB,cAAgB,EAAA;AAClB;AAEO,MAAM,gBAAmB,GAAA;AAAA,EAC9B,aAAe,EAAA,cAAA;AAAA,EACf,aAAe,EAAA,cAAA;AAAA,EACf,aAAe,EAAA,cAAA;AAAA,EACf,UAAY,EAAA,WAAA;AAAA,EAEZ,mBAAqB,EAAA,mBAAA;AAAA,EACrB,mBAAqB,EAAA,mBAAA;AAAA,EACrB,mBAAqB,EAAA,mBAAA;AAAA,EACrB,gBAAkB,EAAA;AACpB;
|
|
1
|
+
{"version":3,"file":"audit-logger.cjs.js","sources":["../../src/audit-log/audit-logger.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n AuthorizeResult,\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport type { PolicyQuery } from '@backstage/plugin-permission-node';\n\nimport type { AuditLogOptions } from '@janus-idp/backstage-plugin-audit-log-node';\n\nimport {\n PermissionAction,\n RoleConditionalPolicyDecision,\n Source,\n toPermissionAction,\n} from '@backstage-community/plugin-rbac-common';\n\nexport const RoleEvents = {\n CREATE_ROLE: 'CreateRole',\n UPDATE_ROLE: 'UpdateRole',\n DELETE_ROLE: 'DeleteRole',\n CREATE_OR_UPDATE_ROLE: 'CreateOrUpdateRole',\n GET_ROLE: 'GetRole',\n\n CREATE_ROLE_ERROR: 'CreateRoleError',\n UPDATE_ROLE_ERROR: 'UpdateRoleError',\n DELETE_ROLE_ERROR: 'DeleteRoleError',\n GET_ROLE_ERROR: 'GetRoleError',\n} as const;\n\nexport const PermissionEvents = {\n CREATE_POLICY: 'CreatePolicy',\n UPDATE_POLICY: 'UpdatePolicy',\n DELETE_POLICY: 'DeletePolicy',\n GET_POLICY: 'GetPolicy',\n\n CREATE_POLICY_ERROR: 'CreatePolicyError',\n UPDATE_POLICY_ERROR: 'UpdatePolicyError',\n DELETE_POLICY_ERROR: 'DeletePolicyError',\n GET_POLICY_ERROR: 'GetPolicyError',\n} as const;\n\nexport type RoleAuditInfo = {\n roleEntityRef: string;\n description?: string;\n source: Source;\n\n members: string[];\n};\n\nexport type RolesAuditInfo = {\n groupingPolicies: string[][];\n source: Source;\n};\n\nexport type PermissionAuditInfo = {\n policies: string[][];\n source: Source;\n};\n\nexport const EvaluationEvents = {\n PERMISSION_EVALUATION_STARTED: 'PermissionEvaluationStarted',\n PERMISSION_EVALUATION_COMPLETED: 'PermissionEvaluationCompleted',\n CONDITION_EVALUATION_COMPLETED: 'ConditionEvaluationCompleted',\n PERMISSION_EVALUATION_FAILED: 'PermissionEvaluationFailed',\n} as const;\n\nexport const ListPluginPoliciesEvents = {\n GET_PLUGINS_POLICIES: 'GetPluginsPolicies',\n GET_PLUGINS_POLICIES_ERROR: 'GetPluginsPoliciesError',\n};\n\nexport const ListConditionEvents = {\n GET_CONDITION_RULES: 'GetConditionRules',\n GET_CONDITION_RULES_ERROR: 'GetConditionRulesError',\n};\n\nexport type EvaluationAuditInfo = {\n userEntityRef: string;\n permissionName: string;\n action: PermissionAction;\n resourceType?: string;\n decision?: PolicyDecision;\n};\n\nexport const PoliciesData = {\n FAILED_TO_FETCH_NEWER_PERMISSIONS: 'FailedToFetchNewerPermissions',\n};\n\nexport const ConditionEvents = {\n CREATE_CONDITION: 'CreateCondition',\n UPDATE_CONDITION: 'UpdateCondition',\n DELETE_CONDITION: 'DeleteCondition',\n GET_CONDITION: 'GetCondition',\n\n CREATE_CONDITION_ERROR: 'CreateConditionError',\n UPDATE_CONDITION_ERROR: 'UpdateConditionError',\n DELETE_CONDITION_ERROR: 'DeleteConditionError',\n GET_CONDITION_ERROR: 'GetConditionError',\n PARSE_CONDITION_ERROR: 'ParseConditionError',\n CHANGE_CONDITIONAL_POLICIES_FILE_ERROR: 'ChangeConditionalPoliciesError',\n CONDITIONAL_POLICIES_FILE_NOT_FOUND: 'ConditionalPoliciesFileNotFound',\n};\n\nexport type ConditionAuditInfo = {\n condition: RoleConditionalPolicyDecision<PermissionAction>;\n};\n\nexport const RBAC_BACKEND = 'rbac-backend';\n\n// Audit log stage for processing Role-Based Access Control (RBAC) data\nexport const HANDLE_RBAC_DATA_STAGE = 'handleRBACData';\n\n// Audit log stage to refresh Role-Based Access Control (RBAC) data\nexport const FETCH_NEWER_PERMISSIONS_STAGE = 'fetchNewerPermissions';\n\n// Audit log stage for determining access rights based on user permissions and resource information\nexport const EVALUATE_PERMISSION_ACCESS_STAGE = 'evaluatePermissionAccess';\n\n// Audit log stage for sending the response to the client about handled permission policies, roles, and condition policies\nexport const SEND_RESPONSE_STAGE = 'sendResponse';\nexport const RESPONSE_ERROR = 'responseError';\n\nexport function createPermissionEvaluationOptions(\n message: string,\n userEntityRef: string,\n request: PolicyQuery,\n policyDecision?: PolicyDecision,\n): AuditLogOptions<EvaluationAuditInfo> {\n const auditInfo: EvaluationAuditInfo = {\n userEntityRef,\n permissionName: request.permission.name,\n action: toPermissionAction(request.permission.attributes),\n };\n\n const resourceType = (request.permission as ResourcePermission).resourceType;\n if (resourceType) {\n auditInfo.resourceType = resourceType;\n }\n\n let eventName;\n if (!policyDecision) {\n eventName = EvaluationEvents.PERMISSION_EVALUATION_STARTED;\n } else {\n auditInfo.decision = policyDecision;\n\n switch (policyDecision.result) {\n case AuthorizeResult.DENY:\n case AuthorizeResult.ALLOW:\n eventName = EvaluationEvents.PERMISSION_EVALUATION_COMPLETED;\n break;\n case AuthorizeResult.CONDITIONAL:\n eventName = EvaluationEvents.CONDITION_EVALUATION_COMPLETED;\n break;\n default:\n throw new Error('Unknown policy decision result');\n }\n }\n\n return {\n actorId: userEntityRef,\n message,\n eventName,\n metadata: auditInfo,\n stage: EVALUATE_PERMISSION_ACCESS_STAGE,\n status: 'succeeded',\n };\n}\n"],"names":["toPermissionAction","AuthorizeResult"],"mappings":"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"}
|
|
@@ -20,9 +20,9 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
|
|
|
20
20
|
this.currentContent = [];
|
|
21
21
|
this.csvFilePolicies = {
|
|
22
22
|
addedPolicies: [],
|
|
23
|
-
addedGroupPolicies: [],
|
|
24
23
|
removedPolicies: [],
|
|
25
|
-
|
|
24
|
+
addedGroupPolicies: /* @__PURE__ */ new Map(),
|
|
25
|
+
removedGroupPolicies: /* @__PURE__ */ new Map()
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
currentContent;
|
|
@@ -62,21 +62,21 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
|
|
|
62
62
|
casbin.newModelFromString(permissionModel.MODEL),
|
|
63
63
|
new lowercaseFileAdapter.LowercaseFileAdapter(this.filePath)
|
|
64
64
|
);
|
|
65
|
-
await this.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
await this.filterPoliciesAndRoles(
|
|
66
|
+
this.enforcer,
|
|
67
|
+
tempEnforcer,
|
|
68
|
+
this.csvFilePolicies.removedPolicies,
|
|
69
|
+
this.csvFilePolicies.removedGroupPolicies,
|
|
70
|
+
true
|
|
71
|
+
);
|
|
72
|
+
await this.filterPoliciesAndRoles(
|
|
73
|
+
tempEnforcer,
|
|
74
|
+
this.enforcer,
|
|
75
|
+
this.csvFilePolicies.addedPolicies,
|
|
76
|
+
this.csvFilePolicies.addedGroupPolicies
|
|
77
|
+
);
|
|
78
78
|
await this.migrateLegacyMetadata(tempEnforcer);
|
|
79
|
-
await this.updatePolicies(content
|
|
79
|
+
await this.updatePolicies(content);
|
|
80
80
|
if (this.allowReload) {
|
|
81
81
|
this.watchFile();
|
|
82
82
|
}
|
|
@@ -136,33 +136,12 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
|
|
|
136
136
|
const newFlatContent = newContent.flatMap((data) => {
|
|
137
137
|
return helper.policyToString(data);
|
|
138
138
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
diffRemoved.forEach((policy) => {
|
|
146
|
-
const convertedPolicy = helper.metadataStringToPolicy(policy);
|
|
147
|
-
if (convertedPolicy[0] === "p") {
|
|
148
|
-
convertedPolicy.splice(0, 1);
|
|
149
|
-
this.csvFilePolicies.removedPolicies.push(convertedPolicy);
|
|
150
|
-
} else if (convertedPolicy[0] === "g") {
|
|
151
|
-
convertedPolicy.splice(0, 1);
|
|
152
|
-
this.csvFilePolicies.removedGroupPolicies.push(convertedPolicy);
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
diffAdded.forEach((policy) => {
|
|
156
|
-
const convertedPolicy = helper.metadataStringToPolicy(policy);
|
|
157
|
-
if (convertedPolicy[0] === "p") {
|
|
158
|
-
convertedPolicy.splice(0, 1);
|
|
159
|
-
this.csvFilePolicies.addedPolicies.push(convertedPolicy);
|
|
160
|
-
} else if (convertedPolicy[0] === "g") {
|
|
161
|
-
convertedPolicy.splice(0, 1);
|
|
162
|
-
this.csvFilePolicies.addedGroupPolicies.push(convertedPolicy);
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
await this.updatePolicies(newContent, tempEnforcer);
|
|
139
|
+
await this.findFileContentDiff(
|
|
140
|
+
currentFlatContent,
|
|
141
|
+
newFlatContent,
|
|
142
|
+
tempEnforcer
|
|
143
|
+
);
|
|
144
|
+
await this.updatePolicies(newContent);
|
|
166
145
|
}
|
|
167
146
|
/**
|
|
168
147
|
* updatePolicies is used to update all of the permission policies and roles within a CSV file.
|
|
@@ -170,69 +149,38 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
|
|
|
170
149
|
* methods for these.
|
|
171
150
|
* It will also update the current contents of the CSV file to the most recent
|
|
172
151
|
* @param newContent The new content present in the CSV file
|
|
173
|
-
* @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies
|
|
174
152
|
*/
|
|
175
|
-
async updatePolicies(newContent
|
|
153
|
+
async updatePolicies(newContent) {
|
|
176
154
|
this.currentContent = newContent;
|
|
177
155
|
if (this.csvFilePolicies.addedPolicies.length > 0)
|
|
178
|
-
await this.addPermissionPolicies(
|
|
156
|
+
await this.addPermissionPolicies();
|
|
179
157
|
if (this.csvFilePolicies.removedPolicies.length > 0)
|
|
180
158
|
await this.removePermissionPolicies();
|
|
181
|
-
if (this.csvFilePolicies.addedGroupPolicies.
|
|
182
|
-
|
|
183
|
-
if (this.csvFilePolicies.removedGroupPolicies.length > 0)
|
|
159
|
+
if (this.csvFilePolicies.addedGroupPolicies.size > 0) await this.addRoles();
|
|
160
|
+
if (this.csvFilePolicies.removedGroupPolicies.size > 0)
|
|
184
161
|
await this.removeRoles();
|
|
185
162
|
}
|
|
186
163
|
/**
|
|
187
164
|
* addPermissionPolicies will add the new permission policies that are present in the CSV file.
|
|
188
|
-
* We will attempt to validate the permission policy and log any warnings that are encountered.
|
|
189
|
-
* If a warning is encountered, we will skip adding the permission policy to the enforcer.
|
|
190
|
-
* @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies
|
|
191
165
|
*/
|
|
192
|
-
async addPermissionPolicies(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
);
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
err = await policiesValidation.checkForDuplicatePolicies(
|
|
213
|
-
tempEnforcer,
|
|
214
|
-
policy,
|
|
215
|
-
this.filePath
|
|
166
|
+
async addPermissionPolicies() {
|
|
167
|
+
try {
|
|
168
|
+
await this.enforcer.addPolicies(this.csvFilePolicies.addedPolicies);
|
|
169
|
+
await this.auditLogger.auditLog({
|
|
170
|
+
actorId: auditLogger.RBAC_BACKEND,
|
|
171
|
+
message: `Created policies`,
|
|
172
|
+
eventName: auditLogger.PermissionEvents.CREATE_POLICY,
|
|
173
|
+
metadata: {
|
|
174
|
+
policies: this.csvFilePolicies.addedPolicies,
|
|
175
|
+
source: "csv-file"
|
|
176
|
+
},
|
|
177
|
+
stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
|
|
178
|
+
status: "succeeded"
|
|
179
|
+
});
|
|
180
|
+
} catch (e) {
|
|
181
|
+
this.logger.warn(
|
|
182
|
+
`Failed to add or update policies ${this.csvFilePolicies.addedPolicies} after modification ${this.filePath}. Cause: ${e}`
|
|
216
183
|
);
|
|
217
|
-
if (err) {
|
|
218
|
-
this.logger.warn(err.message);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
try {
|
|
222
|
-
await this.enforcer.addPolicy(policy);
|
|
223
|
-
await this.auditLogger.auditLog({
|
|
224
|
-
actorId: auditLogger.RBAC_BACKEND,
|
|
225
|
-
message: `Created policy`,
|
|
226
|
-
eventName: auditLogger.PermissionEvents.CREATE_POLICY,
|
|
227
|
-
metadata: { policies: [policy], source: "csv-file" },
|
|
228
|
-
stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
|
|
229
|
-
status: "succeeded"
|
|
230
|
-
});
|
|
231
|
-
} catch (e) {
|
|
232
|
-
this.logger.warn(
|
|
233
|
-
`Failed to add or update policy ${policy} after modification ${this.filePath}. Cause: ${e}`
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
184
|
}
|
|
237
185
|
this.csvFilePolicies.addedPolicies = [];
|
|
238
186
|
}
|
|
@@ -264,60 +212,50 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
|
|
|
264
212
|
}
|
|
265
213
|
/**
|
|
266
214
|
* addRoles will add the new roles that are present in the CSV file.
|
|
267
|
-
* We will attempt to validate the role and log any warnings that are encountered.
|
|
268
|
-
* If a warning is encountered, we will skip adding the role to the enforcer.
|
|
269
|
-
* @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies
|
|
270
215
|
*/
|
|
271
|
-
async addRoles(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
);
|
|
278
|
-
if (err) {
|
|
279
|
-
this.logger.warn(
|
|
280
|
-
`${err.message}, error originates from file ${this.filePath}`
|
|
281
|
-
);
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
err = await policiesValidation.checkForDuplicateGroupPolicies(
|
|
285
|
-
tempEnforcer,
|
|
286
|
-
groupPolicy,
|
|
287
|
-
this.filePath
|
|
288
|
-
);
|
|
289
|
-
if (err) {
|
|
290
|
-
this.logger.warn(err.message);
|
|
291
|
-
continue;
|
|
292
|
-
}
|
|
216
|
+
async addRoles() {
|
|
217
|
+
const addedPolicies = [];
|
|
218
|
+
const updatedPolicies = [];
|
|
219
|
+
for (const [key, value] of this.csvFilePolicies.addedGroupPolicies) {
|
|
220
|
+
const groupPolicies = value.map((member) => {
|
|
221
|
+
return [member, key];
|
|
222
|
+
});
|
|
293
223
|
try {
|
|
294
224
|
const roleMetadata = {
|
|
295
225
|
source: "csv-file",
|
|
296
|
-
roleEntityRef:
|
|
226
|
+
roleEntityRef: key,
|
|
297
227
|
author: CSV_PERMISSION_POLICY_FILE_AUTHOR,
|
|
298
228
|
modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR
|
|
299
229
|
};
|
|
300
230
|
const currentMetadata = await this.roleMetadataStorage.findRoleMetadata(
|
|
301
231
|
roleMetadata.roleEntityRef
|
|
302
232
|
);
|
|
303
|
-
await this.enforcer.
|
|
233
|
+
await this.enforcer.addGroupingPolicies(groupPolicies, roleMetadata);
|
|
304
234
|
const eventName = currentMetadata ? auditLogger.RoleEvents.UPDATE_ROLE : auditLogger.RoleEvents.CREATE_ROLE;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
metadata: { ...roleMetadata, members: [groupPolicy[0]] },
|
|
311
|
-
stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
|
|
312
|
-
status: "succeeded"
|
|
313
|
-
});
|
|
235
|
+
if (eventName === auditLogger.RoleEvents.UPDATE_ROLE) {
|
|
236
|
+
updatedPolicies.push(...groupPolicies);
|
|
237
|
+
} else {
|
|
238
|
+
addedPolicies.push(...groupPolicies);
|
|
239
|
+
}
|
|
314
240
|
} catch (e) {
|
|
315
241
|
this.logger.warn(
|
|
316
|
-
`Failed to add or update group policy ${
|
|
242
|
+
`Failed to add or update group policy ${groupPolicies} after modification ${this.filePath}. Cause: ${e}`
|
|
317
243
|
);
|
|
318
244
|
}
|
|
319
245
|
}
|
|
320
|
-
|
|
246
|
+
if (updatedPolicies.length > 0)
|
|
247
|
+
await this.logRoleEvent(
|
|
248
|
+
auditLogger.RoleEvents.UPDATE_ROLE,
|
|
249
|
+
"Updated roles",
|
|
250
|
+
updatedPolicies
|
|
251
|
+
);
|
|
252
|
+
if (addedPolicies.length > 0)
|
|
253
|
+
await this.logRoleEvent(
|
|
254
|
+
auditLogger.RoleEvents.CREATE_ROLE,
|
|
255
|
+
"Created roles",
|
|
256
|
+
addedPolicies
|
|
257
|
+
);
|
|
258
|
+
this.csvFilePolicies.addedGroupPolicies = /* @__PURE__ */ new Map();
|
|
321
259
|
}
|
|
322
260
|
/**
|
|
323
261
|
* removeRoles will remove the roles that are no longer present in the CSV file.
|
|
@@ -325,51 +263,60 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
|
|
|
325
263
|
* Otherwise, we will remove the role completely.
|
|
326
264
|
*/
|
|
327
265
|
async removeRoles() {
|
|
328
|
-
for (const
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
);
|
|
266
|
+
for (const [key, value] of this.csvFilePolicies.removedGroupPolicies) {
|
|
267
|
+
const isUpdate = await this.enforcer.getFilteredGroupingPolicy(1, key);
|
|
268
|
+
const groupPolicies = value.map((member) => {
|
|
269
|
+
return [member, key];
|
|
270
|
+
});
|
|
334
271
|
try {
|
|
335
|
-
const
|
|
272
|
+
const roleMetadata = {
|
|
336
273
|
source: "csv-file",
|
|
337
|
-
roleEntityRef,
|
|
274
|
+
roleEntityRef: key,
|
|
338
275
|
author: CSV_PERMISSION_POLICY_FILE_AUTHOR,
|
|
339
276
|
modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR
|
|
340
277
|
};
|
|
341
|
-
await this.enforcer.
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
isUpdate.length > 1
|
|
278
|
+
await this.enforcer.removeGroupingPolicies(
|
|
279
|
+
groupPolicies,
|
|
280
|
+
roleMetadata,
|
|
281
|
+
isUpdate.length > 1 && isUpdate.length !== groupPolicies.length
|
|
282
|
+
);
|
|
283
|
+
const isRolePresent = await this.roleMetadataStorage.findRoleMetadata(
|
|
284
|
+
roleMetadata.roleEntityRef
|
|
345
285
|
);
|
|
346
|
-
const isRolePresent = await this.roleMetadataStorage.findRoleMetadata(roleEntityRef);
|
|
347
286
|
const eventName = isRolePresent ? auditLogger.RoleEvents.UPDATE_ROLE : auditLogger.RoleEvents.DELETE_ROLE;
|
|
348
287
|
const message = isRolePresent ? "Updated role: deleted members" : "Deleted role";
|
|
349
288
|
await this.auditLogger.auditLog({
|
|
350
289
|
actorId: auditLogger.RBAC_BACKEND,
|
|
351
290
|
message,
|
|
352
291
|
eventName,
|
|
353
|
-
metadata: { ...
|
|
292
|
+
metadata: { ...roleMetadata, members: value },
|
|
354
293
|
stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
|
|
355
294
|
status: "succeeded"
|
|
356
295
|
});
|
|
357
296
|
} catch (e) {
|
|
358
297
|
this.logger.warn(
|
|
359
|
-
`Failed to remove group policy ${
|
|
298
|
+
`Failed to remove group policy ${groupPolicies} after modification ${this.filePath}. Cause: ${e}`
|
|
360
299
|
);
|
|
361
300
|
}
|
|
362
301
|
}
|
|
363
|
-
this.csvFilePolicies.removedGroupPolicies =
|
|
302
|
+
this.csvFilePolicies.removedGroupPolicies = /* @__PURE__ */ new Map();
|
|
364
303
|
}
|
|
365
304
|
async cleanUpRolesAndPolicies() {
|
|
366
305
|
const roleMetadatas = await this.roleMetadataStorage.filterRoleMetadata("csv-file");
|
|
367
306
|
const fileRoles = roleMetadatas.map((meta) => meta.roleEntityRef);
|
|
368
307
|
if (fileRoles.length > 0) {
|
|
369
308
|
for (const fileRole of fileRoles) {
|
|
370
|
-
this.
|
|
371
|
-
|
|
309
|
+
const filteredPolicies = await this.enforcer.getFilteredGroupingPolicy(
|
|
310
|
+
1,
|
|
311
|
+
fileRole
|
|
372
312
|
);
|
|
313
|
+
for (const groupPolicy of filteredPolicies) {
|
|
314
|
+
this.addGroupPolicyToMap(
|
|
315
|
+
this.csvFilePolicies.removedGroupPolicies,
|
|
316
|
+
groupPolicy[1],
|
|
317
|
+
groupPolicy[0]
|
|
318
|
+
);
|
|
319
|
+
}
|
|
373
320
|
this.csvFilePolicies.removedPolicies.push(
|
|
374
321
|
...await this.enforcer.getFilteredPolicy(0, fileRole)
|
|
375
322
|
);
|
|
@@ -378,6 +325,134 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
|
|
|
378
325
|
await this.removePermissionPolicies();
|
|
379
326
|
await this.removeRoles();
|
|
380
327
|
}
|
|
328
|
+
async filterPoliciesAndRoles(enforcerOne, enforcerTwo, policies, groupPolicies, remove) {
|
|
329
|
+
const policiesToEdit = await enforcerOne.getPolicy();
|
|
330
|
+
const groupPoliciesToEdit = await enforcerOne.getGroupingPolicy();
|
|
331
|
+
for (const policy of policiesToEdit) {
|
|
332
|
+
if (!await enforcerTwo.hasPolicy(...policy) && await this.validateAddedPolicy(
|
|
333
|
+
policy,
|
|
334
|
+
enforcerOne,
|
|
335
|
+
remove
|
|
336
|
+
)) {
|
|
337
|
+
policies.push(policy);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
for (const groupPolicy of groupPoliciesToEdit) {
|
|
341
|
+
if (!await enforcerTwo.hasGroupingPolicy(...groupPolicy) && await this.validateAddedGroupPolicy(
|
|
342
|
+
groupPolicy,
|
|
343
|
+
enforcerOne,
|
|
344
|
+
remove
|
|
345
|
+
)) {
|
|
346
|
+
this.addGroupPolicyToMap(groupPolicies, groupPolicy[1], groupPolicy[0]);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
async validateAddedPolicy(policy, tempEnforcer, remove) {
|
|
351
|
+
const transformedPolicy = helper.transformArrayToPolicy(policy);
|
|
352
|
+
const metadata = await this.roleMetadataStorage.findRoleMetadata(policy[0]);
|
|
353
|
+
if (remove) {
|
|
354
|
+
return metadata?.source === "csv-file";
|
|
355
|
+
}
|
|
356
|
+
let err = policiesValidation.validatePolicy(transformedPolicy);
|
|
357
|
+
if (err) {
|
|
358
|
+
this.logger.warn(
|
|
359
|
+
`Failed to validate policy from file ${this.filePath}. Cause: ${err.message}`
|
|
360
|
+
);
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
err = await policiesValidation.validateSource("csv-file", metadata);
|
|
364
|
+
if (err) {
|
|
365
|
+
this.logger.warn(
|
|
366
|
+
`Unable to add policy ${policy} from file ${this.filePath}. Cause: ${err.message}`
|
|
367
|
+
);
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
err = await policiesValidation.checkForDuplicatePolicies(tempEnforcer, policy, this.filePath);
|
|
371
|
+
if (err) {
|
|
372
|
+
this.logger.warn(err.message);
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
async validateAddedGroupPolicy(groupPolicy, tempEnforcer, remove) {
|
|
378
|
+
const metadata = await this.roleMetadataStorage.findRoleMetadata(
|
|
379
|
+
groupPolicy[1]
|
|
380
|
+
);
|
|
381
|
+
if (remove) {
|
|
382
|
+
return metadata?.source === "csv-file";
|
|
383
|
+
}
|
|
384
|
+
let err = await policiesValidation.validateGroupingPolicy(groupPolicy, metadata, "csv-file");
|
|
385
|
+
if (err) {
|
|
386
|
+
this.logger.warn(
|
|
387
|
+
`${err.message}, error originates from file ${this.filePath}`
|
|
388
|
+
);
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
err = await policiesValidation.checkForDuplicateGroupPolicies(
|
|
392
|
+
tempEnforcer,
|
|
393
|
+
groupPolicy,
|
|
394
|
+
this.filePath
|
|
395
|
+
);
|
|
396
|
+
if (err) {
|
|
397
|
+
this.logger.warn(err.message);
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
async findFileContentDiff(currentFlatContent, newFlatContent, tempEnforcer) {
|
|
403
|
+
const diffRemoved = lodash.difference(currentFlatContent, newFlatContent);
|
|
404
|
+
const diffAdded = lodash.difference(newFlatContent, currentFlatContent);
|
|
405
|
+
await this.migrateLegacyMetadata(tempEnforcer);
|
|
406
|
+
if (diffRemoved.length === 0 && diffAdded.length === 0) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
diffRemoved.forEach((policy) => {
|
|
410
|
+
const convertedPolicy = helper.metadataStringToPolicy(policy);
|
|
411
|
+
if (convertedPolicy[0] === "p") {
|
|
412
|
+
convertedPolicy.splice(0, 1);
|
|
413
|
+
this.csvFilePolicies.removedPolicies.push(convertedPolicy);
|
|
414
|
+
} else if (convertedPolicy[0] === "g") {
|
|
415
|
+
convertedPolicy.splice(0, 1);
|
|
416
|
+
this.addGroupPolicyToMap(
|
|
417
|
+
this.csvFilePolicies.removedGroupPolicies,
|
|
418
|
+
convertedPolicy[1],
|
|
419
|
+
convertedPolicy[0]
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
for (const policy of diffAdded) {
|
|
424
|
+
const convertedPolicy = helper.metadataStringToPolicy(policy);
|
|
425
|
+
if (convertedPolicy[0] === "p") {
|
|
426
|
+
convertedPolicy.splice(0, 1);
|
|
427
|
+
if (await this.validateAddedPolicy(convertedPolicy, tempEnforcer))
|
|
428
|
+
this.csvFilePolicies.addedPolicies.push(convertedPolicy);
|
|
429
|
+
} else if (convertedPolicy[0] === "g") {
|
|
430
|
+
convertedPolicy.splice(0, 1);
|
|
431
|
+
if (await this.validateAddedGroupPolicy(convertedPolicy, tempEnforcer))
|
|
432
|
+
this.addGroupPolicyToMap(
|
|
433
|
+
this.csvFilePolicies.addedGroupPolicies,
|
|
434
|
+
convertedPolicy[1],
|
|
435
|
+
convertedPolicy[0]
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
addGroupPolicyToMap(groupPolicyMap, key, value) {
|
|
441
|
+
if (!groupPolicyMap.has(key)) {
|
|
442
|
+
groupPolicyMap.set(key, []);
|
|
443
|
+
}
|
|
444
|
+
groupPolicyMap.get(key)?.push(value);
|
|
445
|
+
}
|
|
446
|
+
async logRoleEvent(eventName, message, groupingPolicies) {
|
|
447
|
+
await this.auditLogger.auditLog({
|
|
448
|
+
actorId: auditLogger.RBAC_BACKEND,
|
|
449
|
+
message,
|
|
450
|
+
eventName,
|
|
451
|
+
metadata: { groupingPolicies, source: "csv-file" },
|
|
452
|
+
stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
|
|
453
|
+
status: "succeeded"
|
|
454
|
+
});
|
|
455
|
+
}
|
|
381
456
|
}
|
|
382
457
|
|
|
383
458
|
exports.CSVFileWatcher = CSVFileWatcher;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csv-file-watcher.cjs.js","sources":["../../src/file-permissions/csv-file-watcher.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { LoggerService } from '@backstage/backend-plugin-api';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport { Enforcer, newEnforcer, newModelFromString } from 'casbin';\nimport { parse } from 'csv-parse/sync';\nimport { difference } from 'lodash';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n PermissionAuditInfo,\n PermissionEvents,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from '../audit-log/audit-logger';\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport {\n mergeRoleMetadata,\n metadataStringToPolicy,\n policyToString,\n transformArrayToPolicy,\n transformPolicyGroupToLowercase,\n} from '../helper';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { MODEL } from '../service/permission-model';\nimport {\n checkForDuplicateGroupPolicies,\n checkForDuplicatePolicies,\n validateGroupingPolicy,\n validatePolicy,\n validateSource,\n} from '../validation/policies-validation';\nimport { AbstractFileWatcher } from './file-watcher';\nimport { LowercaseFileAdapter } from './lowercase-file-adapter';\n\nexport const CSV_PERMISSION_POLICY_FILE_AUTHOR = 'csv permission policy file';\n\ntype CSVFilePolicies = {\n addedPolicies: string[][];\n addedGroupPolicies: string[][];\n removedPolicies: string[][];\n removedGroupPolicies: string[][];\n};\n\nexport class CSVFileWatcher extends AbstractFileWatcher<string[][]> {\n private currentContent: string[][];\n private csvFilePolicies: CSVFilePolicies;\n\n constructor(\n filePath: string | undefined,\n allowReload: boolean,\n logger: LoggerService,\n private readonly enforcer: EnforcerDelegate,\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly auditLogger: AuditLogger,\n ) {\n super(filePath, allowReload, logger);\n this.currentContent = [];\n this.csvFilePolicies = {\n addedPolicies: [],\n addedGroupPolicies: [],\n removedPolicies: [],\n removedGroupPolicies: [],\n };\n }\n\n /**\n * parse is used to parse the current contents of the CSV file.\n * @returns The CSV file parsed into a string[][].\n */\n parse(): string[][] {\n const content = this.getCurrentContents();\n const data = parse(content, {\n skip_empty_lines: true,\n relax_column_count: true,\n trim: true,\n });\n\n for (const policy of data) {\n transformPolicyGroupToLowercase(policy);\n }\n\n return data;\n }\n\n /**\n * initialize will initialize the CSV file by loading all of the permission policies and roles into\n * the enforcer.\n * First, we will remove all roles and permission policies if they do not exist in the temporary file enforcer.\n * Next, we will add all roles and permission polices if they are new to the CSV file\n * Finally, we will set the file to be watched if allow reload is set\n * @param csvFileName The name of the csvFile\n * @param allowReload Whether or not we will allow reloads of the CSV file\n */\n async initialize(): Promise<void> {\n if (!this.filePath) {\n return;\n }\n let content: string[][] = [];\n // If the file is set load the file contents\n content = this.parse();\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new LowercaseFileAdapter(this.filePath),\n );\n\n // Check for any old policies that will need to be removed\n await this.cleanUpRolesAndPolicies();\n\n // Check for any new policies that need to be added by checking if\n // the policy does not currently exist in the enforcer\n const policiesToAdd = await tempEnforcer.getPolicy();\n const groupPoliciesToAdd = await tempEnforcer.getGroupingPolicy();\n\n for (const policy of policiesToAdd) {\n if (!(await this.enforcer.hasPolicy(...policy))) {\n this.csvFilePolicies.addedPolicies.push(policy);\n }\n }\n\n for (const groupPolicy of groupPoliciesToAdd) {\n if (!(await this.enforcer.hasGroupingPolicy(...groupPolicy))) {\n this.csvFilePolicies.addedGroupPolicies.push(groupPolicy);\n }\n }\n\n await this.migrateLegacyMetadata(tempEnforcer);\n\n // We pass current here because this is during initialization and it has not changed yet\n await this.updatePolicies(content, tempEnforcer);\n\n if (this.allowReload) {\n this.watchFile();\n }\n }\n\n // Check for policies that might need to be updated\n // This will involve update \"legacy\" source in the role metadata if it exist in both the\n // temp enforcer (csv file) and a role metadata storage.\n // We will update role metadata with the new source \"csv-file\"\n private async migrateLegacyMetadata(tempEnforcer: Enforcer) {\n let legacyRolesMetadata =\n await this.roleMetadataStorage.filterRoleMetadata('legacy');\n const legacyRoles = legacyRolesMetadata.map(meta => meta.roleEntityRef);\n if (legacyRoles.length > 0) {\n const legacyGroupPolicies = await tempEnforcer.getFilteredGroupingPolicy(\n 1,\n ...legacyRoles,\n );\n const legacyPolicies = await tempEnforcer.getFilteredPolicy(\n 0,\n ...legacyRoles,\n );\n const legacyRolesFromFile = new Set([\n ...legacyGroupPolicies.map(gp => gp[1]),\n ...legacyPolicies.map(p => p[0]),\n ]);\n legacyRolesMetadata = legacyRolesMetadata.filter(meta =>\n legacyRolesFromFile.has(meta.roleEntityRef),\n );\n for (const legacyRoleMeta of legacyRolesMetadata) {\n const nonLegacyRole = mergeRoleMetadata(legacyRoleMeta, {\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n source: 'csv-file',\n roleEntityRef: legacyRoleMeta.roleEntityRef,\n });\n await this.roleMetadataStorage.updateRoleMetadata(\n nonLegacyRole,\n legacyRoleMeta.roleEntityRef,\n );\n }\n }\n }\n\n /**\n * onChange is called whenever there is a change to the CSV file.\n * It will parse the current and new contents of the CSV file and process the roles and permission policies present.\n * Afterwards, it will find the difference between the current and new contents of the CSV file\n * and sort them into added / removed, permission policies / roles.\n * It will finally call updatePolicies with the new content.\n */\n async onChange(): Promise<void> {\n const newContent = this.parse();\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new LowercaseFileAdapter(this.filePath!),\n );\n\n const currentFlatContent = this.currentContent.flatMap(data => {\n return policyToString(data);\n });\n const newFlatContent = newContent.flatMap(data => {\n return policyToString(data);\n });\n\n const diffRemoved = difference(currentFlatContent, newFlatContent); // policy was removed\n const diffAdded = difference(newFlatContent, currentFlatContent); // policy was added\n\n await this.migrateLegacyMetadata(tempEnforcer);\n\n if (diffRemoved.length === 0 && diffAdded.length === 0) {\n return;\n }\n\n diffRemoved.forEach(policy => {\n const convertedPolicy = metadataStringToPolicy(policy);\n if (convertedPolicy[0] === 'p') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.removedPolicies.push(convertedPolicy);\n } else if (convertedPolicy[0] === 'g') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.removedGroupPolicies.push(convertedPolicy);\n }\n });\n\n diffAdded.forEach(policy => {\n const convertedPolicy = metadataStringToPolicy(policy);\n if (convertedPolicy[0] === 'p') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.addedPolicies.push(convertedPolicy);\n } else if (convertedPolicy[0] === 'g') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.addedGroupPolicies.push(convertedPolicy);\n }\n });\n\n await this.updatePolicies(newContent, tempEnforcer);\n }\n\n /**\n * updatePolicies is used to update all of the permission policies and roles within a CSV file.\n * It will check the number of added and removed permissions policies and roles and call the appropriate\n * methods for these.\n * It will also update the current contents of the CSV file to the most recent\n * @param newContent The new content present in the CSV file\n * @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies\n */\n private async updatePolicies(\n newContent: string[][],\n tempEnforcer: Enforcer,\n ): Promise<void> {\n this.currentContent = newContent;\n\n if (this.csvFilePolicies.addedPolicies.length > 0)\n await this.addPermissionPolicies(tempEnforcer);\n if (this.csvFilePolicies.removedPolicies.length > 0)\n await this.removePermissionPolicies();\n if (this.csvFilePolicies.addedGroupPolicies.length > 0)\n await this.addRoles(tempEnforcer);\n if (this.csvFilePolicies.removedGroupPolicies.length > 0)\n await this.removeRoles();\n }\n\n /**\n * addPermissionPolicies will add the new permission policies that are present in the CSV file.\n * We will attempt to validate the permission policy and log any warnings that are encountered.\n * If a warning is encountered, we will skip adding the permission policy to the enforcer.\n * @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies\n */\n private async addPermissionPolicies(tempEnforcer: Enforcer): Promise<void> {\n for (const policy of this.csvFilePolicies.addedPolicies) {\n const transformedPolicy = transformArrayToPolicy(policy);\n const metadata = await this.roleMetadataStorage.findRoleMetadata(\n policy[0],\n );\n\n let err = validatePolicy(transformedPolicy);\n if (err) {\n this.logger.warn(\n `Failed to validate policy from file ${this.filePath}. Cause: ${err.message}`,\n );\n continue;\n }\n\n err = await validateSource('csv-file', metadata);\n if (err) {\n this.logger.warn(\n `Unable to add policy ${policy} from file ${this.filePath}. Cause: ${err.message}`,\n );\n continue;\n }\n\n err = await checkForDuplicatePolicies(\n tempEnforcer,\n policy,\n this.filePath!,\n );\n if (err) {\n this.logger.warn(err.message);\n continue;\n }\n try {\n await this.enforcer.addPolicy(policy);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created policy`,\n eventName: PermissionEvents.CREATE_POLICY,\n metadata: { policies: [policy], source: 'csv-file' },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to add or update policy ${policy} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n\n this.csvFilePolicies.addedPolicies = [];\n }\n\n /**\n * removePermissionPolicies will remove the permission policies that are no longer present in the CSV file.\n */\n private async removePermissionPolicies(): Promise<void> {\n try {\n await this.enforcer.removePolicies(this.csvFilePolicies.removedPolicies);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Deleted policies`,\n eventName: PermissionEvents.DELETE_POLICY,\n metadata: {\n policies: this.csvFilePolicies.removedPolicies,\n source: 'csv-file',\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to remove policies ${JSON.stringify(\n this.csvFilePolicies.removedPolicies,\n )} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n this.csvFilePolicies.removedPolicies = [];\n }\n\n /**\n * addRoles will add the new roles that are present in the CSV file.\n * We will attempt to validate the role and log any warnings that are encountered.\n * If a warning is encountered, we will skip adding the role to the enforcer.\n * @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies\n */\n private async addRoles(tempEnforcer: Enforcer): Promise<void> {\n for (const groupPolicy of this.csvFilePolicies.addedGroupPolicies) {\n let err = await validateGroupingPolicy(\n groupPolicy,\n this.roleMetadataStorage,\n 'csv-file',\n );\n if (err) {\n this.logger.warn(\n `${err.message}, error originates from file ${this.filePath}`,\n );\n continue;\n }\n\n err = await checkForDuplicateGroupPolicies(\n tempEnforcer,\n groupPolicy,\n this.filePath!,\n );\n if (err) {\n this.logger.warn(err.message);\n continue;\n }\n\n try {\n const roleMetadata: RoleMetadataDao = {\n source: 'csv-file',\n roleEntityRef: groupPolicy[1],\n author: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n };\n\n const currentMetadata = await this.roleMetadataStorage.findRoleMetadata(\n roleMetadata.roleEntityRef,\n );\n\n await this.enforcer.addGroupingPolicy(groupPolicy, roleMetadata);\n\n const eventName = currentMetadata\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.CREATE_ROLE;\n const message = currentMetadata ? 'Updated role' : 'Created role';\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMetadata, members: [groupPolicy[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to add or update group policy ${groupPolicy} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n this.csvFilePolicies.addedGroupPolicies = [];\n }\n\n /**\n * removeRoles will remove the roles that are no longer present in the CSV file.\n * If the role exists with multiple groups and or users, we will update it role information.\n * Otherwise, we will remove the role completely.\n */\n private async removeRoles(): Promise<void> {\n for (const groupPolicy of this.csvFilePolicies.removedGroupPolicies) {\n const roleEntityRef = groupPolicy[1];\n // this requires knowledge of whether or not it is an update\n const isUpdate = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n\n // Need to update the time\n try {\n const metadata: RoleMetadataDao = {\n source: 'csv-file',\n roleEntityRef,\n author: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n };\n\n await this.enforcer.removeGroupingPolicy(\n groupPolicy,\n metadata,\n isUpdate.length > 1,\n );\n\n const isRolePresent =\n await this.roleMetadataStorage.findRoleMetadata(roleEntityRef);\n const eventName = isRolePresent\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.DELETE_ROLE;\n const message = isRolePresent\n ? 'Updated role: deleted members'\n : 'Deleted role';\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...metadata, members: [groupPolicy[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to remove group policy ${groupPolicy} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n this.csvFilePolicies.removedGroupPolicies = [];\n }\n\n async cleanUpRolesAndPolicies(): Promise<void> {\n const roleMetadatas =\n await this.roleMetadataStorage.filterRoleMetadata('csv-file');\n const fileRoles = roleMetadatas.map(meta => meta.roleEntityRef);\n\n if (fileRoles.length > 0) {\n for (const fileRole of fileRoles) {\n this.csvFilePolicies.removedGroupPolicies.push(\n ...(await this.enforcer.getFilteredGroupingPolicy(1, fileRole)),\n );\n this.csvFilePolicies.removedPolicies.push(\n ...(await this.enforcer.getFilteredPolicy(0, fileRole)),\n );\n }\n }\n await this.removePermissionPolicies();\n await this.removeRoles();\n }\n}\n"],"names":["AbstractFileWatcher","parse","transformPolicyGroupToLowercase","newEnforcer","newModelFromString","MODEL","LowercaseFileAdapter","mergeRoleMetadata","policyToString","difference","metadataStringToPolicy","transformArrayToPolicy","validatePolicy","validateSource","checkForDuplicatePolicies","RBAC_BACKEND","PermissionEvents","HANDLE_RBAC_DATA_STAGE","validateGroupingPolicy","checkForDuplicateGroupPolicies","RoleEvents"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"csv-file-watcher.cjs.js","sources":["../../src/file-permissions/csv-file-watcher.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { LoggerService } from '@backstage/backend-plugin-api';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport { Enforcer, newEnforcer, newModelFromString } from 'casbin';\nimport { parse } from 'csv-parse/sync';\nimport { difference } from 'lodash';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n PermissionAuditInfo,\n PermissionEvents,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n RolesAuditInfo,\n} from '../audit-log/audit-logger';\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport {\n mergeRoleMetadata,\n metadataStringToPolicy,\n policyToString,\n transformArrayToPolicy,\n transformPolicyGroupToLowercase,\n} from '../helper';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { MODEL } from '../service/permission-model';\nimport {\n checkForDuplicateGroupPolicies,\n checkForDuplicatePolicies,\n validateGroupingPolicy,\n validatePolicy,\n validateSource,\n} from '../validation/policies-validation';\nimport { AbstractFileWatcher } from './file-watcher';\nimport { LowercaseFileAdapter } from './lowercase-file-adapter';\n\nexport const CSV_PERMISSION_POLICY_FILE_AUTHOR = 'csv permission policy file';\n\ntype CSVFilePolicies = {\n addedPolicies: string[][];\n removedPolicies: string[][];\n addedGroupPolicies: Map<string, string[]>;\n removedGroupPolicies: Map<string, string[]>;\n};\n\nexport class CSVFileWatcher extends AbstractFileWatcher<string[][]> {\n private currentContent: string[][];\n private csvFilePolicies: CSVFilePolicies;\n\n constructor(\n filePath: string | undefined,\n allowReload: boolean,\n logger: LoggerService,\n private readonly enforcer: EnforcerDelegate,\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly auditLogger: AuditLogger,\n ) {\n super(filePath, allowReload, logger);\n this.currentContent = [];\n this.csvFilePolicies = {\n addedPolicies: [],\n removedPolicies: [],\n addedGroupPolicies: new Map<string, string[]>(),\n removedGroupPolicies: new Map<string, string[]>(),\n };\n }\n\n /**\n * parse is used to parse the current contents of the CSV file.\n * @returns The CSV file parsed into a string[][].\n */\n parse(): string[][] {\n const content = this.getCurrentContents();\n const data = parse(content, {\n skip_empty_lines: true,\n relax_column_count: true,\n trim: true,\n });\n\n for (const policy of data) {\n transformPolicyGroupToLowercase(policy);\n }\n\n return data;\n }\n\n /**\n * initialize will initialize the CSV file by loading all of the permission policies and roles into\n * the enforcer.\n * First, we will remove all roles and permission policies if they do not exist in the temporary file enforcer.\n * Next, we will add all roles and permission polices if they are new to the CSV file\n * Finally, we will set the file to be watched if allow reload is set\n * @param csvFileName The name of the csvFile\n * @param allowReload Whether or not we will allow reloads of the CSV file\n */\n async initialize(): Promise<void> {\n if (!this.filePath) {\n return;\n }\n let content: string[][] = [];\n // If the file is set load the file contents\n content = this.parse();\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new LowercaseFileAdapter(this.filePath),\n );\n\n // Check for any old policies that will need to be removed\n await this.filterPoliciesAndRoles(\n this.enforcer,\n tempEnforcer,\n this.csvFilePolicies.removedPolicies,\n this.csvFilePolicies.removedGroupPolicies,\n true,\n );\n\n await this.filterPoliciesAndRoles(\n tempEnforcer,\n this.enforcer,\n this.csvFilePolicies.addedPolicies,\n this.csvFilePolicies.addedGroupPolicies,\n );\n\n await this.migrateLegacyMetadata(tempEnforcer);\n\n // We pass current here because this is during initialization and it has not changed yet\n await this.updatePolicies(content);\n\n if (this.allowReload) {\n this.watchFile();\n }\n }\n\n // Check for policies that might need to be updated\n // This will involve update \"legacy\" source in the role metadata if it exist in both the\n // temp enforcer (csv file) and a role metadata storage.\n // We will update role metadata with the new source \"csv-file\"\n private async migrateLegacyMetadata(tempEnforcer: Enforcer) {\n let legacyRolesMetadata =\n await this.roleMetadataStorage.filterRoleMetadata('legacy');\n const legacyRoles = legacyRolesMetadata.map(meta => meta.roleEntityRef);\n if (legacyRoles.length > 0) {\n const legacyGroupPolicies = await tempEnforcer.getFilteredGroupingPolicy(\n 1,\n ...legacyRoles,\n );\n const legacyPolicies = await tempEnforcer.getFilteredPolicy(\n 0,\n ...legacyRoles,\n );\n const legacyRolesFromFile = new Set([\n ...legacyGroupPolicies.map(gp => gp[1]),\n ...legacyPolicies.map(p => p[0]),\n ]);\n legacyRolesMetadata = legacyRolesMetadata.filter(meta =>\n legacyRolesFromFile.has(meta.roleEntityRef),\n );\n for (const legacyRoleMeta of legacyRolesMetadata) {\n const nonLegacyRole = mergeRoleMetadata(legacyRoleMeta, {\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n source: 'csv-file',\n roleEntityRef: legacyRoleMeta.roleEntityRef,\n });\n await this.roleMetadataStorage.updateRoleMetadata(\n nonLegacyRole,\n legacyRoleMeta.roleEntityRef,\n );\n }\n }\n }\n\n /**\n * onChange is called whenever there is a change to the CSV file.\n * It will parse the current and new contents of the CSV file and process the roles and permission policies present.\n * Afterwards, it will find the difference between the current and new contents of the CSV file\n * and sort them into added / removed, permission policies / roles.\n * It will finally call updatePolicies with the new content.\n */\n async onChange(): Promise<void> {\n const newContent = this.parse();\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new LowercaseFileAdapter(this.filePath!),\n );\n\n const currentFlatContent = this.currentContent.flatMap(data => {\n return policyToString(data);\n });\n const newFlatContent = newContent.flatMap(data => {\n return policyToString(data);\n });\n\n await this.findFileContentDiff(\n currentFlatContent,\n newFlatContent,\n tempEnforcer,\n );\n\n await this.updatePolicies(newContent);\n }\n\n /**\n * updatePolicies is used to update all of the permission policies and roles within a CSV file.\n * It will check the number of added and removed permissions policies and roles and call the appropriate\n * methods for these.\n * It will also update the current contents of the CSV file to the most recent\n * @param newContent The new content present in the CSV file\n */\n private async updatePolicies(newContent: string[][]): Promise<void> {\n this.currentContent = newContent;\n\n if (this.csvFilePolicies.addedPolicies.length > 0)\n await this.addPermissionPolicies();\n if (this.csvFilePolicies.removedPolicies.length > 0)\n await this.removePermissionPolicies();\n if (this.csvFilePolicies.addedGroupPolicies.size > 0) await this.addRoles();\n if (this.csvFilePolicies.removedGroupPolicies.size > 0)\n await this.removeRoles();\n }\n\n /**\n * addPermissionPolicies will add the new permission policies that are present in the CSV file.\n */\n private async addPermissionPolicies(): Promise<void> {\n try {\n await this.enforcer.addPolicies(this.csvFilePolicies.addedPolicies);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created policies`,\n eventName: PermissionEvents.CREATE_POLICY,\n metadata: {\n policies: this.csvFilePolicies.addedPolicies,\n source: 'csv-file',\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to add or update policies ${this.csvFilePolicies.addedPolicies} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n\n this.csvFilePolicies.addedPolicies = [];\n }\n\n /**\n * removePermissionPolicies will remove the permission policies that are no longer present in the CSV file.\n */\n private async removePermissionPolicies(): Promise<void> {\n try {\n await this.enforcer.removePolicies(this.csvFilePolicies.removedPolicies);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Deleted policies`,\n eventName: PermissionEvents.DELETE_POLICY,\n metadata: {\n policies: this.csvFilePolicies.removedPolicies,\n source: 'csv-file',\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to remove policies ${JSON.stringify(\n this.csvFilePolicies.removedPolicies,\n )} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n this.csvFilePolicies.removedPolicies = [];\n }\n\n /**\n * addRoles will add the new roles that are present in the CSV file.\n */\n private async addRoles(): Promise<void> {\n const addedPolicies: string[][] = [];\n const updatedPolicies: string[][] = [];\n\n for (const [key, value] of this.csvFilePolicies.addedGroupPolicies) {\n const groupPolicies = value.map(member => {\n return [member, key];\n });\n\n try {\n const roleMetadata: RoleMetadataDao = {\n source: 'csv-file',\n roleEntityRef: key,\n author: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n };\n\n const currentMetadata = await this.roleMetadataStorage.findRoleMetadata(\n roleMetadata.roleEntityRef,\n );\n\n await this.enforcer.addGroupingPolicies(groupPolicies, roleMetadata);\n const eventName = currentMetadata\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.CREATE_ROLE;\n\n if (eventName === RoleEvents.UPDATE_ROLE) {\n updatedPolicies.push(...groupPolicies);\n } else {\n addedPolicies.push(...groupPolicies);\n }\n } catch (e) {\n this.logger.warn(\n `Failed to add or update group policy ${groupPolicies} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n\n if (updatedPolicies.length > 0)\n await this.logRoleEvent(\n RoleEvents.UPDATE_ROLE,\n 'Updated roles',\n updatedPolicies,\n );\n\n if (addedPolicies.length > 0)\n await this.logRoleEvent(\n RoleEvents.CREATE_ROLE,\n 'Created roles',\n addedPolicies,\n );\n\n this.csvFilePolicies.addedGroupPolicies = new Map<string, string[]>();\n }\n\n /**\n * removeRoles will remove the roles that are no longer present in the CSV file.\n * If the role exists with multiple groups and or users, we will update it role information.\n * Otherwise, we will remove the role completely.\n */\n private async removeRoles(): Promise<void> {\n for (const [key, value] of this.csvFilePolicies.removedGroupPolicies) {\n // This requires knowledge of whether or not it is an update\n const isUpdate = await this.enforcer.getFilteredGroupingPolicy(1, key);\n\n const groupPolicies = value.map(member => {\n return [member, key];\n });\n\n try {\n const roleMetadata: RoleMetadataDao = {\n source: 'csv-file',\n roleEntityRef: key,\n author: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n };\n\n await this.enforcer.removeGroupingPolicies(\n groupPolicies,\n roleMetadata,\n isUpdate.length > 1 && isUpdate.length !== groupPolicies.length,\n );\n\n const isRolePresent = await this.roleMetadataStorage.findRoleMetadata(\n roleMetadata.roleEntityRef,\n );\n const eventName = isRolePresent\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.DELETE_ROLE;\n const message = isRolePresent\n ? 'Updated role: deleted members'\n : 'Deleted role';\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMetadata, members: value },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to remove group policy ${groupPolicies} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n\n this.csvFilePolicies.removedGroupPolicies = new Map<string, string[]>();\n }\n\n async cleanUpRolesAndPolicies(): Promise<void> {\n const roleMetadatas =\n await this.roleMetadataStorage.filterRoleMetadata('csv-file');\n const fileRoles = roleMetadatas.map(meta => meta.roleEntityRef);\n\n if (fileRoles.length > 0) {\n for (const fileRole of fileRoles) {\n const filteredPolicies = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n fileRole,\n );\n for (const groupPolicy of filteredPolicies) {\n this.addGroupPolicyToMap(\n this.csvFilePolicies.removedGroupPolicies,\n groupPolicy[1],\n groupPolicy[0],\n );\n }\n this.csvFilePolicies.removedPolicies.push(\n ...(await this.enforcer.getFilteredPolicy(0, fileRole)),\n );\n }\n }\n await this.removePermissionPolicies();\n await this.removeRoles();\n }\n\n async filterPoliciesAndRoles(\n enforcerOne: Enforcer | EnforcerDelegate,\n enforcerTwo: Enforcer | EnforcerDelegate,\n policies: string[][],\n groupPolicies: Map<string, string[]>,\n remove?: boolean,\n ) {\n // Check for any policies that need to be edited by comparing policies from\n // one enforcer to the other\n const policiesToEdit = await enforcerOne.getPolicy();\n const groupPoliciesToEdit = await enforcerOne.getGroupingPolicy();\n\n for (const policy of policiesToEdit) {\n if (\n !(await enforcerTwo.hasPolicy(...policy)) &&\n (await this.validateAddedPolicy(\n policy,\n enforcerOne as Enforcer,\n remove,\n ))\n ) {\n policies.push(policy);\n }\n }\n\n for (const groupPolicy of groupPoliciesToEdit) {\n if (\n !(await enforcerTwo.hasGroupingPolicy(...groupPolicy)) &&\n (await this.validateAddedGroupPolicy(\n groupPolicy,\n enforcerOne as Enforcer,\n remove,\n ))\n ) {\n this.addGroupPolicyToMap(groupPolicies, groupPolicy[1], groupPolicy[0]);\n }\n }\n }\n\n async validateAddedPolicy(\n policy: string[],\n tempEnforcer: Enforcer,\n remove?: boolean,\n ): Promise<boolean> {\n const transformedPolicy = transformArrayToPolicy(policy);\n const metadata = await this.roleMetadataStorage.findRoleMetadata(policy[0]);\n\n if (remove) {\n return metadata?.source === 'csv-file';\n }\n\n let err = validatePolicy(transformedPolicy);\n if (err) {\n this.logger.warn(\n `Failed to validate policy from file ${this.filePath}. Cause: ${err.message}`,\n );\n return false;\n }\n\n err = await validateSource('csv-file', metadata);\n if (err) {\n this.logger.warn(\n `Unable to add policy ${policy} from file ${this.filePath}. Cause: ${err.message}`,\n );\n return false;\n }\n\n err = await checkForDuplicatePolicies(tempEnforcer, policy, this.filePath!);\n if (err) {\n this.logger.warn(err.message);\n return false;\n }\n\n return true;\n }\n\n async validateAddedGroupPolicy(\n groupPolicy: string[],\n tempEnforcer: Enforcer,\n remove?: boolean,\n ): Promise<boolean> {\n const metadata = await this.roleMetadataStorage.findRoleMetadata(\n groupPolicy[1],\n );\n\n if (remove) {\n return metadata?.source === 'csv-file';\n }\n\n let err = await validateGroupingPolicy(groupPolicy, metadata, 'csv-file');\n if (err) {\n this.logger.warn(\n `${err.message}, error originates from file ${this.filePath}`,\n );\n return false;\n }\n\n err = await checkForDuplicateGroupPolicies(\n tempEnforcer,\n groupPolicy,\n this.filePath!,\n );\n if (err) {\n this.logger.warn(err.message);\n return false;\n }\n\n return true;\n }\n\n async findFileContentDiff(\n currentFlatContent: string[],\n newFlatContent: string[],\n tempEnforcer: Enforcer,\n ) {\n const diffRemoved = difference(currentFlatContent, newFlatContent); // policy was removed\n const diffAdded = difference(newFlatContent, currentFlatContent); // policy was added\n\n await this.migrateLegacyMetadata(tempEnforcer);\n\n if (diffRemoved.length === 0 && diffAdded.length === 0) {\n return;\n }\n\n diffRemoved.forEach(policy => {\n const convertedPolicy = metadataStringToPolicy(policy);\n if (convertedPolicy[0] === 'p') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.removedPolicies.push(convertedPolicy);\n } else if (convertedPolicy[0] === 'g') {\n convertedPolicy.splice(0, 1);\n this.addGroupPolicyToMap(\n this.csvFilePolicies.removedGroupPolicies,\n convertedPolicy[1],\n convertedPolicy[0],\n );\n }\n });\n\n for (const policy of diffAdded) {\n const convertedPolicy = metadataStringToPolicy(policy);\n if (convertedPolicy[0] === 'p') {\n convertedPolicy.splice(0, 1);\n if (await this.validateAddedPolicy(convertedPolicy, tempEnforcer))\n this.csvFilePolicies.addedPolicies.push(convertedPolicy);\n } else if (convertedPolicy[0] === 'g') {\n convertedPolicy.splice(0, 1);\n if (await this.validateAddedGroupPolicy(convertedPolicy, tempEnforcer))\n this.addGroupPolicyToMap(\n this.csvFilePolicies.addedGroupPolicies,\n convertedPolicy[1],\n convertedPolicy[0],\n );\n }\n }\n }\n\n addGroupPolicyToMap(\n groupPolicyMap: Map<string, string[]>,\n key: string,\n value: string,\n ) {\n if (!groupPolicyMap.has(key)) {\n groupPolicyMap.set(key, []);\n }\n groupPolicyMap.get(key)?.push(value);\n }\n\n async logRoleEvent(\n eventName: 'CreateRole' | 'UpdateRole',\n message: string,\n groupingPolicies: string[][],\n ) {\n await this.auditLogger.auditLog<RolesAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { groupingPolicies, source: 'csv-file' },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n}\n"],"names":["AbstractFileWatcher","parse","transformPolicyGroupToLowercase","newEnforcer","newModelFromString","MODEL","LowercaseFileAdapter","mergeRoleMetadata","policyToString","RBAC_BACKEND","PermissionEvents","HANDLE_RBAC_DATA_STAGE","RoleEvents","transformArrayToPolicy","validatePolicy","validateSource","checkForDuplicatePolicies","validateGroupingPolicy","checkForDuplicateGroupPolicies","difference","metadataStringToPolicy"],"mappings":";;;;;;;;;;;;AAsDO,MAAM,iCAAoC,GAAA;AAS1C,MAAM,uBAAuBA,+BAAgC,CAAA;AAAA,EAIlE,YACE,QACA,EAAA,WAAA,EACA,MACiB,EAAA,QAAA,EACA,qBACA,WACjB,EAAA;AACA,IAAM,KAAA,CAAA,QAAA,EAAU,aAAa,MAAM,CAAA;AAJlB,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,mBAAA,GAAA,mBAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAGjB,IAAA,IAAA,CAAK,iBAAiB,EAAC;AACvB,IAAA,IAAA,CAAK,eAAkB,GAAA;AAAA,MACrB,eAAe,EAAC;AAAA,MAChB,iBAAiB,EAAC;AAAA,MAClB,kBAAA,sBAAwB,GAAsB,EAAA;AAAA,MAC9C,oBAAA,sBAA0B,GAAsB;AAAA,KAClD;AAAA;AACF,EAnBQ,cAAA;AAAA,EACA,eAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBR,KAAoB,GAAA;AAClB,IAAM,MAAA,OAAA,GAAU,KAAK,kBAAmB,EAAA;AACxC,IAAM,MAAA,IAAA,GAAOC,WAAM,OAAS,EAAA;AAAA,MAC1B,gBAAkB,EAAA,IAAA;AAAA,MAClB,kBAAoB,EAAA,IAAA;AAAA,MACpB,IAAM,EAAA;AAAA,KACP,CAAA;AAED,IAAA,KAAA,MAAW,UAAU,IAAM,EAAA;AACzB,MAAAC,sCAAA,CAAgC,MAAM,CAAA;AAAA;AAGxC,IAAO,OAAA,IAAA;AAAA;AACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,UAA4B,GAAA;AAChC,IAAI,IAAA,CAAC,KAAK,QAAU,EAAA;AAClB,MAAA;AAAA;AAEF,IAAA,IAAI,UAAsB,EAAC;AAE3B,IAAA,OAAA,GAAU,KAAK,KAAM,EAAA;AAErB,IAAA,MAAM,eAAe,MAAMC,kBAAA;AAAA,MACzBC,0BAAmBC,qBAAK,CAAA;AAAA,MACxB,IAAIC,yCAAqB,CAAA,IAAA,CAAK,QAAQ;AAAA,KACxC;AAGA,IAAA,MAAM,IAAK,CAAA,sBAAA;AAAA,MACT,IAAK,CAAA,QAAA;AAAA,MACL,YAAA;AAAA,MACA,KAAK,eAAgB,CAAA,eAAA;AAAA,MACrB,KAAK,eAAgB,CAAA,oBAAA;AAAA,MACrB;AAAA,KACF;AAEA,IAAA,MAAM,IAAK,CAAA,sBAAA;AAAA,MACT,YAAA;AAAA,MACA,IAAK,CAAA,QAAA;AAAA,MACL,KAAK,eAAgB,CAAA,aAAA;AAAA,MACrB,KAAK,eAAgB,CAAA;AAAA,KACvB;AAEA,IAAM,MAAA,IAAA,CAAK,sBAAsB,YAAY,CAAA;AAG7C,IAAM,MAAA,IAAA,CAAK,eAAe,OAAO,CAAA;AAEjC,IAAA,IAAI,KAAK,WAAa,EAAA;AACpB,MAAA,IAAA,CAAK,SAAU,EAAA;AAAA;AACjB;AACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,sBAAsB,YAAwB,EAAA;AAC1D,IAAA,IAAI,mBACF,GAAA,MAAM,IAAK,CAAA,mBAAA,CAAoB,mBAAmB,QAAQ,CAAA;AAC5D,IAAA,MAAM,WAAc,GAAA,mBAAA,CAAoB,GAAI,CAAA,CAAA,IAAA,KAAQ,KAAK,aAAa,CAAA;AACtE,IAAI,IAAA,WAAA,CAAY,SAAS,CAAG,EAAA;AAC1B,MAAM,MAAA,mBAAA,GAAsB,MAAM,YAAa,CAAA,yBAAA;AAAA,QAC7C,CAAA;AAAA,QACA,GAAG;AAAA,OACL;AACA,MAAM,MAAA,cAAA,GAAiB,MAAM,YAAa,CAAA,iBAAA;AAAA,QACxC,CAAA;AAAA,QACA,GAAG;AAAA,OACL;AACA,MAAM,MAAA,mBAAA,uBAA0B,GAAI,CAAA;AAAA,QAClC,GAAG,mBAAoB,CAAA,GAAA,CAAI,CAAM,EAAA,KAAA,EAAA,CAAG,CAAC,CAAC,CAAA;AAAA,QACtC,GAAG,cAAe,CAAA,GAAA,CAAI,CAAK,CAAA,KAAA,CAAA,CAAE,CAAC,CAAC;AAAA,OAChC,CAAA;AACD,MAAA,mBAAA,GAAsB,mBAAoB,CAAA,MAAA;AAAA,QAAO,CAC/C,IAAA,KAAA,mBAAA,CAAoB,GAAI,CAAA,IAAA,CAAK,aAAa;AAAA,OAC5C;AACA,MAAA,KAAA,MAAW,kBAAkB,mBAAqB,EAAA;AAChD,QAAM,MAAA,aAAA,GAAgBC,yBAAkB,cAAgB,EAAA;AAAA,UACtD,UAAY,EAAA,iCAAA;AAAA,UACZ,MAAQ,EAAA,UAAA;AAAA,UACR,eAAe,cAAe,CAAA;AAAA,SAC/B,CAAA;AACD,QAAA,MAAM,KAAK,mBAAoB,CAAA,kBAAA;AAAA,UAC7B,aAAA;AAAA,UACA,cAAe,CAAA;AAAA,SACjB;AAAA;AACF;AACF;AACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,QAA0B,GAAA;AAC9B,IAAM,MAAA,UAAA,GAAa,KAAK,KAAM,EAAA;AAE9B,IAAA,MAAM,eAAe,MAAMJ,kBAAA;AAAA,MACzBC,0BAAmBC,qBAAK,CAAA;AAAA,MACxB,IAAIC,yCAAqB,CAAA,IAAA,CAAK,QAAS;AAAA,KACzC;AAEA,IAAA,MAAM,kBAAqB,GAAA,IAAA,CAAK,cAAe,CAAA,OAAA,CAAQ,CAAQ,IAAA,KAAA;AAC7D,MAAA,OAAOE,sBAAe,IAAI,CAAA;AAAA,KAC3B,CAAA;AACD,IAAM,MAAA,cAAA,GAAiB,UAAW,CAAA,OAAA,CAAQ,CAAQ,IAAA,KAAA;AAChD,MAAA,OAAOA,sBAAe,IAAI,CAAA;AAAA,KAC3B,CAAA;AAED,IAAA,MAAM,IAAK,CAAA,mBAAA;AAAA,MACT,kBAAA;AAAA,MACA,cAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAM,MAAA,IAAA,CAAK,eAAe,UAAU,CAAA;AAAA;AACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,eAAe,UAAuC,EAAA;AAClE,IAAA,IAAA,CAAK,cAAiB,GAAA,UAAA;AAEtB,IAAI,IAAA,IAAA,CAAK,eAAgB,CAAA,aAAA,CAAc,MAAS,GAAA,CAAA;AAC9C,MAAA,MAAM,KAAK,qBAAsB,EAAA;AACnC,IAAI,IAAA,IAAA,CAAK,eAAgB,CAAA,eAAA,CAAgB,MAAS,GAAA,CAAA;AAChD,MAAA,MAAM,KAAK,wBAAyB,EAAA;AACtC,IAAA,IAAI,KAAK,eAAgB,CAAA,kBAAA,CAAmB,OAAO,CAAG,EAAA,MAAM,KAAK,QAAS,EAAA;AAC1E,IAAI,IAAA,IAAA,CAAK,eAAgB,CAAA,oBAAA,CAAqB,IAAO,GAAA,CAAA;AACnD,MAAA,MAAM,KAAK,WAAY,EAAA;AAAA;AAC3B;AAAA;AAAA;AAAA,EAKA,MAAc,qBAAuC,GAAA;AACnD,IAAI,IAAA;AACF,MAAA,MAAM,IAAK,CAAA,QAAA,CAAS,WAAY,CAAA,IAAA,CAAK,gBAAgB,aAAa,CAAA;AAElE,MAAM,MAAA,IAAA,CAAK,YAAY,QAA8B,CAAA;AAAA,QACnD,OAAS,EAAAC,wBAAA;AAAA,QACT,OAAS,EAAA,CAAA,gBAAA,CAAA;AAAA,QACT,WAAWC,4BAAiB,CAAA,aAAA;AAAA,QAC5B,QAAU,EAAA;AAAA,UACR,QAAA,EAAU,KAAK,eAAgB,CAAA,aAAA;AAAA,UAC/B,MAAQ,EAAA;AAAA,SACV;AAAA,QACA,KAAO,EAAAC,kCAAA;AAAA,QACP,MAAQ,EAAA;AAAA,OACT,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,IAAA;AAAA,QACV,CAAA,iCAAA,EAAoC,KAAK,eAAgB,CAAA,aAAa,uBAAuB,IAAK,CAAA,QAAQ,YAAY,CAAC,CAAA;AAAA,OACzH;AAAA;AAGF,IAAK,IAAA,CAAA,eAAA,CAAgB,gBAAgB,EAAC;AAAA;AACxC;AAAA;AAAA;AAAA,EAKA,MAAc,wBAA0C,GAAA;AACtD,IAAI,IAAA;AACF,MAAA,MAAM,IAAK,CAAA,QAAA,CAAS,cAAe,CAAA,IAAA,CAAK,gBAAgB,eAAe,CAAA;AAEvE,MAAM,MAAA,IAAA,CAAK,YAAY,QAA8B,CAAA;AAAA,QACnD,OAAS,EAAAF,wBAAA;AAAA,QACT,OAAS,EAAA,CAAA,gBAAA,CAAA;AAAA,QACT,WAAWC,4BAAiB,CAAA,aAAA;AAAA,QAC5B,QAAU,EAAA;AAAA,UACR,QAAA,EAAU,KAAK,eAAgB,CAAA,eAAA;AAAA,UAC/B,MAAQ,EAAA;AAAA,SACV;AAAA,QACA,KAAO,EAAAC,kCAAA;AAAA,QACP,MAAQ,EAAA;AAAA,OACT,CAAA;AAAA,aACM,CAAG,EAAA;AACV,MAAA,IAAA,CAAK,MAAO,CAAA,IAAA;AAAA,QACV,6BAA6B,IAAK,CAAA,SAAA;AAAA,UAChC,KAAK,eAAgB,CAAA;AAAA,SACtB,CAAA,oBAAA,EAAuB,IAAK,CAAA,QAAQ,YAAY,CAAC,CAAA;AAAA,OACpD;AAAA;AAEF,IAAK,IAAA,CAAA,eAAA,CAAgB,kBAAkB,EAAC;AAAA;AAC1C;AAAA;AAAA;AAAA,EAKA,MAAc,QAA0B,GAAA;AACtC,IAAA,MAAM,gBAA4B,EAAC;AACnC,IAAA,MAAM,kBAA8B,EAAC;AAErC,IAAA,KAAA,MAAW,CAAC,GAAK,EAAA,KAAK,CAAK,IAAA,IAAA,CAAK,gBAAgB,kBAAoB,EAAA;AAClE,MAAM,MAAA,aAAA,GAAgB,KAAM,CAAA,GAAA,CAAI,CAAU,MAAA,KAAA;AACxC,QAAO,OAAA,CAAC,QAAQ,GAAG,CAAA;AAAA,OACpB,CAAA;AAED,MAAI,IAAA;AACF,QAAA,MAAM,YAAgC,GAAA;AAAA,UACpC,MAAQ,EAAA,UAAA;AAAA,UACR,aAAe,EAAA,GAAA;AAAA,UACf,MAAQ,EAAA,iCAAA;AAAA,UACR,UAAY,EAAA;AAAA,SACd;AAEA,QAAM,MAAA,eAAA,GAAkB,MAAM,IAAA,CAAK,mBAAoB,CAAA,gBAAA;AAAA,UACrD,YAAa,CAAA;AAAA,SACf;AAEA,QAAA,MAAM,IAAK,CAAA,QAAA,CAAS,mBAAoB,CAAA,aAAA,EAAe,YAAY,CAAA;AACnE,QAAA,MAAM,SAAY,GAAA,eAAA,GACdC,sBAAW,CAAA,WAAA,GACXA,sBAAW,CAAA,WAAA;AAEf,QAAI,IAAA,SAAA,KAAcA,uBAAW,WAAa,EAAA;AACxC,UAAgB,eAAA,CAAA,IAAA,CAAK,GAAG,aAAa,CAAA;AAAA,SAChC,MAAA;AACL,UAAc,aAAA,CAAA,IAAA,CAAK,GAAG,aAAa,CAAA;AAAA;AACrC,eACO,CAAG,EAAA;AACV,QAAA,IAAA,CAAK,MAAO,CAAA,IAAA;AAAA,UACV,wCAAwC,aAAa,CAAA,oBAAA,EAAuB,IAAK,CAAA,QAAQ,YAAY,CAAC,CAAA;AAAA,SACxG;AAAA;AACF;AAGF,IAAA,IAAI,gBAAgB,MAAS,GAAA,CAAA;AAC3B,MAAA,MAAM,IAAK,CAAA,YAAA;AAAA,QACTA,sBAAW,CAAA,WAAA;AAAA,QACX,eAAA;AAAA,QACA;AAAA,OACF;AAEF,IAAA,IAAI,cAAc,MAAS,GAAA,CAAA;AACzB,MAAA,MAAM,IAAK,CAAA,YAAA;AAAA,QACTA,sBAAW,CAAA,WAAA;AAAA,QACX,eAAA;AAAA,QACA;AAAA,OACF;AAEF,IAAK,IAAA,CAAA,eAAA,CAAgB,kBAAqB,mBAAA,IAAI,GAAsB,EAAA;AAAA;AACtE;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,WAA6B,GAAA;AACzC,IAAA,KAAA,MAAW,CAAC,GAAK,EAAA,KAAK,CAAK,IAAA,IAAA,CAAK,gBAAgB,oBAAsB,EAAA;AAEpE,MAAA,MAAM,WAAW,MAAM,IAAA,CAAK,QAAS,CAAA,yBAAA,CAA0B,GAAG,GAAG,CAAA;AAErE,MAAM,MAAA,aAAA,GAAgB,KAAM,CAAA,GAAA,CAAI,CAAU,MAAA,KAAA;AACxC,QAAO,OAAA,CAAC,QAAQ,GAAG,CAAA;AAAA,OACpB,CAAA;AAED,MAAI,IAAA;AACF,QAAA,MAAM,YAAgC,GAAA;AAAA,UACpC,MAAQ,EAAA,UAAA;AAAA,UACR,aAAe,EAAA,GAAA;AAAA,UACf,MAAQ,EAAA,iCAAA;AAAA,UACR,UAAY,EAAA;AAAA,SACd;AAEA,QAAA,MAAM,KAAK,QAAS,CAAA,sBAAA;AAAA,UAClB,aAAA;AAAA,UACA,YAAA;AAAA,UACA,QAAS,CAAA,MAAA,GAAS,CAAK,IAAA,QAAA,CAAS,WAAW,aAAc,CAAA;AAAA,SAC3D;AAEA,QAAM,MAAA,aAAA,GAAgB,MAAM,IAAA,CAAK,mBAAoB,CAAA,gBAAA;AAAA,UACnD,YAAa,CAAA;AAAA,SACf;AACA,QAAA,MAAM,SAAY,GAAA,aAAA,GACdA,sBAAW,CAAA,WAAA,GACXA,sBAAW,CAAA,WAAA;AACf,QAAM,MAAA,OAAA,GAAU,gBACZ,+BACA,GAAA,cAAA;AACJ,QAAM,MAAA,IAAA,CAAK,YAAY,QAAwB,CAAA;AAAA,UAC7C,OAAS,EAAAH,wBAAA;AAAA,UACT,OAAA;AAAA,UACA,SAAA;AAAA,UACA,QAAU,EAAA,EAAE,GAAG,YAAA,EAAc,SAAS,KAAM,EAAA;AAAA,UAC5C,KAAO,EAAAE,kCAAA;AAAA,UACP,MAAQ,EAAA;AAAA,SACT,CAAA;AAAA,eACM,CAAG,EAAA;AACV,QAAA,IAAA,CAAK,MAAO,CAAA,IAAA;AAAA,UACV,iCAAiC,aAAa,CAAA,oBAAA,EAAuB,IAAK,CAAA,QAAQ,YAAY,CAAC,CAAA;AAAA,SACjG;AAAA;AACF;AAGF,IAAK,IAAA,CAAA,eAAA,CAAgB,oBAAuB,mBAAA,IAAI,GAAsB,EAAA;AAAA;AACxE,EAEA,MAAM,uBAAyC,GAAA;AAC7C,IAAA,MAAM,aACJ,GAAA,MAAM,IAAK,CAAA,mBAAA,CAAoB,mBAAmB,UAAU,CAAA;AAC9D,IAAA,MAAM,SAAY,GAAA,aAAA,CAAc,GAAI,CAAA,CAAA,IAAA,KAAQ,KAAK,aAAa,CAAA;AAE9D,IAAI,IAAA,SAAA,CAAU,SAAS,CAAG,EAAA;AACxB,MAAA,KAAA,MAAW,YAAY,SAAW,EAAA;AAChC,QAAM,MAAA,gBAAA,GAAmB,MAAM,IAAA,CAAK,QAAS,CAAA,yBAAA;AAAA,UAC3C,CAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,KAAA,MAAW,eAAe,gBAAkB,EAAA;AAC1C,UAAK,IAAA,CAAA,mBAAA;AAAA,YACH,KAAK,eAAgB,CAAA,oBAAA;AAAA,YACrB,YAAY,CAAC,CAAA;AAAA,YACb,YAAY,CAAC;AAAA,WACf;AAAA;AAEF,QAAA,IAAA,CAAK,gBAAgB,eAAgB,CAAA,IAAA;AAAA,UACnC,GAAI,MAAM,IAAA,CAAK,QAAS,CAAA,iBAAA,CAAkB,GAAG,QAAQ;AAAA,SACvD;AAAA;AACF;AAEF,IAAA,MAAM,KAAK,wBAAyB,EAAA;AACpC,IAAA,MAAM,KAAK,WAAY,EAAA;AAAA;AACzB,EAEA,MAAM,sBACJ,CAAA,WAAA,EACA,WACA,EAAA,QAAA,EACA,eACA,MACA,EAAA;AAGA,IAAM,MAAA,cAAA,GAAiB,MAAM,WAAA,CAAY,SAAU,EAAA;AACnD,IAAM,MAAA,mBAAA,GAAsB,MAAM,WAAA,CAAY,iBAAkB,EAAA;AAEhE,IAAA,KAAA,MAAW,UAAU,cAAgB,EAAA;AACnC,MACE,IAAA,CAAE,MAAM,WAAY,CAAA,SAAA,CAAU,GAAG,MAAM,CAAA,IACtC,MAAM,IAAK,CAAA,mBAAA;AAAA,QACV,MAAA;AAAA,QACA,WAAA;AAAA,QACA;AAAA,OAEF,EAAA;AACA,QAAA,QAAA,CAAS,KAAK,MAAM,CAAA;AAAA;AACtB;AAGF,IAAA,KAAA,MAAW,eAAe,mBAAqB,EAAA;AAC7C,MACE,IAAA,CAAE,MAAM,WAAY,CAAA,iBAAA,CAAkB,GAAG,WAAW,CAAA,IACnD,MAAM,IAAK,CAAA,wBAAA;AAAA,QACV,WAAA;AAAA,QACA,WAAA;AAAA,QACA;AAAA,OAEF,EAAA;AACA,QAAA,IAAA,CAAK,oBAAoB,aAAe,EAAA,WAAA,CAAY,CAAC,CAAG,EAAA,WAAA,CAAY,CAAC,CAAC,CAAA;AAAA;AACxE;AACF;AACF,EAEA,MAAM,mBAAA,CACJ,MACA,EAAA,YAAA,EACA,MACkB,EAAA;AAClB,IAAM,MAAA,iBAAA,GAAoBE,8BAAuB,MAAM,CAAA;AACvD,IAAA,MAAM,WAAW,MAAM,IAAA,CAAK,oBAAoB,gBAAiB,CAAA,MAAA,CAAO,CAAC,CAAC,CAAA;AAE1E,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,OAAO,UAAU,MAAW,KAAA,UAAA;AAAA;AAG9B,IAAI,IAAA,GAAA,GAAMC,kCAAe,iBAAiB,CAAA;AAC1C,IAAA,IAAI,GAAK,EAAA;AACP,MAAA,IAAA,CAAK,MAAO,CAAA,IAAA;AAAA,QACV,CAAuC,oCAAA,EAAA,IAAA,CAAK,QAAQ,CAAA,SAAA,EAAY,IAAI,OAAO,CAAA;AAAA,OAC7E;AACA,MAAO,OAAA,KAAA;AAAA;AAGT,IAAM,GAAA,GAAA,MAAMC,iCAAe,CAAA,UAAA,EAAY,QAAQ,CAAA;AAC/C,IAAA,IAAI,GAAK,EAAA;AACP,MAAA,IAAA,CAAK,MAAO,CAAA,IAAA;AAAA,QACV,wBAAwB,MAAM,CAAA,WAAA,EAAc,KAAK,QAAQ,CAAA,SAAA,EAAY,IAAI,OAAO,CAAA;AAAA,OAClF;AACA,MAAO,OAAA,KAAA;AAAA;AAGT,IAAA,GAAA,GAAM,MAAMC,4CAAA,CAA0B,YAAc,EAAA,MAAA,EAAQ,KAAK,QAAS,CAAA;AAC1E,IAAA,IAAI,GAAK,EAAA;AACP,MAAK,IAAA,CAAA,MAAA,CAAO,IAAK,CAAA,GAAA,CAAI,OAAO,CAAA;AAC5B,MAAO,OAAA,KAAA;AAAA;AAGT,IAAO,OAAA,IAAA;AAAA;AACT,EAEA,MAAM,wBAAA,CACJ,WACA,EAAA,YAAA,EACA,MACkB,EAAA;AAClB,IAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,mBAAoB,CAAA,gBAAA;AAAA,MAC9C,YAAY,CAAC;AAAA,KACf;AAEA,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,OAAO,UAAU,MAAW,KAAA,UAAA;AAAA;AAG9B,IAAA,IAAI,GAAM,GAAA,MAAMC,yCAAuB,CAAA,WAAA,EAAa,UAAU,UAAU,CAAA;AACxE,IAAA,IAAI,GAAK,EAAA;AACP,MAAA,IAAA,CAAK,MAAO,CAAA,IAAA;AAAA,QACV,CAAG,EAAA,GAAA,CAAI,OAAO,CAAA,6BAAA,EAAgC,KAAK,QAAQ,CAAA;AAAA,OAC7D;AACA,MAAO,OAAA,KAAA;AAAA;AAGT,IAAA,GAAA,GAAM,MAAMC,iDAAA;AAAA,MACV,YAAA;AAAA,MACA,WAAA;AAAA,MACA,IAAK,CAAA;AAAA,KACP;AACA,IAAA,IAAI,GAAK,EAAA;AACP,MAAK,IAAA,CAAA,MAAA,CAAO,IAAK,CAAA,GAAA,CAAI,OAAO,CAAA;AAC5B,MAAO,OAAA,KAAA;AAAA;AAGT,IAAO,OAAA,IAAA;AAAA;AACT,EAEA,MAAM,mBAAA,CACJ,kBACA,EAAA,cAAA,EACA,YACA,EAAA;AACA,IAAM,MAAA,WAAA,GAAcC,iBAAW,CAAA,kBAAA,EAAoB,cAAc,CAAA;AACjE,IAAM,MAAA,SAAA,GAAYA,iBAAW,CAAA,cAAA,EAAgB,kBAAkB,CAAA;AAE/D,IAAM,MAAA,IAAA,CAAK,sBAAsB,YAAY,CAAA;AAE7C,IAAA,IAAI,WAAY,CAAA,MAAA,KAAW,CAAK,IAAA,SAAA,CAAU,WAAW,CAAG,EAAA;AACtD,MAAA;AAAA;AAGF,IAAA,WAAA,CAAY,QAAQ,CAAU,MAAA,KAAA;AAC5B,MAAM,MAAA,eAAA,GAAkBC,8BAAuB,MAAM,CAAA;AACrD,MAAI,IAAA,eAAA,CAAgB,CAAC,CAAA,KAAM,GAAK,EAAA;AAC9B,QAAgB,eAAA,CAAA,MAAA,CAAO,GAAG,CAAC,CAAA;AAC3B,QAAK,IAAA,CAAA,eAAA,CAAgB,eAAgB,CAAA,IAAA,CAAK,eAAe,CAAA;AAAA,OAChD,MAAA,IAAA,eAAA,CAAgB,CAAC,CAAA,KAAM,GAAK,EAAA;AACrC,QAAgB,eAAA,CAAA,MAAA,CAAO,GAAG,CAAC,CAAA;AAC3B,QAAK,IAAA,CAAA,mBAAA;AAAA,UACH,KAAK,eAAgB,CAAA,oBAAA;AAAA,UACrB,gBAAgB,CAAC,CAAA;AAAA,UACjB,gBAAgB,CAAC;AAAA,SACnB;AAAA;AACF,KACD,CAAA;AAED,IAAA,KAAA,MAAW,UAAU,SAAW,EAAA;AAC9B,MAAM,MAAA,eAAA,GAAkBA,8BAAuB,MAAM,CAAA;AACrD,MAAI,IAAA,eAAA,CAAgB,CAAC,CAAA,KAAM,GAAK,EAAA;AAC9B,QAAgB,eAAA,CAAA,MAAA,CAAO,GAAG,CAAC,CAAA;AAC3B,QAAA,IAAI,MAAM,IAAA,CAAK,mBAAoB,CAAA,eAAA,EAAiB,YAAY,CAAA;AAC9D,UAAK,IAAA,CAAA,eAAA,CAAgB,aAAc,CAAA,IAAA,CAAK,eAAe,CAAA;AAAA,OAChD,MAAA,IAAA,eAAA,CAAgB,CAAC,CAAA,KAAM,GAAK,EAAA;AACrC,QAAgB,eAAA,CAAA,MAAA,CAAO,GAAG,CAAC,CAAA;AAC3B,QAAA,IAAI,MAAM,IAAA,CAAK,wBAAyB,CAAA,eAAA,EAAiB,YAAY,CAAA;AACnE,UAAK,IAAA,CAAA,mBAAA;AAAA,YACH,KAAK,eAAgB,CAAA,kBAAA;AAAA,YACrB,gBAAgB,CAAC,CAAA;AAAA,YACjB,gBAAgB,CAAC;AAAA,WACnB;AAAA;AACJ;AACF;AACF,EAEA,mBAAA,CACE,cACA,EAAA,GAAA,EACA,KACA,EAAA;AACA,IAAA,IAAI,CAAC,cAAA,CAAe,GAAI,CAAA,GAAG,CAAG,EAAA;AAC5B,MAAe,cAAA,CAAA,GAAA,CAAI,GAAK,EAAA,EAAE,CAAA;AAAA;AAE5B,IAAA,cAAA,CAAe,GAAI,CAAA,GAAG,CAAG,EAAA,IAAA,CAAK,KAAK,CAAA;AAAA;AACrC,EAEA,MAAM,YAAA,CACJ,SACA,EAAA,OAAA,EACA,gBACA,EAAA;AACA,IAAM,MAAA,IAAA,CAAK,YAAY,QAAyB,CAAA;AAAA,MAC9C,OAAS,EAAAX,wBAAA;AAAA,MACT,OAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAU,EAAA,EAAE,gBAAkB,EAAA,MAAA,EAAQ,UAAW,EAAA;AAAA,MACjD,KAAO,EAAAE,kCAAA;AAAA,MACP,MAAQ,EAAA;AAAA,KACT,CAAA;AAAA;AAEL;;;;;"}
|
|
@@ -52,11 +52,10 @@ class Connection {
|
|
|
52
52
|
async addRoles(roles) {
|
|
53
53
|
for (const role of roles) {
|
|
54
54
|
if (!await this.enforcer.hasGroupingPolicy(...role)) {
|
|
55
|
-
const
|
|
56
|
-
role
|
|
57
|
-
this.roleMetadataStorage,
|
|
58
|
-
this.id
|
|
55
|
+
const metadata = await this.roleMetadataStorage.findRoleMetadata(
|
|
56
|
+
role[1]
|
|
59
57
|
);
|
|
58
|
+
const err = await policiesValidation.validateGroupingPolicy(role, metadata, this.id);
|
|
60
59
|
if (err) {
|
|
61
60
|
this.logger.warn(err.message);
|
|
62
61
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect-providers.cjs.js","sources":["../../src/providers/connect-providers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { LoggerService } from '@backstage/backend-plugin-api';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport {\n Enforcer,\n newEnforcer,\n newModelFromString,\n StringAdapter,\n} from 'casbin';\n\nimport type {\n RBACProvider,\n RBACProviderConnection,\n} from '@backstage-community/plugin-rbac-node';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n PermissionAuditInfo,\n PermissionEvents,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from '../audit-log/audit-logger';\nimport { RoleMetadataStorage } from '../database/role-metadata';\nimport {\n transformArrayToPolicy,\n transformRolesGroupToLowercase,\n typedPoliciesToString,\n} from '../helper';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { MODEL } from '../service/permission-model';\nimport {\n validateGroupingPolicy,\n validatePolicy,\n validateSource,\n} from '../validation/policies-validation';\n\nexport class Connection implements RBACProviderConnection {\n constructor(\n private readonly id: string,\n private readonly enforcer: EnforcerDelegate,\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly logger: LoggerService,\n private readonly auditLogger: AuditLogger,\n ) {}\n\n async applyRoles(roles: string[][]): Promise<void> {\n const lowercasedRoles = transformRolesGroupToLowercase(roles);\n const stringPolicy = typedPoliciesToString(lowercasedRoles, 'g');\n const providerRolesforRemoval: string[][] = [];\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new StringAdapter(stringPolicy),\n );\n\n const providerRoles = await this.getProviderRoles();\n\n await this.enforcer.loadPolicy();\n // Get the roles for this provider coming from rbac plugin\n for (const providerRole of providerRoles) {\n providerRolesforRemoval.push(\n ...(await this.enforcer.getFilteredGroupingPolicy(1, providerRole)),\n );\n }\n\n // Remove role\n // role exists in rbac but does not exist in provider\n await this.removeRoles(providerRolesforRemoval, tempEnforcer);\n\n // Add the role\n // role exists in provider but does not exist in rbac\n await this.addRoles(lowercasedRoles);\n }\n\n async applyPermissions(permissions: string[][]): Promise<void> {\n const stringPolicy = typedPoliciesToString(permissions, 'p');\n\n const providerPermissions: string[][] = [];\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new StringAdapter(stringPolicy),\n );\n\n const providerRoles = await this.getProviderRoles();\n\n await this.enforcer.loadPolicy();\n // Get the roles for this provider coming from rbac plugin\n for (const providerRole of providerRoles) {\n providerPermissions.push(\n ...(await this.enforcer.getFilteredPolicy(0, providerRole)),\n );\n }\n\n await this.removePermissions(providerPermissions, tempEnforcer);\n\n await this.addPermissions(permissions);\n }\n\n private async addRoles(roles: string[][]): Promise<void> {\n for (const role of roles) {\n if (!(await this.enforcer.hasGroupingPolicy(...role))) {\n const err = await validateGroupingPolicy(\n role,\n this.roleMetadataStorage,\n this.id,\n );\n\n if (err) {\n this.logger.warn(err.message);\n continue; // Skip adding this role as there was an error\n }\n\n let roleMeta = await this.roleMetadataStorage.findRoleMetadata(role[1]);\n\n const eventName = roleMeta\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.CREATE_ROLE;\n const message = roleMeta ? 'Updated role' : 'Created role';\n\n // role does not exist in rbac, create the metadata for it\n if (!roleMeta) {\n roleMeta = {\n modifiedBy: this.id,\n source: this.id,\n roleEntityRef: role[1],\n };\n }\n\n await this.enforcer.addGroupingPolicy(role, roleMeta);\n\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMeta, members: [role[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n }\n }\n\n private async removeRoles(\n providerRoles: string[][],\n tempEnforcer: Enforcer,\n ): Promise<void> {\n // Remove role\n // role exists in rbac but does not exist in provider\n const lowercasedProviderRoles =\n transformRolesGroupToLowercase(providerRoles);\n for (const role of lowercasedProviderRoles) {\n if (!(await tempEnforcer.hasGroupingPolicy(...role))) {\n const roleMeta = await this.roleMetadataStorage.findRoleMetadata(\n role[1],\n );\n\n const currentRole = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n role[1],\n );\n\n if (!roleMeta) {\n this.logger.warn('role does not exist');\n continue;\n }\n\n const singleRole = roleMeta && currentRole.length === 1;\n\n let eventName: string;\n let message: string;\n\n // Only one role exists in rbac remove role metadata as well\n if (singleRole) {\n eventName = RoleEvents.DELETE_ROLE;\n message = 'Deleted role';\n await this.enforcer.removeGroupingPolicy(role, roleMeta);\n\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMeta, members: [role[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n continue; // Move on to the next role\n }\n\n eventName = RoleEvents.UPDATE_ROLE;\n message = 'Updated role: deleted members';\n await this.enforcer.removeGroupingPolicy(role, roleMeta, true);\n\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMeta, members: [role[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n }\n }\n\n private async addPermissions(permissions: string[][]): Promise<void> {\n for (const permission of permissions) {\n if (!(await this.enforcer.hasPolicy(...permission))) {\n const transformedPolicy = transformArrayToPolicy(permission);\n const metadata = await this.roleMetadataStorage.findRoleMetadata(\n permission[0],\n );\n\n let err = validatePolicy(transformedPolicy);\n if (err) {\n this.logger.warn(`Invalid permission policy, ${err}`);\n continue; // Skip this invalid permission policy\n }\n\n err = await validateSource(this.id, metadata);\n if (err) {\n this.logger.warn(\n `Unable to add policy ${permission}. Cause: ${err.message}`,\n );\n continue;\n }\n\n await this.enforcer.addPolicy(permission);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created policy`,\n eventName: PermissionEvents.CREATE_POLICY,\n metadata: { policies: [permission], source: this.id },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n }\n }\n\n private async removePermissions(\n providerPermissions: string[][],\n tempEnforcer: Enforcer,\n ): Promise<void> {\n const removedPermissions: string[][] = [];\n for (const permission of providerPermissions) {\n if (!(await tempEnforcer.hasPolicy(...permission))) {\n await this.enforcer.removePolicy(permission);\n removedPermissions.push(permission);\n }\n\n if (removedPermissions.length > 0) {\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Deleted policies`,\n eventName: PermissionEvents.DELETE_POLICY,\n metadata: {\n policies: removedPermissions,\n source: this.id,\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n }\n }\n\n private async getProviderRoles(): Promise<string[]> {\n const currentRoles = await this.roleMetadataStorage.filterRoleMetadata(\n this.id,\n );\n return currentRoles.map(meta => meta.roleEntityRef);\n }\n}\n\nexport async function connectRBACProviders(\n providers: RBACProvider[],\n enforcer: EnforcerDelegate,\n roleMetadataStorage: RoleMetadataStorage,\n logger: LoggerService,\n auditLogger: AuditLogger,\n) {\n await Promise.all(\n providers.map(async provider => {\n try {\n const connection = new Connection(\n provider.getProviderName(),\n enforcer,\n roleMetadataStorage,\n logger,\n auditLogger,\n );\n return provider.connect(connection);\n } catch (error) {\n throw new Error(\n `Unable to connect provider ${provider.getProviderName()}, ${error}`,\n );\n }\n }),\n );\n}\n"],"names":["transformRolesGroupToLowercase","typedPoliciesToString","newEnforcer","newModelFromString","MODEL","StringAdapter","validateGroupingPolicy","RoleEvents","RBAC_BACKEND","HANDLE_RBAC_DATA_STAGE","transformArrayToPolicy","validatePolicy","validateSource","PermissionEvents"],"mappings":";;;;;;;;AAoDO,MAAM,UAA6C,CAAA;AAAA,EACxD,WACmB,CAAA,EAAA,EACA,QACA,EAAA,mBAAA,EACA,QACA,WACjB,EAAA;AALiB,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,mBAAA,GAAA,mBAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAAA;AAChB,EAEH,MAAM,WAAW,KAAkC,EAAA;AACjD,IAAM,MAAA,eAAA,GAAkBA,sCAA+B,KAAK,CAAA;AAC5D,IAAM,MAAA,YAAA,GAAeC,4BAAsB,CAAA,eAAA,EAAiB,GAAG,CAAA;AAC/D,IAAA,MAAM,0BAAsC,EAAC;AAE7C,IAAA,MAAM,eAAe,MAAMC,kBAAA;AAAA,MACzBC,0BAAmBC,qBAAK,CAAA;AAAA,MACxB,IAAIC,qBAAc,YAAY;AAAA,KAChC;AAEA,IAAM,MAAA,aAAA,GAAgB,MAAM,IAAA,CAAK,gBAAiB,EAAA;AAElD,IAAM,MAAA,IAAA,CAAK,SAAS,UAAW,EAAA;AAE/B,IAAA,KAAA,MAAW,gBAAgB,aAAe,EAAA;AACxC,MAAwB,uBAAA,CAAA,IAAA;AAAA,QACtB,GAAI,MAAM,IAAA,CAAK,QAAS,CAAA,yBAAA,CAA0B,GAAG,YAAY;AAAA,OACnE;AAAA;AAKF,IAAM,MAAA,IAAA,CAAK,WAAY,CAAA,uBAAA,EAAyB,YAAY,CAAA;AAI5D,IAAM,MAAA,IAAA,CAAK,SAAS,eAAe,CAAA;AAAA;AACrC,EAEA,MAAM,iBAAiB,WAAwC,EAAA;AAC7D,IAAM,MAAA,YAAA,GAAeJ,4BAAsB,CAAA,WAAA,EAAa,GAAG,CAAA;AAE3D,IAAA,MAAM,sBAAkC,EAAC;AAEzC,IAAA,MAAM,eAAe,MAAMC,kBAAA;AAAA,MACzBC,0BAAmBC,qBAAK,CAAA;AAAA,MACxB,IAAIC,qBAAc,YAAY;AAAA,KAChC;AAEA,IAAM,MAAA,aAAA,GAAgB,MAAM,IAAA,CAAK,gBAAiB,EAAA;AAElD,IAAM,MAAA,IAAA,CAAK,SAAS,UAAW,EAAA;AAE/B,IAAA,KAAA,MAAW,gBAAgB,aAAe,EAAA;AACxC,MAAoB,mBAAA,CAAA,IAAA;AAAA,QAClB,GAAI,MAAM,IAAA,CAAK,QAAS,CAAA,iBAAA,CAAkB,GAAG,YAAY;AAAA,OAC3D;AAAA;AAGF,IAAM,MAAA,IAAA,CAAK,iBAAkB,CAAA,mBAAA,EAAqB,YAAY,CAAA;AAE9D,IAAM,MAAA,IAAA,CAAK,eAAe,WAAW,CAAA;AAAA;AACvC,EAEA,MAAc,SAAS,KAAkC,EAAA;AACvD,IAAA,KAAA,MAAW,QAAQ,KAAO,EAAA;AACxB,MAAA,IAAI,CAAE,MAAM,IAAA,CAAK,SAAS,iBAAkB,CAAA,GAAG,IAAI,CAAI,EAAA;AACrD,QAAA,MAAM,MAAM,MAAMC,yCAAA;AAAA,UAChB,IAAA;AAAA,UACA,IAAK,CAAA,mBAAA;AAAA,UACL,IAAK,CAAA;AAAA,SACP;AAEA,QAAA,IAAI,GAAK,EAAA;AACP,UAAK,IAAA,CAAA,MAAA,CAAO,IAAK,CAAA,GAAA,CAAI,OAAO,CAAA;AAC5B,UAAA;AAAA;AAGF,QAAA,IAAI,WAAW,MAAM,IAAA,CAAK,oBAAoB,gBAAiB,CAAA,IAAA,CAAK,CAAC,CAAC,CAAA;AAEtE,QAAA,MAAM,SAAY,GAAA,QAAA,GACdC,sBAAW,CAAA,WAAA,GACXA,sBAAW,CAAA,WAAA;AACf,QAAM,MAAA,OAAA,GAAU,WAAW,cAAiB,GAAA,cAAA;AAG5C,QAAA,IAAI,CAAC,QAAU,EAAA;AACb,UAAW,QAAA,GAAA;AAAA,YACT,YAAY,IAAK,CAAA,EAAA;AAAA,YACjB,QAAQ,IAAK,CAAA,EAAA;AAAA,YACb,aAAA,EAAe,KAAK,CAAC;AAAA,WACvB;AAAA;AAGF,QAAA,MAAM,IAAK,CAAA,QAAA,CAAS,iBAAkB,CAAA,IAAA,EAAM,QAAQ,CAAA;AAEpD,QAAM,MAAA,IAAA,CAAK,YAAY,QAAwB,CAAA;AAAA,UAC7C,OAAS,EAAAC,wBAAA;AAAA,UACT,OAAA;AAAA,UACA,SAAA;AAAA,UACA,QAAA,EAAU,EAAE,GAAG,QAAA,EAAU,SAAS,CAAC,IAAA,CAAK,CAAC,CAAC,CAAE,EAAA;AAAA,UAC5C,KAAO,EAAAC,kCAAA;AAAA,UACP,MAAQ,EAAA;AAAA,SACT,CAAA;AAAA;AACH;AACF;AACF,EAEA,MAAc,WACZ,CAAA,aAAA,EACA,YACe,EAAA;AAGf,IAAM,MAAA,uBAAA,GACJT,sCAA+B,aAAa,CAAA;AAC9C,IAAA,KAAA,MAAW,QAAQ,uBAAyB,EAAA;AAC1C,MAAA,IAAI,CAAE,MAAM,YAAA,CAAa,iBAAkB,CAAA,GAAG,IAAI,CAAI,EAAA;AACpD,QAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,mBAAoB,CAAA,gBAAA;AAAA,UAC9C,KAAK,CAAC;AAAA,SACR;AAEA,QAAM,MAAA,WAAA,GAAc,MAAM,IAAA,CAAK,QAAS,CAAA,yBAAA;AAAA,UACtC,CAAA;AAAA,UACA,KAAK,CAAC;AAAA,SACR;AAEA,QAAA,IAAI,CAAC,QAAU,EAAA;AACb,UAAK,IAAA,CAAA,MAAA,CAAO,KAAK,qBAAqB,CAAA;AACtC,UAAA;AAAA;AAGF,QAAM,MAAA,UAAA,GAAa,QAAY,IAAA,WAAA,CAAY,MAAW,KAAA,CAAA;AAEtD,QAAI,IAAA,SAAA;AACJ,QAAI,IAAA,OAAA;AAGJ,QAAA,IAAI,UAAY,EAAA;AACd,UAAA,SAAA,GAAYO,sBAAW,CAAA,WAAA;AACvB,UAAU,OAAA,GAAA,cAAA;AACV,UAAA,MAAM,IAAK,CAAA,QAAA,CAAS,oBAAqB,CAAA,IAAA,EAAM,QAAQ,CAAA;AAEvD,UAAM,MAAA,IAAA,CAAK,YAAY,QAAwB,CAAA;AAAA,YAC7C,OAAS,EAAAC,wBAAA;AAAA,YACT,OAAA;AAAA,YACA,SAAA;AAAA,YACA,QAAA,EAAU,EAAE,GAAG,QAAA,EAAU,SAAS,CAAC,IAAA,CAAK,CAAC,CAAC,CAAE,EAAA;AAAA,YAC5C,KAAO,EAAAC,kCAAA;AAAA,YACP,MAAQ,EAAA;AAAA,WACT,CAAA;AACD,UAAA;AAAA;AAGF,QAAA,SAAA,GAAYF,sBAAW,CAAA,WAAA;AACvB,QAAU,OAAA,GAAA,+BAAA;AACV,QAAA,MAAM,IAAK,CAAA,QAAA,CAAS,oBAAqB,CAAA,IAAA,EAAM,UAAU,IAAI,CAAA;AAE7D,QAAM,MAAA,IAAA,CAAK,YAAY,QAAwB,CAAA;AAAA,UAC7C,OAAS,EAAAC,wBAAA;AAAA,UACT,OAAA;AAAA,UACA,SAAA;AAAA,UACA,QAAA,EAAU,EAAE,GAAG,QAAA,EAAU,SAAS,CAAC,IAAA,CAAK,CAAC,CAAC,CAAE,EAAA;AAAA,UAC5C,KAAO,EAAAC,kCAAA;AAAA,UACP,MAAQ,EAAA;AAAA,SACT,CAAA;AAAA;AACH;AACF;AACF,EAEA,MAAc,eAAe,WAAwC,EAAA;AACnE,IAAA,KAAA,MAAW,cAAc,WAAa,EAAA;AACpC,MAAA,IAAI,CAAE,MAAM,IAAA,CAAK,SAAS,SAAU,CAAA,GAAG,UAAU,CAAI,EAAA;AACnD,QAAM,MAAA,iBAAA,GAAoBC,8BAAuB,UAAU,CAAA;AAC3D,QAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,mBAAoB,CAAA,gBAAA;AAAA,UAC9C,WAAW,CAAC;AAAA,SACd;AAEA,QAAI,IAAA,GAAA,GAAMC,kCAAe,iBAAiB,CAAA;AAC1C,QAAA,IAAI,GAAK,EAAA;AACP,UAAA,IAAA,CAAK,MAAO,CAAA,IAAA,CAAK,CAA8B,2BAAA,EAAA,GAAG,CAAE,CAAA,CAAA;AACpD,UAAA;AAAA;AAGF,QAAA,GAAA,GAAM,MAAMC,iCAAA,CAAe,IAAK,CAAA,EAAA,EAAI,QAAQ,CAAA;AAC5C,QAAA,IAAI,GAAK,EAAA;AACP,UAAA,IAAA,CAAK,MAAO,CAAA,IAAA;AAAA,YACV,CAAwB,qBAAA,EAAA,UAAU,CAAY,SAAA,EAAA,GAAA,CAAI,OAAO,CAAA;AAAA,WAC3D;AACA,UAAA;AAAA;AAGF,QAAM,MAAA,IAAA,CAAK,QAAS,CAAA,SAAA,CAAU,UAAU,CAAA;AAExC,QAAM,MAAA,IAAA,CAAK,YAAY,QAA8B,CAAA;AAAA,UACnD,OAAS,EAAAJ,wBAAA;AAAA,UACT,OAAS,EAAA,CAAA,cAAA,CAAA;AAAA,UACT,WAAWK,4BAAiB,CAAA,aAAA;AAAA,UAC5B,QAAA,EAAU,EAAE,QAAU,EAAA,CAAC,UAAU,CAAG,EAAA,MAAA,EAAQ,KAAK,EAAG,EAAA;AAAA,UACpD,KAAO,EAAAJ,kCAAA;AAAA,UACP,MAAQ,EAAA;AAAA,SACT,CAAA;AAAA;AACH;AACF;AACF,EAEA,MAAc,iBACZ,CAAA,mBAAA,EACA,YACe,EAAA;AACf,IAAA,MAAM,qBAAiC,EAAC;AACxC,IAAA,KAAA,MAAW,cAAc,mBAAqB,EAAA;AAC5C,MAAA,IAAI,CAAE,MAAM,YAAA,CAAa,SAAU,CAAA,GAAG,UAAU,CAAI,EAAA;AAClD,QAAM,MAAA,IAAA,CAAK,QAAS,CAAA,YAAA,CAAa,UAAU,CAAA;AAC3C,QAAA,kBAAA,CAAmB,KAAK,UAAU,CAAA;AAAA;AAGpC,MAAI,IAAA,kBAAA,CAAmB,SAAS,CAAG,EAAA;AACjC,QAAM,MAAA,IAAA,CAAK,YAAY,QAA8B,CAAA;AAAA,UACnD,OAAS,EAAAD,wBAAA;AAAA,UACT,OAAS,EAAA,CAAA,gBAAA,CAAA;AAAA,UACT,WAAWK,4BAAiB,CAAA,aAAA;AAAA,UAC5B,QAAU,EAAA;AAAA,YACR,QAAU,EAAA,kBAAA;AAAA,YACV,QAAQ,IAAK,CAAA;AAAA,WACf;AAAA,UACA,KAAO,EAAAJ,kCAAA;AAAA,UACP,MAAQ,EAAA;AAAA,SACT,CAAA;AAAA;AACH;AACF;AACF,EAEA,MAAc,gBAAsC,GAAA;AAClD,IAAM,MAAA,YAAA,GAAe,MAAM,IAAA,CAAK,mBAAoB,CAAA,kBAAA;AAAA,MAClD,IAAK,CAAA;AAAA,KACP;AACA,IAAA,OAAO,YAAa,CAAA,GAAA,CAAI,CAAQ,IAAA,KAAA,IAAA,CAAK,aAAa,CAAA;AAAA;AAEtD;AAEA,eAAsB,oBACpB,CAAA,SAAA,EACA,QACA,EAAA,mBAAA,EACA,QACA,WACA,EAAA;AACA,EAAA,MAAM,OAAQ,CAAA,GAAA;AAAA,IACZ,SAAA,CAAU,GAAI,CAAA,OAAM,QAAY,KAAA;AAC9B,MAAI,IAAA;AACF,QAAA,MAAM,aAAa,IAAI,UAAA;AAAA,UACrB,SAAS,eAAgB,EAAA;AAAA,UACzB,QAAA;AAAA,UACA,mBAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACF;AACA,QAAO,OAAA,QAAA,CAAS,QAAQ,UAAU,CAAA;AAAA,eAC3B,KAAO,EAAA;AACd,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAA8B,2BAAA,EAAA,QAAA,CAAS,eAAgB,EAAC,KAAK,KAAK,CAAA;AAAA,SACpE;AAAA;AACF,KACD;AAAA,GACH;AACF;;;;;"}
|
|
1
|
+
{"version":3,"file":"connect-providers.cjs.js","sources":["../../src/providers/connect-providers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { LoggerService } from '@backstage/backend-plugin-api';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport {\n Enforcer,\n newEnforcer,\n newModelFromString,\n StringAdapter,\n} from 'casbin';\n\nimport type {\n RBACProvider,\n RBACProviderConnection,\n} from '@backstage-community/plugin-rbac-node';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n PermissionAuditInfo,\n PermissionEvents,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from '../audit-log/audit-logger';\nimport { RoleMetadataStorage } from '../database/role-metadata';\nimport {\n transformArrayToPolicy,\n transformRolesGroupToLowercase,\n typedPoliciesToString,\n} from '../helper';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { MODEL } from '../service/permission-model';\nimport {\n validateGroupingPolicy,\n validatePolicy,\n validateSource,\n} from '../validation/policies-validation';\n\nexport class Connection implements RBACProviderConnection {\n constructor(\n private readonly id: string,\n private readonly enforcer: EnforcerDelegate,\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly logger: LoggerService,\n private readonly auditLogger: AuditLogger,\n ) {}\n\n async applyRoles(roles: string[][]): Promise<void> {\n const lowercasedRoles = transformRolesGroupToLowercase(roles);\n const stringPolicy = typedPoliciesToString(lowercasedRoles, 'g');\n const providerRolesforRemoval: string[][] = [];\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new StringAdapter(stringPolicy),\n );\n\n const providerRoles = await this.getProviderRoles();\n\n await this.enforcer.loadPolicy();\n // Get the roles for this provider coming from rbac plugin\n for (const providerRole of providerRoles) {\n providerRolesforRemoval.push(\n ...(await this.enforcer.getFilteredGroupingPolicy(1, providerRole)),\n );\n }\n\n // Remove role\n // role exists in rbac but does not exist in provider\n await this.removeRoles(providerRolesforRemoval, tempEnforcer);\n\n // Add the role\n // role exists in provider but does not exist in rbac\n await this.addRoles(lowercasedRoles);\n }\n\n async applyPermissions(permissions: string[][]): Promise<void> {\n const stringPolicy = typedPoliciesToString(permissions, 'p');\n\n const providerPermissions: string[][] = [];\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new StringAdapter(stringPolicy),\n );\n\n const providerRoles = await this.getProviderRoles();\n\n await this.enforcer.loadPolicy();\n // Get the roles for this provider coming from rbac plugin\n for (const providerRole of providerRoles) {\n providerPermissions.push(\n ...(await this.enforcer.getFilteredPolicy(0, providerRole)),\n );\n }\n\n await this.removePermissions(providerPermissions, tempEnforcer);\n\n await this.addPermissions(permissions);\n }\n\n private async addRoles(roles: string[][]): Promise<void> {\n for (const role of roles) {\n if (!(await this.enforcer.hasGroupingPolicy(...role))) {\n const metadata = await this.roleMetadataStorage.findRoleMetadata(\n role[1],\n );\n const err = await validateGroupingPolicy(role, metadata, this.id);\n\n if (err) {\n this.logger.warn(err.message);\n continue; // Skip adding this role as there was an error\n }\n\n let roleMeta = await this.roleMetadataStorage.findRoleMetadata(role[1]);\n\n const eventName = roleMeta\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.CREATE_ROLE;\n const message = roleMeta ? 'Updated role' : 'Created role';\n\n // role does not exist in rbac, create the metadata for it\n if (!roleMeta) {\n roleMeta = {\n modifiedBy: this.id,\n source: this.id,\n roleEntityRef: role[1],\n };\n }\n\n await this.enforcer.addGroupingPolicy(role, roleMeta);\n\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMeta, members: [role[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n }\n }\n\n private async removeRoles(\n providerRoles: string[][],\n tempEnforcer: Enforcer,\n ): Promise<void> {\n // Remove role\n // role exists in rbac but does not exist in provider\n const lowercasedProviderRoles =\n transformRolesGroupToLowercase(providerRoles);\n for (const role of lowercasedProviderRoles) {\n if (!(await tempEnforcer.hasGroupingPolicy(...role))) {\n const roleMeta = await this.roleMetadataStorage.findRoleMetadata(\n role[1],\n );\n\n const currentRole = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n role[1],\n );\n\n if (!roleMeta) {\n this.logger.warn('role does not exist');\n continue;\n }\n\n const singleRole = roleMeta && currentRole.length === 1;\n\n let eventName: string;\n let message: string;\n\n // Only one role exists in rbac remove role metadata as well\n if (singleRole) {\n eventName = RoleEvents.DELETE_ROLE;\n message = 'Deleted role';\n await this.enforcer.removeGroupingPolicy(role, roleMeta);\n\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMeta, members: [role[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n continue; // Move on to the next role\n }\n\n eventName = RoleEvents.UPDATE_ROLE;\n message = 'Updated role: deleted members';\n await this.enforcer.removeGroupingPolicy(role, roleMeta, true);\n\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMeta, members: [role[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n }\n }\n\n private async addPermissions(permissions: string[][]): Promise<void> {\n for (const permission of permissions) {\n if (!(await this.enforcer.hasPolicy(...permission))) {\n const transformedPolicy = transformArrayToPolicy(permission);\n const metadata = await this.roleMetadataStorage.findRoleMetadata(\n permission[0],\n );\n\n let err = validatePolicy(transformedPolicy);\n if (err) {\n this.logger.warn(`Invalid permission policy, ${err}`);\n continue; // Skip this invalid permission policy\n }\n\n err = await validateSource(this.id, metadata);\n if (err) {\n this.logger.warn(\n `Unable to add policy ${permission}. Cause: ${err.message}`,\n );\n continue;\n }\n\n await this.enforcer.addPolicy(permission);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created policy`,\n eventName: PermissionEvents.CREATE_POLICY,\n metadata: { policies: [permission], source: this.id },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n }\n }\n\n private async removePermissions(\n providerPermissions: string[][],\n tempEnforcer: Enforcer,\n ): Promise<void> {\n const removedPermissions: string[][] = [];\n for (const permission of providerPermissions) {\n if (!(await tempEnforcer.hasPolicy(...permission))) {\n await this.enforcer.removePolicy(permission);\n removedPermissions.push(permission);\n }\n\n if (removedPermissions.length > 0) {\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Deleted policies`,\n eventName: PermissionEvents.DELETE_POLICY,\n metadata: {\n policies: removedPermissions,\n source: this.id,\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n }\n }\n\n private async getProviderRoles(): Promise<string[]> {\n const currentRoles = await this.roleMetadataStorage.filterRoleMetadata(\n this.id,\n );\n return currentRoles.map(meta => meta.roleEntityRef);\n }\n}\n\nexport async function connectRBACProviders(\n providers: RBACProvider[],\n enforcer: EnforcerDelegate,\n roleMetadataStorage: RoleMetadataStorage,\n logger: LoggerService,\n auditLogger: AuditLogger,\n) {\n await Promise.all(\n providers.map(async provider => {\n try {\n const connection = new Connection(\n provider.getProviderName(),\n enforcer,\n roleMetadataStorage,\n logger,\n auditLogger,\n );\n return provider.connect(connection);\n } catch (error) {\n throw new Error(\n `Unable to connect provider ${provider.getProviderName()}, ${error}`,\n );\n }\n }),\n );\n}\n"],"names":["transformRolesGroupToLowercase","typedPoliciesToString","newEnforcer","newModelFromString","MODEL","StringAdapter","validateGroupingPolicy","RoleEvents","RBAC_BACKEND","HANDLE_RBAC_DATA_STAGE","transformArrayToPolicy","validatePolicy","validateSource","PermissionEvents"],"mappings":"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"}
|
|
@@ -108,7 +108,7 @@ function validateEntityReference(entityRef, role) {
|
|
|
108
108
|
}
|
|
109
109
|
return undefined;
|
|
110
110
|
}
|
|
111
|
-
async function validateGroupingPolicy(groupPolicy,
|
|
111
|
+
async function validateGroupingPolicy(groupPolicy, metadata, source) {
|
|
112
112
|
if (groupPolicy.length !== 2) {
|
|
113
113
|
return new Error(`Group policy should have length 2`);
|
|
114
114
|
}
|
|
@@ -141,7 +141,6 @@ async function validateGroupingPolicy(groupPolicy, roleMetadataStorage, source)
|
|
|
141
141
|
`Group policy is invalid: ${groupPolicy}. User membership information could be provided only with help of Catalog API.`
|
|
142
142
|
);
|
|
143
143
|
}
|
|
144
|
-
const metadata = await roleMetadataStorage.findRoleMetadata(parent);
|
|
145
144
|
err = await validateSource(source, metadata);
|
|
146
145
|
if (metadata && err) {
|
|
147
146
|
return new errors.NotAllowedError(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policies-validation.cjs.js","sources":["../../src/validation/policies-validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model';\nimport { NotAllowedError } from '@backstage/errors';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\n\nimport { Enforcer } from 'casbin';\n\nimport {\n isValidPermissionAction,\n PermissionActionValues,\n Role,\n RoleBasedPolicy,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\n\n/**\n * validateSource validates the source to the role that is being modified. This includes comparing the source from the\n * originating role to the source that the modification is coming from.\n * We do this to ensure consistency between permissions and roles and where they are originally defined.\n * This is a strict comparison where the source of all new roles (grouping policies) and permissions must match\n * the source of the first role that was created.\n * We are not strict for permission policies defined with an originating role source of configuration.\n * @param source The source in which the modification is coming from\n * @param roleMetadata The original role that was created\n * @returns An error in the event that the source does not match the originating role\n */\nexport const validateSource = async (\n source: Source,\n roleMetadata: RoleMetadataDao | undefined,\n): Promise<Error | undefined> => {\n if (!roleMetadata) {\n return undefined; // Role does not exist yet, there is no conflict with the source\n }\n\n if (roleMetadata.source !== source && roleMetadata.source !== 'legacy') {\n return new Error(\n `source does not match originating role ${\n roleMetadata.roleEntityRef\n }, consider making changes to the '${roleMetadata.source.toLocaleUpperCase()}'`,\n );\n }\n\n return undefined;\n};\n\n// This should be called on add and edit and delete\nexport function validatePolicy(policy: RoleBasedPolicy): Error | undefined {\n const err = validateEntityReference(policy.entityReference);\n if (err) {\n return err;\n }\n\n if (!policy.permission) {\n return new Error(`'permission' field must not be empty`);\n }\n\n if (!policy.policy) {\n return new Error(`'policy' field must not be empty`);\n } else if (!isValidPermissionAction(policy.policy)) {\n return new Error(\n `'policy' has invalid value: '${\n policy.policy\n }'. It should be one of: ${PermissionActionValues.join(', ')}`,\n );\n }\n\n if (!policy.effect) {\n return new Error(`'effect' field must not be empty`);\n } else if (!isValidEffectValue(policy.effect)) {\n return new Error(\n `'effect' has invalid value: '${\n policy.effect\n }'. It should be: '${AuthorizeResult.ALLOW.toLocaleLowerCase()}' or '${AuthorizeResult.DENY.toLocaleLowerCase()}'`,\n );\n }\n\n return undefined;\n}\n\nexport function validateRole(role: Role): Error | undefined {\n if (!role.name) {\n return new Error(`'name' field must not be empty`);\n }\n\n let err = validateEntityReference(role.name, true);\n if (err) {\n return err;\n }\n\n if (!role.memberReferences || role.memberReferences.length === 0) {\n return new Error(`'memberReferences' field must not be empty`);\n }\n\n for (const member of role.memberReferences) {\n err = validateEntityReference(member);\n if (err) {\n return err;\n }\n }\n return undefined;\n}\n\nfunction isValidEffectValue(effect: string): boolean {\n return (\n effect === AuthorizeResult.ALLOW.toLocaleLowerCase() ||\n effect === AuthorizeResult.DENY.toLocaleLowerCase()\n );\n}\n\nfunction isValidEntityName(name: string): boolean {\n const validNamePattern = /^[a-zA-Z0-9]+([._-][a-zA-Z0-9]+)*$/;\n return validNamePattern.test(name) && name.length <= 63;\n}\n\nfunction isValidEntityNamespace(namespace: string): boolean {\n const validNamespacePattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n return validNamespacePattern.test(namespace) && namespace.length <= 63;\n}\n\n// We supports only full form entity reference: [<kind>:][<namespace>/]<name>\nexport function validateEntityReference(\n entityRef?: string,\n role?: boolean,\n): Error | undefined {\n if (!entityRef) {\n return new Error(`'entityReference' must not be empty`);\n }\n\n let entityRefCompound: CompoundEntityRef;\n try {\n entityRefCompound = parseEntityRef(entityRef);\n } catch (err) {\n return err as Error;\n }\n\n const entityRefFull = `${entityRefCompound.kind}:${entityRefCompound.namespace}/${entityRefCompound.name}`;\n if (entityRefFull !== entityRef) {\n return new Error(\n `entity reference '${entityRef}' does not match the required format [<kind>:][<namespace>/]<name>. Provide, please, full entity reference.`,\n );\n }\n\n if (role && entityRefCompound.kind !== 'role') {\n return new Error(\n `Unsupported kind ${entityRefCompound.kind}. Supported value should be \"role\"`,\n );\n }\n\n if (\n entityRefCompound.kind !== 'user' &&\n entityRefCompound.kind !== 'group' &&\n entityRefCompound.kind !== 'role'\n ) {\n return new Error(\n `Unsupported kind ${entityRefCompound.kind}. List supported values [\"user\", \"group\", \"role\"]`,\n );\n }\n\n if (!isValidEntityName(entityRefCompound.name)) {\n return new Error(\n `The name '${entityRefCompound.name}' in the entity reference must be a string that is sequences of [a-zA-Z0-9] separated by any of [-_.], at most 63 characters in total`,\n );\n }\n\n if (!isValidEntityNamespace(entityRefCompound.namespace)) {\n return new Error(\n `The namespace '${entityRefCompound.namespace}' in the entity reference must be a string that is sequences of [a-z0-9] separated by [-], at most 63 characters in total`,\n );\n }\n\n return undefined;\n}\n\nexport async function validateGroupingPolicy(\n groupPolicy: string[],\n roleMetadataStorage: RoleMetadataStorage,\n source: Source,\n): Promise<Error | undefined> {\n if (groupPolicy.length !== 2) {\n return new Error(`Group policy should have length 2`);\n }\n\n const member = groupPolicy[0];\n let err = validateEntityReference(member);\n if (err) {\n return new Error(\n `Failed to validate group policy ${groupPolicy}. Cause: ${err.message}`,\n );\n }\n const parent = groupPolicy[1];\n err = validateEntityReference(parent);\n if (err) {\n return new Error(\n `Failed to validate group policy ${groupPolicy}. Cause: ${err.message}`,\n );\n }\n if (member.startsWith(`role:`)) {\n return new Error(\n `Group policy is invalid: ${groupPolicy}. rbac-backend plugin doesn't support role inheritance.`,\n );\n }\n if (member.startsWith(`group:`) && parent.startsWith(`group:`)) {\n return new Error(\n `Group policy is invalid: ${groupPolicy}. Group inheritance information could be provided only with help of Catalog API.`,\n );\n }\n if (member.startsWith(`user:`) && parent.startsWith(`group:`)) {\n return new Error(\n `Group policy is invalid: ${groupPolicy}. User membership information could be provided only with help of Catalog API.`,\n );\n }\n\n const metadata = await roleMetadataStorage.findRoleMetadata(parent);\n\n err = await validateSource(source, metadata);\n if (metadata && err) {\n return new NotAllowedError(\n `Unable to validate role ${groupPolicy}. Cause: ${err.message}`,\n );\n }\n\n return undefined;\n}\n\nexport const checkForDuplicatePolicies = async (\n fileEnf: Enforcer,\n policy: string[],\n policyFile: string,\n): Promise<Error | undefined> => {\n const duplicates = await fileEnf.getFilteredPolicy(0, ...policy);\n if (duplicates.length > 1) {\n return new Error(\n `Duplicate policy: ${policy} found in the file ${policyFile}`,\n );\n }\n\n const flipPolicyEffect = [\n policy[0],\n policy[1],\n policy[2],\n policy[3] === 'deny' ? 'allow' : 'deny',\n ];\n\n // Check if the same policy exists but with a different effect\n const dupWithDifferentEffect = await fileEnf.getFilteredPolicy(\n 0,\n ...flipPolicyEffect,\n );\n\n if (dupWithDifferentEffect.length > 0) {\n return new Error(\n `Duplicate policy: ${policy[0]}, ${policy[1]}, ${policy[2]} with different effect found in the file ${policyFile}`,\n );\n }\n\n return undefined;\n};\n\nexport const checkForDuplicateGroupPolicies = async (\n fileEnf: Enforcer,\n policy: string[],\n policyFile: string,\n): Promise<Error | undefined> => {\n const duplicates = await fileEnf.getFilteredGroupingPolicy(0, ...policy);\n\n if (duplicates.length > 1) {\n return new Error(\n `Duplicate role: ${policy} found in the file ${policyFile}`,\n );\n }\n return undefined;\n};\n"],"names":["isValidPermissionAction","PermissionActionValues","AuthorizeResult","parseEntityRef","NotAllowedError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"policies-validation.cjs.js","sources":["../../src/validation/policies-validation.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model';\nimport { NotAllowedError } from '@backstage/errors';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\n\nimport { Enforcer } from 'casbin';\n\nimport {\n isValidPermissionAction,\n PermissionActionValues,\n Role,\n RoleBasedPolicy,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport { RoleMetadataDao } from '../database/role-metadata';\n\n/**\n * validateSource validates the source to the role that is being modified. This includes comparing the source from the\n * originating role to the source that the modification is coming from.\n * We do this to ensure consistency between permissions and roles and where they are originally defined.\n * This is a strict comparison where the source of all new roles (grouping policies) and permissions must match\n * the source of the first role that was created.\n * We are not strict for permission policies defined with an originating role source of configuration.\n * @param source The source in which the modification is coming from\n * @param roleMetadata The original role that was created\n * @returns An error in the event that the source does not match the originating role\n */\nexport const validateSource = async (\n source: Source,\n roleMetadata: RoleMetadataDao | undefined,\n): Promise<Error | undefined> => {\n if (!roleMetadata) {\n return undefined; // Role does not exist yet, there is no conflict with the source\n }\n\n if (roleMetadata.source !== source && roleMetadata.source !== 'legacy') {\n return new Error(\n `source does not match originating role ${\n roleMetadata.roleEntityRef\n }, consider making changes to the '${roleMetadata.source.toLocaleUpperCase()}'`,\n );\n }\n\n return undefined;\n};\n\n// This should be called on add and edit and delete\nexport function validatePolicy(policy: RoleBasedPolicy): Error | undefined {\n const err = validateEntityReference(policy.entityReference);\n if (err) {\n return err;\n }\n\n if (!policy.permission) {\n return new Error(`'permission' field must not be empty`);\n }\n\n if (!policy.policy) {\n return new Error(`'policy' field must not be empty`);\n } else if (!isValidPermissionAction(policy.policy)) {\n return new Error(\n `'policy' has invalid value: '${\n policy.policy\n }'. It should be one of: ${PermissionActionValues.join(', ')}`,\n );\n }\n\n if (!policy.effect) {\n return new Error(`'effect' field must not be empty`);\n } else if (!isValidEffectValue(policy.effect)) {\n return new Error(\n `'effect' has invalid value: '${\n policy.effect\n }'. It should be: '${AuthorizeResult.ALLOW.toLocaleLowerCase()}' or '${AuthorizeResult.DENY.toLocaleLowerCase()}'`,\n );\n }\n\n return undefined;\n}\n\nexport function validateRole(role: Role): Error | undefined {\n if (!role.name) {\n return new Error(`'name' field must not be empty`);\n }\n\n let err = validateEntityReference(role.name, true);\n if (err) {\n return err;\n }\n\n if (!role.memberReferences || role.memberReferences.length === 0) {\n return new Error(`'memberReferences' field must not be empty`);\n }\n\n for (const member of role.memberReferences) {\n err = validateEntityReference(member);\n if (err) {\n return err;\n }\n }\n return undefined;\n}\n\nfunction isValidEffectValue(effect: string): boolean {\n return (\n effect === AuthorizeResult.ALLOW.toLocaleLowerCase() ||\n effect === AuthorizeResult.DENY.toLocaleLowerCase()\n );\n}\n\nfunction isValidEntityName(name: string): boolean {\n const validNamePattern = /^[a-zA-Z0-9]+([._-][a-zA-Z0-9]+)*$/;\n return validNamePattern.test(name) && name.length <= 63;\n}\n\nfunction isValidEntityNamespace(namespace: string): boolean {\n const validNamespacePattern = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n return validNamespacePattern.test(namespace) && namespace.length <= 63;\n}\n\n// We supports only full form entity reference: [<kind>:][<namespace>/]<name>\nexport function validateEntityReference(\n entityRef?: string,\n role?: boolean,\n): Error | undefined {\n if (!entityRef) {\n return new Error(`'entityReference' must not be empty`);\n }\n\n let entityRefCompound: CompoundEntityRef;\n try {\n entityRefCompound = parseEntityRef(entityRef);\n } catch (err) {\n return err as Error;\n }\n\n const entityRefFull = `${entityRefCompound.kind}:${entityRefCompound.namespace}/${entityRefCompound.name}`;\n if (entityRefFull !== entityRef) {\n return new Error(\n `entity reference '${entityRef}' does not match the required format [<kind>:][<namespace>/]<name>. Provide, please, full entity reference.`,\n );\n }\n\n if (role && entityRefCompound.kind !== 'role') {\n return new Error(\n `Unsupported kind ${entityRefCompound.kind}. Supported value should be \"role\"`,\n );\n }\n\n if (\n entityRefCompound.kind !== 'user' &&\n entityRefCompound.kind !== 'group' &&\n entityRefCompound.kind !== 'role'\n ) {\n return new Error(\n `Unsupported kind ${entityRefCompound.kind}. List supported values [\"user\", \"group\", \"role\"]`,\n );\n }\n\n if (!isValidEntityName(entityRefCompound.name)) {\n return new Error(\n `The name '${entityRefCompound.name}' in the entity reference must be a string that is sequences of [a-zA-Z0-9] separated by any of [-_.], at most 63 characters in total`,\n );\n }\n\n if (!isValidEntityNamespace(entityRefCompound.namespace)) {\n return new Error(\n `The namespace '${entityRefCompound.namespace}' in the entity reference must be a string that is sequences of [a-z0-9] separated by [-], at most 63 characters in total`,\n );\n }\n\n return undefined;\n}\n\nexport async function validateGroupingPolicy(\n groupPolicy: string[],\n metadata: RoleMetadataDao | undefined,\n source: Source,\n): Promise<Error | undefined> {\n if (groupPolicy.length !== 2) {\n return new Error(`Group policy should have length 2`);\n }\n\n const member = groupPolicy[0];\n let err = validateEntityReference(member);\n if (err) {\n return new Error(\n `Failed to validate group policy ${groupPolicy}. Cause: ${err.message}`,\n );\n }\n const parent = groupPolicy[1];\n err = validateEntityReference(parent);\n if (err) {\n return new Error(\n `Failed to validate group policy ${groupPolicy}. Cause: ${err.message}`,\n );\n }\n if (member.startsWith(`role:`)) {\n return new Error(\n `Group policy is invalid: ${groupPolicy}. rbac-backend plugin doesn't support role inheritance.`,\n );\n }\n if (member.startsWith(`group:`) && parent.startsWith(`group:`)) {\n return new Error(\n `Group policy is invalid: ${groupPolicy}. Group inheritance information could be provided only with help of Catalog API.`,\n );\n }\n if (member.startsWith(`user:`) && parent.startsWith(`group:`)) {\n return new Error(\n `Group policy is invalid: ${groupPolicy}. User membership information could be provided only with help of Catalog API.`,\n );\n }\n\n err = await validateSource(source, metadata);\n if (metadata && err) {\n return new NotAllowedError(\n `Unable to validate role ${groupPolicy}. Cause: ${err.message}`,\n );\n }\n\n return undefined;\n}\n\nexport const checkForDuplicatePolicies = async (\n fileEnf: Enforcer,\n policy: string[],\n policyFile: string,\n): Promise<Error | undefined> => {\n const duplicates = await fileEnf.getFilteredPolicy(0, ...policy);\n if (duplicates.length > 1) {\n return new Error(\n `Duplicate policy: ${policy} found in the file ${policyFile}`,\n );\n }\n\n const flipPolicyEffect = [\n policy[0],\n policy[1],\n policy[2],\n policy[3] === 'deny' ? 'allow' : 'deny',\n ];\n\n // Check if the same policy exists but with a different effect\n const dupWithDifferentEffect = await fileEnf.getFilteredPolicy(\n 0,\n ...flipPolicyEffect,\n );\n\n if (dupWithDifferentEffect.length > 0) {\n return new Error(\n `Duplicate policy: ${policy[0]}, ${policy[1]}, ${policy[2]} with different effect found in the file ${policyFile}`,\n );\n }\n\n return undefined;\n};\n\nexport const checkForDuplicateGroupPolicies = async (\n fileEnf: Enforcer,\n policy: string[],\n policyFile: string,\n): Promise<Error | undefined> => {\n const duplicates = await fileEnf.getFilteredGroupingPolicy(0, ...policy);\n\n if (duplicates.length > 1) {\n return new Error(\n `Duplicate role: ${policy} found in the file ${policyFile}`,\n );\n }\n return undefined;\n};\n"],"names":["isValidPermissionAction","PermissionActionValues","AuthorizeResult","parseEntityRef","NotAllowedError"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage-community/plugin-rbac-backend",
|
|
3
|
-
"version": "5.5.
|
|
3
|
+
"version": "5.5.3",
|
|
4
4
|
"main": "dist/index.cjs.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@backstage-community/plugin-rbac-common": "^1.13.0",
|
|
38
|
-
"@backstage-community/plugin-rbac-node": "^1.9.
|
|
38
|
+
"@backstage-community/plugin-rbac-node": "^1.9.1",
|
|
39
39
|
"@backstage/backend-defaults": "^0.7.0",
|
|
40
40
|
"@backstage/backend-plugin-api": "^1.1.1",
|
|
41
41
|
"@backstage/catalog-client": "^1.9.1",
|
|
@@ -70,7 +70,6 @@
|
|
|
70
70
|
"@types/supertest": "2.0.16",
|
|
71
71
|
"knex-mock-client": "2.0.1",
|
|
72
72
|
"msw": "1.3.5",
|
|
73
|
-
"prettier": "3.4.2",
|
|
74
73
|
"qs": "6.13.1",
|
|
75
74
|
"supertest": "6.3.4"
|
|
76
75
|
},
|