@backstage-community/plugin-rbac-backend 5.2.5 → 5.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ### Dependencies
2
2
 
3
+ ## 5.2.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 7843798: Updated dependency `qs` to `6.13.1`.
8
+ - 4b3653a: Clean up api report warnings and remove unnecessary files
9
+ - Updated dependencies [4b3653a]
10
+ - @backstage-community/plugin-rbac-common@1.12.3
11
+ - @backstage-community/plugin-rbac-node@1.8.3
12
+
13
+ ## 5.2.6
14
+
15
+ ### Patch Changes
16
+
17
+ - 4084738: Ensures that the permissions and roles are properly synced during request handling. This is important in high availability scenarios as we need to ensure data is up to date during scaling.
18
+
3
19
  ## 5.2.5
4
20
 
5
21
  ### Patch Changes
@@ -30,9 +30,7 @@ const useAdminsFromConfig = async (admins, enf, auditLogger$1, roleMetadataStora
30
30
  newGroupPolicies.set(entityRef, ADMIN_ROLE_NAME);
31
31
  }
32
32
  }
33
- const adminRoleMeta = await roleMetadataStorage.findRoleMetadata(
34
- ADMIN_ROLE_NAME
35
- );
33
+ const adminRoleMeta = await roleMetadataStorage.findRoleMetadata(ADMIN_ROLE_NAME);
36
34
  const trx = await knex.transaction();
37
35
  let addedRoleMembers;
