@backstage-community/plugin-rbac-backend 5.2.4 → 5.2.6

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 (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/admin-permissions/admin-creation.cjs.js +1 -3
  3. package/dist/admin-permissions/admin-creation.cjs.js.map +1 -1
  4. package/dist/audit-log/audit-logger.cjs.js.map +1 -1
  5. package/dist/audit-log/rest-errors-interceptor.cjs.js.map +1 -1
  6. package/dist/conditional-aliases/alias-resolver.cjs.js.map +1 -1
  7. package/dist/database/casbin-adapter-factory.cjs.js.map +1 -1
  8. package/dist/database/conditional-storage.cjs.js.map +1 -1
  9. package/dist/database/migration.cjs.js.map +1 -1
  10. package/dist/database/role-metadata.cjs.js.map +1 -1
  11. package/dist/file-permissions/csv-file-watcher.cjs.js +4 -30
  12. package/dist/file-permissions/csv-file-watcher.cjs.js.map +1 -1
  13. package/dist/file-permissions/file-watcher.cjs.js.map +1 -1
  14. package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js +2 -6
  15. package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js.map +1 -1
  16. package/dist/helper.cjs.js.map +1 -1
  17. package/dist/plugin.cjs.js.map +1 -1
  18. package/dist/policies/allow-all-policy.cjs.js.map +1 -1
  19. package/dist/policies/permission-policy.cjs.js +1 -3
  20. package/dist/policies/permission-policy.cjs.js.map +1 -1
  21. package/dist/providers/connect-providers.cjs.js +1 -1
  22. package/dist/providers/connect-providers.cjs.js.map +1 -1
  23. package/dist/role-manager/ancestor-search-memo.cjs.js.map +1 -1
  24. package/dist/role-manager/member-list.cjs.js.map +1 -1
  25. package/dist/role-manager/role-manager.cjs.js.map +1 -1
  26. package/dist/service/enforcer-delegate.cjs.js +72 -10
  27. package/dist/service/enforcer-delegate.cjs.js.map +1 -1
  28. package/dist/service/permission-model.cjs.js.map +1 -1
  29. package/dist/service/plugin-endpoints.cjs.js.map +1 -1
  30. package/dist/service/policies-rest-api.cjs.js +4 -12
  31. package/dist/service/policies-rest-api.cjs.js.map +1 -1
  32. package/dist/service/policy-builder.cjs.js.map +1 -1
  33. package/dist/service/router.cjs.js.map +1 -1
  34. package/dist/validation/condition-validation.cjs.js.map +1 -1
  35. package/dist/validation/policies-validation.cjs.js.map +1 -1
  36. package/package.json +14 -4
@@ -53,9 +53,7 @@ class YamlConditinalPoliciesFileWatcher extends fileWatcher.AbstractFileWatcher
53
53
  const newConds = this.parse().filter((c) => c);
54
54
  const addedConds = [];
55
55
  const removedConds = [];
56
- const csvFileRoles = await this.roleMetadataStorage.filterRoleMetadata(
57
- "csv-file"
58
- );
56
+ const csvFileRoles = await this.roleMetadataStorage.filterRoleMetadata("csv-file");
59
57
  const existedFileConds = (await this.conditionalStorage.filterConditions(
60
58
  csvFileRoles.map((role) => role.roleEntityRef)
61
59
  )).map((condition) => {
@@ -188,9 +186,7 @@ class YamlConditinalPoliciesFileWatcher extends fileWatcher.AbstractFileWatcher
188
186
  });
189
187
  }
