@backstage-community/plugin-rbac-backend 5.2.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +671 -0
  2. package/README.md +220 -0
  3. package/config.d.ts +68 -0
  4. package/dist/admin-permissions/admin-creation.cjs.js +117 -0
  5. package/dist/admin-permissions/admin-creation.cjs.js.map +1 -0
  6. package/dist/audit-log/audit-logger.cjs.js +108 -0
  7. package/dist/audit-log/audit-logger.cjs.js.map +1 -0
  8. package/dist/audit-log/rest-errors-interceptor.cjs.js +100 -0
  9. package/dist/audit-log/rest-errors-interceptor.cjs.js.map +1 -0
  10. package/dist/conditional-aliases/alias-resolver.cjs.js +76 -0
  11. package/dist/conditional-aliases/alias-resolver.cjs.js.map +1 -0
  12. package/dist/database/casbin-adapter-factory.cjs.js +87 -0
  13. package/dist/database/casbin-adapter-factory.cjs.js.map +1 -0
  14. package/dist/database/conditional-storage.cjs.js +172 -0
  15. package/dist/database/conditional-storage.cjs.js.map +1 -0
  16. package/dist/database/migration.cjs.js +21 -0
  17. package/dist/database/migration.cjs.js.map +1 -0
  18. package/dist/database/role-metadata.cjs.js +89 -0
  19. package/dist/database/role-metadata.cjs.js.map +1 -0
  20. package/dist/file-permissions/csv-file-watcher.cjs.js +407 -0
  21. package/dist/file-permissions/csv-file-watcher.cjs.js.map +1 -0
  22. package/dist/file-permissions/file-watcher.cjs.js +46 -0
  23. package/dist/file-permissions/file-watcher.cjs.js.map +1 -0
  24. package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js +208 -0
  25. package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js.map +1 -0
  26. package/dist/helper.cjs.js +171 -0
  27. package/dist/helper.cjs.js.map +1 -0
  28. package/dist/index.cjs.js +14 -0
  29. package/dist/index.cjs.js.map +1 -0
  30. package/dist/index.d.ts +45 -0
  31. package/dist/plugin.cjs.js +79 -0
  32. package/dist/plugin.cjs.js.map +1 -0
  33. package/dist/policies/allow-all-policy.cjs.js +12 -0
  34. package/dist/policies/allow-all-policy.cjs.js.map +1 -0
  35. package/dist/policies/permission-policy.cjs.js +243 -0
  36. package/dist/policies/permission-policy.cjs.js.map +1 -0
  37. package/dist/providers/connect-providers.cjs.js +211 -0
  38. package/dist/providers/connect-providers.cjs.js.map +1 -0
  39. package/dist/role-manager/ancestor-search-memo.cjs.js +159 -0
  40. package/dist/role-manager/ancestor-search-memo.cjs.js.map +1 -0
  41. package/dist/role-manager/member-list.cjs.js +101 -0
  42. package/dist/role-manager/member-list.cjs.js.map +1 -0
  43. package/dist/role-manager/role-manager.cjs.js +281 -0
  44. package/dist/role-manager/role-manager.cjs.js.map +1 -0
  45. package/dist/service/enforcer-delegate.cjs.js +353 -0
  46. package/dist/service/enforcer-delegate.cjs.js.map +1 -0
  47. package/dist/service/permission-model.cjs.js +21 -0
  48. package/dist/service/permission-model.cjs.js.map +1 -0
  49. package/dist/service/plugin-endpoints.cjs.js +121 -0
  50. package/dist/service/plugin-endpoints.cjs.js.map +1 -0
  51. package/dist/service/policies-rest-api.cjs.js +949 -0
  52. package/dist/service/policies-rest-api.cjs.js.map +1 -0
  53. package/dist/service/policy-builder.cjs.js +134 -0
  54. package/dist/service/policy-builder.cjs.js.map +1 -0
  55. package/dist/service/router.cjs.js +24 -0
  56. package/dist/service/router.cjs.js.map +1 -0
  57. package/dist/validation/condition-validation.cjs.js +107 -0
  58. package/dist/validation/condition-validation.cjs.js.map +1 -0
  59. package/dist/validation/policies-validation.cjs.js +194 -0
  60. package/dist/validation/policies-validation.cjs.js.map +1 -0
  61. package/migrations/20231015161232_migrations.js +41 -0
  62. package/migrations/20231212224526_migrations.js +84 -0
  63. package/migrations/20231221113214_migrations.js +60 -0
  64. package/migrations/20240201144429_migrations.js +37 -0
  65. package/migrations/20240215154456_migrations.js +143 -0
  66. package/migrations/20240308134410_migrations.js +31 -0
  67. package/migrations/20240308134941_migrations.js +43 -0
  68. package/migrations/20240404111242_migrations.js +53 -0
  69. package/migrations/20240611092136_migrations.js +29 -0
  70. package/package.json +98 -0