38
36
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"admin-creation.cjs.js","sources":["../../src/admin-permissions/admin-creation.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 { Config } from '@backstage/config';\n\nimport { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport { Knex } from 'knex';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n PermissionAuditInfo,\n PermissionEvents,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from '../audit-log/audit-logger';\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport { removeTheDifference } from '../helper';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { validateEntityReference } from '../validation/policies-validation';\n\nexport const ADMIN_ROLE_NAME = 'role:default/rbac_admin';\nexport const ADMIN_ROLE_AUTHOR = 'application configuration';\nconst DEF_ADMIN_ROLE_DESCRIPTION =\n 'The default permission policy for the admin role allows for the creation, deletion, updating, and reading of roles and permission policies.';\n\nconst getAdminRoleMetadata = (): RoleMetadataDao => {\n const currentDate: Date = new Date();\n return {\n source: 'configuration',\n roleEntityRef: ADMIN_ROLE_NAME,\n description: DEF_ADMIN_ROLE_DESCRIPTION,\n author: ADMIN_ROLE_AUTHOR,\n modifiedBy: ADMIN_ROLE_AUTHOR,\n lastModified: currentDate.toUTCString(),\n createdAt: currentDate.toUTCString(),\n };\n};\n\nexport const useAdminsFromConfig = async (\n admins: Config[],\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n roleMetadataStorage: RoleMetadataStorage,\n knex: Knex,\n) => {\n const addedGroupPolicies = new Map<string, string>();\n const newGroupPolicies = new Map<string, string>();\n\n for (const admin of admins) {\n const entityRef = admin.getString('name');\n validateEntityReference(entityRef);\n\n addedGroupPolicies.set(entityRef, ADMIN_ROLE_NAME);\n\n if (!(await enf.hasGroupingPolicy(...[entityRef, ADMIN_ROLE_NAME]))) {\n newGroupPolicies.set(entityRef, ADMIN_ROLE_NAME);\n }\n }\n\n const adminRoleMeta = await roleMetadataStorage.findRoleMetadata(\n ADMIN_ROLE_NAME,\n );\n\n const trx = await knex.transaction();\n let addedRoleMembers;\n try {\n if (!adminRoleMeta) {\n // even if there are no user, we still create default role metadata for admins\n await roleMetadataStorage.createRoleMetadata(getAdminRoleMetadata(), trx);\n } else if (adminRoleMeta.source === 'legacy') {\n await roleMetadataStorage.updateRoleMetadata(\n getAdminRoleMetadata(),\n ADMIN_ROLE_NAME,\n trx,\n );\n }\n\n addedRoleMembers = Array.from<string[]>(newGroupPolicies.entries());\n await enf.addGroupingPolicies(\n addedRoleMembers,\n getAdminRoleMetadata(),\n trx,\n );\n\n await trx.commit();\n } catch (error) {\n await trx.rollback(error);\n throw error;\n }\n\n await auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created or updated role`,\n eventName: RoleEvents.CREATE_OR_UPDATE_ROLE,\n metadata: {\n ...getAdminRoleMetadata(),\n members: addedRoleMembers.map(gp => gp[0]),\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n\n const configGroupPolicies = await enf.getFilteredGroupingPolicy(\n 1,\n ADMIN_ROLE_NAME,\n );\n\n await removeTheDifference(\n configGroupPolicies.map(gp => gp[0]),\n Array.from<string>(addedGroupPolicies.keys()),\n 'configuration',\n ADMIN_ROLE_NAME,\n enf,\n auditLogger,\n ADMIN_ROLE_AUTHOR,\n );\n};\n\nconst addAdminPermissions = async (\n policies: string[][],\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n) => {\n const policiesToAdd: string[][] = [];\n for (const policy of policies) {\n if (!(await enf.hasPolicy(...policy))) {\n policiesToAdd.push(policy);\n }\n }\n await enf.addPolicies(policiesToAdd);\n\n await auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created RBAC admin permissions`,\n eventName: PermissionEvents.CREATE_POLICY,\n metadata: { policies: policies, source: 'configuration' },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n};\n\nexport const setAdminPermissions = async (\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n) => {\n const adminPermissions = [\n [ADMIN_ROLE_NAME, 'policy-entity', 'read', 'allow'],\n [ADMIN_ROLE_NAME, 'policy-entity', 'create', 'allow'],\n [ADMIN_ROLE_NAME, 'policy-entity', 'delete', 'allow'],\n [ADMIN_ROLE_NAME, 'policy-entity', 'update', 'allow'],\n // Needed for the RBAC frontend plugin.\n [ADMIN_ROLE_NAME, 'catalog-entity', 'read', 'allow'],\n ];\n await addAdminPermissions(adminPermissions, enf, auditLogger);\n};\n"],"names":["auditLogger","validateEntityReference","RBAC_BACKEND","RoleEvents","HANDLE_RBAC_DATA_STAGE","removeTheDifference","PermissionEvents"],"mappings":"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"}
1
+ {"version":3,"file":"admin-creation.cjs.js","sources":["../../src/admin-permissions/admin-creation.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 { Config } from '@backstage/config';\n\nimport { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport { Knex } from 'knex';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n PermissionAuditInfo,\n PermissionEvents,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from '../audit-log/audit-logger';\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport { removeTheDifference } from '../helper';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { validateEntityReference } from '../validation/policies-validation';\n\nexport const ADMIN_ROLE_NAME = 'role:default/rbac_admin';\nexport const ADMIN_ROLE_AUTHOR = 'application configuration';\nconst DEF_ADMIN_ROLE_DESCRIPTION =\n 'The default permission policy for the admin role allows for the creation, deletion, updating, and reading of roles and permission policies.';\n\nconst getAdminRoleMetadata = (): RoleMetadataDao => {\n const currentDate: Date = new Date();\n return {\n source: 'configuration',\n roleEntityRef: ADMIN_ROLE_NAME,\n description: DEF_ADMIN_ROLE_DESCRIPTION,\n author: ADMIN_ROLE_AUTHOR,\n modifiedBy: ADMIN_ROLE_AUTHOR,\n lastModified: currentDate.toUTCString(),\n createdAt: currentDate.toUTCString(),\n };\n};\n\nexport const useAdminsFromConfig = async (\n admins: Config[],\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n roleMetadataStorage: RoleMetadataStorage,\n knex: Knex,\n) => {\n const addedGroupPolicies = new Map<string, string>();\n const newGroupPolicies = new Map<string, string>();\n\n for (const admin of admins) {\n const entityRef = admin.getString('name');\n validateEntityReference(entityRef);\n\n addedGroupPolicies.set(entityRef, ADMIN_ROLE_NAME);\n\n if (!(await enf.hasGroupingPolicy(...[entityRef, ADMIN_ROLE_NAME]))) {\n newGroupPolicies.set(entityRef, ADMIN_ROLE_NAME);\n }\n }\n\n const adminRoleMeta =\n await roleMetadataStorage.findRoleMetadata(ADMIN_ROLE_NAME);\n\n const trx = await knex.transaction();\n let addedRoleMembers;\n try {\n if (!adminRoleMeta) {\n // even if there are no user, we still create default role metadata for admins\n await roleMetadataStorage.createRoleMetadata(getAdminRoleMetadata(), trx);\n } else if (adminRoleMeta.source === 'legacy') {\n await roleMetadataStorage.updateRoleMetadata(\n getAdminRoleMetadata(),\n ADMIN_ROLE_NAME,\n trx,\n );\n }\n\n addedRoleMembers = Array.from<string[]>(newGroupPolicies.entries());\n await enf.addGroupingPolicies(\n addedRoleMembers,\n getAdminRoleMetadata(),\n trx,\n );\n\n await trx.commit();\n } catch (error) {\n await trx.rollback(error);\n throw error;\n }\n\n await auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created or updated role`,\n eventName: RoleEvents.CREATE_OR_UPDATE_ROLE,\n metadata: {\n ...getAdminRoleMetadata(),\n members: addedRoleMembers.map(gp => gp[0]),\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n\n const configGroupPolicies = await enf.getFilteredGroupingPolicy(\n 1,\n ADMIN_ROLE_NAME,\n );\n\n await removeTheDifference(\n configGroupPolicies.map(gp => gp[0]),\n Array.from<string>(addedGroupPolicies.keys()),\n 'configuration',\n ADMIN_ROLE_NAME,\n enf,\n auditLogger,\n ADMIN_ROLE_AUTHOR,\n );\n};\n\nconst addAdminPermissions = async (\n policies: string[][],\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n) => {\n const policiesToAdd: string[][] = [];\n for (const policy of policies) {\n if (!(await enf.hasPolicy(...policy))) {\n policiesToAdd.push(policy);\n }\n }\n await enf.addPolicies(policiesToAdd);\n\n await auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created RBAC admin permissions`,\n eventName: PermissionEvents.CREATE_POLICY,\n metadata: { policies: policies, source: 'configuration' },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n};\n\nexport const setAdminPermissions = async (\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n) => {\n const adminPermissions = [\n [ADMIN_ROLE_NAME, 'policy-entity', 'read', 'allow'],\n [ADMIN_ROLE_NAME, 'policy-entity', 'create', 'allow'],\n [ADMIN_ROLE_NAME, 'policy-entity', 'delete', 'allow'],\n [ADMIN_ROLE_NAME, 'policy-entity', 'update', 'allow'],\n // Needed for the RBAC frontend plugin.\n [ADMIN_ROLE_NAME, 'catalog-entity', 'read', 'allow'],\n ];\n await addAdminPermissions(adminPermissions, enf, auditLogger);\n};\n"],"names":["auditLogger","validateEntityReference","RBAC_BACKEND","RoleEvents","HANDLE_RBAC_DATA_STAGE","removeTheDifference","PermissionEvents"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"casbin-adapter-factory.cjs.js","sources":["../../src/database/casbin-adapter-factory.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 { Config } from '@backstage/config';\nimport type { ConfigApi } from '@backstage/core-plugin-api';\n\nimport { Knex } from 'knex';\nimport TypeORMAdapter from 'typeorm-adapter';\n\nimport { resolve } from 'path';\nimport type { ConnectionOptions, TlsOptions } from 'tls';\n\nimport '@backstage/backend-defaults/database';\n\nconst DEFAULT_SQLITE3_STORAGE_FILE_NAME = 'rbac.sqlite';\n\nexport class CasbinDBAdapterFactory {\n public constructor(\n private readonly config: ConfigApi,\n private readonly databaseClient: Knex,\n ) {}\n\n public async createAdapter(): Promise<TypeORMAdapter> {\n const databaseConfig = this.config.getOptionalConfig('backend.database');\n const client = databaseConfig?.getOptionalString('client');\n\n let adapter;\n if (client === 'pg') {\n const dbName = await this.databaseClient.client.config.connection\n .database;\n const schema =\n (await this.databaseClient.client.searchPath?.[0]) ?? 'public';\n\n const ssl = this.handlePostgresSSL(databaseConfig!);\n\n adapter = await TypeORMAdapter.newAdapter({\n type: 'postgres',\n host: databaseConfig?.getString('connection.host'),\n port: databaseConfig?.getNumber('connection.port'),\n username: databaseConfig?.getString('connection.user'),\n password: databaseConfig?.getString('connection.password'),\n ssl,\n database: dbName,\n schema: schema,\n });\n }\n\n if (client === 'better-sqlite3') {\n let storage;\n if (typeof databaseConfig?.get('connection')?.valueOf() === 'string') {\n storage = databaseConfig?.getString('connection');\n } else if (databaseConfig?.has('connection.directory')) {\n const storageDir = databaseConfig?.getString('connection.directory');\n storage = resolve(storageDir, DEFAULT_SQLITE3_STORAGE_FILE_NAME);\n }\n\n adapter = await TypeORMAdapter.newAdapter({\n type: 'better-sqlite3',\n // Storage type or path to the storage.\n database: storage || ':memory:',\n });\n }\n\n if (!adapter) {\n throw new Error(`Unsupported database client ${client}`);\n }\n\n return adapter;\n }\n\n private handlePostgresSSL(\n dbConfig: Config,\n ): boolean | TlsOptions | undefined {\n const connection = dbConfig.getOptional<Knex.PgConnectionConfig | string>(\n 'connection',\n );\n if (!connection) {\n return undefined;\n }\n\n if (typeof connection === 'string' || connection instanceof String) {\n throw new Error(\n `rbac backend plugin doesn't support postgres connection in a string format yet`,\n );\n }\n\n const ssl: boolean | ConnectionOptions | undefined = connection.ssl;\n\n if (ssl === undefined) {\n return undefined;\n }\n\n if (typeof ssl === 'boolean') {\n return ssl;\n }\n\n if (typeof ssl === 'object') {\n const { ca, rejectUnauthorized } = ssl as ConnectionOptions;\n const tlsOpts = { ca, rejectUnauthorized };\n\n // SSL object was defined with some options that we don't support yet.\n if (Object.values(tlsOpts).every(el => el === undefined)) {\n return true;\n }\n\n return tlsOpts;\n }\n\n return undefined;\n }\n}\n"],"names":["TypeORMAdapter","resolve"],"mappings":"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"}
1
+ {"version":3,"file":"casbin-adapter-factory.cjs.js","sources":["../../src/database/casbin-adapter-factory.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 { Config } from '@backstage/config';\nimport type { ConfigApi } from '@backstage/core-plugin-api';\n\nimport { Knex } from 'knex';\nimport TypeORMAdapter from 'typeorm-adapter';\n\nimport { resolve } from 'path';\nimport type { ConnectionOptions, TlsOptions } from 'tls';\n\nimport '@backstage/backend-defaults/database';\n\nconst DEFAULT_SQLITE3_STORAGE_FILE_NAME = 'rbac.sqlite';\n\nexport class CasbinDBAdapterFactory {\n public constructor(\n private readonly config: ConfigApi,\n private readonly databaseClient: Knex,\n ) {}\n\n public async createAdapter(): Promise<TypeORMAdapter> {\n const databaseConfig = this.config.getOptionalConfig('backend.database');\n const client = databaseConfig?.getOptionalString('client');\n\n let adapter;\n if (client === 'pg') {\n const dbName =\n await this.databaseClient.client.config.connection.database;\n const schema =\n (await this.databaseClient.client.searchPath?.[0]) ?? 'public';\n\n const ssl = this.handlePostgresSSL(databaseConfig!);\n\n adapter = await TypeORMAdapter.newAdapter({\n type: 'postgres',\n host: databaseConfig?.getString('connection.host'),\n port: databaseConfig?.getNumber('connection.port'),\n username: databaseConfig?.getString('connection.user'),\n password: databaseConfig?.getString('connection.password'),\n ssl,\n database: dbName,\n schema: schema,\n });\n }\n\n if (client === 'better-sqlite3') {\n let storage;\n if (typeof databaseConfig?.get('connection')?.valueOf() === 'string') {\n storage = databaseConfig?.getString('connection');\n } else if (databaseConfig?.has('connection.directory')) {\n const storageDir = databaseConfig?.getString('connection.directory');\n storage = resolve(storageDir, DEFAULT_SQLITE3_STORAGE_FILE_NAME);\n }\n\n adapter = await TypeORMAdapter.newAdapter({\n type: 'better-sqlite3',\n // Storage type or path to the storage.\n database: storage || ':memory:',\n });\n }\n\n if (!adapter) {\n throw new Error(`Unsupported database client ${client}`);\n }\n\n return adapter;\n }\n\n private handlePostgresSSL(\n dbConfig: Config,\n ): boolean | TlsOptions | undefined {\n const connection = dbConfig.getOptional<Knex.PgConnectionConfig | string>(\n 'connection',\n );\n if (!connection) {\n return undefined;\n }\n\n if (typeof connection === 'string' || connection instanceof String) {\n throw new Error(\n `rbac backend plugin doesn't support postgres connection in a string format yet`,\n );\n }\n\n const ssl: boolean | ConnectionOptions | undefined = connection.ssl;\n\n if (ssl === undefined) {\n return undefined;\n }\n\n if (typeof ssl === 'boolean') {\n return ssl;\n }\n\n if (typeof ssl === 'object') {\n const { ca, rejectUnauthorized } = ssl as ConnectionOptions;\n const tlsOpts = { ca, rejectUnauthorized };\n\n // SSL object was defined with some options that we don't support yet.\n if (Object.values(tlsOpts).every(el => el === undefined)) {\n return true;\n }\n\n return tlsOpts;\n }\n\n return undefined;\n }\n}\n"],"names":["TypeORMAdapter","resolve"],"mappings":"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"}
@@ -58,27 +58,7 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
58
58
  casbin.newModelFromString(permissionModel.MODEL),
59
59
  new casbin.FileAdapter(this.filePath)
60
60
  );