190
188
  async cleanUpConditionalPolicies() {
191
- const csvFileRoles = await this.roleMetadataStorage.filterRoleMetadata(
192
- "csv-file"
193
- );
189
+ const csvFileRoles = await this.roleMetadataStorage.filterRoleMetadata("csv-file");
194
190
  const existedFileConds = (await this.conditionalStorage.filterConditions(
195
191
  csvFileRoles.map((role) => role.roleEntityRef)
196
192
  )).map((condition) => {
@@ -1 +1 @@
1
- {"version":3,"file":"yaml-conditional-file-watcher.cjs.js","sources":["../../src/file-permissions/yaml-conditional-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 { AuthService, LoggerService } from '@backstage/backend-plugin-api';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport yaml from 'js-yaml';\nimport { omit } from 'lodash';\n\nimport type {\n PermissionAction,\n RoleConditionalPolicyDecision,\n} from '@backstage-community/plugin-rbac-common';\n\nimport fs from 'fs';\n\nimport {\n ConditionAuditInfo,\n ConditionEvents,\n HANDLE_RBAC_DATA_STAGE,\n} from '../audit-log/audit-logger';\nimport { ConditionalStorage } from '../database/conditional-storage';\nimport { RoleMetadataStorage } from '../database/role-metadata';\nimport { deepSortEqual, processConditionMapping } from '../helper';\nimport { RoleEventEmitter, RoleEvents } from '../service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from '../service/plugin-endpoints';\nimport { validateRoleCondition } from '../validation/condition-validation';\nimport { AbstractFileWatcher } from './file-watcher';\n\ntype ConditionalPoliciesDiff = {\n addedConditions: RoleConditionalPolicyDecision<PermissionAction>[];\n removedConditions: RoleConditionalPolicyDecision<PermissionAction>[];\n};\n\nexport class YamlConditinalPoliciesFileWatcher extends AbstractFileWatcher<\n RoleConditionalPolicyDecision<PermissionAction>[]\n> {\n private conditionsDiff: ConditionalPoliciesDiff;\n\n constructor(\n filePath: string | undefined,\n allowReload: boolean,\n logger: LoggerService,\n private readonly conditionalStorage: ConditionalStorage,\n private readonly auditLogger: AuditLogger,\n private readonly auth: AuthService,\n private readonly pluginMetadataCollector: PluginPermissionMetadataCollector,\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly roleEventEmitter: RoleEventEmitter<RoleEvents>,\n ) {\n super(filePath, allowReload, logger);\n\n this.conditionsDiff = {\n addedConditions: [],\n removedConditions: [],\n };\n }\n\n async initialize(): Promise<void> {\n if (!this.filePath) {\n return;\n }\n const fileExists = fs.existsSync(this.filePath);\n if (!fileExists) {\n const err = new Error(`File '${this.filePath}' was not found`);\n this.handleError(\n err.message,\n err,\n ConditionEvents.CONDITIONAL_POLICIES_FILE_NOT_FOUND,\n );\n return;\n }\n\n this.roleEventEmitter.on('roleAdded', this.onChange.bind(this));\n await this.onChange();\n\n if (this.allowReload) {\n this.watchFile();\n }\n }\n\n async onChange(): Promise<void> {\n try {\n const newConds = this.parse().filter(c => c);\n\n const addedConds: RoleConditionalPolicyDecision<PermissionAction>[] = [];\n const removedConds: RoleConditionalPolicyDecision<PermissionAction>[] =\n [];\n\n const csvFileRoles = await this.roleMetadataStorage.filterRoleMetadata(\n 'csv-file',\n );\n const existedFileConds = (\n await this.conditionalStorage.filterConditions(\n csvFileRoles.map(role => role.roleEntityRef),\n )\n ).map(condition => {\n return {\n ...condition,\n permissionMapping: condition.permissionMapping.map(pm => pm.action),\n };\n });\n\n // Find added conditions\n for (const condition of newConds) {\n const roleMetadata = csvFileRoles.find(\n role => condition.roleEntityRef === role.roleEntityRef,\n );\n if (!roleMetadata) {\n this.logger.warn(\n `skip to add condition for role '${condition.roleEntityRef}'. The role either does not exist or was not created from a CSV file.`,\n );\n continue;\n }\n if (roleMetadata.source !== 'csv-file') {\n this.logger.warn(\n `skip to add condition for role '${condition.roleEntityRef}'. Role is not from csv-file`,\n );\n continue;\n }\n\n const existingCondition = existedFileConds.find(c =>\n deepSortEqual(omit(c, ['id']), omit(condition, ['id'])),\n );\n\n if (!existingCondition) {\n addedConds.push(condition);\n }\n }\n\n // Find removed conditions\n for (const condition of existedFileConds) {\n if (\n !newConds.find(c =>\n deepSortEqual(omit(c, ['id']), omit(condition, ['id'])),\n )\n ) {\n removedConds.push(condition);\n }\n }\n\n this.conditionsDiff = {\n addedConditions: addedConds,\n removedConditions: removedConds,\n };\n\n await this.handleFileChanges();\n } catch (error) {\n await this.handleError(\n `Error handling changes from conditional policies file ${this.filePath}`,\n error,\n ConditionEvents.CHANGE_CONDITIONAL_POLICIES_FILE_ERROR,\n );\n }\n }\n\n /**\n * Reads the current contents of the file and parses it.\n * @returns parsed data.\n */\n parse(): RoleConditionalPolicyDecision<PermissionAction>[] {\n const fileContents = this.getCurrentContents();\n const data = yaml.loadAll(\n fileContents,\n ) as RoleConditionalPolicyDecision<PermissionAction>[];\n\n for (const condition of data) {\n validateRoleCondition(condition);\n }\n\n return data;\n }\n\n private async handleFileChanges(): Promise<void> {\n await this.removeConditions();\n await this.addConditions();\n }\n\n private async addConditions(): Promise<void> {\n try {\n for (const condition of this.conditionsDiff.addedConditions) {\n const conditionToCreate = await processConditionMapping(\n condition,\n this.pluginMetadataCollector,\n this.auth,\n );\n\n await this.conditionalStorage.createCondition(conditionToCreate);\n\n await this.auditLogger.auditLog<ConditionAuditInfo>({\n message: `Created conditional permission policy`,\n eventName: ConditionEvents.CREATE_CONDITION,\n metadata: { condition },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n } catch (error) {\n await this.handleError(\n 'Failed to create condition',\n error,\n ConditionEvents.CREATE_CONDITION_ERROR,\n );\n }\n this.conditionsDiff.addedConditions = [];\n }\n\n private async removeConditions(): Promise<void> {\n try {\n for (const condition of this.conditionsDiff.removedConditions) {\n const conditionToDelete = (\n await this.conditionalStorage.filterConditions(\n condition.roleEntityRef,\n condition.pluginId,\n condition.resourceType,\n condition.permissionMapping,\n )\n )[0];\n await this.conditionalStorage.deleteCondition(conditionToDelete.id!);\n\n await this.auditLogger.auditLog<ConditionAuditInfo>({\n message: `Deleted conditional permission policy`,\n eventName: ConditionEvents.DELETE_CONDITION,\n metadata: { condition },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n } catch (error) {\n await this.handleError(\n 'Failed to delete condition by id',\n error,\n ConditionEvents.DELETE_CONDITION_ERROR,\n );\n }\n\n this.conditionsDiff.removedConditions = [];\n }\n\n private async handleError(\n message: string,\n error: unknown,\n event: string,\n ): Promise<void> {\n await this.auditLogger.auditLog({\n message,\n eventName: event,\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'failed',\n errors: [error],\n });\n }\n\n async cleanUpConditionalPolicies(): Promise<void> {\n const csvFileRoles = await this.roleMetadataStorage.filterRoleMetadata(\n 'csv-file',\n );\n const existedFileConds = (\n await this.conditionalStorage.filterConditions(\n csvFileRoles.map(role => role.roleEntityRef),\n )\n ).map(condition => {\n return {\n ...condition,\n permissionMapping: condition.permissionMapping.map(pm => pm.action),\n };\n });\n this.conditionsDiff.removedConditions = existedFileConds;\n await this.removeConditions();\n }\n}\n"],"names":["AbstractFileWatcher","fs","ConditionEvents","deepSortEqual","omit","yaml","validateRoleCondition","processConditionMapping","HANDLE_RBAC_DATA_STAGE"],"mappings":"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"}
1
+ {"version":3,"file":"yaml-conditional-file-watcher.cjs.js","sources":["../../src/file-permissions/yaml-conditional-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 { AuthService, LoggerService } from '@backstage/backend-plugin-api';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport yaml from 'js-yaml';\nimport { omit } from 'lodash';\n\nimport type {\n PermissionAction,\n RoleConditionalPolicyDecision,\n} from '@backstage-community/plugin-rbac-common';\n\nimport fs from 'fs';\n\nimport {\n ConditionAuditInfo,\n ConditionEvents,\n HANDLE_RBAC_DATA_STAGE,\n} from '../audit-log/audit-logger';\nimport { ConditionalStorage } from '../database/conditional-storage';\nimport { RoleMetadataStorage } from '../database/role-metadata';\nimport { deepSortEqual, processConditionMapping } from '../helper';\nimport { RoleEventEmitter, RoleEvents } from '../service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from '../service/plugin-endpoints';\nimport { validateRoleCondition } from '../validation/condition-validation';\nimport { AbstractFileWatcher } from './file-watcher';\n\ntype ConditionalPoliciesDiff = {\n addedConditions: RoleConditionalPolicyDecision<PermissionAction>[];\n removedConditions: RoleConditionalPolicyDecision<PermissionAction>[];\n};\n\nexport class YamlConditinalPoliciesFileWatcher extends AbstractFileWatcher<\n RoleConditionalPolicyDecision<PermissionAction>[]\n> {\n private conditionsDiff: ConditionalPoliciesDiff;\n\n constructor(\n filePath: string | undefined,\n allowReload: boolean,\n logger: LoggerService,\n private readonly conditionalStorage: ConditionalStorage,\n private readonly auditLogger: AuditLogger,\n private readonly auth: AuthService,\n private readonly pluginMetadataCollector: PluginPermissionMetadataCollector,\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly roleEventEmitter: RoleEventEmitter<RoleEvents>,\n ) {\n super(filePath, allowReload, logger);\n\n this.conditionsDiff = {\n addedConditions: [],\n removedConditions: [],\n };\n }\n\n async initialize(): Promise<void> {\n if (!this.filePath) {\n return;\n }\n const fileExists = fs.existsSync(this.filePath);\n if (!fileExists) {\n const err = new Error(`File '${this.filePath}' was not found`);\n this.handleError(\n err.message,\n err,\n ConditionEvents.CONDITIONAL_POLICIES_FILE_NOT_FOUND,\n );\n return;\n }\n\n this.roleEventEmitter.on('roleAdded', this.onChange.bind(this));\n await this.onChange();\n\n if (this.allowReload) {\n this.watchFile();\n }\n }\n\n async onChange(): Promise<void> {\n try {\n const newConds = this.parse().filter(c => c);\n\n const addedConds: RoleConditionalPolicyDecision<PermissionAction>[] = [];\n const removedConds: RoleConditionalPolicyDecision<PermissionAction>[] =\n [];\n\n const csvFileRoles =\n await this.roleMetadataStorage.filterRoleMetadata('csv-file');\n const existedFileConds = (\n await this.conditionalStorage.filterConditions(\n csvFileRoles.map(role => role.roleEntityRef),\n )\n ).map(condition => {\n return {\n ...condition,\n permissionMapping: condition.permissionMapping.map(pm => pm.action),\n };\n });\n\n // Find added conditions\n for (const condition of newConds) {\n const roleMetadata = csvFileRoles.find(\n role => condition.roleEntityRef === role.roleEntityRef,\n );\n if (!roleMetadata) {\n this.logger.warn(\n `skip to add condition for role '${condition.roleEntityRef}'. The role either does not exist or was not created from a CSV file.`,\n );\n continue;\n }\n if (roleMetadata.source !== 'csv-file') {\n this.logger.warn(\n `skip to add condition for role '${condition.roleEntityRef}'. Role is not from csv-file`,\n );\n continue;\n }\n\n const existingCondition = existedFileConds.find(c =>\n deepSortEqual(omit(c, ['id']), omit(condition, ['id'])),\n );\n\n if (!existingCondition) {\n addedConds.push(condition);\n }\n }\n\n // Find removed conditions\n for (const condition of existedFileConds) {\n if (\n !newConds.find(c =>\n deepSortEqual(omit(c, ['id']), omit(condition, ['id'])),\n )\n ) {\n removedConds.push(condition);\n }\n }\n\n this.conditionsDiff = {\n addedConditions: addedConds,\n removedConditions: removedConds,\n };\n\n await this.handleFileChanges();\n } catch (error) {\n await this.handleError(\n `Error handling changes from conditional policies file ${this.filePath}`,\n error,\n ConditionEvents.CHANGE_CONDITIONAL_POLICIES_FILE_ERROR,\n );\n }\n }\n\n /**\n * Reads the current contents of the file and parses it.\n * @returns parsed data.\n */\n parse(): RoleConditionalPolicyDecision<PermissionAction>[] {\n const fileContents = this.getCurrentContents();\n const data = yaml.loadAll(\n fileContents,\n ) as RoleConditionalPolicyDecision<PermissionAction>[];\n\n for (const condition of data) {\n validateRoleCondition(condition);\n }\n\n return data;\n }\n\n private async handleFileChanges(): Promise<void> {\n await this.removeConditions();\n await this.addConditions();\n }\n\n private async addConditions(): Promise<void> {\n try {\n for (const condition of this.conditionsDiff.addedConditions) {\n const conditionToCreate = await processConditionMapping(\n condition,\n this.pluginMetadataCollector,\n this.auth,\n );\n\n await this.conditionalStorage.createCondition(conditionToCreate);\n\n await this.auditLogger.auditLog<ConditionAuditInfo>({\n message: `Created conditional permission policy`,\n eventName: ConditionEvents.CREATE_CONDITION,\n metadata: { condition },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n } catch (error) {\n await this.handleError(\n 'Failed to create condition',\n error,\n ConditionEvents.CREATE_CONDITION_ERROR,\n );\n }\n this.conditionsDiff.addedConditions = [];\n }\n\n private async removeConditions(): Promise<void> {\n try {\n for (const condition of this.conditionsDiff.removedConditions) {\n const conditionToDelete = (\n await this.conditionalStorage.filterConditions(\n condition.roleEntityRef,\n condition.pluginId,\n condition.resourceType,\n condition.permissionMapping,\n )\n )[0];\n await this.conditionalStorage.deleteCondition(conditionToDelete.id!);\n\n await this.auditLogger.auditLog<ConditionAuditInfo>({\n message: `Deleted conditional permission policy`,\n eventName: ConditionEvents.DELETE_CONDITION,\n metadata: { condition },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n }\n } catch (error) {\n await this.handleError(\n 'Failed to delete condition by id',\n error,\n ConditionEvents.DELETE_CONDITION_ERROR,\n );\n }\n\n this.conditionsDiff.removedConditions = [];\n }\n\n private async handleError(\n message: string,\n error: unknown,\n event: string,\n ): Promise<void> {\n await this.auditLogger.auditLog({\n message,\n eventName: event,\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'failed',\n errors: [error],\n });\n }\n\n async cleanUpConditionalPolicies(): Promise<void> {\n const csvFileRoles =\n await this.roleMetadataStorage.filterRoleMetadata('csv-file');\n const existedFileConds = (\n await this.conditionalStorage.filterConditions(\n csvFileRoles.map(role => role.roleEntityRef),\n )\n ).map(condition => {\n return {\n ...condition,\n permissionMapping: condition.permissionMapping.map(pm => pm.action),\n };\n });\n this.conditionsDiff.removedConditions = existedFileConds;\n await this.removeConditions();\n }\n}\n"],"names":["AbstractFileWatcher","fs","ConditionEvents","deepSortEqual","omit","yaml","validateRoleCondition","processConditionMapping","HANDLE_RBAC_DATA_STAGE"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"helper.cjs.js","sources":["../src/helper.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 { AuthService } from '@backstage/backend-plugin-api';\nimport type { MetadataResponse } from '@backstage/plugin-permission-node';\n\nimport { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport {\n difference,\n fromPairs,\n isArray,\n isEqual,\n isPlainObject,\n omitBy,\n sortBy,\n toPairs,\n ValueKeyIteratee,\n} from 'lodash';\n\nimport {\n PermissionAction,\n PermissionInfo,\n RoleBasedPolicy,\n RoleConditionalPolicyDecision,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from './audit-log/audit-logger';\nimport { RoleMetadataDao, RoleMetadataStorage } from './database/role-metadata';\nimport { EnforcerDelegate } from './service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from './service/plugin-endpoints';\n\nexport function policyToString(policy: string[]): string {\n return `[${policy.join(', ')}]`;\n}\n\nexport function typedPolicyToString(policy: string[], type: string): string {\n return `${type}, ${policy.join(', ')}`;\n}\n\nexport function policiesToString(policies: string[][]): string {\n const policiesString = policies\n .map(policy => policyToString(policy))\n .join(',');\n return `[${policiesString}]`;\n}\n\nexport function typedPoliciesToString(\n policies: string[][],\n type: string,\n): string {\n const policiesString = policies\n .map(policy => {\n return policy.length !== 0 ? typedPolicyToString(policy, type) : '';\n })\n .join('\\n');\n return `\n ${policiesString}\n `;\n}\n\nexport function metadataStringToPolicy(policy: string): string[] {\n return policy.replace('[', '').replace(']', '').split(', ');\n}\n\nexport async function removeTheDifference(\n originalGroup: string[],\n addedGroup: string[],\n source: Source,\n roleEntityRef: string,\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n modifiedBy: string,\n): Promise<void> {\n originalGroup.sort((a, b) => a.localeCompare(b));\n addedGroup.sort((a, b) => a.localeCompare(b));\n const missing = difference(originalGroup, addedGroup);\n\n const groupPolicies: string[][] = [];\n for (const missingRole of missing) {\n groupPolicies.push([missingRole, roleEntityRef]);\n }\n\n if (groupPolicies.length === 0) {\n return;\n }\n\n const roleMetadata = { source, modifiedBy, roleEntityRef };\n await enf.removeGroupingPolicies(groupPolicies, roleMetadata, false);\n\n const remainingMembers = await enf.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n const message =\n remainingMembers.length > 0\n ? 'Updated role: deleted members'\n : 'Deleted role';\n const eventName =\n remainingMembers.length > 0\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.DELETE_ROLE;\n await auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: {\n ...roleMetadata,\n members: groupPolicies.map(gp => gp[0]),\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n}\n\nexport function transformArrayToPolicy(policyArray: string[]): RoleBasedPolicy {\n const [entityReference, permission, policy, effect] = policyArray;\n return { entityReference, permission, policy, effect };\n}\n\nexport function deepSortedEqual(\n obj1: Record<string, any>,\n obj2: Record<string, any>,\n excludeFields?: string[],\n): boolean {\n let copyObj1;\n let copyObj2;\n if (excludeFields) {\n const excludeFieldsPredicate: ValueKeyIteratee<any> = (_value, key) => {\n for (const field of excludeFields) {\n if (key === field) {\n return true;\n }\n }\n return false;\n };\n copyObj1 = omitBy(obj1, excludeFieldsPredicate);\n copyObj2 = omitBy(obj2, excludeFieldsPredicate);\n }\n\n const sortedObj1 = sortBy(toPairs(copyObj1 || obj1), ([key]) => key);\n const sortedObj2 = sortBy(toPairs(copyObj2 || obj2), ([key]) => key);\n\n return isEqual(sortedObj1, sortedObj2);\n}\n\nexport function isPermissionAction(action: string): action is PermissionAction {\n return ['create', 'read', 'update', 'delete', 'use'].includes(\n action as PermissionAction,\n );\n}\n\nexport async function buildRoleSourceMap(\n policies: string[][],\n roleMetadata: RoleMetadataStorage,\n): Promise<Map<string, Source | undefined>> {\n return await policies.reduce(\n async (\n acc: Promise<Map<string, Source | undefined>>,\n policy: string[],\n ): Promise<Map<string, Source | undefined>> => {\n const roleEntityRef = policy[0];\n const acummulator = await acc;\n if (!acummulator.has(roleEntityRef)) {\n const metadata = await roleMetadata.findRoleMetadata(roleEntityRef);\n acummulator.set(roleEntityRef, metadata?.source);\n }\n return acummulator;\n },\n Promise.resolve(new Map<string, Source | undefined>()),\n );\n}\n\nexport function mergeRoleMetadata(\n currentMetadata: RoleMetadataDao,\n newMetadata: RoleMetadataDao,\n): RoleMetadataDao {\n const mergedMetaData: RoleMetadataDao = { ...currentMetadata };\n mergedMetaData.lastModified =\n newMetadata.lastModified ?? new Date().toUTCString();\n mergedMetaData.modifiedBy = newMetadata.modifiedBy;\n mergedMetaData.description =\n newMetadata.description ?? currentMetadata.description;\n mergedMetaData.roleEntityRef = newMetadata.roleEntityRef;\n mergedMetaData.source = newMetadata.source;\n return mergedMetaData;\n}\n\nexport async function processConditionMapping(\n roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction>,\n pluginPermMetaData: PluginPermissionMetadataCollector,\n auth: AuthService,\n): Promise<RoleConditionalPolicyDecision<PermissionInfo>> {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: roleConditionPolicy.pluginId,\n });\n\n const rule: MetadataResponse | undefined =\n await pluginPermMetaData.getMetadataByPluginId(\n roleConditionPolicy.pluginId,\n token,\n );\n if (!rule?.permissions) {\n throw new Error(\n `Unable to get permission list for plugin ${roleConditionPolicy.pluginId}`,\n );\n }\n\n const permInfo: PermissionInfo[] = [];\n for (const action of roleConditionPolicy.permissionMapping) {\n const perm = rule.permissions.find(\n permission =>\n permission.type === 'resource' &&\n (action === permission.attributes.action ||\n (action === 'use' && permission.attributes.action === undefined)),\n );\n if (!perm) {\n throw new Error(\n `Unable to find permission to get permission name for resource type '${\n roleConditionPolicy.resourceType\n }' and action ${JSON.stringify(action)}`,\n );\n }\n permInfo.push({ name: perm.name, action });\n }\n\n return {\n ...roleConditionPolicy,\n permissionMapping: permInfo,\n };\n}\n\nexport function deepSort(value: any): any {\n if (isArray(value)) {\n return sortBy(value.map(deepSort));\n } else if (isPlainObject(value)) {\n return fromPairs(\n sortBy(\n toPairs(value).map(([k, v]: [string, any]) => [k, deepSort(v)]),\n 0,\n ),\n );\n }\n return value;\n}\n\nexport function deepSortEqual(obj1: any, obj2: any): boolean {\n return isEqual(deepSort(obj1), deepSort(obj2));\n}\n"],"names":["auditLogger","difference","RoleEvents","RBAC_BACKEND","HANDLE_RBAC_DATA_STAGE","omitBy","sortBy","toPairs","isEqual","isArray","isPlainObject","fromPairs"],"mappings":"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"}
1
+ {"version":3,"file":"helper.cjs.js","sources":["../src/helper.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 { AuthService } from '@backstage/backend-plugin-api';\nimport type { MetadataResponse } from '@backstage/plugin-permission-node';\n\nimport { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport {\n difference,\n fromPairs,\n isArray,\n isEqual,\n isPlainObject,\n omitBy,\n sortBy,\n toPairs,\n ValueKeyIteratee,\n} from 'lodash';\n\nimport {\n PermissionAction,\n PermissionInfo,\n RoleBasedPolicy,\n RoleConditionalPolicyDecision,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from './audit-log/audit-logger';\nimport { RoleMetadataDao, RoleMetadataStorage } from './database/role-metadata';\nimport { EnforcerDelegate } from './service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from './service/plugin-endpoints';\n\nexport function policyToString(policy: string[]): string {\n return `[${policy.join(', ')}]`;\n}\n\nexport function typedPolicyToString(policy: string[], type: string): string {\n return `${type}, ${policy.join(', ')}`;\n}\n\nexport function policiesToString(policies: string[][]): string {\n const policiesString = policies\n .map(policy => policyToString(policy))\n .join(',');\n return `[${policiesString}]`;\n}\n\nexport function typedPoliciesToString(\n policies: string[][],\n type: string,\n): string {\n const policiesString = policies\n .map(policy => {\n return policy.length !== 0 ? typedPolicyToString(policy, type) : '';\n })\n .join('\\n');\n return `\n ${policiesString}\n `;\n}\n\nexport function metadataStringToPolicy(policy: string): string[] {\n return policy.replace('[', '').replace(']', '').split(', ');\n}\n\nexport async function removeTheDifference(\n originalGroup: string[],\n addedGroup: string[],\n source: Source,\n roleEntityRef: string,\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n modifiedBy: string,\n): Promise<void> {\n originalGroup.sort((a, b) => a.localeCompare(b));\n addedGroup.sort((a, b) => a.localeCompare(b));\n const missing = difference(originalGroup, addedGroup);\n\n const groupPolicies: string[][] = [];\n for (const missingRole of missing) {\n groupPolicies.push([missingRole, roleEntityRef]);\n }\n\n if (groupPolicies.length === 0) {\n return;\n }\n\n const roleMetadata = { source, modifiedBy, roleEntityRef };\n await enf.removeGroupingPolicies(groupPolicies, roleMetadata, false);\n\n const remainingMembers = await enf.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n const message =\n remainingMembers.length > 0\n ? 'Updated role: deleted members'\n : 'Deleted role';\n const eventName =\n remainingMembers.length > 0\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.DELETE_ROLE;\n await auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: {\n ...roleMetadata,\n members: groupPolicies.map(gp => gp[0]),\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n}\n\nexport function transformArrayToPolicy(policyArray: string[]): RoleBasedPolicy {\n const [entityReference, permission, policy, effect] = policyArray;\n return { entityReference, permission, policy, effect };\n}\n\nexport function deepSortedEqual(\n obj1: Record<string, any>,\n obj2: Record<string, any>,\n excludeFields?: string[],\n): boolean {\n let copyObj1;\n let copyObj2;\n if (excludeFields) {\n const excludeFieldsPredicate: ValueKeyIteratee<any> = (_value, key) => {\n for (const field of excludeFields) {\n if (key === field) {\n return true;\n }\n }\n return false;\n };\n copyObj1 = omitBy(obj1, excludeFieldsPredicate);\n copyObj2 = omitBy(obj2, excludeFieldsPredicate);\n }\n\n const sortedObj1 = sortBy(toPairs(copyObj1 || obj1), ([key]) => key);\n const sortedObj2 = sortBy(toPairs(copyObj2 || obj2), ([key]) => key);\n\n return isEqual(sortedObj1, sortedObj2);\n}\n\nexport function isPermissionAction(action: string): action is PermissionAction {\n return ['create', 'read', 'update', 'delete', 'use'].includes(\n action as PermissionAction,\n );\n}\n\nexport async function buildRoleSourceMap(\n policies: string[][],\n roleMetadata: RoleMetadataStorage,\n): Promise<Map<string, Source | undefined>> {\n return await policies.reduce(\n async (\n acc: Promise<Map<string, Source | undefined>>,\n policy: string[],\n ): Promise<Map<string, Source | undefined>> => {\n const roleEntityRef = policy[0];\n const acummulator = await acc;\n if (!acummulator.has(roleEntityRef)) {\n const metadata = await roleMetadata.findRoleMetadata(roleEntityRef);\n acummulator.set(roleEntityRef, metadata?.source);\n }\n return acummulator;\n },\n Promise.resolve(new Map<string, Source | undefined>()),\n );\n}\n\nexport function mergeRoleMetadata(\n currentMetadata: RoleMetadataDao,\n newMetadata: RoleMetadataDao,\n): RoleMetadataDao {\n const mergedMetaData: RoleMetadataDao = { ...currentMetadata };\n mergedMetaData.lastModified =\n newMetadata.lastModified ?? new Date().toUTCString();\n mergedMetaData.modifiedBy = newMetadata.modifiedBy;\n mergedMetaData.description =\n newMetadata.description ?? currentMetadata.description;\n mergedMetaData.roleEntityRef = newMetadata.roleEntityRef;\n mergedMetaData.source = newMetadata.source;\n return mergedMetaData;\n}\n\nexport async function processConditionMapping(\n roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction>,\n pluginPermMetaData: PluginPermissionMetadataCollector,\n auth: AuthService,\n): Promise<RoleConditionalPolicyDecision<PermissionInfo>> {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: roleConditionPolicy.pluginId,\n });\n\n const rule: MetadataResponse | undefined =\n await pluginPermMetaData.getMetadataByPluginId(\n roleConditionPolicy.pluginId,\n token,\n );\n if (!rule?.permissions) {\n throw new Error(\n `Unable to get permission list for plugin ${roleConditionPolicy.pluginId}`,\n );\n }\n\n const permInfo: PermissionInfo[] = [];\n for (const action of roleConditionPolicy.permissionMapping) {\n const perm = rule.permissions.find(\n permission =>\n permission.type === 'resource' &&\n (action === permission.attributes.action ||\n (action === 'use' && permission.attributes.action === undefined)),\n );\n if (!perm) {\n throw new Error(\n `Unable to find permission to get permission name for resource type '${\n roleConditionPolicy.resourceType\n }' and action ${JSON.stringify(action)}`,\n );\n }\n permInfo.push({ name: perm.name, action });\n }\n\n return {\n ...roleConditionPolicy,\n permissionMapping: permInfo,\n };\n}\n\nexport function deepSort(value: any): any {\n if (isArray(value)) {\n return sortBy(value.map(deepSort));\n } else if (isPlainObject(value)) {\n return fromPairs(\n sortBy(\n toPairs(value).map(([k, v]: [string, any]) => [k, deepSort(v)]),\n 0,\n ),\n );\n }\n return value;\n}\n\nexport function deepSortEqual(obj1: any, obj2: any): boolean {\n return isEqual(deepSort(obj1), deepSort(obj2));\n}\n"],"names":["auditLogger","difference","RoleEvents","RBAC_BACKEND","HANDLE_RBAC_DATA_STAGE","omitBy","sortBy","toPairs","isEqual","isArray","isPlainObject","fromPairs"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.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 coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\n\nimport { PolicyBuilder } from '@backstage-community/plugin-rbac-backend';\nimport {\n PluginIdProvider,\n PluginIdProviderExtensionPoint,\n pluginIdProviderExtensionPoint,\n RBACProvider,\n rbacProviderExtensionPoint,\n} from '@backstage-community/plugin-rbac-node';\n\n/**\n * RBAC plugin\n *\n */\nexport const rbacPlugin = createBackendPlugin({\n pluginId: 'permission',\n register(env) {\n const pluginIdProviderExtensionPointImpl = new (class PluginIdProviderImpl\n implements PluginIdProviderExtensionPoint\n {\n pluginIdProviders: PluginIdProvider[] = [];\n\n addPluginIdProvider(pluginIdProvider: PluginIdProvider): void {\n this.pluginIdProviders.push(pluginIdProvider);\n }\n })();\n\n env.registerExtensionPoint(\n pluginIdProviderExtensionPoint,\n pluginIdProviderExtensionPointImpl,\n );\n\n const rbacProviders = new Array<RBACProvider>();\n\n env.registerExtensionPoint(rbacProviderExtensionPoint, {\n addRBACProvider(\n ...providers: Array<RBACProvider | Array<RBACProvider>>\n ): void {\n rbacProviders.push(...providers.flat());\n },\n });\n\n env.registerInit({\n deps: {\n http: coreServices.httpRouter,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n discovery: coreServices.discovery,\n permissions: coreServices.permissions,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n userInfo: coreServices.userInfo,\n lifecycle: coreServices.lifecycle,\n },\n async init({\n http,\n config,\n logger,\n discovery,\n permissions,\n auth,\n httpAuth,\n userInfo,\n lifecycle,\n }) {\n http.use(\n await PolicyBuilder.build(\n {\n config,\n logger,\n discovery,\n permissions,\n auth,\n httpAuth,\n userInfo,\n lifecycle,\n },\n {\n getPluginIds: () =>\n Array.from(\n new Set(\n pluginIdProviderExtensionPointImpl.pluginIdProviders.flatMap(\n p => p.getPluginIds(),\n ),\n ),\n ),\n },\n rbacProviders,\n ),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","pluginIdProviderExtensionPoint","rbacProviderExtensionPoint","coreServices","PolicyBuilder"],"mappings":";;;;;;AAiCO,MAAM,aAAaA,oCAAoB,CAAA;AAAA,EAC5C,QAAU,EAAA,YAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAM,MAAA,kCAAA,GAAqC,IAAK,MAAM,oBAEtD,CAAA;AAAA,MACE,oBAAwC,EAAC,CAAA;AAAA,MAEzC,oBAAoB,gBAA0C,EAAA;AAC5D,QAAK,IAAA,CAAA,iBAAA,CAAkB,KAAK,gBAAgB,CAAA,CAAA;AAAA,OAC9C;AAAA,KACC,EAAA,CAAA;AAEH,IAAI,GAAA,CAAA,sBAAA;AAAA,MACFC,6CAAA;AAAA,MACA,kCAAA;AAAA,KACF,CAAA;AAEA,IAAM,MAAA,aAAA,GAAgB,IAAI,KAAoB,EAAA,CAAA;AAE9C,IAAA,GAAA,CAAI,uBAAuBC,yCAA4B,EAAA;AAAA,MACrD,mBACK,SACG,EAAA;AACN,QAAA,aAAA,CAAc,IAAK,CAAA,GAAG,SAAU,CAAA,IAAA,EAAM,CAAA,CAAA;AAAA,OACxC;AAAA,KACD,CAAA,CAAA;AAED,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,MAAMC,6BAAa,CAAA,UAAA;AAAA,QACnB,QAAQA,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,aAAaA,6BAAa,CAAA,WAAA;AAAA,QAC1B,MAAMA,6BAAa,CAAA,IAAA;AAAA,QACnB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,WAAWA,6BAAa,CAAA,SAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,IAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,WAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,OACC,EAAA;AACD,QAAK,IAAA,CAAA,GAAA;AAAA,UACH,MAAMC,+BAAc,CAAA,KAAA;AAAA,YAClB;AAAA,cACE,MAAA;AAAA,cACA,MAAA;AAAA,cACA,SAAA;AAAA,cACA,WAAA;AAAA,cACA,IAAA;AAAA,cACA,QAAA;AAAA,cACA,QAAA;AAAA,cACA,SAAA;AAAA,aACF;AAAA,YACA;AAAA,cACE,YAAA,EAAc,MACZ,KAAM,CAAA,IAAA;AAAA,gBACJ,IAAI,GAAA;AAAA,kBACF,mCAAmC,iBAAkB,CAAA,OAAA;AAAA,oBACnD,CAAA,CAAA,KAAK,EAAE,YAAa,EAAA;AAAA,mBACtB;AAAA,iBACF;AAAA,eACF;AAAA,aACJ;AAAA,YACA,aAAA;AAAA,WACF;AAAA,SACF,CAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.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 coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\n\nimport { PolicyBuilder } from '@backstage-community/plugin-rbac-backend';\nimport {\n PluginIdProvider,\n PluginIdProviderExtensionPoint,\n pluginIdProviderExtensionPoint,\n RBACProvider,\n rbacProviderExtensionPoint,\n} from '@backstage-community/plugin-rbac-node';\n\n/**\n * RBAC plugin\n *\n */\nexport const rbacPlugin = createBackendPlugin({\n pluginId: 'permission',\n register(env) {\n const pluginIdProviderExtensionPointImpl = new (class PluginIdProviderImpl\n implements PluginIdProviderExtensionPoint\n {\n pluginIdProviders: PluginIdProvider[] = [];\n\n addPluginIdProvider(pluginIdProvider: PluginIdProvider): void {\n this.pluginIdProviders.push(pluginIdProvider);\n }\n })();\n\n env.registerExtensionPoint(\n pluginIdProviderExtensionPoint,\n pluginIdProviderExtensionPointImpl,\n );\n\n const rbacProviders = new Array<RBACProvider>();\n\n env.registerExtensionPoint(rbacProviderExtensionPoint, {\n addRBACProvider(\n ...providers: Array<RBACProvider | Array<RBACProvider>>\n ): void {\n rbacProviders.push(...providers.flat());\n },\n });\n\n env.registerInit({\n deps: {\n http: coreServices.httpRouter,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n discovery: coreServices.discovery,\n permissions: coreServices.permissions,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n userInfo: coreServices.userInfo,\n lifecycle: coreServices.lifecycle,\n },\n async init({\n http,\n config,\n logger,\n discovery,\n permissions,\n auth,\n httpAuth,\n userInfo,\n lifecycle,\n }) {\n http.use(\n await PolicyBuilder.build(\n {\n config,\n logger,\n discovery,\n permissions,\n auth,\n httpAuth,\n userInfo,\n lifecycle,\n },\n {\n getPluginIds: () =>\n Array.from(\n new Set(\n pluginIdProviderExtensionPointImpl.pluginIdProviders.flatMap(\n p => p.getPluginIds(),\n ),\n ),\n ),\n },\n rbacProviders,\n ),\n );\n },\n });\n },\n});\n"],"names":["createBackendPlugin","pluginIdProviderExtensionPoint","rbacProviderExtensionPoint","coreServices","PolicyBuilder"],"mappings":";;;;;;AAiCO,MAAM,aAAaA,oCAAoB,CAAA;AAAA,EAC5C,QAAU,EAAA,YAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAM,MAAA,kCAAA,GAAqC,IAAK,MAAM,oBAEtD,CAAA;AAAA,MACE,oBAAwC,EAAC;AAAA,MAEzC,oBAAoB,gBAA0C,EAAA;AAC5D,QAAK,IAAA,CAAA,iBAAA,CAAkB,KAAK,gBAAgB,CAAA;AAAA;AAC9C,KACC,EAAA;AAEH,IAAI,GAAA,CAAA,sBAAA;AAAA,MACFC,6CAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAM,MAAA,aAAA,GAAgB,IAAI,KAAoB,EAAA;AAE9C,IAAA,GAAA,CAAI,uBAAuBC,yCAA4B,EAAA;AAAA,MACrD,mBACK,SACG,EAAA;AACN,QAAA,aAAA,CAAc,IAAK,CAAA,GAAG,SAAU,CAAA,IAAA,EAAM,CAAA;AAAA;AACxC,KACD,CAAA;AAED,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,MAAMC,6BAAa,CAAA,UAAA;AAAA,QACnB,QAAQA,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,aAAaA,6BAAa,CAAA,WAAA;AAAA,QAC1B,MAAMA,6BAAa,CAAA,IAAA;AAAA,QACnB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,WAAWA,6BAAa,CAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,IAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,WAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACC,EAAA;AACD,QAAK,IAAA,CAAA,GAAA;AAAA,UACH,MAAMC,+BAAc,CAAA,KAAA;AAAA,YAClB;AAAA,cACE,MAAA;AAAA,cACA,MAAA;AAAA,cACA,SAAA;AAAA,cACA,WAAA;AAAA,cACA,IAAA;AAAA,cACA,QAAA;AAAA,cACA,QAAA;AAAA,cACA;AAAA,aACF;AAAA,YACA;AAAA,cACE,YAAA,EAAc,MACZ,KAAM,CAAA,IAAA;AAAA,gBACJ,IAAI,GAAA;AAAA,kBACF,mCAAmC,iBAAkB,CAAA,OAAA;AAAA,oBACnD,CAAA,CAAA,KAAK,EAAE,YAAa;AAAA;AACtB;AACF;AACF,aACJ;AAAA,YACA;AAAA;AACF,SACF;AAAA;AACF,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"allow-all-policy.cjs.js","sources":["../../src/policies/allow-all-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 {\n AuthorizeResult,\n PolicyDecision,\n} from '@backstage/plugin-permission-common';\nimport {\n PermissionPolicy,\n PolicyQuery,\n PolicyQueryUser,\n} from '@backstage/plugin-permission-node';\n\nexport class AllowAllPolicy implements PermissionPolicy {\n async handle(\n _request: PolicyQuery,\n _user?: PolicyQueryUser,\n ): Promise<PolicyDecision> {\n return { result: AuthorizeResult.ALLOW };\n }\n}\n"],"names":["AuthorizeResult"],"mappings":";;;;AAyBO,MAAM,cAA2C,CAAA;AAAA,EACtD,MAAM,MACJ,CAAA,QAAA,EACA,KACyB,EAAA;AACzB,IAAO,OAAA,EAAE,MAAQ,EAAAA,sCAAA,CAAgB,KAAM,EAAA,CAAA;AAAA,GACzC;AACF;;;;"}
1
+ {"version":3,"file":"allow-all-policy.cjs.js","sources":["../../src/policies/allow-all-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 {\n AuthorizeResult,\n PolicyDecision,\n} from '@backstage/plugin-permission-common';\nimport {\n PermissionPolicy,\n PolicyQuery,\n PolicyQueryUser,\n} from '@backstage/plugin-permission-node';\n\nexport class AllowAllPolicy implements PermissionPolicy {\n async handle(\n _request: PolicyQuery,\n _user?: PolicyQueryUser,\n ): Promise<PolicyDecision> {\n return { result: AuthorizeResult.ALLOW };\n }\n}\n"],"names":["AuthorizeResult"],"mappings":";;;;AAyBO,MAAM,cAA2C,CAAA;AAAA,EACtD,MAAM,MACJ,CAAA,QAAA,EACA,KACyB,EAAA;AACzB,IAAO,OAAA,EAAE,MAAQ,EAAAA,sCAAA,CAAgB,KAAM,EAAA;AAAA;AAE3C;;;;"}
@@ -165,9 +165,7 @@ class RBACPermissionPolicy {
165
165
  }
166
166
  }
167
167
  async hasImplicitPermissionSpecifiedByName(userEntityRef, permissionName, action) {
168
- const userPerms = await this.enforcer.getImplicitPermissionsForUser(
169
- userEntityRef
170
- );
168
+ const userPerms = await this.enforcer.getImplicitPermissionsForUser(userEntityRef);
171
169
  for (const perm of userPerms) {
172
170
  if (permissionName === perm[1] && action === perm[2]) {
173
171
  return true;
@@ -1 +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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"}
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 =\n await this.enforcer.getImplicitPermissionsForUser(userEntityRef);\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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"}
@@ -160,7 +160,7 @@ class Connection {
160
160
  const removedPermissions = [];
161
161
  for (const permission of providerPermissions) {
162
162
  if (!await tempEnforcer.hasPolicy(...permission)) {
163
- this.enforcer.removePolicy(permission);
163
+ await this.enforcer.removePolicy(permission);
164
164
  removedPermissions.push(permission);
165
165
  }
166
166
  if (removedPermissions.length > 0) {
@@ -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 { 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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"}
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 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":["typedPoliciesToString","newEnforcer","newModelFromString","MODEL","StringAdapter","validateGroupingPolicy","RoleEvents","RBAC_BACKEND","HANDLE_RBAC_DATA_STAGE","transformArrayToPolicy","validatePolicy","validateSource","PermissionEvents"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ancestor-search-memo.cjs.js","sources":["../../src/role-manager/ancestor-search-memo.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 { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport type { CatalogApi } from '@backstage/catalog-client';\nimport type { Entity } from '@backstage/catalog-model';\n\nimport { alg, Graph } from '@dagrejs/graphlib';\nimport { Knex } from 'knex';\n\nexport interface Relation {\n source_entity_ref: string;\n target_entity_ref: string;\n}\n\nexport type ASMGroup = Relation | Entity;\n\n// AncestorSearchMemo - should be used to build group hierarchy graph for User entity reference.\n// It supports search group entity reference link in the graph.\n// Also AncestorSearchMemo supports detection cycle dependencies between groups in the graph.\n//\nexport class AncestorSearchMemo {\n private graph: Graph;\n\n private catalogApi: CatalogApi;\n private catalogDBClient: Knex;\n private auth: AuthService;\n\n private userEntityRef: string;\n private maxDepth?: number;\n\n constructor(\n userEntityRef: string,\n catalogApi: CatalogApi,\n catalogDBClient: Knex,\n auth: AuthService,\n maxDepth?: number,\n ) {\n this.graph = new Graph({ directed: true });\n this.userEntityRef = userEntityRef;\n this.catalogApi = catalogApi;\n this.catalogDBClient = catalogDBClient;\n this.auth = auth;\n this.maxDepth = maxDepth;\n }\n\n isAcyclic(): boolean {\n return alg.isAcyclic(this.graph);\n }\n\n findCycles(): string[][] {\n return alg.findCycles(this.graph);\n }\n\n setEdge(parentEntityRef: string, childEntityRef: string) {\n this.graph.setEdge(parentEntityRef, childEntityRef);\n }\n\n setNode(entityRef: string): void {\n this.graph.setNode(entityRef);\n }\n\n hasEntityRef(groupRef: string): boolean {\n return this.graph.hasNode(groupRef);\n }\n\n debugNodesAndEdges(logger: LoggerService, userEntity: string): void {\n logger.debug(\n `SubGraph edges: ${JSON.stringify(this.graph.edges())} for ${userEntity}`,\n );\n logger.debug(\n `SubGraph nodes: ${JSON.stringify(this.graph.nodes())} for ${userEntity}`,\n );\n }\n\n getNodes(): string[] {\n return this.graph.nodes();\n }\n\n async doesRelationTableExist(): Promise<boolean> {\n try {\n return await this.catalogDBClient.schema.hasTable('relations');\n } catch (error) {\n return false;\n }\n }\n\n async getAllGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group' },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getAllRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where('type', 'childOf');\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n async getUserGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group', 'relations.hasMember': this.userEntityRef },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getUserRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where({ type: 'memberOf', source_entity_ref: this.userEntityRef });\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n traverseGroups(\n memo: AncestorSearchMemo,\n group: Entity,\n allGroups: Entity[],\n current_depth: number,\n ) {\n const groupName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${group.metadata.name.toLocaleLowerCase('en-US')}`;\n if (!memo.hasEntityRef(groupName)) {\n memo.setNode(groupName);\n }\n\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth) {\n return;\n }\n const depth = current_depth + 1;\n\n const parent = group.spec?.parent as string;\n const parentGroup = allGroups.find(g => g.metadata.name === parent);\n\n if (parentGroup) {\n const parentName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${parentGroup.metadata.name.toLocaleLowerCase('en-US')}`;\n memo.setEdge(parentName, groupName);\n\n if (memo.isAcyclic()) {\n this.traverseGroups(memo, parentGroup, allGroups, depth);\n }\n }\n }\n\n traverseRelations(\n memo: AncestorSearchMemo,\n relation: Relation,\n allRelations: Relation[],\n current_depth: number,\n ) {\n // We add one to the maxDepth here because the user is considered the starting node\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth + 1) {\n return;\n }\n const depth = current_depth + 1;\n\n if (!memo.hasEntityRef(relation.source_entity_ref)) {\n memo.setNode(relation.source_entity_ref);\n }\n\n memo.setEdge(relation.target_entity_ref, relation.source_entity_ref);\n\n const parentGroup = allRelations.find(\n g => g.source_entity_ref === relation.target_entity_ref,\n );\n\n if (parentGroup && memo.isAcyclic()) {\n this.traverseRelations(memo, parentGroup, allRelations, depth);\n }\n }\n\n async buildUserGraph(memo: AncestorSearchMemo) {\n if (await this.doesRelationTableExist()) {\n const userRelations = await this.getUserRelations();\n const allRelations = await this.getAllRelations();\n userRelations.forEach(group =>\n this.traverseRelations(\n memo,\n group as Relation,\n allRelations as Relation[],\n 0,\n ),\n );\n } else {\n const userGroups = await this.getUserGroups();\n const allGroups = await this.getAllGroups();\n userGroups.forEach(group =>\n this.traverseGroups(memo, group as Entity, allGroups as Entity[], 0),\n );\n }\n }\n}\n"],"names":["Graph","alg"],"mappings":"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"}
1
+ {"version":3,"file":"ancestor-search-memo.cjs.js","sources":["../../src/role-manager/ancestor-search-memo.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 { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport type { CatalogApi } from '@backstage/catalog-client';\nimport type { Entity } from '@backstage/catalog-model';\n\nimport { alg, Graph } from '@dagrejs/graphlib';\nimport { Knex } from 'knex';\n\nexport interface Relation {\n source_entity_ref: string;\n target_entity_ref: string;\n}\n\nexport type ASMGroup = Relation | Entity;\n\n// AncestorSearchMemo - should be used to build group hierarchy graph for User entity reference.\n// It supports search group entity reference link in the graph.\n// Also AncestorSearchMemo supports detection cycle dependencies between groups in the graph.\n//\nexport class AncestorSearchMemo {\n private graph: Graph;\n\n private catalogApi: CatalogApi;\n private catalogDBClient: Knex;\n private auth: AuthService;\n\n private userEntityRef: string;\n private maxDepth?: number;\n\n constructor(\n userEntityRef: string,\n catalogApi: CatalogApi,\n catalogDBClient: Knex,\n auth: AuthService,\n maxDepth?: number,\n ) {\n this.graph = new Graph({ directed: true });\n this.userEntityRef = userEntityRef;\n this.catalogApi = catalogApi;\n this.catalogDBClient = catalogDBClient;\n this.auth = auth;\n this.maxDepth = maxDepth;\n }\n\n isAcyclic(): boolean {\n return alg.isAcyclic(this.graph);\n }\n\n findCycles(): string[][] {\n return alg.findCycles(this.graph);\n }\n\n setEdge(parentEntityRef: string, childEntityRef: string) {\n this.graph.setEdge(parentEntityRef, childEntityRef);\n }\n\n setNode(entityRef: string): void {\n this.graph.setNode(entityRef);\n }\n\n hasEntityRef(groupRef: string): boolean {\n return this.graph.hasNode(groupRef);\n }\n\n debugNodesAndEdges(logger: LoggerService, userEntity: string): void {\n logger.debug(\n `SubGraph edges: ${JSON.stringify(this.graph.edges())} for ${userEntity}`,\n );\n logger.debug(\n `SubGraph nodes: ${JSON.stringify(this.graph.nodes())} for ${userEntity}`,\n );\n }\n\n getNodes(): string[] {\n return this.graph.nodes();\n }\n\n async doesRelationTableExist(): Promise<boolean> {\n try {\n return await this.catalogDBClient.schema.hasTable('relations');\n } catch (error) {\n return false;\n }\n }\n\n async getAllGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group' },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getAllRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where('type', 'childOf');\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n async getUserGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group', 'relations.hasMember': this.userEntityRef },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getUserRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where({ type: 'memberOf', source_entity_ref: this.userEntityRef });\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n traverseGroups(\n memo: AncestorSearchMemo,\n group: Entity,\n allGroups: Entity[],\n current_depth: number,\n ) {\n const groupName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${group.metadata.name.toLocaleLowerCase('en-US')}`;\n if (!memo.hasEntityRef(groupName)) {\n memo.setNode(groupName);\n }\n\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth) {\n return;\n }\n const depth = current_depth + 1;\n\n const parent = group.spec?.parent as string;\n const parentGroup = allGroups.find(g => g.metadata.name === parent);\n\n if (parentGroup) {\n const parentName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${parentGroup.metadata.name.toLocaleLowerCase('en-US')}`;\n memo.setEdge(parentName, groupName);\n\n if (memo.isAcyclic()) {\n this.traverseGroups(memo, parentGroup, allGroups, depth);\n }\n }\n }\n\n traverseRelations(\n memo: AncestorSearchMemo,\n relation: Relation,\n allRelations: Relation[],\n current_depth: number,\n ) {\n // We add one to the maxDepth here because the user is considered the starting node\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth + 1) {\n return;\n }\n const depth = current_depth + 1;\n\n if (!memo.hasEntityRef(relation.source_entity_ref)) {\n memo.setNode(relation.source_entity_ref);\n }\n\n memo.setEdge(relation.target_entity_ref, relation.source_entity_ref);\n\n const parentGroup = allRelations.find(\n g => g.source_entity_ref === relation.target_entity_ref,\n );\n\n if (parentGroup && memo.isAcyclic()) {\n this.traverseRelations(memo, parentGroup, allRelations, depth);\n }\n }\n\n async buildUserGraph(memo: AncestorSearchMemo) {\n if (await this.doesRelationTableExist()) {\n const userRelations = await this.getUserRelations();\n const allRelations = await this.getAllRelations();\n userRelations.forEach(group =>\n this.traverseRelations(\n memo,\n group as Relation,\n allRelations as Relation[],\n 0,\n ),\n );\n } else {\n const userGroups = await this.getUserGroups();\n const allGroups = await this.getAllGroups();\n userGroups.forEach(group =>\n this.traverseGroups(memo, group as Entity, allGroups as Entity[], 0),\n );\n }\n }\n}\n"],"names":["Graph","alg"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"member-list.cjs.js","sources":["../../src/role-manager/member-list.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 { Knex } from 'knex';\n\nexport class RoleMemberList {\n public name: string;\n\n private members: string[];\n private roles: string[];\n\n public constructor(name: string) {\n this.name = name;\n this.members = [];\n this.roles = [];\n }\n\n /**\n * addMembers will add members to the RoleMemberList\n * @param members The members to be added.\n */\n public addMembers(members: string[]): void {\n this.members = members;\n }\n\n /**\n * addMember will add a single member to the RoleMemberList, skips adding the user in the\n * event that they already exist in the members array.\n * @param member The member to be added.\n */\n public addMember(member: string): void {\n if (this.members.some(n => n === member)) {\n return;\n }\n this.members.push(member);\n }\n\n /**\n * hasMember will check if a particular member exists in the members array.\n * @param name The member to be checked for.\n */\n public hasMember(name: string): boolean {\n return this.members.includes(name);\n }\n\n /**\n * deleteMember will remove a user from the members array.\n * @param member The member to be removed.\n */\n public deleteMember(member: string): void {\n this.members = this.members.filter(n => n !== member);\n }\n\n /**\n * buildMembers will query the `casbin_rule` database table to ensure that the role\n * that we have cached is up to date.\n * This is important in multi node scenarios where the cached roles in role manager can become\n * out of sync with the database.\n * @param roleMemberList The RoleMemberList to be updated.\n * @param client The database client.\n */\n public async buildMembers(\n roleMemberList: RoleMemberList,\n client: Knex,\n ): Promise<void> {\n try {\n const members: string[] = await client\n .table('casbin_rule')\n .where('v1', this.name)\n .pluck('v0')\n .distinct();\n\n roleMemberList.addMembers(members);\n } catch (error) {\n throw new Error(\n `Unable to find members for the role ${this.name}. Cause: ${error}`,\n );\n }\n }\n\n /**\n * getMembers will return the members of the RoleMemberList\n * @returns The members.\n */\n getMembers(): string[] {\n return this.members;\n }\n\n /**\n * addRoles will add roles to the RoleMemberList\n * @param roles The roles to be added.\n */\n public addRoles(roles: string[]): void {\n this.roles = roles;\n }\n\n /**\n * buildRoles will query the `casbin_rule` database table to quickly grab all of the\n * roles that a particular user is attached to.\n * @param roleMemberList The RoleMemberList to be updated.\n * @param userAndGroups The user and groups to query with.\n * @param client The database client.\n */\n public async buildRoles(\n roleMemberList: RoleMemberList,\n userAndGroups: string[],\n client: Knex,\n ): Promise<void> {\n try {\n const roles: string[] = await client\n .table('casbin_rule')\n .whereIn('v0', userAndGroups)\n .pluck('v1')\n .distinct();\n\n roleMemberList.addRoles(roles);\n } catch (error) {\n throw new Error(`Unable to find all roles. Cause: ${error}`);\n }\n }\n\n /**\n * getRoles will return the roles of the RoleMemberList.\n * @returns The roles.\n */\n getRoles(): string[] {\n return this.roles;\n }\n}\n"],"names":[],"mappings":";;AAiBO,MAAM,cAAe,CAAA;AAAA,EACnB,IAAA,CAAA;AAAA,EAEC,OAAA,CAAA;AAAA,EACA,KAAA,CAAA;AAAA,EAED,YAAY,IAAc,EAAA;AAC/B,IAAA,IAAA,CAAK,IAAO,GAAA,IAAA,CAAA;AACZ,IAAA,IAAA,CAAK,UAAU,EAAC,CAAA;AAChB,IAAA,IAAA,CAAK,QAAQ,EAAC,CAAA;AAAA,GAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,WAAW,OAAyB,EAAA;AACzC,IAAA,IAAA,CAAK,OAAU,GAAA,OAAA,CAAA;AAAA,GACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,UAAU,MAAsB,EAAA;AACrC,IAAA,IAAI,KAAK,OAAQ,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,KAAM,MAAM,CAAG,EAAA;AACxC,MAAA,OAAA;AAAA,KACF;AACA,IAAK,IAAA,CAAA,OAAA,CAAQ,KAAK,MAAM,CAAA,CAAA;AAAA,GAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAU,IAAuB,EAAA;AACtC,IAAO,OAAA,IAAA,CAAK,OAAQ,CAAA,QAAA,CAAS,IAAI,CAAA,CAAA;AAAA,GACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,aAAa,MAAsB,EAAA;AACxC,IAAA,IAAA,CAAK,UAAU,IAAK,CAAA,OAAA,CAAQ,MAAO,CAAA,CAAA,CAAA,KAAK,MAAM,MAAM,CAAA,CAAA;AAAA,GACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,YACX,CAAA,cAAA,EACA,MACe,EAAA;AACf,IAAI,IAAA;AACF,MAAA,MAAM,OAAoB,GAAA,MAAM,MAC7B,CAAA,KAAA,CAAM,aAAa,CACnB,CAAA,KAAA,CAAM,IAAM,EAAA,IAAA,CAAK,IAAI,CAAA,CACrB,KAAM,CAAA,IAAI,EACV,QAAS,EAAA,CAAA;AAEZ,MAAA,cAAA,CAAe,WAAW,OAAO,CAAA,CAAA;AAAA,aAC1B,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAuC,oCAAA,EAAA,IAAA,CAAK,IAAI,CAAA,SAAA,EAAY,KAAK,CAAA,CAAA;AAAA,OACnE,CAAA;AAAA,KACF;AAAA,GACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAuB,GAAA;AACrB,IAAA,OAAO,IAAK,CAAA,OAAA,CAAA;AAAA,GACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,SAAS,KAAuB,EAAA;AACrC,IAAA,IAAA,CAAK,KAAQ,GAAA,KAAA,CAAA;AAAA,GACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,UAAA,CACX,cACA,EAAA,aAAA,EACA,MACe,EAAA;AACf,IAAI,IAAA;AACF,MAAA,MAAM,KAAkB,GAAA,MAAM,MAC3B,CAAA,KAAA,CAAM,aAAa,CAAA,CACnB,OAAQ,CAAA,IAAA,EAAM,aAAa,CAAA,CAC3B,KAAM,CAAA,IAAI,EACV,QAAS,EAAA,CAAA;AAEZ,MAAA,cAAA,CAAe,SAAS,KAAK,CAAA,CAAA;AAAA,aACtB,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAoC,iCAAA,EAAA,KAAK,CAAE,CAAA,CAAA,CAAA;AAAA,KAC7D;AAAA,GACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAqB,GAAA;AACnB,IAAA,OAAO,IAAK,CAAA,KAAA,CAAA;AAAA,GACd;AACF;;;;"}
1
+ {"version":3,"file":"member-list.cjs.js","sources":["../../src/role-manager/member-list.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 { Knex } from 'knex';\n\nexport class RoleMemberList {\n public name: string;\n\n private members: string[];\n private roles: string[];\n\n public constructor(name: string) {\n this.name = name;\n this.members = [];\n this.roles = [];\n }\n\n /**\n * addMembers will add members to the RoleMemberList\n * @param members The members to be added.\n */\n public addMembers(members: string[]): void {\n this.members = members;\n }\n\n /**\n * addMember will add a single member to the RoleMemberList, skips adding the user in the\n * event that they already exist in the members array.\n * @param member The member to be added.\n */\n public addMember(member: string): void {\n if (this.members.some(n => n === member)) {\n return;\n }\n this.members.push(member);\n }\n\n /**\n * hasMember will check if a particular member exists in the members array.\n * @param name The member to be checked for.\n */\n public hasMember(name: string): boolean {\n return this.members.includes(name);\n }\n\n /**\n * deleteMember will remove a user from the members array.\n * @param member The member to be removed.\n */\n public deleteMember(member: string): void {\n this.members = this.members.filter(n => n !== member);\n }\n\n /**\n * buildMembers will query the `casbin_rule` database table to ensure that the role\n * that we have cached is up to date.\n * This is important in multi node scenarios where the cached roles in role manager can become\n * out of sync with the database.\n * @param roleMemberList The RoleMemberList to be updated.\n * @param client The database client.\n */\n public async buildMembers(\n roleMemberList: RoleMemberList,\n client: Knex,\n ): Promise<void> {\n try {\n const members: string[] = await client\n .table('casbin_rule')\n .where('v1', this.name)\n .pluck('v0')\n .distinct();\n\n roleMemberList.addMembers(members);\n } catch (error) {\n throw new Error(\n `Unable to find members for the role ${this.name}. Cause: ${error}`,\n );\n }\n }\n\n /**\n * getMembers will return the members of the RoleMemberList\n * @returns The members.\n */\n getMembers(): string[] {\n return this.members;\n }\n\n /**\n * addRoles will add roles to the RoleMemberList\n * @param roles The roles to be added.\n */\n public addRoles(roles: string[]): void {\n this.roles = roles;\n }\n\n /**\n * buildRoles will query the `casbin_rule` database table to quickly grab all of the\n * roles that a particular user is attached to.\n * @param roleMemberList The RoleMemberList to be updated.\n * @param userAndGroups The user and groups to query with.\n * @param client The database client.\n */\n public async buildRoles(\n roleMemberList: RoleMemberList,\n userAndGroups: string[],\n client: Knex,\n ): Promise<void> {\n try {\n const roles: string[] = await client\n .table('casbin_rule')\n .whereIn('v0', userAndGroups)\n .pluck('v1')\n .distinct();\n\n roleMemberList.addRoles(roles);\n } catch (error) {\n throw new Error(`Unable to find all roles. Cause: ${error}`);\n }\n }\n\n /**\n * getRoles will return the roles of the RoleMemberList.\n * @returns The roles.\n */\n getRoles(): string[] {\n return this.roles;\n }\n}\n"],"names":[],"mappings":";;AAiBO,MAAM,cAAe,CAAA;AAAA,EACnB,IAAA;AAAA,EAEC,OAAA;AAAA,EACA,KAAA;AAAA,EAED,YAAY,IAAc,EAAA;AAC/B,IAAA,IAAA,CAAK,IAAO,GAAA,IAAA;AACZ,IAAA,IAAA,CAAK,UAAU,EAAC;AAChB,IAAA,IAAA,CAAK,QAAQ,EAAC;AAAA;AAChB;AAAA;AAAA;AAAA;AAAA,EAMO,WAAW,OAAyB,EAAA;AACzC,IAAA,IAAA,CAAK,OAAU,GAAA,OAAA;AAAA;AACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,UAAU,MAAsB,EAAA;AACrC,IAAA,IAAI,KAAK,OAAQ,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,KAAM,MAAM,CAAG,EAAA;AACxC,MAAA;AAAA;AAEF,IAAK,IAAA,CAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA;AAC1B;AAAA;AAAA;AAAA;AAAA,EAMO,UAAU,IAAuB,EAAA;AACtC,IAAO,OAAA,IAAA,CAAK,OAAQ,CAAA,QAAA,CAAS,IAAI,CAAA;AAAA;AACnC;AAAA;AAAA;AAAA;AAAA,EAMO,aAAa,MAAsB,EAAA;AACxC,IAAA,IAAA,CAAK,UAAU,IAAK,CAAA,OAAA,CAAQ,MAAO,CAAA,CAAA,CAAA,KAAK,MAAM,MAAM,CAAA;AAAA;AACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,YACX,CAAA,cAAA,EACA,MACe,EAAA;AACf,IAAI,IAAA;AACF,MAAA,MAAM,OAAoB,GAAA,MAAM,MAC7B,CAAA,KAAA,CAAM,aAAa,CACnB,CAAA,KAAA,CAAM,IAAM,EAAA,IAAA,CAAK,IAAI,CAAA,CACrB,KAAM,CAAA,IAAI,EACV,QAAS,EAAA;AAEZ,MAAA,cAAA,CAAe,WAAW,OAAO,CAAA;AAAA,aAC1B,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAuC,oCAAA,EAAA,IAAA,CAAK,IAAI,CAAA,SAAA,EAAY,KAAK,CAAA;AAAA,OACnE;AAAA;AACF;AACF;AAAA;AAAA;AAAA;AAAA,EAMA,UAAuB,GAAA;AACrB,IAAA,OAAO,IAAK,CAAA,OAAA;AAAA;AACd;AAAA;AAAA;AAAA;AAAA,EAMO,SAAS,KAAuB,EAAA;AACrC,IAAA,IAAA,CAAK,KAAQ,GAAA,KAAA;AAAA;AACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,UAAA,CACX,cACA,EAAA,aAAA,EACA,MACe,EAAA;AACf,IAAI,IAAA;AACF,MAAA,MAAM,KAAkB,GAAA,MAAM,MAC3B,CAAA,KAAA,CAAM,aAAa,CAAA,CACnB,OAAQ,CAAA,IAAA,EAAM,aAAa,CAAA,CAC3B,KAAM,CAAA,IAAI,EACV,QAAS,EAAA;AAEZ,MAAA,cAAA,CAAe,SAAS,KAAK,CAAA;AAAA,aACtB,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAoC,iCAAA,EAAA,KAAK,CAAE,CAAA,CAAA;AAAA;AAC7D;AACF;AAAA;AAAA;AAAA;AAAA,EAMA,QAAqB,GAAA;AACnB,IAAA,OAAO,IAAK,CAAA,KAAA;AAAA;AAEhB;;;;"}