@backstage-community/plugin-rbac-backend 5.3.1 → 5.5.0

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,23 @@
1
1
  ### Dependencies
2
2
 
3
+ ## 5.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 36e2c6c: Reduces the number of times that we build the group hierarchy graphs during evaluation. Originally, during time of evaluation, we would build a graph to of all of the groups that a user was directly or indirectly a member of. Now, we only build the graph once and pass along all of the roles that the user is directly or indirectly attached to.
8
+
9
+ ## 5.4.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 5d5c02a: Backstage version bump to v1.35.0
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [5d5c02a]
18
+ - @backstage-community/plugin-rbac-common@1.13.0
19
+ - @backstage-community/plugin-rbac-node@1.9.0
20
+
3
21
  ## 5.3.1
4
22
 
5
23
  ### Patch Changes
@@ -58,7 +58,7 @@ class CasbinDBAdapterFactory {
58
58
  "connection"
59
59
  );
60
60
  if (!connection) {
61
- return void 0;
61
+ return undefined;
62
62
  }
63
63
  if (typeof connection === "string" || connection instanceof String) {
64
64
  throw new Error(
@@ -66,8 +66,8 @@ class CasbinDBAdapterFactory {
66
66
  );
67
67
  }
68
68
  const ssl = connection.ssl;
69
- if (ssl === void 0) {
70
- return void 0;
69
+ if (ssl === undefined) {
70
+ return undefined;
71
71
  }
72
72
  if (typeof ssl === "boolean") {
73
73
  return ssl;
@@ -75,12 +75,12 @@ class CasbinDBAdapterFactory {
75
75
  if (typeof ssl === "object") {
76
76
  const { ca, rejectUnauthorized } = ssl;
77
77
  const tlsOpts = { ca, rejectUnauthorized };
78
- if (Object.values(tlsOpts).every((el) => el === void 0)) {
78
+ if (Object.values(tlsOpts).every((el) => el === undefined)) {
79
79
  return true;
80
80
  }
81
81
  return tlsOpts;
82
82
  }
83
- return void 0;
83
+ return undefined;
84
84
  }
85
85
  }
86
86
 
@@ -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 =\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 poolSize: databaseConfig?.getOptionalNumber('knexConfig.pool.max'),\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 poolSize: databaseConfig?.getOptionalNumber('knexConfig.pool.max'),\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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"}
@@ -94,7 +94,7 @@ class DataBaseConditionalStorage {
94
94
  if (daoRaw) {
95
95
  return this.daoToConditionalDecision(daoRaw);
96
96
  }
97
- return void 0;
97
+ return undefined;
98
98
  }
99
99
  async deleteCondition(id) {
100
100
  const condition = await this.getCondition(id);
@@ -1 +1 @@
1
- {"version":3,"file":"conditional-storage.cjs.js","sources":["../../src/database/conditional-storage.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 { ConflictError, InputError, NotFoundError } from '@backstage/errors';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\n\nimport { Knex } from 'knex';\n\nimport type {\n PermissionAction,\n PermissionInfo,\n RoleConditionalPolicyDecision,\n} from '@backstage-community/plugin-rbac-common';\n\nexport const CONDITIONAL_TABLE = 'role-condition-policies';\n\nexport interface ConditionalPolicyDecisionDAO {\n result: AuthorizeResult.CONDITIONAL;\n id?: number;\n roleEntityRef: string;\n permissions: string;\n pluginId: string;\n resourceType: string;\n conditionsJson: string;\n}\n\nexport interface ConditionalStorage {\n filterConditions(\n roleEntityRef?: string | string[],\n pluginId?: string,\n resourceType?: string,\n actions?: PermissionAction[],\n permissionNames?: string[],\n ): Promise<RoleConditionalPolicyDecision<PermissionInfo>[]>;\n createCondition(\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): Promise<number>;\n checkConflictedConditions(\n roleEntityRef: string,\n resourceType: string,\n pluginId: string,\n queryPermissionNames: string[],\n idToExclude?: number,\n ): Promise<void>;\n getCondition(\n id: number,\n ): Promise<RoleConditionalPolicyDecision<PermissionInfo> | undefined>;\n deleteCondition(id: number): Promise<void>;\n updateCondition(\n id: number,\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): Promise<void>;\n}\n\nexport class DataBaseConditionalStorage implements ConditionalStorage {\n public constructor(private readonly knex: Knex<any, any[]>) {}\n\n async filterConditions(\n roleEntityRef?: string | string[],\n pluginId?: string,\n resourceType?: string,\n actions?: PermissionAction[],\n permissionNames?: string[],\n ): Promise<RoleConditionalPolicyDecision<PermissionInfo>[]> {\n const daoRaws = await this.knex.table(CONDITIONAL_TABLE).where(builder => {\n if (pluginId) {\n builder.where('pluginId', pluginId);\n }\n if (resourceType) {\n builder.where('resourceType', resourceType);\n }\n if (roleEntityRef) {\n if (Array.isArray(roleEntityRef)) {\n builder.whereIn('roleEntityRef', roleEntityRef);\n } else {\n builder.where('roleEntityRef', roleEntityRef);\n }\n }\n });\n\n let conditions: RoleConditionalPolicyDecision<PermissionInfo>[] = [];\n if (daoRaws) {\n conditions = daoRaws.map(dao => this.daoToConditionalDecision(dao));\n }\n\n if (permissionNames && permissionNames.length > 0) {\n conditions = conditions.filter(condition => {\n return permissionNames.every(permissionName =>\n condition.permissionMapping\n .map(permInfo => permInfo.name)\n .includes(permissionName),\n );\n });\n }\n\n if (actions && actions.length > 0) {\n conditions = conditions.filter(condition => {\n return actions.every(action =>\n condition.permissionMapping\n .map(permInfo => permInfo.action)\n .includes(action),\n );\n });\n }\n\n return conditions;\n }\n\n async createCondition(\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): Promise<number> {\n await this.checkConflictedConditions(\n conditionalDecision.roleEntityRef,\n conditionalDecision.resourceType,\n conditionalDecision.pluginId,\n conditionalDecision.permissionMapping.map(permInfo => permInfo.action),\n );\n\n const conditionRaw = this.toDAO(conditionalDecision);\n const result = await this.knex\n .table(CONDITIONAL_TABLE)\n .insert<ConditionalPolicyDecisionDAO>(conditionRaw)\n .returning('id');\n if (result && result?.length > 0) {\n return result[0].id;\n }\n\n throw new Error(`Failed to create the condition.`);\n }\n\n async checkConflictedConditions(\n roleEntityRef: string,\n resourceType: string,\n pluginId: string,\n queryConditionActions: PermissionAction[],\n idToExclude?: number,\n ): Promise<void> {\n let conditionsForTheSameResource = await this.filterConditions(\n roleEntityRef,\n pluginId,\n resourceType,\n );\n conditionsForTheSameResource = conditionsForTheSameResource.filter(\n c => c.id !== idToExclude,\n );\n\n if (conditionsForTheSameResource) {\n const conflictedCondition = conditionsForTheSameResource.find(\n condition => {\n const conditionActions = condition.permissionMapping.map(\n permInfo => permInfo.action,\n );\n return queryConditionActions.some(action =>\n conditionActions.includes(action),\n );\n },\n );\n\n if (conflictedCondition) {\n const conflictedActions = queryConditionActions.filter(action =>\n conflictedCondition.permissionMapping.some(p => p.action === action),\n );\n throw new ConflictError(\n `Found condition with conflicted permission action '${JSON.stringify(\n conflictedActions,\n )}'. Role could have multiple ` +\n `conditions for the same resource type '${conflictedCondition.resourceType}', but with different permission action sets.`,\n );\n }\n }\n }\n\n async getCondition(\n id: number,\n ): Promise<RoleConditionalPolicyDecision<PermissionInfo> | undefined> {\n const daoRaw = await this.knex\n .table(CONDITIONAL_TABLE)\n .where('id', id)\n .first();\n\n if (daoRaw) {\n return this.daoToConditionalDecision(daoRaw);\n }\n return undefined;\n }\n\n async deleteCondition(id: number): Promise<void> {\n const condition = await this.getCondition(id);\n if (!condition) {\n throw new NotFoundError(`Condition with id ${id} was not found`);\n }\n await this.knex?.table(CONDITIONAL_TABLE).delete().whereIn('id', [id]);\n }\n\n async updateCondition(\n id: number,\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): Promise<void> {\n const condition = await this.getCondition(id);\n if (!condition) {\n throw new NotFoundError(`Condition with id ${id} was not found`);\n }\n\n await this.checkConflictedConditions(\n conditionalDecision.roleEntityRef,\n conditionalDecision.resourceType,\n conditionalDecision.pluginId,\n conditionalDecision.permissionMapping.map(perm => perm.action),\n id,\n );\n\n const conditionRaw = this.toDAO(conditionalDecision);\n conditionRaw.id = id;\n const result = await this.knex\n .table(CONDITIONAL_TABLE)\n .where('id', conditionRaw.id)\n .update<ConditionalPolicyDecisionDAO>(conditionRaw)\n .returning('id');\n\n if (!result || result.length === 0) {\n throw new Error(`Failed to update the condition with id: ${id}.`);\n }\n }\n\n private toDAO(\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): ConditionalPolicyDecisionDAO {\n const {\n result,\n pluginId,\n resourceType,\n conditions,\n roleEntityRef,\n permissionMapping,\n } = conditionalDecision;\n const conditionsJson = JSON.stringify(conditions);\n return {\n result,\n pluginId,\n resourceType,\n conditionsJson,\n roleEntityRef,\n permissions: JSON.stringify(permissionMapping),\n };\n }\n\n private daoToConditionalDecision(\n dao: ConditionalPolicyDecisionDAO,\n ): RoleConditionalPolicyDecision<PermissionInfo> {\n if (!dao.id) {\n throw new InputError(`Missed id in the dao object: ${dao}`);\n }\n const {\n id,\n result,\n pluginId,\n resourceType,\n conditionsJson,\n roleEntityRef,\n permissions,\n } = dao;\n\n const conditions = JSON.parse(conditionsJson);\n return {\n id,\n result,\n pluginId,\n resourceType,\n conditions,\n roleEntityRef,\n permissionMapping: JSON.parse(permissions),\n };\n }\n}\n"],"names":["ConflictError","NotFoundError","InputError"],"mappings":"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"}
1
+ {"version":3,"file":"conditional-storage.cjs.js","sources":["../../src/database/conditional-storage.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 { ConflictError, InputError, NotFoundError } from '@backstage/errors';\nimport { AuthorizeResult } from '@backstage/plugin-permission-common';\n\nimport { Knex } from 'knex';\n\nimport type {\n PermissionAction,\n PermissionInfo,\n RoleConditionalPolicyDecision,\n} from '@backstage-community/plugin-rbac-common';\n\nexport const CONDITIONAL_TABLE = 'role-condition-policies';\n\nexport interface ConditionalPolicyDecisionDAO {\n result: AuthorizeResult.CONDITIONAL;\n id?: number;\n roleEntityRef: string;\n permissions: string;\n pluginId: string;\n resourceType: string;\n conditionsJson: string;\n}\n\nexport interface ConditionalStorage {\n filterConditions(\n roleEntityRef?: string | string[],\n pluginId?: string,\n resourceType?: string,\n actions?: PermissionAction[],\n permissionNames?: string[],\n ): Promise<RoleConditionalPolicyDecision<PermissionInfo>[]>;\n createCondition(\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): Promise<number>;\n checkConflictedConditions(\n roleEntityRef: string,\n resourceType: string,\n pluginId: string,\n queryPermissionNames: string[],\n idToExclude?: number,\n ): Promise<void>;\n getCondition(\n id: number,\n ): Promise<RoleConditionalPolicyDecision<PermissionInfo> | undefined>;\n deleteCondition(id: number): Promise<void>;\n updateCondition(\n id: number,\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): Promise<void>;\n}\n\nexport class DataBaseConditionalStorage implements ConditionalStorage {\n public constructor(private readonly knex: Knex<any, any[]>) {}\n\n async filterConditions(\n roleEntityRef?: string | string[],\n pluginId?: string,\n resourceType?: string,\n actions?: PermissionAction[],\n permissionNames?: string[],\n ): Promise<RoleConditionalPolicyDecision<PermissionInfo>[]> {\n const daoRaws = await this.knex.table(CONDITIONAL_TABLE).where(builder => {\n if (pluginId) {\n builder.where('pluginId', pluginId);\n }\n if (resourceType) {\n builder.where('resourceType', resourceType);\n }\n if (roleEntityRef) {\n if (Array.isArray(roleEntityRef)) {\n builder.whereIn('roleEntityRef', roleEntityRef);\n } else {\n builder.where('roleEntityRef', roleEntityRef);\n }\n }\n });\n\n let conditions: RoleConditionalPolicyDecision<PermissionInfo>[] = [];\n if (daoRaws) {\n conditions = daoRaws.map(dao => this.daoToConditionalDecision(dao));\n }\n\n if (permissionNames && permissionNames.length > 0) {\n conditions = conditions.filter(condition => {\n return permissionNames.every(permissionName =>\n condition.permissionMapping\n .map(permInfo => permInfo.name)\n .includes(permissionName),\n );\n });\n }\n\n if (actions && actions.length > 0) {\n conditions = conditions.filter(condition => {\n return actions.every(action =>\n condition.permissionMapping\n .map(permInfo => permInfo.action)\n .includes(action),\n );\n });\n }\n\n return conditions;\n }\n\n async createCondition(\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): Promise<number> {\n await this.checkConflictedConditions(\n conditionalDecision.roleEntityRef,\n conditionalDecision.resourceType,\n conditionalDecision.pluginId,\n conditionalDecision.permissionMapping.map(permInfo => permInfo.action),\n );\n\n const conditionRaw = this.toDAO(conditionalDecision);\n const result = await this.knex\n .table(CONDITIONAL_TABLE)\n .insert<ConditionalPolicyDecisionDAO>(conditionRaw)\n .returning('id');\n if (result && result?.length > 0) {\n return result[0].id;\n }\n\n throw new Error(`Failed to create the condition.`);\n }\n\n async checkConflictedConditions(\n roleEntityRef: string,\n resourceType: string,\n pluginId: string,\n queryConditionActions: PermissionAction[],\n idToExclude?: number,\n ): Promise<void> {\n let conditionsForTheSameResource = await this.filterConditions(\n roleEntityRef,\n pluginId,\n resourceType,\n );\n conditionsForTheSameResource = conditionsForTheSameResource.filter(\n c => c.id !== idToExclude,\n );\n\n if (conditionsForTheSameResource) {\n const conflictedCondition = conditionsForTheSameResource.find(\n condition => {\n const conditionActions = condition.permissionMapping.map(\n permInfo => permInfo.action,\n );\n return queryConditionActions.some(action =>\n conditionActions.includes(action),\n );\n },\n );\n\n if (conflictedCondition) {\n const conflictedActions = queryConditionActions.filter(action =>\n conflictedCondition.permissionMapping.some(p => p.action === action),\n );\n throw new ConflictError(\n `Found condition with conflicted permission action '${JSON.stringify(\n conflictedActions,\n )}'. Role could have multiple ` +\n `conditions for the same resource type '${conflictedCondition.resourceType}', but with different permission action sets.`,\n );\n }\n }\n }\n\n async getCondition(\n id: number,\n ): Promise<RoleConditionalPolicyDecision<PermissionInfo> | undefined> {\n const daoRaw = await this.knex\n .table(CONDITIONAL_TABLE)\n .where('id', id)\n .first();\n\n if (daoRaw) {\n return this.daoToConditionalDecision(daoRaw);\n }\n return undefined;\n }\n\n async deleteCondition(id: number): Promise<void> {\n const condition = await this.getCondition(id);\n if (!condition) {\n throw new NotFoundError(`Condition with id ${id} was not found`);\n }\n await this.knex?.table(CONDITIONAL_TABLE).delete().whereIn('id', [id]);\n }\n\n async updateCondition(\n id: number,\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): Promise<void> {\n const condition = await this.getCondition(id);\n if (!condition) {\n throw new NotFoundError(`Condition with id ${id} was not found`);\n }\n\n await this.checkConflictedConditions(\n conditionalDecision.roleEntityRef,\n conditionalDecision.resourceType,\n conditionalDecision.pluginId,\n conditionalDecision.permissionMapping.map(perm => perm.action),\n id,\n );\n\n const conditionRaw = this.toDAO(conditionalDecision);\n conditionRaw.id = id;\n const result = await this.knex\n .table(CONDITIONAL_TABLE)\n .where('id', conditionRaw.id)\n .update<ConditionalPolicyDecisionDAO>(conditionRaw)\n .returning('id');\n\n if (!result || result.length === 0) {\n throw new Error(`Failed to update the condition with id: ${id}.`);\n }\n }\n\n private toDAO(\n conditionalDecision: RoleConditionalPolicyDecision<PermissionInfo>,\n ): ConditionalPolicyDecisionDAO {\n const {\n result,\n pluginId,\n resourceType,\n conditions,\n roleEntityRef,\n permissionMapping,\n } = conditionalDecision;\n const conditionsJson = JSON.stringify(conditions);\n return {\n result,\n pluginId,\n resourceType,\n conditionsJson,\n roleEntityRef,\n permissions: JSON.stringify(permissionMapping),\n };\n }\n\n private daoToConditionalDecision(\n dao: ConditionalPolicyDecisionDAO,\n ): RoleConditionalPolicyDecision<PermissionInfo> {\n if (!dao.id) {\n throw new InputError(`Missed id in the dao object: ${dao}`);\n }\n const {\n id,\n result,\n pluginId,\n resourceType,\n conditionsJson,\n roleEntityRef,\n permissions,\n } = dao;\n\n const conditions = JSON.parse(conditionsJson);\n return {\n id,\n result,\n pluginId,\n resourceType,\n conditions,\n roleEntityRef,\n permissionMapping: JSON.parse(permissions),\n };\n }\n}\n"],"names":["ConflictError","NotFoundError","InputError"],"mappings":"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"}
@@ -133,7 +133,7 @@ async function processConditionMapping(roleConditionPolicy, pluginPermMetaData,
133
133
  const permInfo = [];
134
134
  for (const action of roleConditionPolicy.permissionMapping) {
135
135
  const perm = rule.permissions.find(
136
- (permission) => permission.type === "resource" && (action === permission.attributes.action || action === "use" && permission.attributes.action === void 0)
136
+ (permission) => permission.type === "resource" && (action === permission.attributes.action || action === "use" && permission.attributes.action === undefined)
137
137
  );
138
138
  if (!perm) {
139
139
  throw new Error(
@@ -1 +1 @@
1
- {"version":3,"file":"helper.cjs.js","sources":["../src/helper.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AuthService } from '@backstage/backend-plugin-api';\nimport type { MetadataResponse } from '@backstage/plugin-permission-node';\n\nimport { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport {\n difference,\n fromPairs,\n isArray,\n isEqual,\n isPlainObject,\n omitBy,\n sortBy,\n toPairs,\n ValueKeyIteratee,\n} from 'lodash';\n\nimport {\n PermissionAction,\n PermissionInfo,\n RoleBasedPolicy,\n RoleConditionalPolicyDecision,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from './audit-log/audit-logger';\nimport { RoleMetadataDao, RoleMetadataStorage } from './database/role-metadata';\nimport { EnforcerDelegate } from './service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from './service/plugin-endpoints';\n\nexport function policyToString(policy: string[]): string {\n return `[${policy.join(', ')}]`;\n}\n\nexport function typedPolicyToString(policy: string[], type: string): string {\n return `${type}, ${policy.join(', ')}`;\n}\n\nexport function policiesToString(policies: string[][]): string {\n const policiesString = policies\n .map(policy => policyToString(policy))\n .join(',');\n return `[${policiesString}]`;\n}\n\nexport function typedPoliciesToString(\n policies: string[][],\n type: string,\n): string {\n const policiesString = policies\n .map(policy => {\n return policy.length !== 0 ? typedPolicyToString(policy, type) : '';\n })\n .join('\\n');\n return `\n ${policiesString}\n `;\n}\n\nexport function metadataStringToPolicy(policy: string): string[] {\n return policy.replace('[', '').replace(']', '').split(', ');\n}\n\nexport async function removeTheDifference(\n originalGroup: string[],\n addedGroup: string[],\n source: Source,\n roleEntityRef: string,\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n modifiedBy: string,\n): Promise<void> {\n originalGroup.sort((a, b) => a.localeCompare(b));\n addedGroup.sort((a, b) => a.localeCompare(b));\n const missing = difference(originalGroup, addedGroup);\n\n const groupPolicies: string[][] = [];\n for (const missingRole of missing) {\n groupPolicies.push([missingRole, roleEntityRef]);\n }\n\n if (groupPolicies.length === 0) {\n return;\n }\n\n const roleMetadata = { source, modifiedBy, roleEntityRef };\n await enf.removeGroupingPolicies(groupPolicies, roleMetadata, false);\n\n const remainingMembers = await enf.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n const message =\n remainingMembers.length > 0\n ? 'Updated role: deleted members'\n : 'Deleted role';\n const eventName =\n remainingMembers.length > 0\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.DELETE_ROLE;\n await auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: {\n ...roleMetadata,\n members: groupPolicies.map(gp => gp[0]),\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n}\n\nexport function transformArrayToPolicy(policyArray: string[]): RoleBasedPolicy {\n const [entityReference, permission, policy, effect] = policyArray;\n return { entityReference, permission, policy, effect };\n}\n\nexport function transformPolicyGroupToLowercase(policyArray: string[]) {\n if (\n policyArray.length > 1 &&\n policyArray[0].startsWith('g') &&\n (policyArray[1].startsWith('user') || policyArray[1].startsWith('group'))\n ) {\n policyArray[1] = policyArray[1].toLocaleLowerCase('en-US');\n }\n}\n\nexport function transformRolesGroupToLowercase(roles: string[][]) {\n return roles.map(role =>\n role.length >= 1\n ? [role[0].toLocaleLowerCase('en-US'), ...role.slice(1)]\n : role,\n );\n}\n\nexport function deepSortedEqual(\n obj1: Record<string, any>,\n obj2: Record<string, any>,\n excludeFields?: string[],\n): boolean {\n let copyObj1;\n let copyObj2;\n if (excludeFields) {\n const excludeFieldsPredicate: ValueKeyIteratee<any> = (_value, key) => {\n for (const field of excludeFields) {\n if (key === field) {\n return true;\n }\n }\n return false;\n };\n copyObj1 = omitBy(obj1, excludeFieldsPredicate);\n copyObj2 = omitBy(obj2, excludeFieldsPredicate);\n }\n\n const sortedObj1 = sortBy(toPairs(copyObj1 || obj1), ([key]) => key);\n const sortedObj2 = sortBy(toPairs(copyObj2 || obj2), ([key]) => key);\n\n return isEqual(sortedObj1, sortedObj2);\n}\n\nexport function isPermissionAction(action: string): action is PermissionAction {\n return ['create', 'read', 'update', 'delete', 'use'].includes(\n action as PermissionAction,\n );\n}\n\nexport async function buildRoleSourceMap(\n policies: string[][],\n roleMetadata: RoleMetadataStorage,\n): Promise<Map<string, Source | undefined>> {\n return await policies.reduce(\n async (\n acc: Promise<Map<string, Source | undefined>>,\n policy: string[],\n ): Promise<Map<string, Source | undefined>> => {\n const roleEntityRef = policy[0];\n const acummulator = await acc;\n if (!acummulator.has(roleEntityRef)) {\n const metadata = await roleMetadata.findRoleMetadata(roleEntityRef);\n acummulator.set(roleEntityRef, metadata?.source);\n }\n return acummulator;\n },\n Promise.resolve(new Map<string, Source | undefined>()),\n );\n}\n\nexport function mergeRoleMetadata(\n currentMetadata: RoleMetadataDao,\n newMetadata: RoleMetadataDao,\n): RoleMetadataDao {\n const mergedMetaData: RoleMetadataDao = { ...currentMetadata };\n mergedMetaData.lastModified =\n newMetadata.lastModified ?? new Date().toUTCString();\n mergedMetaData.modifiedBy = newMetadata.modifiedBy;\n mergedMetaData.description =\n newMetadata.description ?? currentMetadata.description;\n mergedMetaData.roleEntityRef = newMetadata.roleEntityRef;\n mergedMetaData.source = newMetadata.source;\n return mergedMetaData;\n}\n\nexport async function processConditionMapping(\n roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction>,\n pluginPermMetaData: PluginPermissionMetadataCollector,\n auth: AuthService,\n): Promise<RoleConditionalPolicyDecision<PermissionInfo>> {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: roleConditionPolicy.pluginId,\n });\n\n const rule: MetadataResponse | undefined =\n await pluginPermMetaData.getMetadataByPluginId(\n roleConditionPolicy.pluginId,\n token,\n );\n if (!rule?.permissions) {\n throw new Error(\n `Unable to get permission list for plugin ${roleConditionPolicy.pluginId}`,\n );\n }\n\n const permInfo: PermissionInfo[] = [];\n for (const action of roleConditionPolicy.permissionMapping) {\n const perm = rule.permissions.find(\n permission =>\n permission.type === 'resource' &&\n (action === permission.attributes.action ||\n (action === 'use' && permission.attributes.action === undefined)),\n );\n if (!perm) {\n throw new Error(\n `Unable to find permission to get permission name for resource type '${\n roleConditionPolicy.resourceType\n }' and action ${JSON.stringify(action)}`,\n );\n }\n permInfo.push({ name: perm.name, action });\n }\n\n return {\n ...roleConditionPolicy,\n permissionMapping: permInfo,\n };\n}\n\nexport function deepSort(value: any): any {\n if (isArray(value)) {\n return sortBy(value.map(deepSort));\n } else if (isPlainObject(value)) {\n return fromPairs(\n sortBy(\n toPairs(value).map(([k, v]: [string, any]) => [k, deepSort(v)]),\n 0,\n ),\n );\n }\n return value;\n}\n\nexport function deepSortEqual(obj1: any, obj2: any): boolean {\n return isEqual(deepSort(obj1), deepSort(obj2));\n}\n"],"names":["auditLogger","difference","RoleEvents","RBAC_BACKEND","HANDLE_RBAC_DATA_STAGE","omitBy","sortBy","toPairs","isEqual","isArray","isPlainObject","fromPairs"],"mappings":"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"}
1
+ {"version":3,"file":"helper.cjs.js","sources":["../src/helper.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AuthService } from '@backstage/backend-plugin-api';\nimport type { MetadataResponse } from '@backstage/plugin-permission-node';\n\nimport { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport {\n difference,\n fromPairs,\n isArray,\n isEqual,\n isPlainObject,\n omitBy,\n sortBy,\n toPairs,\n ValueKeyIteratee,\n} from 'lodash';\n\nimport {\n PermissionAction,\n PermissionInfo,\n RoleBasedPolicy,\n RoleConditionalPolicyDecision,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport {\n HANDLE_RBAC_DATA_STAGE,\n RBAC_BACKEND,\n RoleAuditInfo,\n RoleEvents,\n} from './audit-log/audit-logger';\nimport { RoleMetadataDao, RoleMetadataStorage } from './database/role-metadata';\nimport { EnforcerDelegate } from './service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from './service/plugin-endpoints';\n\nexport function policyToString(policy: string[]): string {\n return `[${policy.join(', ')}]`;\n}\n\nexport function typedPolicyToString(policy: string[], type: string): string {\n return `${type}, ${policy.join(', ')}`;\n}\n\nexport function policiesToString(policies: string[][]): string {\n const policiesString = policies\n .map(policy => policyToString(policy))\n .join(',');\n return `[${policiesString}]`;\n}\n\nexport function typedPoliciesToString(\n policies: string[][],\n type: string,\n): string {\n const policiesString = policies\n .map(policy => {\n return policy.length !== 0 ? typedPolicyToString(policy, type) : '';\n })\n .join('\\n');\n return `\n ${policiesString}\n `;\n}\n\nexport function metadataStringToPolicy(policy: string): string[] {\n return policy.replace('[', '').replace(']', '').split(', ');\n}\n\nexport async function removeTheDifference(\n originalGroup: string[],\n addedGroup: string[],\n source: Source,\n roleEntityRef: string,\n enf: EnforcerDelegate,\n auditLogger: AuditLogger,\n modifiedBy: string,\n): Promise<void> {\n originalGroup.sort((a, b) => a.localeCompare(b));\n addedGroup.sort((a, b) => a.localeCompare(b));\n const missing = difference(originalGroup, addedGroup);\n\n const groupPolicies: string[][] = [];\n for (const missingRole of missing) {\n groupPolicies.push([missingRole, roleEntityRef]);\n }\n\n if (groupPolicies.length === 0) {\n return;\n }\n\n const roleMetadata = { source, modifiedBy, roleEntityRef };\n await enf.removeGroupingPolicies(groupPolicies, roleMetadata, false);\n\n const remainingMembers = await enf.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n const message =\n remainingMembers.length > 0\n ? 'Updated role: deleted members'\n : 'Deleted role';\n const eventName =\n remainingMembers.length > 0\n ? RoleEvents.UPDATE_ROLE\n : RoleEvents.DELETE_ROLE;\n await auditLogger.auditLog<RoleAuditInfo>({\n actorId: RBAC_BACKEND,\n message,\n eventName,\n metadata: {\n ...roleMetadata,\n members: groupPolicies.map(gp => gp[0]),\n },\n stage: HANDLE_RBAC_DATA_STAGE,\n status: 'succeeded',\n });\n}\n\nexport function transformArrayToPolicy(policyArray: string[]): RoleBasedPolicy {\n const [entityReference, permission, policy, effect] = policyArray;\n return { entityReference, permission, policy, effect };\n}\n\nexport function transformPolicyGroupToLowercase(policyArray: string[]) {\n if (\n policyArray.length > 1 &&\n policyArray[0].startsWith('g') &&\n (policyArray[1].startsWith('user') || policyArray[1].startsWith('group'))\n ) {\n policyArray[1] = policyArray[1].toLocaleLowerCase('en-US');\n }\n}\n\nexport function transformRolesGroupToLowercase(roles: string[][]) {\n return roles.map(role =>\n role.length >= 1\n ? [role[0].toLocaleLowerCase('en-US'), ...role.slice(1)]\n : role,\n );\n}\n\nexport function deepSortedEqual(\n obj1: Record<string, any>,\n obj2: Record<string, any>,\n excludeFields?: string[],\n): boolean {\n let copyObj1;\n let copyObj2;\n if (excludeFields) {\n const excludeFieldsPredicate: ValueKeyIteratee<any> = (_value, key) => {\n for (const field of excludeFields) {\n if (key === field) {\n return true;\n }\n }\n return false;\n };\n copyObj1 = omitBy(obj1, excludeFieldsPredicate);\n copyObj2 = omitBy(obj2, excludeFieldsPredicate);\n }\n\n const sortedObj1 = sortBy(toPairs(copyObj1 || obj1), ([key]) => key);\n const sortedObj2 = sortBy(toPairs(copyObj2 || obj2), ([key]) => key);\n\n return isEqual(sortedObj1, sortedObj2);\n}\n\nexport function isPermissionAction(action: string): action is PermissionAction {\n return ['create', 'read', 'update', 'delete', 'use'].includes(\n action as PermissionAction,\n );\n}\n\nexport async function buildRoleSourceMap(\n policies: string[][],\n roleMetadata: RoleMetadataStorage,\n): Promise<Map<string, Source | undefined>> {\n return await policies.reduce(\n async (\n acc: Promise<Map<string, Source | undefined>>,\n policy: string[],\n ): Promise<Map<string, Source | undefined>> => {\n const roleEntityRef = policy[0];\n const acummulator = await acc;\n if (!acummulator.has(roleEntityRef)) {\n const metadata = await roleMetadata.findRoleMetadata(roleEntityRef);\n acummulator.set(roleEntityRef, metadata?.source);\n }\n return acummulator;\n },\n Promise.resolve(new Map<string, Source | undefined>()),\n );\n}\n\nexport function mergeRoleMetadata(\n currentMetadata: RoleMetadataDao,\n newMetadata: RoleMetadataDao,\n): RoleMetadataDao {\n const mergedMetaData: RoleMetadataDao = { ...currentMetadata };\n mergedMetaData.lastModified =\n newMetadata.lastModified ?? new Date().toUTCString();\n mergedMetaData.modifiedBy = newMetadata.modifiedBy;\n mergedMetaData.description =\n newMetadata.description ?? currentMetadata.description;\n mergedMetaData.roleEntityRef = newMetadata.roleEntityRef;\n mergedMetaData.source = newMetadata.source;\n return mergedMetaData;\n}\n\nexport async function processConditionMapping(\n roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction>,\n pluginPermMetaData: PluginPermissionMetadataCollector,\n auth: AuthService,\n): Promise<RoleConditionalPolicyDecision<PermissionInfo>> {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: roleConditionPolicy.pluginId,\n });\n\n const rule: MetadataResponse | undefined =\n await pluginPermMetaData.getMetadataByPluginId(\n roleConditionPolicy.pluginId,\n token,\n );\n if (!rule?.permissions) {\n throw new Error(\n `Unable to get permission list for plugin ${roleConditionPolicy.pluginId}`,\n );\n }\n\n const permInfo: PermissionInfo[] = [];\n for (const action of roleConditionPolicy.permissionMapping) {\n const perm = rule.permissions.find(\n permission =>\n permission.type === 'resource' &&\n (action === permission.attributes.action ||\n (action === 'use' && permission.attributes.action === undefined)),\n );\n if (!perm) {\n throw new Error(\n `Unable to find permission to get permission name for resource type '${\n roleConditionPolicy.resourceType\n }' and action ${JSON.stringify(action)}`,\n );\n }\n permInfo.push({ name: perm.name, action });\n }\n\n return {\n ...roleConditionPolicy,\n permissionMapping: permInfo,\n };\n}\n\nexport function deepSort(value: any): any {\n if (isArray(value)) {\n return sortBy(value.map(deepSort));\n } else if (isPlainObject(value)) {\n return fromPairs(\n sortBy(\n toPairs(value).map(([k, v]: [string, any]) => [k, deepSort(v)]),\n 0,\n ),\n );\n }\n return value;\n}\n\nexport function deepSortEqual(obj1: any, obj2: any): boolean {\n return isEqual(deepSort(obj1), deepSort(obj2));\n}\n"],"names":["auditLogger","difference","RoleEvents","RBAC_BACKEND","HANDLE_RBAC_DATA_STAGE","omitBy","sortBy","toPairs","isEqual","isArray","isPlainObject","fromPairs"],"mappings":"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"}
@@ -129,7 +129,6 @@ class RBACPermissionPolicy {
129
129
  return { result: pluginPermissionCommon.AuthorizeResult.ALLOW };
130
130
  }
131
131
  const permissionName = request.permission.name;
132
- await this.enforcer.loadPolicy();
133
132
  const roles = await this.enforcer.getRolesForUser(userEntityRef);
134
133
  if (pluginPermissionCommon.isResourcePermission(request.permission)) {
135
134
  const resourceType = request.permission.resourceType;
@@ -145,9 +144,9 @@ class RBACPermissionPolicy {
145
144
  }
146
145
  }
147
146
  const hasNamedPermission = await this.hasImplicitPermissionSpecifiedByName(
148
- userEntityRef,
149
147
  permissionName,
150
- action
148
+ action,
149
+ roles
151
150
  );
152
151
  const obj = hasNamedPermission ? permissionName : resourceType;
153
152
  status = await this.isAuthorized(userEntityRef, obj, action, roles);
@@ -180,10 +179,15 @@ class RBACPermissionPolicy {
180
179
  return { result: pluginPermissionCommon.AuthorizeResult.DENY };
181
180
  }
182
181
  }
183
- async hasImplicitPermissionSpecifiedByName(userEntityRef, permissionName, action) {
184
- const userPerms = await this.enforcer.getImplicitPermissionsForUser(userEntityRef);
185
- for (const perm of userPerms) {
186
- if (permissionName === perm[1] && action === perm[2]) {
182
+ async hasImplicitPermissionSpecifiedByName(permissionName, action, roles) {
183
+ for (const role of roles) {
184
+ const perms = await this.enforcer.getFilteredPolicy(
185
+ 0,
186
+ role,
187
+ permissionName,
188
+ action
189
+ );
190
+ if (perms.length > 0) {
187
191
  return true;
188
192
  }
189
193
  }
@@ -201,7 +205,7 @@ class RBACPermissionPolicy {
201
205
  for (const role of roles) {
202
206
  const conditionalDecisions = await this.conditionStorage.filterConditions(
203
207
  role,
204
- void 0,
208
+ undefined,
205
209
  resourceType,
206
210
  [action],
207
211
  [permissionName]
@@ -246,7 +250,7 @@ class RBACPermissionPolicy {
246
250
  await this.auditLogger.auditLog(auditOptions);
247
251
  return result;
248
252
  }
249
- return void 0;
253
+ return undefined;
250
254
  }
251
255
  }
252
256
 
@@ -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 await 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 if (this.superUserList!.includes(userEntityRef)) {\n const msg = evaluatePermMsg(\n userEntityRef,\n AuthorizeResult.ALLOW,\n request.permission,\n );\n\n auditOptions = createPermissionEvaluationOptions(\n msg,\n userEntityRef,\n request,\n { result: AuthorizeResult.ALLOW },\n );\n await this.auditLogger.auditLog(auditOptions);\n\n return { result: AuthorizeResult.ALLOW };\n }\n\n const permissionName = request.permission.name;\n await this.enforcer.loadPolicy();\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 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 await 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 if (this.superUserList!.includes(userEntityRef)) {\n const msg = evaluatePermMsg(\n userEntityRef,\n AuthorizeResult.ALLOW,\n request.permission,\n );\n\n auditOptions = createPermissionEvaluationOptions(\n msg,\n userEntityRef,\n request,\n { result: AuthorizeResult.ALLOW },\n );\n await this.auditLogger.auditLog(auditOptions);\n\n return { result: AuthorizeResult.ALLOW };\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 permissionName,\n action,\n roles,\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 permissionName: string,\n action: string,\n roles: string[],\n ): Promise<boolean> {\n for (const role of roles) {\n const perms = await this.enforcer.getFilteredPolicy(\n 0,\n role,\n permissionName,\n action,\n );\n if (perms.length > 0) {\n return true;\n }\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 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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"}
@@ -101,7 +101,7 @@ class AncestorSearchMemo {
101
101
  if (!memo.hasEntityRef(groupName)) {
102
102
  memo.setNode(groupName);
103
103
  }
104
- if (this.maxDepth !== void 0 && current_depth >= this.maxDepth) {
104
+ if (this.maxDepth !== undefined && current_depth >= this.maxDepth) {
105
105
  return;
106
106
  }
107
107
  const depth = current_depth + 1;
@@ -118,7 +118,7 @@ class AncestorSearchMemo {
118
118
  }
119
119
  }
120
120
  traverseRelations(memo, relation, allRelations, current_depth) {
121
- if (this.maxDepth !== void 0 && current_depth >= this.maxDepth + 1) {
121
+ if (this.maxDepth !== undefined && current_depth >= this.maxDepth + 1) {
122
122
  return;
123
123
  }
124
124
  const depth = current_depth + 1;
@@ -1 +1 @@
1
- {"version":3,"file":"ancestor-search-memo.cjs.js","sources":["../../src/role-manager/ancestor-search-memo.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport type { CatalogApi } from '@backstage/catalog-client';\nimport type { Entity } from '@backstage/catalog-model';\n\nimport { alg, Graph } from '@dagrejs/graphlib';\nimport { Knex } from 'knex';\n\nexport interface Relation {\n source_entity_ref: string;\n target_entity_ref: string;\n}\n\nexport type ASMGroup = Relation | Entity;\n\n// AncestorSearchMemo - should be used to build group hierarchy graph for User entity reference.\n// It supports search group entity reference link in the graph.\n// Also AncestorSearchMemo supports detection cycle dependencies between groups in the graph.\n//\nexport class AncestorSearchMemo {\n private graph: Graph;\n\n private catalogApi: CatalogApi;\n private catalogDBClient: Knex;\n private auth: AuthService;\n\n private userEntityRef: string;\n private maxDepth?: number;\n\n constructor(\n userEntityRef: string,\n catalogApi: CatalogApi,\n catalogDBClient: Knex,\n auth: AuthService,\n maxDepth?: number,\n ) {\n this.graph = new Graph({ directed: true });\n this.userEntityRef = userEntityRef;\n this.catalogApi = catalogApi;\n this.catalogDBClient = catalogDBClient;\n this.auth = auth;\n this.maxDepth = maxDepth;\n }\n\n isAcyclic(): boolean {\n return alg.isAcyclic(this.graph);\n }\n\n findCycles(): string[][] {\n return alg.findCycles(this.graph);\n }\n\n setEdge(parentEntityRef: string, childEntityRef: string) {\n this.graph.setEdge(parentEntityRef, childEntityRef);\n }\n\n setNode(entityRef: string): void {\n this.graph.setNode(entityRef);\n }\n\n hasEntityRef(groupRef: string): boolean {\n return this.graph.hasNode(groupRef);\n }\n\n debugNodesAndEdges(logger: LoggerService, userEntity: string): void {\n logger.debug(\n `SubGraph edges: ${JSON.stringify(this.graph.edges())} for ${userEntity}`,\n );\n logger.debug(\n `SubGraph nodes: ${JSON.stringify(this.graph.nodes())} for ${userEntity}`,\n );\n }\n\n getNodes(): string[] {\n return this.graph.nodes();\n }\n\n async doesRelationTableExist(): Promise<boolean> {\n try {\n return await this.catalogDBClient.schema.hasTable('relations');\n } catch (error) {\n return false;\n }\n }\n\n async getAllGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group' },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getAllRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where('type', 'childOf');\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n async getUserGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group', 'relations.hasMember': this.userEntityRef },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getUserRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where({ type: 'memberOf', source_entity_ref: this.userEntityRef });\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n traverseGroups(\n memo: AncestorSearchMemo,\n group: Entity,\n allGroups: Entity[],\n current_depth: number,\n ) {\n const groupName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${group.metadata.name.toLocaleLowerCase('en-US')}`;\n if (!memo.hasEntityRef(groupName)) {\n memo.setNode(groupName);\n }\n\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth) {\n return;\n }\n const depth = current_depth + 1;\n\n const parent = group.spec?.parent as string;\n const parentGroup = allGroups.find(g => g.metadata.name === parent);\n\n if (parentGroup) {\n const parentName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${parentGroup.metadata.name.toLocaleLowerCase('en-US')}`;\n memo.setEdge(parentName, groupName);\n\n if (memo.isAcyclic()) {\n this.traverseGroups(memo, parentGroup, allGroups, depth);\n }\n }\n }\n\n traverseRelations(\n memo: AncestorSearchMemo,\n relation: Relation,\n allRelations: Relation[],\n current_depth: number,\n ) {\n // We add one to the maxDepth here because the user is considered the starting node\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth + 1) {\n return;\n }\n const depth = current_depth + 1;\n\n if (!memo.hasEntityRef(relation.source_entity_ref)) {\n memo.setNode(relation.source_entity_ref);\n }\n\n memo.setEdge(relation.target_entity_ref, relation.source_entity_ref);\n\n const parentGroup = allRelations.find(\n g => g.source_entity_ref === relation.target_entity_ref,\n );\n\n if (parentGroup && memo.isAcyclic()) {\n this.traverseRelations(memo, parentGroup, allRelations, depth);\n }\n }\n\n async buildUserGraph(memo: AncestorSearchMemo) {\n if (await this.doesRelationTableExist()) {\n const userRelations = await this.getUserRelations();\n const allRelations = await this.getAllRelations();\n userRelations.forEach(group =>\n this.traverseRelations(\n memo,\n group as Relation,\n allRelations as Relation[],\n 0,\n ),\n );\n } else {\n const userGroups = await this.getUserGroups();\n const allGroups = await this.getAllGroups();\n userGroups.forEach(group =>\n this.traverseGroups(memo, group as Entity, allGroups as Entity[], 0),\n );\n }\n }\n}\n"],"names":["Graph","alg"],"mappings":"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"}
1
+ {"version":3,"file":"ancestor-search-memo.cjs.js","sources":["../../src/role-manager/ancestor-search-memo.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport type { CatalogApi } from '@backstage/catalog-client';\nimport type { Entity } from '@backstage/catalog-model';\n\nimport { alg, Graph } from '@dagrejs/graphlib';\nimport { Knex } from 'knex';\n\nexport interface Relation {\n source_entity_ref: string;\n target_entity_ref: string;\n}\n\nexport type ASMGroup = Relation | Entity;\n\n// AncestorSearchMemo - should be used to build group hierarchy graph for User entity reference.\n// It supports search group entity reference link in the graph.\n// Also AncestorSearchMemo supports detection cycle dependencies between groups in the graph.\n//\nexport class AncestorSearchMemo {\n private graph: Graph;\n\n private catalogApi: CatalogApi;\n private catalogDBClient: Knex;\n private auth: AuthService;\n\n private userEntityRef: string;\n private maxDepth?: number;\n\n constructor(\n userEntityRef: string,\n catalogApi: CatalogApi,\n catalogDBClient: Knex,\n auth: AuthService,\n maxDepth?: number,\n ) {\n this.graph = new Graph({ directed: true });\n this.userEntityRef = userEntityRef;\n this.catalogApi = catalogApi;\n this.catalogDBClient = catalogDBClient;\n this.auth = auth;\n this.maxDepth = maxDepth;\n }\n\n isAcyclic(): boolean {\n return alg.isAcyclic(this.graph);\n }\n\n findCycles(): string[][] {\n return alg.findCycles(this.graph);\n }\n\n setEdge(parentEntityRef: string, childEntityRef: string) {\n this.graph.setEdge(parentEntityRef, childEntityRef);\n }\n\n setNode(entityRef: string): void {\n this.graph.setNode(entityRef);\n }\n\n hasEntityRef(groupRef: string): boolean {\n return this.graph.hasNode(groupRef);\n }\n\n debugNodesAndEdges(logger: LoggerService, userEntity: string): void {\n logger.debug(\n `SubGraph edges: ${JSON.stringify(this.graph.edges())} for ${userEntity}`,\n );\n logger.debug(\n `SubGraph nodes: ${JSON.stringify(this.graph.nodes())} for ${userEntity}`,\n );\n }\n\n getNodes(): string[] {\n return this.graph.nodes();\n }\n\n async doesRelationTableExist(): Promise<boolean> {\n try {\n return await this.catalogDBClient.schema.hasTable('relations');\n } catch (error) {\n return false;\n }\n }\n\n async getAllGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group' },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getAllRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where('type', 'childOf');\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n async getUserGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group', 'relations.hasMember': this.userEntityRef },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getUserRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where({ type: 'memberOf', source_entity_ref: this.userEntityRef });\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n traverseGroups(\n memo: AncestorSearchMemo,\n group: Entity,\n allGroups: Entity[],\n current_depth: number,\n ) {\n const groupName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${group.metadata.name.toLocaleLowerCase('en-US')}`;\n if (!memo.hasEntityRef(groupName)) {\n memo.setNode(groupName);\n }\n\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth) {\n return;\n }\n const depth = current_depth + 1;\n\n const parent = group.spec?.parent as string;\n const parentGroup = allGroups.find(g => g.metadata.name === parent);\n\n if (parentGroup) {\n const parentName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${parentGroup.metadata.name.toLocaleLowerCase('en-US')}`;\n memo.setEdge(parentName, groupName);\n\n if (memo.isAcyclic()) {\n this.traverseGroups(memo, parentGroup, allGroups, depth);\n }\n }\n }\n\n traverseRelations(\n memo: AncestorSearchMemo,\n relation: Relation,\n allRelations: Relation[],\n current_depth: number,\n ) {\n // We add one to the maxDepth here because the user is considered the starting node\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth + 1) {\n return;\n }\n const depth = current_depth + 1;\n\n if (!memo.hasEntityRef(relation.source_entity_ref)) {\n memo.setNode(relation.source_entity_ref);\n }\n\n memo.setEdge(relation.target_entity_ref, relation.source_entity_ref);\n\n const parentGroup = allRelations.find(\n g => g.source_entity_ref === relation.target_entity_ref,\n );\n\n if (parentGroup && memo.isAcyclic()) {\n this.traverseRelations(memo, parentGroup, allRelations, depth);\n }\n }\n\n async buildUserGraph(memo: AncestorSearchMemo) {\n if (await this.doesRelationTableExist()) {\n const userRelations = await this.getUserRelations();\n const allRelations = await this.getAllRelations();\n userRelations.forEach(group =>\n this.traverseRelations(\n memo,\n group as Relation,\n allRelations as Relation[],\n 0,\n ),\n );\n } else {\n const userGroups = await this.getUserGroups();\n const allGroups = await this.getAllGroups();\n userGroups.forEach(group =>\n this.traverseGroups(memo, group as Entity, allGroups as Entity[], 0),\n );\n }\n }\n}\n"],"names":["Graph","alg"],"mappings":"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"}
@@ -82,7 +82,7 @@ class RoleMemberList {
82
82
  */
83
83
  async buildRoles(roleMemberList, userAndGroups, client) {
84
84
  try {
85
- const roles = await client.table("casbin_rule").whereIn("v0", userAndGroups).pluck("v1").distinct();
85
+ const roles = await client.table("casbin_rule").where("ptype", "g").whereIn("v0", userAndGroups).pluck("v1").distinct();
86
86
  roleMemberList.addRoles(roles);
87
87
  } catch (error) {
88
88
  throw new Error(`Unable to find all roles. Cause: ${error}`);
@@ -1 +1 @@
1
- {"version":3,"file":"member-list.cjs.js","sources":["../../src/role-manager/member-list.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Knex } from 'knex';\n\nexport class RoleMemberList {\n public name: string;\n\n private members: string[];\n private roles: string[];\n\n public constructor(name: string) {\n this.name = name;\n this.members = [];\n this.roles = [];\n }\n\n /**\n * addMembers will add members to the RoleMemberList\n * @param members The members to be added.\n */\n public addMembers(members: string[]): void {\n this.members = members;\n }\n\n /**\n * addMember will add a single member to the RoleMemberList, skips adding the user in the\n * event that they already exist in the members array.\n * @param member The member to be added.\n */\n public addMember(member: string): void {\n if (this.members.some(n => n === member)) {\n return;\n }\n this.members.push(member);\n }\n\n /**\n * hasMember will check if a particular member exists in the members array.\n * @param name The member to be checked for.\n */\n public hasMember(name: string): boolean {\n return this.members.includes(name);\n }\n\n /**\n * deleteMember will remove a user from the members array.\n * @param member The member to be removed.\n */\n public deleteMember(member: string): void {\n this.members = this.members.filter(n => n !== member);\n }\n\n /**\n * buildMembers will query the `casbin_rule` database table to ensure that the role\n * that we have cached is up to date.\n * This is important in multi node scenarios where the cached roles in role manager can become\n * out of sync with the database.\n * @param roleMemberList The RoleMemberList to be updated.\n * @param client The database client.\n */\n public async buildMembers(\n roleMemberList: RoleMemberList,\n client: Knex,\n ): Promise<void> {\n try {\n const members: string[] = await client\n .table('casbin_rule')\n .where('v1', this.name)\n .pluck('v0')\n .distinct();\n\n roleMemberList.addMembers(members);\n } catch (error) {\n throw new Error(\n `Unable to find members for the role ${this.name}. Cause: ${error}`,\n );\n }\n }\n\n /**\n * getMembers will return the members of the RoleMemberList\n * @returns The members.\n */\n getMembers(): string[] {\n return this.members;\n }\n\n /**\n * addRoles will add roles to the RoleMemberList\n * @param roles The roles to be added.\n */\n public addRoles(roles: string[]): void {\n this.roles = roles;\n }\n\n /**\n * buildRoles will query the `casbin_rule` database table to quickly grab all of the\n * roles that a particular user is attached to.\n * @param roleMemberList The RoleMemberList to be updated.\n * @param userAndGroups The user and groups to query with.\n * @param client The database client.\n */\n public async buildRoles(\n roleMemberList: RoleMemberList,\n userAndGroups: string[],\n client: Knex,\n ): Promise<void> {\n try {\n const roles: string[] = await client\n .table('casbin_rule')\n .whereIn('v0', userAndGroups)\n .pluck('v1')\n .distinct();\n\n roleMemberList.addRoles(roles);\n } catch (error) {\n throw new Error(`Unable to find all roles. Cause: ${error}`);\n }\n }\n\n /**\n * getRoles will return the roles of the RoleMemberList.\n * @returns The roles.\n */\n getRoles(): string[] {\n return this.roles;\n }\n}\n"],"names":[],"mappings":";;AAiBO,MAAM,cAAe,CAAA;AAAA,EACnB,IAAA;AAAA,EAEC,OAAA;AAAA,EACA,KAAA;AAAA,EAED,YAAY,IAAc,EAAA;AAC/B,IAAA,IAAA,CAAK,IAAO,GAAA,IAAA;AACZ,IAAA,IAAA,CAAK,UAAU,EAAC;AAChB,IAAA,IAAA,CAAK,QAAQ,EAAC;AAAA;AAChB;AAAA;AAAA;AAAA;AAAA,EAMO,WAAW,OAAyB,EAAA;AACzC,IAAA,IAAA,CAAK,OAAU,GAAA,OAAA;AAAA;AACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,UAAU,MAAsB,EAAA;AACrC,IAAA,IAAI,KAAK,OAAQ,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,KAAM,MAAM,CAAG,EAAA;AACxC,MAAA;AAAA;AAEF,IAAK,IAAA,CAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA;AAC1B;AAAA;AAAA;AAAA;AAAA,EAMO,UAAU,IAAuB,EAAA;AACtC,IAAO,OAAA,IAAA,CAAK,OAAQ,CAAA,QAAA,CAAS,IAAI,CAAA;AAAA;AACnC;AAAA;AAAA;AAAA;AAAA,EAMO,aAAa,MAAsB,EAAA;AACxC,IAAA,IAAA,CAAK,UAAU,IAAK,CAAA,OAAA,CAAQ,MAAO,CAAA,CAAA,CAAA,KAAK,MAAM,MAAM,CAAA;AAAA;AACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,YACX,CAAA,cAAA,EACA,MACe,EAAA;AACf,IAAI,IAAA;AACF,MAAA,MAAM,OAAoB,GAAA,MAAM,MAC7B,CAAA,KAAA,CAAM,aAAa,CACnB,CAAA,KAAA,CAAM,IAAM,EAAA,IAAA,CAAK,IAAI,CAAA,CACrB,KAAM,CAAA,IAAI,EACV,QAAS,EAAA;AAEZ,MAAA,cAAA,CAAe,WAAW,OAAO,CAAA;AAAA,aAC1B,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAuC,oCAAA,EAAA,IAAA,CAAK,IAAI,CAAA,SAAA,EAAY,KAAK,CAAA;AAAA,OACnE;AAAA;AACF;AACF;AAAA;AAAA;AAAA;AAAA,EAMA,UAAuB,GAAA;AACrB,IAAA,OAAO,IAAK,CAAA,OAAA;AAAA;AACd;AAAA;AAAA;AAAA;AAAA,EAMO,SAAS,KAAuB,EAAA;AACrC,IAAA,IAAA,CAAK,KAAQ,GAAA,KAAA;AAAA;AACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,UAAA,CACX,cACA,EAAA,aAAA,EACA,MACe,EAAA;AACf,IAAI,IAAA;AACF,MAAA,MAAM,KAAkB,GAAA,MAAM,MAC3B,CAAA,KAAA,CAAM,aAAa,CAAA,CACnB,OAAQ,CAAA,IAAA,EAAM,aAAa,CAAA,CAC3B,KAAM,CAAA,IAAI,EACV,QAAS,EAAA;AAEZ,MAAA,cAAA,CAAe,SAAS,KAAK,CAAA;AAAA,aACtB,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAoC,iCAAA,EAAA,KAAK,CAAE,CAAA,CAAA;AAAA;AAC7D;AACF;AAAA;AAAA;AAAA;AAAA,EAMA,QAAqB,GAAA;AACnB,IAAA,OAAO,IAAK,CAAA,KAAA;AAAA;AAEhB;;;;"}
1
+ {"version":3,"file":"member-list.cjs.js","sources":["../../src/role-manager/member-list.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Knex } from 'knex';\n\nexport class RoleMemberList {\n public name: string;\n\n private members: string[];\n private roles: string[];\n\n public constructor(name: string) {\n this.name = name;\n this.members = [];\n this.roles = [];\n }\n\n /**\n * addMembers will add members to the RoleMemberList\n * @param members The members to be added.\n */\n public addMembers(members: string[]): void {\n this.members = members;\n }\n\n /**\n * addMember will add a single member to the RoleMemberList, skips adding the user in the\n * event that they already exist in the members array.\n * @param member The member to be added.\n */\n public addMember(member: string): void {\n if (this.members.some(n => n === member)) {\n return;\n }\n this.members.push(member);\n }\n\n /**\n * hasMember will check if a particular member exists in the members array.\n * @param name The member to be checked for.\n */\n public hasMember(name: string): boolean {\n return this.members.includes(name);\n }\n\n /**\n * deleteMember will remove a user from the members array.\n * @param member The member to be removed.\n */\n public deleteMember(member: string): void {\n this.members = this.members.filter(n => n !== member);\n }\n\n /**\n * buildMembers will query the `casbin_rule` database table to ensure that the role\n * that we have cached is up to date.\n * This is important in multi node scenarios where the cached roles in role manager can become\n * out of sync with the database.\n * @param roleMemberList The RoleMemberList to be updated.\n * @param client The database client.\n */\n public async buildMembers(\n roleMemberList: RoleMemberList,\n client: Knex,\n ): Promise<void> {\n try {\n const members: string[] = await client\n .table('casbin_rule')\n .where('v1', this.name)\n .pluck('v0')\n .distinct();\n\n roleMemberList.addMembers(members);\n } catch (error) {\n throw new Error(\n `Unable to find members for the role ${this.name}. Cause: ${error}`,\n );\n }\n }\n\n /**\n * getMembers will return the members of the RoleMemberList\n * @returns The members.\n */\n getMembers(): string[] {\n return this.members;\n }\n\n /**\n * addRoles will add roles to the RoleMemberList\n * @param roles The roles to be added.\n */\n public addRoles(roles: string[]): void {\n this.roles = roles;\n }\n\n /**\n * buildRoles will query the `casbin_rule` database table to quickly grab all of the\n * roles that a particular user is attached to.\n * @param roleMemberList The RoleMemberList to be updated.\n * @param userAndGroups The user and groups to query with.\n * @param client The database client.\n */\n public async buildRoles(\n roleMemberList: RoleMemberList,\n userAndGroups: string[],\n client: Knex,\n ): Promise<void> {\n try {\n const roles: string[] = await client\n .table('casbin_rule')\n .where('ptype', 'g')\n .whereIn('v0', userAndGroups)\n .pluck('v1')\n .distinct();\n\n roleMemberList.addRoles(roles);\n } catch (error) {\n throw new Error(`Unable to find all roles. Cause: ${error}`);\n }\n }\n\n /**\n * getRoles will return the roles of the RoleMemberList.\n * @returns The roles.\n */\n getRoles(): string[] {\n return this.roles;\n }\n}\n"],"names":[],"mappings":";;AAiBO,MAAM,cAAe,CAAA;AAAA,EACnB,IAAA;AAAA,EAEC,OAAA;AAAA,EACA,KAAA;AAAA,EAED,YAAY,IAAc,EAAA;AAC/B,IAAA,IAAA,CAAK,IAAO,GAAA,IAAA;AACZ,IAAA,IAAA,CAAK,UAAU,EAAC;AAChB,IAAA,IAAA,CAAK,QAAQ,EAAC;AAAA;AAChB;AAAA;AAAA;AAAA;AAAA,EAMO,WAAW,OAAyB,EAAA;AACzC,IAAA,IAAA,CAAK,OAAU,GAAA,OAAA;AAAA;AACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,UAAU,MAAsB,EAAA;AACrC,IAAA,IAAI,KAAK,OAAQ,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,KAAM,MAAM,CAAG,EAAA;AACxC,MAAA;AAAA;AAEF,IAAK,IAAA,CAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA;AAC1B;AAAA;AAAA;AAAA;AAAA,EAMO,UAAU,IAAuB,EAAA;AACtC,IAAO,OAAA,IAAA,CAAK,OAAQ,CAAA,QAAA,CAAS,IAAI,CAAA;AAAA;AACnC;AAAA;AAAA;AAAA;AAAA,EAMO,aAAa,MAAsB,EAAA;AACxC,IAAA,IAAA,CAAK,UAAU,IAAK,CAAA,OAAA,CAAQ,MAAO,CAAA,CAAA,CAAA,KAAK,MAAM,MAAM,CAAA;AAAA;AACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,YACX,CAAA,cAAA,EACA,MACe,EAAA;AACf,IAAI,IAAA;AACF,MAAA,MAAM,OAAoB,GAAA,MAAM,MAC7B,CAAA,KAAA,CAAM,aAAa,CACnB,CAAA,KAAA,CAAM,IAAM,EAAA,IAAA,CAAK,IAAI,CAAA,CACrB,KAAM,CAAA,IAAI,EACV,QAAS,EAAA;AAEZ,MAAA,cAAA,CAAe,WAAW,OAAO,CAAA;AAAA,aAC1B,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAuC,oCAAA,EAAA,IAAA,CAAK,IAAI,CAAA,SAAA,EAAY,KAAK,CAAA;AAAA,OACnE;AAAA;AACF;AACF;AAAA;AAAA;AAAA;AAAA,EAMA,UAAuB,GAAA;AACrB,IAAA,OAAO,IAAK,CAAA,OAAA;AAAA;AACd;AAAA;AAAA;AAAA;AAAA,EAMO,SAAS,KAAuB,EAAA;AACrC,IAAA,IAAA,CAAK,KAAQ,GAAA,KAAA;AAAA;AACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,UAAA,CACX,cACA,EAAA,aAAA,EACA,MACe,EAAA;AACf,IAAI,IAAA;AACF,MAAA,MAAM,QAAkB,MAAM,MAAA,CAC3B,KAAM,CAAA,aAAa,EACnB,KAAM,CAAA,OAAA,EAAS,GAAG,CAAA,CAClB,QAAQ,IAAM,EAAA,aAAa,EAC3B,KAAM,CAAA,IAAI,EACV,QAAS,EAAA;AAEZ,MAAA,cAAA,CAAe,SAAS,KAAK,CAAA;AAAA,aACtB,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAoC,iCAAA,EAAA,KAAK,CAAE,CAAA,CAAA;AAAA;AAC7D;AACF;AAAA;AAAA;AAAA;AAAA,EAMA,QAAqB,GAAA;AACnB,IAAA,OAAO,IAAK,CAAA,KAAA;AAAA;AAEhB;;;;"}