61
- const roleMetadatas = await this.roleMetadataStorage.filterRoleMetadata(
62
- "csv-file"
63
- );
64
- const fileRoles = roleMetadatas.map((meta) => meta.roleEntityRef);
65
- if (fileRoles.length > 0) {
66
- const groupingPoliciesToRemove = await this.enforcer.getFilteredGroupingPolicy(1, ...fileRoles);
67
- for (const gPolicy of groupingPoliciesToRemove) {
68
- if (!await tempEnforcer.hasGroupingPolicy(...gPolicy)) {
69
- this.csvFilePolicies.removedGroupPolicies.push(gPolicy);
70
- }
71
- }
72
- const policiesToRemove = await this.enforcer.getFilteredPolicy(
73
- 0,
74
- ...fileRoles
75
- );
76
- for (const policy of policiesToRemove) {
77
- if (!await tempEnforcer.hasPolicy(...policy)) {
78
- this.csvFilePolicies.removedPolicies.push(policy);
79
- }
80
- }
81
- }
61
+ await this.cleanUpRolesAndPolicies();
82
62
  const policiesToAdd = await tempEnforcer.getPolicy();
83
63
  const groupPoliciesToAdd = await tempEnforcer.getGroupingPolicy();
84
64
  for (const policy of policiesToAdd) {
@@ -102,9 +82,7 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
102
82
  // temp enforcer (csv file) and a role metadata storage.
103
83
  // We will update role metadata with the new source "csv-file"
104
84
  async migrateLegacyMetadata(tempEnforcer) {
105
- let legacyRolesMetadata = await this.roleMetadataStorage.filterRoleMetadata(
106
- "legacy"
107
- );
85
+ let legacyRolesMetadata = await this.roleMetadataStorage.filterRoleMetadata("legacy");
108
86
  const legacyRoles = legacyRolesMetadata.map((meta) => meta.roleEntityRef);
109
87
  if (legacyRoles.length > 0) {
110
88
  const legacyGroupPolicies = await tempEnforcer.getFilteredGroupingPolicy(
@@ -361,9 +339,7 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
361
339
  metadata,
362
340
  isUpdate.length > 1
363
341
  );
364
- const isRolePresent = await this.roleMetadataStorage.findRoleMetadata(
365
- roleEntityRef
366
- );
342
+ const isRolePresent = await this.roleMetadataStorage.findRoleMetadata(roleEntityRef);
367
343
  const eventName = isRolePresent ? auditLogger.RoleEvents.UPDATE_ROLE : auditLogger.RoleEvents.DELETE_ROLE;
368
344
  const message = isRolePresent ? "Updated role: deleted members" : "Deleted role";
369
345
  await this.auditLogger.auditLog({
@@ -383,9 +359,7 @@ class CSVFileWatcher extends fileWatcher.AbstractFileWatcher {
383
359
  this.csvFilePolicies.removedGroupPolicies = [];
384
360
  }
385
361
  async cleanUpRolesAndPolicies() {
386
- const roleMetadatas = await this.roleMetadataStorage.filterRoleMetadata(
387
- "csv-file"
388
- );
362
+ const roleMetadatas = await this.roleMetadataStorage.filterRoleMetadata("csv-file");
389
363
  const fileRoles = roleMetadatas.map((meta) => meta.roleEntityRef);
390
364
  if (fileRoles.length > 0) {
391
365
  for (const fileRole of fileRoles) {
@@ -1 +1 @@
1
- {"version":3,"file":"csv-file-watcher.cjs.js","sources":["../../src/file-permissions/csv-file-watcher.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { LoggerService } from '@backstage/backend-plugin-api';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport { Enforcer, FileAdapter, newEnforcer, newModelFromString } from 'casbin';\nimport { parse } from 'csv-parse/sync';\nimport { difference } from 'lodash';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n PermissionAuditInfo,\n PermissionEvents,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from '../audit-log/audit-logger';\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport {\n mergeRoleMetadata,\n metadataStringToPolicy,\n policyToString,\n transformArrayToPolicy,\n} from '../helper';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { MODEL } from '../service/permission-model';\nimport {\n checkForDuplicateGroupPolicies,\n checkForDuplicatePolicies,\n validateGroupingPolicy,\n validatePolicy,\n validateSource,\n} from '../validation/policies-validation';\nimport { AbstractFileWatcher } from './file-watcher';\n\nexport const CSV_PERMISSION_POLICY_FILE_AUTHOR = 'csv permission policy file';\n\ntype CSVFilePolicies = {\n addedPolicies: string[][];\n addedGroupPolicies: string[][];\n removedPolicies: string[][];\n removedGroupPolicies: string[][];\n};\n\nexport class CSVFileWatcher extends AbstractFileWatcher<string[][]> {\n private currentContent: string[][];\n private csvFilePolicies: CSVFilePolicies;\n\n constructor(\n filePath: string | undefined,\n allowReload: boolean,\n logger: LoggerService,\n private readonly enforcer: EnforcerDelegate,\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly auditLogger: AuditLogger,\n ) {\n super(filePath, allowReload, logger);\n this.currentContent = [];\n this.csvFilePolicies = {\n addedPolicies: [],\n addedGroupPolicies: [],\n removedPolicies: [],\n removedGroupPolicies: [],\n };\n }\n\n /**\n * parse is used to parse the current contents of the CSV file.\n * @returns The CSV file parsed into a string[][].\n */\n parse(): string[][] {\n const content = this.getCurrentContents();\n const parser = parse(content, {\n skip_empty_lines: true,\n relax_column_count: true,\n trim: true,\n });\n\n return parser;\n }\n\n /**\n * initialize will initialize the CSV file by loading all of the permission policies and roles into\n * the enforcer.\n * First, we will remove all roles and permission policies if they do not exist in the temporary file enforcer.\n * Next, we will add all roles and permission polices if they are new to the CSV file\n * Finally, we will set the file to be watched if allow reload is set\n * @param csvFileName The name of the csvFile\n * @param allowReload Whether or not we will allow reloads of the CSV file\n */\n async initialize(): Promise<void> {\n if (!this.filePath) {\n return;\n }\n let content: string[][] = [];\n // If the file is set load the file contents\n content = this.parse();\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new FileAdapter(this.filePath),\n );\n\n // Check for any old policies that will need to be removed by checking if\n // the policy no longer exists in the temp enforcer (csv file)\n const roleMetadatas = await this.roleMetadataStorage.filterRoleMetadata(\n 'csv-file',\n );\n const fileRoles = roleMetadatas.map(meta => meta.roleEntityRef);\n\n if (fileRoles.length > 0) {\n const groupingPoliciesToRemove =\n await this.enforcer.getFilteredGroupingPolicy(1, ...fileRoles);\n for (const gPolicy of groupingPoliciesToRemove) {\n if (!(await tempEnforcer.hasGroupingPolicy(...gPolicy))) {\n this.csvFilePolicies.removedGroupPolicies.push(gPolicy);\n }\n }\n const policiesToRemove = await this.enforcer.getFilteredPolicy(\n 0,\n ...fileRoles,\n );\n for (const policy of policiesToRemove) {\n if (!(await tempEnforcer.hasPolicy(...policy))) {\n this.csvFilePolicies.removedPolicies.push(policy);\n }\n }\n }\n\n // Check for any new policies that need to be added by checking if\n // the policy does not currently exist in the enforcer\n const policiesToAdd = await tempEnforcer.getPolicy();\n const groupPoliciesToAdd = await tempEnforcer.getGroupingPolicy();\n\n for (const policy of policiesToAdd) {\n if (!(await this.enforcer.hasPolicy(...policy))) {\n this.csvFilePolicies.addedPolicies.push(policy);\n }\n }\n\n for (const groupPolicy of groupPoliciesToAdd) {\n if (!(await this.enforcer.hasGroupingPolicy(...groupPolicy))) {\n this.csvFilePolicies.addedGroupPolicies.push(groupPolicy);\n }\n }\n\n await this.migrateLegacyMetadata(tempEnforcer);\n\n // We pass current here because this is during initialization and it has not changed yet\n await this.updatePolicies(content, tempEnforcer);\n\n if (this.allowReload) {\n this.watchFile();\n }\n }\n\n // Check for policies that might need to be updated\n // This will involve update \"legacy\" source in the role metadata if it exist in both the\n // temp enforcer (csv file) and a role metadata storage.\n // We will update role metadata with the new source \"csv-file\"\n private async migrateLegacyMetadata(tempEnforcer: Enforcer) {\n let legacyRolesMetadata = await this.roleMetadataStorage.filterRoleMetadata(\n 'legacy',\n );\n const legacyRoles = legacyRolesMetadata.map(meta => meta.roleEntityRef);\n if (legacyRoles.length > 0) {\n const legacyGroupPolicies = await tempEnforcer.getFilteredGroupingPolicy(\n 1,\n ...legacyRoles,\n );\n const legacyPolicies = await tempEnforcer.getFilteredPolicy(\n 0,\n ...legacyRoles,\n );\n const legacyRolesFromFile = new Set([\n ...legacyGroupPolicies.map(gp => gp[1]),\n ...legacyPolicies.map(p => p[0]),\n ]);\n legacyRolesMetadata = legacyRolesMetadata.filter(meta =>\n legacyRolesFromFile.has(meta.roleEntityRef),\n );\n for (const legacyRoleMeta of legacyRolesMetadata) {\n const nonLegacyRole = mergeRoleMetadata(legacyRoleMeta, {\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n source: 'csv-file',\n roleEntityRef: legacyRoleMeta.roleEntityRef,\n });\n await this.roleMetadataStorage.updateRoleMetadata(\n nonLegacyRole,\n legacyRoleMeta.roleEntityRef,\n );\n }\n }\n }\n\n /**\n * onChange is called whenever there is a change to the CSV file.\n * It will parse the current and new contents of the CSV file and process the roles and permission policies present.\n * Afterwards, it will find the difference between the current and new contents of the CSV file\n * and sort them into added / removed, permission policies / roles.\n * It will finally call updatePolicies with the new content.\n */\n async onChange(): Promise<void> {\n const newContent = this.parse();\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new FileAdapter(this.filePath!),\n );\n\n const currentFlatContent = this.currentContent.flatMap(data => {\n return policyToString(data);\n });\n const newFlatContent = newContent.flatMap(data => {\n return policyToString(data);\n });\n\n const diffRemoved = difference(currentFlatContent, newFlatContent); // policy was removed\n const diffAdded = difference(newFlatContent, currentFlatContent); // policy was added\n\n await this.migrateLegacyMetadata(tempEnforcer);\n\n if (diffRemoved.length === 0 && diffAdded.length === 0) {\n return;\n }\n\n diffRemoved.forEach(policy => {\n const convertedPolicy = metadataStringToPolicy(policy);\n if (convertedPolicy[0] === 'p') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.removedPolicies.push(convertedPolicy);\n } else if (convertedPolicy[0] === 'g') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.removedGroupPolicies.push(convertedPolicy);\n }\n });\n\n diffAdded.forEach(policy => {\n const convertedPolicy = metadataStringToPolicy(policy);\n if (convertedPolicy[0] === 'p') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.addedPolicies.push(convertedPolicy);\n } else if (convertedPolicy[0] === 'g') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.addedGroupPolicies.push(convertedPolicy);\n }\n });\n\n await this.updatePolicies(newContent, tempEnforcer);\n }\n\n /**\n * updatePolicies is used to update all of the permission policies and roles within a CSV file.\n * It will check the number of added and removed permissions policies and roles and call the appropriate\n * methods for these.\n * It will also update the current contents of the CSV file to the most recent\n * @param newContent The new content present in the CSV file\n * @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies\n */\n private async updatePolicies(\n newContent: string[][],\n tempEnforcer: Enforcer,\n ): Promise<void> {\n this.currentContent = newContent;\n\n if (this.csvFilePolicies.addedPolicies.length > 0)\n await this.addPermissionPolicies(tempEnforcer);\n if (this.csvFilePolicies.removedPolicies.length > 0)\n await this.removePermissionPolicies();\n if (this.csvFilePolicies.addedGroupPolicies.length > 0)\n await this.addRoles(tempEnforcer);\n if (this.csvFilePolicies.removedGroupPolicies.length > 0)\n await this.removeRoles();\n }\n\n /**\n * addPermissionPolicies will add the new permission policies that are present in the CSV file.\n * We will attempt to validate the permission policy and log any warnings that are encountered.\n * If a warning is encountered, we will skip adding the permission policy to the enforcer.\n * @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies\n */\n private async addPermissionPolicies(tempEnforcer: Enforcer): Promise<void> {\n for (const policy of this.csvFilePolicies.addedPolicies) {\n const transformedPolicy = transformArrayToPolicy(policy);\n const metadata = await this.roleMetadataStorage.findRoleMetadata(\n policy[0],\n );\n\n let err = validatePolicy(transformedPolicy);\n if (err) {\n this.logger.warn(\n `Failed to validate policy from file ${this.filePath}. Cause: ${err.message}`,\n );\n continue;\n }\n\n err = await validateSource('csv-file', metadata);\n if (err) {\n this.logger.warn(\n `Unable to add policy ${policy} from file ${this.filePath}. Cause: ${err.message}`,\n );\n continue;\n }\n\n err = await checkForDuplicatePolicies(\n tempEnforcer,\n policy,\n this.filePath!,\n );\n if (err) {\n this.logger.warn(err.message);\n continue;\n }\n try {\n await this.enforcer.addPolicy(policy);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created policy`,\n eventName: PermissionEvents.CREATE_POLICY,\n metadata: { policies: [policy], source: 'csv-file' },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to add or update policy ${policy} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n\n this.csvFilePolicies.addedPolicies = [];\n }\n\n /**\n * removePermissionPolicies will remove the permission policies that are no longer present in the CSV file.\n */\n private async removePermissionPolicies(): Promise<void> {\n try {\n await this.enforcer.removePolicies(this.csvFilePolicies.removedPolicies);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Deleted policies`,\n eventName: PermissionEvents.DELETE_POLICY,\n metadata: {\n policies: this.csvFilePolicies.removedPolicies,\n source: 'csv-file',\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to remove policies ${JSON.stringify(\n this.csvFilePolicies.removedPolicies,\n )} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n this.csvFilePolicies.removedPolicies = [];\n }\n\n /**\n * addRoles will add the new roles that are present in the CSV file.\n * We will attempt to validate the role and log any warnings that are encountered.\n * If a warning is encountered, we will skip adding the role to the enforcer.\n * @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies\n */\n private async addRoles(tempEnforcer: Enforcer): Promise<void> {\n for (const groupPolicy of this.csvFilePolicies.addedGroupPolicies) {\n let err = await validateGroupingPolicy(\n groupPolicy,\n this.roleMetadataStorage,\n 'csv-file',\n );\n if (err) {\n this.logger.warn(\n `${err.message}, error originates from file ${this.filePath}`,\n );\n continue;\n }\n\n err = await checkForDuplicateGroupPolicies(\n tempEnforcer,\n groupPolicy,\n this.filePath!,\n );\n if (err) {\n this.logger.warn(err.message);\n continue;\n }\n\n try {\n const roleMetadata: RoleMetadataDao = {\n source: 'csv-file',\n roleEntityRef: groupPolicy[1],\n author: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n };\n\n const currentMetadata = await this.roleMetadataStorage.findRoleMetadata(\n roleMetadata.roleEntityRef,\n );\n\n await this.enforcer.addGroupingPolicy(groupPolicy, roleMetadata);\n\n const eventName = currentMetadata\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.CREATE_ROLE;\n const message = currentMetadata ? 'Updated role' : 'Created role';\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMetadata, members: [groupPolicy[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to add or update group policy ${groupPolicy} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n this.csvFilePolicies.addedGroupPolicies = [];\n }\n\n /**\n * removeRoles will remove the roles that are no longer present in the CSV file.\n * If the role exists with multiple groups and or users, we will update it role information.\n * Otherwise, we will remove the role completely.\n */\n private async removeRoles(): Promise<void> {\n for (const groupPolicy of this.csvFilePolicies.removedGroupPolicies) {\n const roleEntityRef = groupPolicy[1];\n // this requires knowledge of whether or not it is an update\n const isUpdate = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n\n // Need to update the time\n try {\n const metadata: RoleMetadataDao = {\n source: 'csv-file',\n roleEntityRef,\n author: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n };\n\n await this.enforcer.removeGroupingPolicy(\n groupPolicy,\n metadata,\n isUpdate.length > 1,\n );\n\n const isRolePresent = await this.roleMetadataStorage.findRoleMetadata(\n roleEntityRef,\n );\n const eventName = isRolePresent\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.DELETE_ROLE;\n const message = isRolePresent\n ? 'Updated role: deleted members'\n : 'Deleted role';\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...metadata, members: [groupPolicy[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to remove group policy ${groupPolicy} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n this.csvFilePolicies.removedGroupPolicies = [];\n }\n\n async cleanUpRolesAndPolicies(): Promise<void> {\n const roleMetadatas = await this.roleMetadataStorage.filterRoleMetadata(\n 'csv-file',\n );\n const fileRoles = roleMetadatas.map(meta => meta.roleEntityRef);\n\n if (fileRoles.length > 0) {\n for (const fileRole of fileRoles) {\n this.csvFilePolicies.removedGroupPolicies.push(\n ...(await this.enforcer.getFilteredGroupingPolicy(1, fileRole)),\n );\n this.csvFilePolicies.removedPolicies.push(\n ...(await this.enforcer.getFilteredPolicy(0, fileRole)),\n );\n }\n }\n await this.removePermissionPolicies();\n await this.removeRoles();\n }\n}\n"],"names":["AbstractFileWatcher","parse","newEnforcer","newModelFromString","MODEL","FileAdapter","mergeRoleMetadata","policyToString","difference","metadataStringToPolicy","transformArrayToPolicy","validatePolicy","validateSource","checkForDuplicatePolicies","RBAC_BACKEND","PermissionEvents","HANDLE_RBAC_DATA_STAGE","validateGroupingPolicy","checkForDuplicateGroupPolicies","RoleEvents"],"mappings":"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"}
1
+ {"version":3,"file":"csv-file-watcher.cjs.js","sources":["../../src/file-permissions/csv-file-watcher.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { LoggerService } from '@backstage/backend-plugin-api';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport { Enforcer, FileAdapter, newEnforcer, newModelFromString } from 'casbin';\nimport { parse } from 'csv-parse/sync';\nimport { difference } from 'lodash';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n PermissionAuditInfo,\n PermissionEvents,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from '../audit-log/audit-logger';\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport {\n mergeRoleMetadata,\n metadataStringToPolicy,\n policyToString,\n transformArrayToPolicy,\n} from '../helper';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { MODEL } from '../service/permission-model';\nimport {\n checkForDuplicateGroupPolicies,\n checkForDuplicatePolicies,\n validateGroupingPolicy,\n validatePolicy,\n validateSource,\n} from '../validation/policies-validation';\nimport { AbstractFileWatcher } from './file-watcher';\n\nexport const CSV_PERMISSION_POLICY_FILE_AUTHOR = 'csv permission policy file';\n\ntype CSVFilePolicies = {\n addedPolicies: string[][];\n addedGroupPolicies: string[][];\n removedPolicies: string[][];\n removedGroupPolicies: string[][];\n};\n\nexport class CSVFileWatcher extends AbstractFileWatcher<string[][]> {\n private currentContent: string[][];\n private csvFilePolicies: CSVFilePolicies;\n\n constructor(\n filePath: string | undefined,\n allowReload: boolean,\n logger: LoggerService,\n private readonly enforcer: EnforcerDelegate,\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly auditLogger: AuditLogger,\n ) {\n super(filePath, allowReload, logger);\n this.currentContent = [];\n this.csvFilePolicies = {\n addedPolicies: [],\n addedGroupPolicies: [],\n removedPolicies: [],\n removedGroupPolicies: [],\n };\n }\n\n /**\n * parse is used to parse the current contents of the CSV file.\n * @returns The CSV file parsed into a string[][].\n */\n parse(): string[][] {\n const content = this.getCurrentContents();\n const parser = parse(content, {\n skip_empty_lines: true,\n relax_column_count: true,\n trim: true,\n });\n\n return parser;\n }\n\n /**\n * initialize will initialize the CSV file by loading all of the permission policies and roles into\n * the enforcer.\n * First, we will remove all roles and permission policies if they do not exist in the temporary file enforcer.\n * Next, we will add all roles and permission polices if they are new to the CSV file\n * Finally, we will set the file to be watched if allow reload is set\n * @param csvFileName The name of the csvFile\n * @param allowReload Whether or not we will allow reloads of the CSV file\n */\n async initialize(): Promise<void> {\n if (!this.filePath) {\n return;\n }\n let content: string[][] = [];\n // If the file is set load the file contents\n content = this.parse();\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new FileAdapter(this.filePath),\n );\n\n // Check for any old policies that will need to be removed\n await this.cleanUpRolesAndPolicies();\n\n // Check for any new policies that need to be added by checking if\n // the policy does not currently exist in the enforcer\n const policiesToAdd = await tempEnforcer.getPolicy();\n const groupPoliciesToAdd = await tempEnforcer.getGroupingPolicy();\n\n for (const policy of policiesToAdd) {\n if (!(await this.enforcer.hasPolicy(...policy))) {\n this.csvFilePolicies.addedPolicies.push(policy);\n }\n }\n\n for (const groupPolicy of groupPoliciesToAdd) {\n if (!(await this.enforcer.hasGroupingPolicy(...groupPolicy))) {\n this.csvFilePolicies.addedGroupPolicies.push(groupPolicy);\n }\n }\n\n await this.migrateLegacyMetadata(tempEnforcer);\n\n // We pass current here because this is during initialization and it has not changed yet\n await this.updatePolicies(content, tempEnforcer);\n\n if (this.allowReload) {\n this.watchFile();\n }\n }\n\n // Check for policies that might need to be updated\n // This will involve update \"legacy\" source in the role metadata if it exist in both the\n // temp enforcer (csv file) and a role metadata storage.\n // We will update role metadata with the new source \"csv-file\"\n private async migrateLegacyMetadata(tempEnforcer: Enforcer) {\n let legacyRolesMetadata =\n await this.roleMetadataStorage.filterRoleMetadata('legacy');\n const legacyRoles = legacyRolesMetadata.map(meta => meta.roleEntityRef);\n if (legacyRoles.length > 0) {\n const legacyGroupPolicies = await tempEnforcer.getFilteredGroupingPolicy(\n 1,\n ...legacyRoles,\n );\n const legacyPolicies = await tempEnforcer.getFilteredPolicy(\n 0,\n ...legacyRoles,\n );\n const legacyRolesFromFile = new Set([\n ...legacyGroupPolicies.map(gp => gp[1]),\n ...legacyPolicies.map(p => p[0]),\n ]);\n legacyRolesMetadata = legacyRolesMetadata.filter(meta =>\n legacyRolesFromFile.has(meta.roleEntityRef),\n );\n for (const legacyRoleMeta of legacyRolesMetadata) {\n const nonLegacyRole = mergeRoleMetadata(legacyRoleMeta, {\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n source: 'csv-file',\n roleEntityRef: legacyRoleMeta.roleEntityRef,\n });\n await this.roleMetadataStorage.updateRoleMetadata(\n nonLegacyRole,\n legacyRoleMeta.roleEntityRef,\n );\n }\n }\n }\n\n /**\n * onChange is called whenever there is a change to the CSV file.\n * It will parse the current and new contents of the CSV file and process the roles and permission policies present.\n * Afterwards, it will find the difference between the current and new contents of the CSV file\n * and sort them into added / removed, permission policies / roles.\n * It will finally call updatePolicies with the new content.\n */\n async onChange(): Promise<void> {\n const newContent = this.parse();\n\n const tempEnforcer = await newEnforcer(\n newModelFromString(MODEL),\n new FileAdapter(this.filePath!),\n );\n\n const currentFlatContent = this.currentContent.flatMap(data => {\n return policyToString(data);\n });\n const newFlatContent = newContent.flatMap(data => {\n return policyToString(data);\n });\n\n const diffRemoved = difference(currentFlatContent, newFlatContent); // policy was removed\n const diffAdded = difference(newFlatContent, currentFlatContent); // policy was added\n\n await this.migrateLegacyMetadata(tempEnforcer);\n\n if (diffRemoved.length === 0 && diffAdded.length === 0) {\n return;\n }\n\n diffRemoved.forEach(policy => {\n const convertedPolicy = metadataStringToPolicy(policy);\n if (convertedPolicy[0] === 'p') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.removedPolicies.push(convertedPolicy);\n } else if (convertedPolicy[0] === 'g') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.removedGroupPolicies.push(convertedPolicy);\n }\n });\n\n diffAdded.forEach(policy => {\n const convertedPolicy = metadataStringToPolicy(policy);\n if (convertedPolicy[0] === 'p') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.addedPolicies.push(convertedPolicy);\n } else if (convertedPolicy[0] === 'g') {\n convertedPolicy.splice(0, 1);\n this.csvFilePolicies.addedGroupPolicies.push(convertedPolicy);\n }\n });\n\n await this.updatePolicies(newContent, tempEnforcer);\n }\n\n /**\n * updatePolicies is used to update all of the permission policies and roles within a CSV file.\n * It will check the number of added and removed permissions policies and roles and call the appropriate\n * methods for these.\n * It will also update the current contents of the CSV file to the most recent\n * @param newContent The new content present in the CSV file\n * @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies\n */\n private async updatePolicies(\n newContent: string[][],\n tempEnforcer: Enforcer,\n ): Promise<void> {\n this.currentContent = newContent;\n\n if (this.csvFilePolicies.addedPolicies.length > 0)\n await this.addPermissionPolicies(tempEnforcer);\n if (this.csvFilePolicies.removedPolicies.length > 0)\n await this.removePermissionPolicies();\n if (this.csvFilePolicies.addedGroupPolicies.length > 0)\n await this.addRoles(tempEnforcer);\n if (this.csvFilePolicies.removedGroupPolicies.length > 0)\n await this.removeRoles();\n }\n\n /**\n * addPermissionPolicies will add the new permission policies that are present in the CSV file.\n * We will attempt to validate the permission policy and log any warnings that are encountered.\n * If a warning is encountered, we will skip adding the permission policy to the enforcer.\n * @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies\n */\n private async addPermissionPolicies(tempEnforcer: Enforcer): Promise<void> {\n for (const policy of this.csvFilePolicies.addedPolicies) {\n const transformedPolicy = transformArrayToPolicy(policy);\n const metadata = await this.roleMetadataStorage.findRoleMetadata(\n policy[0],\n );\n\n let err = validatePolicy(transformedPolicy);\n if (err) {\n this.logger.warn(\n `Failed to validate policy from file ${this.filePath}. Cause: ${err.message}`,\n );\n continue;\n }\n\n err = await validateSource('csv-file', metadata);\n if (err) {\n this.logger.warn(\n `Unable to add policy ${policy} from file ${this.filePath}. Cause: ${err.message}`,\n );\n continue;\n }\n\n err = await checkForDuplicatePolicies(\n tempEnforcer,\n policy,\n this.filePath!,\n );\n if (err) {\n this.logger.warn(err.message);\n continue;\n }\n try {\n await this.enforcer.addPolicy(policy);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Created policy`,\n eventName: PermissionEvents.CREATE_POLICY,\n metadata: { policies: [policy], source: 'csv-file' },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to add or update policy ${policy} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n\n this.csvFilePolicies.addedPolicies = [];\n }\n\n /**\n * removePermissionPolicies will remove the permission policies that are no longer present in the CSV file.\n */\n private async removePermissionPolicies(): Promise<void> {\n try {\n await this.enforcer.removePolicies(this.csvFilePolicies.removedPolicies);\n\n await this.auditLogger.auditLog<PermissionAuditInfo>({\n actorId: RBAC_BACKEND,\n message: `Deleted policies`,\n eventName: PermissionEvents.DELETE_POLICY,\n metadata: {\n policies: this.csvFilePolicies.removedPolicies,\n source: 'csv-file',\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to remove policies ${JSON.stringify(\n this.csvFilePolicies.removedPolicies,\n )} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n this.csvFilePolicies.removedPolicies = [];\n }\n\n /**\n * addRoles will add the new roles that are present in the CSV file.\n * We will attempt to validate the role and log any warnings that are encountered.\n * If a warning is encountered, we will skip adding the role to the enforcer.\n * @param tempEnforcer Temporary enforcer for checking for duplicates when adding policies\n */\n private async addRoles(tempEnforcer: Enforcer): Promise<void> {\n for (const groupPolicy of this.csvFilePolicies.addedGroupPolicies) {\n let err = await validateGroupingPolicy(\n groupPolicy,\n this.roleMetadataStorage,\n 'csv-file',\n );\n if (err) {\n this.logger.warn(\n `${err.message}, error originates from file ${this.filePath}`,\n );\n continue;\n }\n\n err = await checkForDuplicateGroupPolicies(\n tempEnforcer,\n groupPolicy,\n this.filePath!,\n );\n if (err) {\n this.logger.warn(err.message);\n continue;\n }\n\n try {\n const roleMetadata: RoleMetadataDao = {\n source: 'csv-file',\n roleEntityRef: groupPolicy[1],\n author: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n };\n\n const currentMetadata = await this.roleMetadataStorage.findRoleMetadata(\n roleMetadata.roleEntityRef,\n );\n\n await this.enforcer.addGroupingPolicy(groupPolicy, roleMetadata);\n\n const eventName = currentMetadata\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.CREATE_ROLE;\n const message = currentMetadata ? 'Updated role' : 'Created role';\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...roleMetadata, members: [groupPolicy[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to add or update group policy ${groupPolicy} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n this.csvFilePolicies.addedGroupPolicies = [];\n }\n\n /**\n * removeRoles will remove the roles that are no longer present in the CSV file.\n * If the role exists with multiple groups and or users, we will update it role information.\n * Otherwise, we will remove the role completely.\n */\n private async removeRoles(): Promise<void> {\n for (const groupPolicy of this.csvFilePolicies.removedGroupPolicies) {\n const roleEntityRef = groupPolicy[1];\n // this requires knowledge of whether or not it is an update\n const isUpdate = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n\n // Need to update the time\n try {\n const metadata: RoleMetadataDao = {\n source: 'csv-file',\n roleEntityRef,\n author: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n modifiedBy: CSV_PERMISSION_POLICY_FILE_AUTHOR,\n };\n\n await this.enforcer.removeGroupingPolicy(\n groupPolicy,\n metadata,\n isUpdate.length > 1,\n );\n\n const isRolePresent =\n await this.roleMetadataStorage.findRoleMetadata(roleEntityRef);\n const eventName = isRolePresent\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.DELETE_ROLE;\n const message = isRolePresent\n ? 'Updated role: deleted members'\n : 'Deleted role';\n await this.auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: { ...metadata, members: [groupPolicy[0]] },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n } catch (e) {\n this.logger.warn(\n `Failed to remove group policy ${groupPolicy} after modification ${this.filePath}. Cause: ${e}`,\n );\n }\n }\n this.csvFilePolicies.removedGroupPolicies = [];\n }\n\n async cleanUpRolesAndPolicies(): Promise<void> {\n const roleMetadatas =\n await this.roleMetadataStorage.filterRoleMetadata('csv-file');\n const fileRoles = roleMetadatas.map(meta => meta.roleEntityRef);\n\n if (fileRoles.length > 0) {\n for (const fileRole of fileRoles) {\n this.csvFilePolicies.removedGroupPolicies.push(\n ...(await this.enforcer.getFilteredGroupingPolicy(1, fileRole)),\n );\n this.csvFilePolicies.removedPolicies.push(\n ...(await this.enforcer.getFilteredPolicy(0, fileRole)),\n );\n }\n }\n await this.removePermissionPolicies();\n await this.removeRoles();\n }\n}\n"],"names":["AbstractFileWatcher","parse","newEnforcer","newModelFromString","MODEL","FileAdapter","mergeRoleMetadata","policyToString","difference","metadataStringToPolicy","transformArrayToPolicy","validatePolicy","validateSource","checkForDuplicatePolicies","RBAC_BACKEND","PermissionEvents","HANDLE_RBAC_DATA_STAGE","validateGroupingPolicy","checkForDuplicateGroupPolicies","RoleEvents"],"mappings":"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"}
@@ -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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"}
package/dist/index.d.ts CHANGED
@@ -7,12 +7,21 @@ import { PermissionPolicy } from '@backstage/plugin-permission-node';
7
7
  import { PluginIdProvider, RBACProvider } from '@backstage-community/plugin-rbac-node';
8
8
  export { PluginIdProvider } from '@backstage-community/plugin-rbac-node';
9
9
 
10
+ /**
11
+ * @public
12
+ */
10
13
  interface RouterOptions {
11
14
  logger: LoggerService;
12
15
  config: Config;
13
16
  }
17
+ /**
18
+ * @public
19
+ */
14
20
  declare function createRouter(options: RouterOptions): Promise<express.Router>;
15
21
 
22
+ /**
23
+ * @public
24
+ */
16
25
  type EnvOptions = {
17
26
  config: Config;
18
27
  logger: LoggerService;
@@ -23,6 +32,9 @@ type EnvOptions = {
23
32
  userInfo: UserInfoService;
24
33
  lifecycle: LifecycleService;
25
34
  };
35
+ /**
36
+ * @public
37
+ */
26
38
  type RBACRouterOptions = {
27
39
  config: Config;
28
40
  logger: LoggerService;
@@ -32,11 +44,15 @@ type RBACRouterOptions = {
32
44
  httpAuth: HttpAuthService;
33
45
  userInfo: UserInfoService;
34
46
  };
47
+ /**
48
+ * @public
49
+ */
35
50
  declare class PolicyBuilder {
36
51
  static build(env: EnvOptions, pluginIdProvider?: PluginIdProvider, rbacProviders?: Array<RBACProvider>): Promise<Router>;
37
52
  }
38
53
 
39
54
  /**
55
+ * @public
40
56
  * RBAC plugin
41
57
  *
42
58
  */
@@ -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;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
+ {"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 * @public\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":";;;;;;AAkCO,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;;;;"}
@@ -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) {