@@ -0,0 +1,243 @@
1
+ 'use strict';
2
+
3
+ var pluginPermissionCommon = require('@backstage/plugin-permission-common');
4
+ var pluginRbacCommon = require('@backstage-community/plugin-rbac-common');
5
+ var adminCreation = require('../admin-permissions/admin-creation.cjs.js');
6
+ var auditLogger = require('../audit-log/audit-logger.cjs.js');
7
+ var aliasResolver = require('../conditional-aliases/alias-resolver.cjs.js');
8
+ var csvFileWatcher = require('../file-permissions/csv-file-watcher.cjs.js');
9
+ var yamlConditionalFileWatcher = require('../file-permissions/yaml-conditional-file-watcher.cjs.js');
10
+
11
+ const evaluatePermMsg = (userEntityRef, result, permission) => `${userEntityRef} is ${result} for permission '${permission.name}'${pluginPermissionCommon.isResourcePermission(permission) ? `, resource type '${permission.resourceType}'` : ""} and action '${pluginRbacCommon.toPermissionAction(permission.attributes)}'`;
12
+ class RBACPermissionPolicy {
13
+ constructor(enforcer, auditLogger, conditionStorage, superUserList) {
14
+ this.enforcer = enforcer;
15
+ this.auditLogger = auditLogger;
16
+ this.conditionStorage = conditionStorage;
17
+ this.superUserList = superUserList;
18
+ }
19
+ superUserList;
20
+ static async build(logger, auditLogger, configApi, conditionalStorage, enforcerDelegate, roleMetadataStorage, knex, pluginMetadataCollector, auth) {
21
+ const superUserList = [];
22
+ const adminUsers = configApi.getOptionalConfigArray(
23
+ "permission.rbac.admin.users"
24
+ );
25
+ const superUsers = configApi.getOptionalConfigArray(
26
+ "permission.rbac.admin.superUsers"
27
+ );
28
+ const policiesFile = configApi.getOptionalString(
29
+ "permission.rbac.policies-csv-file"
30
+ );
31
+ const allowReload = configApi.getOptionalBoolean("permission.rbac.policyFileReload") || false;
32
+ const conditionalPoliciesFile = configApi.getOptionalString(
33
+ "permission.rbac.conditionalPoliciesFile"
34
+ );
35
+ if (superUsers && superUsers.length > 0) {
36
+ for (const user of superUsers) {
37
+ const userName = user.getString("name");
38
+ superUserList.push(userName);
39
+ }
40
+ }
41
+ await adminCreation.useAdminsFromConfig(
42
+ adminUsers || [],
43
+ enforcerDelegate,
44
+ auditLogger,
45
+ roleMetadataStorage,
46
+ knex
47
+ );
48
+ await adminCreation.setAdminPermissions(enforcerDelegate, auditLogger);
49
+ if ((!adminUsers || adminUsers.length === 0) && (!superUsers || superUsers.length === 0)) {
50
+ logger.warn(
51
+ "There are no admins or super admins configured for the RBAC-backend plugin."
52
+ );
53
+ }
54
+ const csvFile = new csvFileWatcher.CSVFileWatcher(
55
+ policiesFile,
56
+ allowReload,
57
+ logger,
58
+ enforcerDelegate,
59
+ roleMetadataStorage,
60
+ auditLogger
61
+ );
62
+ await csvFile.initialize();
63
+ const conditionalFile = new yamlConditionalFileWatcher.YamlConditinalPoliciesFileWatcher(
64
+ conditionalPoliciesFile,
65
+ allowReload,
66
+ logger,
67
+ conditionalStorage,
68
+ auditLogger,
69
+ auth,
70
+ pluginMetadataCollector,
71
+ roleMetadataStorage,
72
+ enforcerDelegate
73
+ );
74
+ await conditionalFile.initialize();
75
+ if (!conditionalPoliciesFile) {
76
+ logger.info("conditional policies file feature was disabled");
77
+ await conditionalFile.cleanUpConditionalPolicies();
78
+ }
79
+ if (!policiesFile) {
80
+ logger.info("csv policies file feature was disabled");
81
+ await csvFile.cleanUpRolesAndPolicies();
82
+ }
83
+ return new RBACPermissionPolicy(
84
+ enforcerDelegate,
85
+ auditLogger,
86
+ conditionalStorage,
87
+ superUserList
88
+ );
89
+ }
90
+ async handle(request, user) {
91
+ const userEntityRef = user?.info.userEntityRef ?? `user without entity`;
92
+ let auditOptions = auditLogger.createPermissionEvaluationOptions(
93
+ `Policy check for ${userEntityRef}`,
94
+ userEntityRef,
95
+ request
96
+ );
97
+ this.auditLogger.auditLog(auditOptions);
98
+ try {
99
+ let status = false;
100
+ const action = pluginRbacCommon.toPermissionAction(request.permission.attributes);
101
+ if (!user) {
102
+ const msg2 = evaluatePermMsg(
103
+ userEntityRef,
104
+ pluginPermissionCommon.AuthorizeResult.DENY,
105
+ request.permission
106
+ );
107
+ auditOptions = auditLogger.createPermissionEvaluationOptions(
108
+ msg2,
109
+ userEntityRef,
110
+ request,
111
+ { result: pluginPermissionCommon.AuthorizeResult.DENY }
112
+ );
113
+ await this.auditLogger.auditLog(auditOptions);
114
+ return { result: pluginPermissionCommon.AuthorizeResult.DENY };
115
+ }
116
+ const permissionName = request.permission.name;
117
+ const roles = await this.enforcer.getRolesForUser(userEntityRef);
118
+ if (pluginPermissionCommon.isResourcePermission(request.permission)) {
119
+ const resourceType = request.permission.resourceType;
120
+ if (user) {
121
+ const conditionResult = await this.handleConditions(
122
+ userEntityRef,
123
+ request,
124
+ roles,
125
+ user.info
126
+ );
127
+ if (conditionResult) {
128
+ return conditionResult;
129
+ }
130
+ }
131
+ const hasNamedPermission = await this.hasImplicitPermissionSpecifiedByName(
132
+ userEntityRef,
133
+ permissionName,
134
+ action
135
+ );
136
+ const obj = hasNamedPermission ? permissionName : resourceType;
137
+ status = await this.isAuthorized(userEntityRef, obj, action, roles);
138
+ } else {
139
+ status = await this.isAuthorized(
140
+ userEntityRef,
141
+ permissionName,
142
+ action,
143
+ roles
144
+ );
145
+ }
146
+ const result = status ? pluginPermissionCommon.AuthorizeResult.ALLOW : pluginPermissionCommon.AuthorizeResult.DENY;
147
+ const msg = evaluatePermMsg(userEntityRef, result, request.permission);
148
+ auditOptions = auditLogger.createPermissionEvaluationOptions(
149
+ msg,
150
+ userEntityRef,
151
+ request,
152
+ { result }
153
+ );
154
+ await this.auditLogger.auditLog(auditOptions);
155
+ return { result };
156
+ } catch (error) {
157
+ await this.auditLogger.auditLog({
158
+ message: "Permission policy check failed",
159
+ eventName: auditLogger.EvaluationEvents.PERMISSION_EVALUATION_FAILED,
160
+ stage: auditLogger.EVALUATE_PERMISSION_ACCESS_STAGE,
161
+ status: "failed",
162
+ errors: [error]
163
+ });
164
+ return { result: pluginPermissionCommon.AuthorizeResult.DENY };
165
+ }
166
+ }
167
+ async hasImplicitPermissionSpecifiedByName(userEntityRef, permissionName, action) {
168
+ const userPerms = await this.enforcer.getImplicitPermissionsForUser(
169
+ userEntityRef
170
+ );
171
+ for (const perm of userPerms) {
172
+ if (permissionName === perm[1] && action === perm[2]) {
173
+ return true;
174
+ }
175
+ }
176
+ return false;
177
+ }
178
+ isAuthorized = async (userIdentity, permission, action, roles) => {
179
+ if (this.superUserList.includes(userIdentity)) {
180
+ return true;
181
+ }
182
+ return await this.enforcer.enforce(userIdentity, permission, action, roles);
183
+ };
184
+ async handleConditions(userEntityRef, request, roles, userInfo) {
185
+ const permissionName = request.permission.name;
186
+ const resourceType = request.permission.resourceType;
187
+ const action = pluginRbacCommon.toPermissionAction(request.permission.attributes);
188
+ const conditions = [];
189
+ let pluginId = "";
190
+ for (const role of roles) {
191
+ const conditionalDecisions = await this.conditionStorage.filterConditions(
192
+ role,
193
+ void 0,
194
+ resourceType,
195
+ [action],
196
+ [permissionName]
197
+ );
198
+ if (conditionalDecisions.length === 1) {
199
+ pluginId = conditionalDecisions[0].pluginId;
200
+ conditions.push(conditionalDecisions[0].conditions);
201
+ }
202
+ if (conditionalDecisions.length > 1) {
203
+ const msg = `Detected ${JSON.stringify(
204
+ conditionalDecisions
205
+ )} collisions for conditional policies. Expected to find a stored single condition for permission with name ${permissionName}, resource type ${resourceType}, action ${action} for user ${userEntityRef}`;
206
+ const auditOptions = auditLogger.createPermissionEvaluationOptions(
207
+ msg,
208
+ userEntityRef,
209
+ request,
210
+ { result: pluginPermissionCommon.AuthorizeResult.DENY }
211
+ );
212
+ await this.auditLogger.auditLog(auditOptions);
213
+ return {
214
+ result: pluginPermissionCommon.AuthorizeResult.DENY
215
+ };
216
+ }
217
+ }
218
+ if (conditions.length > 0) {
219
+ const result = {
220
+ pluginId,
221
+ result: pluginPermissionCommon.AuthorizeResult.CONDITIONAL,
222
+ resourceType,
223
+ conditions: {
224
+ anyOf: conditions
225
+ }
226
+ };
227
+ aliasResolver.replaceAliases(result.conditions, userInfo);
228
+ const msg = `Send condition to plugin with id ${pluginId} to evaluate permission ${permissionName} with resource type ${resourceType} and action ${action} for user ${userEntityRef}`;
229
+ const auditOptions = auditLogger.createPermissionEvaluationOptions(
230
+ msg,
231
+ userEntityRef,
232
+ request,
233
+ result
234
+ );
235
+ await this.auditLogger.auditLog(auditOptions);
236
+ return result;
237
+ }
238
+ return void 0;
239
+ }
240
+ }
241
+
242
+ exports.RBACPermissionPolicy = RBACPermissionPolicy;
243
+ //# sourceMappingURL=permission-policy.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-policy.cjs.js","sources":["../../src/policies/permission-policy.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 {\n AuthService,\n BackstageUserInfo,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport type { ConfigApi } from '@backstage/core-plugin-api';\nimport {\n AuthorizeResult,\n ConditionalPolicyDecision,\n isResourcePermission,\n Permission,\n PermissionCondition,\n PermissionCriteria,\n PermissionRuleParams,\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport type {\n PermissionPolicy,\n PolicyQuery,\n PolicyQueryUser,\n} from '@backstage/plugin-permission-node';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport type { Knex } from 'knex';\n\nimport {\n NonEmptyArray,\n toPermissionAction,\n} from '@backstage-community/plugin-rbac-common';\n\nimport {\n setAdminPermissions,\n useAdminsFromConfig,\n} from '../admin-permissions/admin-creation';\nimport {\n createPermissionEvaluationOptions,\n EVALUATE_PERMISSION_ACCESS_STAGE,\n EvaluationEvents,\n} from '../audit-log/audit-logger';\nimport { replaceAliases } from '../conditional-aliases/alias-resolver';\nimport { ConditionalStorage } from '../database/conditional-storage';\nimport { RoleMetadataStorage } from '../database/role-metadata';\nimport { CSVFileWatcher } from '../file-permissions/csv-file-watcher';\nimport { YamlConditinalPoliciesFileWatcher } from '../file-permissions/yaml-conditional-file-watcher';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from '../service/plugin-endpoints';\n\nconst evaluatePermMsg = (\n userEntityRef: string | undefined,\n result: AuthorizeResult,\n permission: Permission,\n) =>\n `${userEntityRef} is ${result} for permission '${permission.name}'${\n isResourcePermission(permission)\n ? `, resource type '${permission.resourceType}'`\n : ''\n } and action '${toPermissionAction(permission.attributes)}'`;\n\nexport class RBACPermissionPolicy implements PermissionPolicy {\n private readonly superUserList?: string[];\n\n public static async build(\n logger: LoggerService,\n auditLogger: AuditLogger,\n configApi: ConfigApi,\n conditionalStorage: ConditionalStorage,\n enforcerDelegate: EnforcerDelegate,\n roleMetadataStorage: RoleMetadataStorage,\n knex: Knex,\n pluginMetadataCollector: PluginPermissionMetadataCollector,\n auth: AuthService,\n ): Promise<RBACPermissionPolicy> {\n const superUserList: string[] = [];\n const adminUsers = configApi.getOptionalConfigArray(\n 'permission.rbac.admin.users',\n );\n\n const superUsers = configApi.getOptionalConfigArray(\n 'permission.rbac.admin.superUsers',\n );\n\n const policiesFile = configApi.getOptionalString(\n 'permission.rbac.policies-csv-file',\n );\n\n const allowReload =\n configApi.getOptionalBoolean('permission.rbac.policyFileReload') || false;\n\n const conditionalPoliciesFile = configApi.getOptionalString(\n 'permission.rbac.conditionalPoliciesFile',\n );\n\n if (superUsers && superUsers.length > 0) {\n for (const user of superUsers) {\n const userName = user.getString('name');\n superUserList.push(userName);\n }\n }\n\n await useAdminsFromConfig(\n adminUsers || [],\n enforcerDelegate,\n auditLogger,\n roleMetadataStorage,\n knex,\n );\n await setAdminPermissions(enforcerDelegate, auditLogger);\n\n if (\n (!adminUsers || adminUsers.length === 0) &&\n (!superUsers || superUsers.length === 0)\n ) {\n logger.warn(\n 'There are no admins or super admins configured for the RBAC-backend plugin.',\n );\n }\n\n const csvFile = new CSVFileWatcher(\n policiesFile,\n allowReload,\n logger,\n enforcerDelegate,\n roleMetadataStorage,\n auditLogger,\n );\n await csvFile.initialize();\n\n const conditionalFile = new YamlConditinalPoliciesFileWatcher(\n conditionalPoliciesFile,\n allowReload,\n logger,\n conditionalStorage,\n auditLogger,\n auth,\n pluginMetadataCollector,\n roleMetadataStorage,\n enforcerDelegate,\n );\n await conditionalFile.initialize();\n\n if (!conditionalPoliciesFile) {\n // clean up conditional policies corresponding to roles from csv file\n logger.info('conditional policies file feature was disabled');\n await conditionalFile.cleanUpConditionalPolicies();\n }\n if (!policiesFile) {\n // remove roles and policies from csv file\n logger.info('csv policies file feature was disabled');\n await csvFile.cleanUpRolesAndPolicies();\n }\n\n return new RBACPermissionPolicy(\n enforcerDelegate,\n auditLogger,\n conditionalStorage,\n superUserList,\n );\n }\n\n private constructor(\n private readonly enforcer: EnforcerDelegate,\n private readonly auditLogger: AuditLogger,\n private readonly conditionStorage: ConditionalStorage,\n superUserList?: string[],\n ) {\n this.superUserList = superUserList;\n }\n\n async handle(\n request: PolicyQuery,\n user?: PolicyQueryUser,\n ): Promise<PolicyDecision> {\n const userEntityRef = user?.info.userEntityRef ?? `user without entity`;\n\n let auditOptions = createPermissionEvaluationOptions(\n `Policy check for ${userEntityRef}`,\n userEntityRef,\n request,\n );\n this.auditLogger.auditLog(auditOptions);\n\n try {\n let status = false;\n\n const action = toPermissionAction(request.permission.attributes);\n if (!user) {\n const msg = evaluatePermMsg(\n userEntityRef,\n AuthorizeResult.DENY,\n request.permission,\n );\n auditOptions = createPermissionEvaluationOptions(\n msg,\n userEntityRef,\n request,\n { result: AuthorizeResult.DENY },\n );\n await this.auditLogger.auditLog(auditOptions);\n return { result: AuthorizeResult.DENY };\n }\n\n const permissionName = request.permission.name;\n const roles = await this.enforcer.getRolesForUser(userEntityRef);\n\n if (isResourcePermission(request.permission)) {\n const resourceType = request.permission.resourceType;\n\n // handle conditions if they are present\n if (user) {\n const conditionResult = await this.handleConditions(\n userEntityRef,\n request,\n roles,\n user.info,\n );\n if (conditionResult) {\n return conditionResult;\n }\n }\n\n // handle permission with 'resource' type\n const hasNamedPermission =\n await this.hasImplicitPermissionSpecifiedByName(\n userEntityRef,\n permissionName,\n action,\n );\n // Let's set up higher priority for permission specified by name, than by resource type\n const obj = hasNamedPermission ? permissionName : resourceType;\n\n status = await this.isAuthorized(userEntityRef, obj, action, roles);\n } else {\n // handle permission with 'basic' type\n status = await this.isAuthorized(\n userEntityRef,\n permissionName,\n action,\n roles,\n );\n }\n\n const result = status ? AuthorizeResult.ALLOW : AuthorizeResult.DENY;\n\n const msg = evaluatePermMsg(userEntityRef, result, request.permission);\n auditOptions = createPermissionEvaluationOptions(\n msg,\n userEntityRef,\n request,\n { result },\n );\n await this.auditLogger.auditLog(auditOptions);\n return { result };\n } catch (error) {\n await this.auditLogger.auditLog({\n message: 'Permission policy check failed',\n eventName: EvaluationEvents.PERMISSION_EVALUATION_FAILED,\n stage: EVALUATE_PERMISSION_ACCESS_STAGE,\n status: 'failed',\n errors: [error],\n });\n return { result: AuthorizeResult.DENY };\n }\n }\n\n private async hasImplicitPermissionSpecifiedByName(\n userEntityRef: string,\n permissionName: string,\n action: string,\n ): Promise<boolean> {\n const userPerms = await this.enforcer.getImplicitPermissionsForUser(\n userEntityRef,\n );\n for (const perm of userPerms) {\n if (permissionName === perm[1] && action === perm[2]) {\n return true;\n }\n }\n return false;\n }\n\n private isAuthorized = async (\n userIdentity: string,\n permission: string,\n action: string,\n roles: string[],\n ): Promise<boolean> => {\n if (this.superUserList!.includes(userIdentity)) {\n return true;\n }\n\n return await this.enforcer.enforce(userIdentity, permission, action, roles);\n };\n\n private async handleConditions(\n userEntityRef: string,\n request: PolicyQuery,\n roles: string[],\n userInfo: BackstageUserInfo,\n ): Promise<PolicyDecision | undefined> {\n const permissionName = request.permission.name;\n const resourceType = (request.permission as ResourcePermission)\n .resourceType;\n const action = toPermissionAction(request.permission.attributes);\n\n const conditions: PermissionCriteria<\n PermissionCondition<string, PermissionRuleParams>\n >[] = [];\n let pluginId = '';\n for (const role of roles) {\n const conditionalDecisions = await this.conditionStorage.filterConditions(\n role,\n undefined,\n resourceType,\n [action],\n [permissionName],\n );\n\n if (conditionalDecisions.length === 1) {\n pluginId = conditionalDecisions[0].pluginId;\n conditions.push(conditionalDecisions[0].conditions);\n }\n\n // this error is unexpected and should not happen, but just in case handle it.\n if (conditionalDecisions.length > 1) {\n const msg = `Detected ${JSON.stringify(\n conditionalDecisions,\n )} collisions for conditional policies. Expected to find a stored single condition for permission with name ${permissionName}, resource type ${resourceType}, action ${action} for user ${userEntityRef}`;\n const auditOptions = createPermissionEvaluationOptions(\n msg,\n userEntityRef,\n request,\n { result: AuthorizeResult.DENY },\n );\n await this.auditLogger.auditLog(auditOptions);\n return {\n result: AuthorizeResult.DENY,\n };\n }\n }\n\n if (conditions.length > 0) {\n const result: ConditionalPolicyDecision = {\n pluginId,\n result: AuthorizeResult.CONDITIONAL,\n resourceType,\n conditions: {\n anyOf: conditions as NonEmptyArray<\n PermissionCriteria<\n PermissionCondition<string, PermissionRuleParams>\n >\n >,\n },\n };\n\n replaceAliases(result.conditions, userInfo);\n\n const msg = `Send condition to plugin with id ${pluginId} to evaluate permission ${permissionName} with resource type ${resourceType} and action ${action} for user ${userEntityRef}`;\n const auditOptions = createPermissionEvaluationOptions(\n msg,\n userEntityRef,\n request,\n result,\n );\n await this.auditLogger.auditLog(auditOptions);\n return result;\n }\n return undefined;\n }\n}\n"],"names":["isResourcePermission","toPermissionAction","useAdminsFromConfig","setAdminPermissions","CSVFileWatcher","YamlConditinalPoliciesFileWatcher","createPermissionEvaluationOptions","msg","AuthorizeResult","EvaluationEvents","EVALUATE_PERMISSION_ACCESS_STAGE","replaceAliases"],"mappings":"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"}
@@ -0,0 +1,211 @@
1
+ 'use strict';
2
+
3
+ var casbin = require('casbin');
4
+ var auditLogger = require('../audit-log/audit-logger.cjs.js');
5
+ var helper = require('../helper.cjs.js');
6
+ var permissionModel = require('../service/permission-model.cjs.js');
7
+ var policiesValidation = require('../validation/policies-validation.cjs.js');
8
+
9
+ class Connection {
10
+ constructor(id, enforcer, roleMetadataStorage, logger, auditLogger) {
11
+ this.id = id;
12
+ this.enforcer = enforcer;
13
+ this.roleMetadataStorage = roleMetadataStorage;
14
+ this.logger = logger;
15
+ this.auditLogger = auditLogger;
16
+ }
17
+ async applyRoles(roles) {
18
+ const stringPolicy = helper.typedPoliciesToString(roles, "g");
19
+ const providerRolesforRemoval = [];
20
+ const tempEnforcer = await casbin.newEnforcer(
21
+ casbin.newModelFromString(permissionModel.MODEL),
22
+ new casbin.StringAdapter(stringPolicy)
23
+ );
24
+ const providerRoles = await this.getProviderRoles();
25
+ for (const providerRole of providerRoles) {
26
+ providerRolesforRemoval.push(
27
+ ...await this.enforcer.getFilteredGroupingPolicy(1, providerRole)
28
+ );
29
+ }
30
+ await this.removeRoles(providerRolesforRemoval, tempEnforcer);
31
+ await this.addRoles(roles);
32
+ }
33
+ async applyPermissions(permissions) {
34
+ const stringPolicy = helper.typedPoliciesToString(permissions, "p");
35
+ const providerPermissions = [];
36
+ const tempEnforcer = await casbin.newEnforcer(
37
+ casbin.newModelFromString(permissionModel.MODEL),
38
+ new casbin.StringAdapter(stringPolicy)
39
+ );
40
+ const providerRoles = await this.getProviderRoles();
41
+ for (const providerRole of providerRoles) {
42
+ providerPermissions.push(
43
+ ...await this.enforcer.getFilteredPolicy(0, providerRole)
44
+ );
45
+ }
46
+ await this.removePermissions(providerPermissions, tempEnforcer);
47
+ await this.addPermissions(permissions);
48
+ }
49
+ async addRoles(roles) {
50
+ for (const role of roles) {
51
+ if (!await this.enforcer.hasGroupingPolicy(...role)) {
52
+ const err = await policiesValidation.validateGroupingPolicy(
53
+ role,
54
+ this.roleMetadataStorage,
55
+ this.id
56
+ );
57
+ if (err) {
58
+ this.logger.warn(err.message);
59
+ continue;
60
+ }
61
+ let roleMeta = await this.roleMetadataStorage.findRoleMetadata(role[1]);
62
+ const eventName = roleMeta ? auditLogger.RoleEvents.UPDATE_ROLE : auditLogger.RoleEvents.CREATE_ROLE;
63
+ const message = roleMeta ? "Updated role" : "Created role";
64
+ if (!roleMeta) {
65
+ roleMeta = {
66
+ modifiedBy: this.id,
67
+ source: this.id,
68
+ roleEntityRef: role[1]
69
+ };
70
+ }
71
+ await this.enforcer.addGroupingPolicy(role, roleMeta);
72
+ await this.auditLogger.auditLog({
73
+ actorId: auditLogger.RBAC_BACKEND,
74
+ message,
75
+ eventName,
76
+ metadata: { ...roleMeta, members: [role[0]] },
77
+ stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
78
+ status: "succeeded"
79
+ });
80
+ }
81
+ }
82
+ }
83
+ async removeRoles(providerRoles, tempEnforcer) {
84
+ for (const role of providerRoles) {
85
+ if (!await tempEnforcer.hasGroupingPolicy(...role)) {
86
+ const roleMeta = await this.roleMetadataStorage.findRoleMetadata(
87
+ role[1]
88
+ );
89
+ const currentRole = await this.enforcer.getFilteredGroupingPolicy(
90
+ 1,
91
+ role[1]
92
+ );
93
+ if (!roleMeta) {
94
+ this.logger.warn("role does not exist");
95
+ continue;
96
+ }
97
+ const singleRole = roleMeta && currentRole.length === 1;
98
+ let eventName;
99
+ let message;
100
+ if (singleRole) {
101
+ eventName = auditLogger.RoleEvents.DELETE_ROLE;
102
+ message = "Deleted role";
103
+ await this.enforcer.removeGroupingPolicy(role, roleMeta);
104
+ await this.auditLogger.auditLog({
105
+ actorId: auditLogger.RBAC_BACKEND,
106
+ message,
107
+ eventName,
108
+ metadata: { ...roleMeta, members: [role[0]] },
109
+ stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
110
+ status: "succeeded"
111
+ });
112
+ continue;
113
+ }
114
+ eventName = auditLogger.RoleEvents.UPDATE_ROLE;
115
+ message = "Updated role: deleted members";
116
+ await this.enforcer.removeGroupingPolicy(role, roleMeta, true);
117
+ await this.auditLogger.auditLog({
118
+ actorId: auditLogger.RBAC_BACKEND,
119
+ message,
120
+ eventName,
121
+ metadata: { ...roleMeta, members: [role[0]] },
122
+ stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
123
+ status: "succeeded"
124
+ });
125
+ }
126
+ }
127
+ }
128
+ async addPermissions(permissions) {
129
+ for (const permission of permissions) {
130
+ if (!await this.enforcer.hasPolicy(...permission)) {
131
+ const transformedPolicy = helper.transformArrayToPolicy(permission);
132
+ const metadata = await this.roleMetadataStorage.findRoleMetadata(
133
+ permission[0]
134
+ );
135
+ let err = policiesValidation.validatePolicy(transformedPolicy);
136
+ if (err) {
137
+ this.logger.warn(`Invalid permission policy, ${err}`);
138
+ continue;
139
+ }
140
+ err = await policiesValidation.validateSource(this.id, metadata);
141
+ if (err) {
142
+ this.logger.warn(
143
+ `Unable to add policy ${permission}. Cause: ${err.message}`
144
+ );
145
+ continue;
146
+ }
147
+ await this.enforcer.addPolicy(permission);
148
+ await this.auditLogger.auditLog({
149
+ actorId: auditLogger.RBAC_BACKEND,
150
+ message: `Created policy`,
151
+ eventName: auditLogger.PermissionEvents.CREATE_POLICY,
152
+ metadata: { policies: [permission], source: this.id },
153
+ stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
154
+ status: "succeeded"
155
+ });
156
+ }
157
+ }
158
+ }
159
+ async removePermissions(providerPermissions, tempEnforcer) {
160
+ const removedPermissions = [];
161
+ for (const permission of providerPermissions) {
162
+ if (!await tempEnforcer.hasPolicy(...permission)) {
163
+ this.enforcer.removePolicy(permission);
164
+ removedPermissions.push(permission);
165
+ }
166
+ if (removedPermissions.length > 0) {
167
+ await this.auditLogger.auditLog({
168
+ actorId: auditLogger.RBAC_BACKEND,
169
+ message: `Deleted policies`,
170
+ eventName: auditLogger.PermissionEvents.DELETE_POLICY,
171
+ metadata: {
172
+ policies: removedPermissions,
173
+ source: this.id
174
+ },
175
+ stage: auditLogger.HANDLE_RBAC_DATA_STAGE,
176
+ status: "succeeded"
177
+ });
178
+ }
179
+ }
180
+ }
181
+ async getProviderRoles() {
182
+ const currentRoles = await this.roleMetadataStorage.filterRoleMetadata(
183
+ this.id
184
+ );
185
+ return currentRoles.map((meta) => meta.roleEntityRef);
186
+ }
187
+ }
188
+ async function connectRBACProviders(providers, enforcer, roleMetadataStorage, logger, auditLogger) {
189
+ await Promise.all(
190
+ providers.map(async (provider) => {
191
+ try {
192
+ const connection = new Connection(
193
+ provider.getProviderName(),
194
+ enforcer,
195
+ roleMetadataStorage,
196
+ logger,
197
+ auditLogger
198
+ );
199
+ return provider.connect(connection);
200
+ } catch (error) {
201
+ throw new Error(
202
+ `Unable to connect provider ${provider.getProviderName()}, ${error}`
203
+ );
204
+ }
205
+ })
206
+ );
207
+ }
208
+
209
+ exports.Connection = Connection;
210
+ exports.connectRBACProviders = connectRBACProviders;
211
+ //# sourceMappingURL=connect-providers.cjs.js.map
@@ -0,0 +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 { transformArrayToPolicy, typedPoliciesToString } 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 stringPolicy = typedPoliciesToString(roles, 'g');\n\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 // 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(roles);\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 // 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 for (const role of providerRoles) {\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 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":["typedPoliciesToString","newEnforcer","newModelFromString","MODEL","StringAdapter","validateGroupingPolicy","RoleEvents","RBAC_BACKEND","HANDLE_RBAC_DATA_STAGE","transformArrayToPolicy","validatePolicy","validateSource","PermissionEvents"],"mappings":"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"}