@backstage-community/plugin-rbac-backend 5.3.0 → 5.4.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 +18 -0
- package/dist/database/casbin-adapter-factory.cjs.js +5 -5
- package/dist/database/casbin-adapter-factory.cjs.js.map +1 -1
- package/dist/database/conditional-storage.cjs.js +1 -1
- package/dist/database/conditional-storage.cjs.js.map +1 -1
- package/dist/helper.cjs.js +1 -1
- package/dist/helper.cjs.js.map +1 -1
- package/dist/policies/permission-policy.cjs.js +18 -6
- package/dist/policies/permission-policy.cjs.js.map +1 -1
- package/dist/role-manager/ancestor-search-memo.cjs.js +2 -2
- package/dist/role-manager/ancestor-search-memo.cjs.js.map +1 -1
- package/dist/role-manager/role-manager.cjs.js +3 -3
- package/dist/role-manager/role-manager.cjs.js.map +1 -1
- package/dist/service/enforcer-delegate.cjs.js.map +1 -1
- package/dist/service/plugin-endpoints.cjs.js +2 -2
- package/dist/service/plugin-endpoints.cjs.js.map +1 -1
- package/dist/service/policies-rest-api.cjs.js +2 -2
- package/dist/service/policies-rest-api.cjs.js.map +1 -1
- package/dist/validation/policies-validation.cjs.js +8 -8
- package/dist/validation/policies-validation.cjs.js.map +1 -1
- package/package.json +24 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
### Dependencies
|
|
2
2
|
|
|
3
|
+
## 5.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5d5c02a: Backstage version bump to v1.35.0
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [5d5c02a]
|
|
12
|
+
- @backstage-community/plugin-rbac-common@1.13.0
|
|
13
|
+
- @backstage-community/plugin-rbac-node@1.9.0
|
|
14
|
+
|
|
15
|
+
## 5.3.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 1d5dd17: Evaluate the permissions for a superuser earlier in the process to avoid the unintended consequence of having conditional permissions policies applied to a superuser.
|
|
20
|
+
|
|
3
21
|
## 5.3.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
|
@@ -58,7 +58,7 @@ class CasbinDBAdapterFactory {
|
|
|
58
58
|
"connection"
|
|
59
59
|
);
|
|
60
60
|
if (!connection) {
|
|
61
|
-
return
|
|
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 ===
|
|
70
|
-
return
|
|
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 ===
|
|
78
|
+
if (Object.values(tlsOpts).every((el) => el === undefined)) {
|
|
79
79
|
return true;
|
|
80
80
|
}
|
|
81
81
|
return tlsOpts;
|
|
82
82
|
}
|
|
83
|
-
return
|
|
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":";;;;;;;;;;AA0BA,MAAM,iCAAoC,GAAA,aAAA;AAEnC,MAAM,sBAAuB,CAAA;AAAA,EAC3B,WAAA,CACY,QACA,cACjB,EAAA;AAFiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,cAAA,GAAA,cAAA;AAAA;AAChB,EAEH,MAAa,aAAyC,GAAA;AACpD,IAAA,MAAM,cAAiB,GAAA,IAAA,CAAK,MAAO,CAAA,iBAAA,CAAkB,kBAAkB,CAAA;AACvE,IAAM,MAAA,MAAA,GAAS,cAAgB,EAAA,iBAAA,CAAkB,QAAQ,CAAA;AAEzD,IAAI,IAAA,OAAA;AACJ,IAAA,IAAI,WAAW,IAAM,EAAA;AACnB,MAAA,MAAM,SACJ,MAAM,IAAA,CAAK,cAAe,CAAA,MAAA,CAAO,OAAO,UAAW,CAAA,QAAA;AACrD,MAAA,MAAM,SACH,MAAM,IAAA,CAAK,eAAe,MAAO,CAAA,UAAA,GAAa,CAAC,CAAM,IAAA,QAAA;AAExD,MAAM,MAAA,GAAA,GAAM,IAAK,CAAA,iBAAA,CAAkB,cAAe,CAAA;AAElD,MAAU,OAAA,GAAA,MAAMA,gCAAe,UAAW,CAAA;AAAA,QACxC,IAAM,EAAA,UAAA;AAAA,QACN,IAAA,EAAM,cAAgB,EAAA,SAAA,CAAU,iBAAiB,CAAA;AAAA,QACjD,IAAA,EAAM,cAAgB,EAAA,SAAA,CAAU,iBAAiB,CAAA;AAAA,QACjD,QAAA,EAAU,cAAgB,EAAA,SAAA,CAAU,iBAAiB,CAAA;AAAA,QACrD,QAAA,EAAU,cAAgB,EAAA,SAAA,CAAU,qBAAqB,CAAA;AAAA,QACzD,GAAA;AAAA,QACA,QAAU,EAAA,MAAA;AAAA,QACV,MAAA;AAAA,QACA,QAAA,EAAU,cAAgB,EAAA,iBAAA,CAAkB,qBAAqB;AAAA,OAClE,CAAA;AAAA;AAGH,IAAA,IAAI,WAAW,gBAAkB,EAAA;AAC/B,MAAI,IAAA,OAAA;AACJ,MAAA,IAAI,OAAO,cAAgB,EAAA,GAAA,CAAI,YAAY,CAAG,EAAA,OAAA,OAAc,QAAU,EAAA;AACpE,QAAU,OAAA,GAAA,cAAA,EAAgB,UAAU,YAAY,CAAA;AAAA,OACvC,MAAA,IAAA,cAAA,EAAgB,GAAI,CAAA,sBAAsB,CAAG,EAAA;AACtD,QAAM,MAAA,UAAA,GAAa,cAAgB,EAAA,SAAA,CAAU,sBAAsB,CAAA;AACnE,QAAU,OAAA,GAAAC,YAAA,CAAQ,YAAY,iCAAiC,CAAA;AAAA;AAGjE,MAAU,OAAA,GAAA,MAAMD,gCAAe,UAAW,CAAA;AAAA,QACxC,IAAM,EAAA,gBAAA;AAAA;AAAA,QAEN,UAAU,OAAW,IAAA;AAAA,OACtB,CAAA;AAAA;AAGH,IAAA,IAAI,CAAC,OAAS,EAAA;AACZ,MAAA,MAAM,IAAI,KAAA,CAAM,CAA+B,4BAAA,EAAA,MAAM,CAAE,CAAA,CAAA;AAAA;AAGzD,IAAO,OAAA,OAAA;AAAA;AACT,EAEQ,kBACN,QACkC,EAAA;AAClC,IAAA,MAAM,aAAa,QAAS,CAAA,WAAA;AAAA,MAC1B;AAAA,KACF;AACA,IAAA,IAAI,CAAC,UAAY,EAAA;AACf,MAAO,OAAA,
|
|
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 +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":";;;;AA0BO,MAAM,iBAAoB,GAAA;AAwC1B,MAAM,0BAAyD,CAAA;AAAA,EAC7D,YAA6B,IAAwB,EAAA;AAAxB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAyB,EAE7D,MAAM,gBACJ,CAAA,aAAA,EACA,QACA,EAAA,YAAA,EACA,SACA,eAC0D,EAAA;AAC1D,IAAM,MAAA,OAAA,GAAU,MAAM,IAAK,CAAA,IAAA,CAAK,MAAM,iBAAiB,CAAA,CAAE,MAAM,CAAW,OAAA,KAAA;AACxE,MAAA,IAAI,QAAU,EAAA;AACZ,QAAQ,OAAA,CAAA,KAAA,CAAM,YAAY,QAAQ,CAAA;AAAA;AAEpC,MAAA,IAAI,YAAc,EAAA;AAChB,QAAQ,OAAA,CAAA,KAAA,CAAM,gBAAgB,YAAY,CAAA;AAAA;AAE5C,MAAA,IAAI,aAAe,EAAA;AACjB,QAAI,IAAA,KAAA,CAAM,OAAQ,CAAA,aAAa,CAAG,EAAA;AAChC,UAAQ,OAAA,CAAA,OAAA,CAAQ,iBAAiB,aAAa,CAAA;AAAA,SACzC,MAAA;AACL,UAAQ,OAAA,CAAA,KAAA,CAAM,iBAAiB,aAAa,CAAA;AAAA;AAC9C;AACF,KACD,CAAA;AAED,IAAA,IAAI,aAA8D,EAAC;AACnE,IAAA,IAAI,OAAS,EAAA;AACX,MAAA,UAAA,GAAa,QAAQ,GAAI,CAAA,CAAA,GAAA,KAAO,IAAK,CAAA,wBAAA,CAAyB,GAAG,CAAC,CAAA;AAAA;AAGpE,IAAI,IAAA,eAAA,IAAmB,eAAgB,CAAA,MAAA,GAAS,CAAG,EAAA;AACjD,MAAa,UAAA,GAAA,UAAA,CAAW,OAAO,CAAa,SAAA,KAAA;AAC1C,QAAA,OAAO,eAAgB,CAAA,KAAA;AAAA,UAAM,CAAA,cAAA,KAC3B,UAAU,iBACP,CAAA,GAAA,CAAI,cAAY,QAAS,CAAA,IAAI,CAC7B,CAAA,QAAA,CAAS,cAAc;AAAA,SAC5B;AAAA,OACD,CAAA;AAAA;AAGH,IAAI,IAAA,OAAA,IAAW,OAAQ,CAAA,MAAA,GAAS,CAAG,EAAA;AACjC,MAAa,UAAA,GAAA,UAAA,CAAW,OAAO,CAAa,SAAA,KAAA;AAC1C,QAAA,OAAO,OAAQ,CAAA,KAAA;AAAA,UAAM,CAAA,MAAA,KACnB,UAAU,iBACP,CAAA,GAAA,CAAI,cAAY,QAAS,CAAA,MAAM,CAC/B,CAAA,QAAA,CAAS,MAAM;AAAA,SACpB;AAAA,OACD,CAAA;AAAA;AAGH,IAAO,OAAA,UAAA;AAAA;AACT,EAEA,MAAM,gBACJ,mBACiB,EAAA;AACjB,IAAA,MAAM,IAAK,CAAA,yBAAA;AAAA,MACT,mBAAoB,CAAA,aAAA;AAAA,MACpB,mBAAoB,CAAA,YAAA;AAAA,MACpB,mBAAoB,CAAA,QAAA;AAAA,MACpB,mBAAoB,CAAA,iBAAA,CAAkB,GAAI,CAAA,CAAA,QAAA,KAAY,SAAS,MAAM;AAAA,KACvE;AAEA,IAAM,MAAA,YAAA,GAAe,IAAK,CAAA,KAAA,CAAM,mBAAmB,CAAA;AACnD,IAAM,MAAA,MAAA,GAAS,MAAM,IAAA,CAAK,IACvB,CAAA,KAAA,CAAM,iBAAiB,CAAA,CACvB,MAAqC,CAAA,YAAY,CACjD,CAAA,SAAA,CAAU,IAAI,CAAA;AACjB,IAAI,IAAA,MAAA,IAAU,MAAQ,EAAA,MAAA,GAAS,CAAG,EAAA;AAChC,MAAO,OAAA,MAAA,CAAO,CAAC,CAAE,CAAA,EAAA;AAAA;AAGnB,IAAM,MAAA,IAAI,MAAM,CAAiC,+BAAA,CAAA,CAAA;AAAA;AACnD,EAEA,MAAM,yBACJ,CAAA,aAAA,EACA,YACA,EAAA,QAAA,EACA,uBACA,WACe,EAAA;AACf,IAAI,IAAA,4BAAA,GAA+B,MAAM,IAAK,CAAA,gBAAA;AAAA,MAC5C,aAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,4BAAA,GAA+B,4BAA6B,CAAA,MAAA;AAAA,MAC1D,CAAA,CAAA,KAAK,EAAE,EAAO,KAAA;AAAA,KAChB;AAEA,IAAA,IAAI,4BAA8B,EAAA;AAChC,MAAA,MAAM,sBAAsB,4BAA6B,CAAA,IAAA;AAAA,QACvD,CAAa,SAAA,KAAA;AACX,UAAM,MAAA,gBAAA,GAAmB,UAAU,iBAAkB,CAAA,GAAA;AAAA,YACnD,cAAY,QAAS,CAAA;AAAA,WACvB;AACA,UAAA,OAAO,qBAAsB,CAAA,IAAA;AAAA,YAAK,CAAA,MAAA,KAChC,gBAAiB,CAAA,QAAA,CAAS,MAAM;AAAA,WAClC;AAAA;AACF,OACF;AAEA,MAAA,IAAI,mBAAqB,EAAA;AACvB,QAAA,MAAM,oBAAoB,qBAAsB,CAAA,MAAA;AAAA,UAAO,YACrD,mBAAoB,CAAA,iBAAA,CAAkB,KAAK,CAAK,CAAA,KAAA,CAAA,CAAE,WAAW,MAAM;AAAA,SACrE;AACA,QAAA,MAAM,IAAIA,oBAAA;AAAA,UACR,sDAAsD,IAAK,CAAA,SAAA;AAAA,YACzD;AAAA,WACD,CAC2C,mEAAA,EAAA,mBAAA,CAAoB,YAAY,CAAA,6CAAA;AAAA,SAC9E;AAAA;AACF;AACF;AACF,EAEA,MAAM,aACJ,EACoE,EAAA;AACpE,IAAM,MAAA,MAAA,GAAS,MAAM,IAAA,CAAK,IACvB,CAAA,KAAA,CAAM,iBAAiB,CAAA,CACvB,KAAM,CAAA,IAAA,EAAM,EAAE,CAAA,CACd,KAAM,EAAA;AAET,IAAA,IAAI,MAAQ,EAAA;AACV,MAAO,OAAA,IAAA,CAAK,yBAAyB,MAAM,CAAA;AAAA;AAE7C,IAAO,OAAA,SAAA;AAAA;AACT,EAEA,MAAM,gBAAgB,EAA2B,EAAA;AAC/C,IAAA,MAAM,SAAY,GAAA,MAAM,IAAK,CAAA,YAAA,CAAa,EAAE,CAAA;AAC5C,IAAA,IAAI,CAAC,SAAW,EAAA;AACd,MAAA,MAAM,IAAIC,oBAAA,CAAc,CAAqB,kBAAA,EAAA,EAAE,CAAgB,cAAA,CAAA,CAAA;AAAA;AAEjE,IAAM,MAAA,IAAA,CAAK,IAAM,EAAA,KAAA,CAAM,iBAAiB,CAAA,CAAE,MAAO,EAAA,CAAE,OAAQ,CAAA,IAAA,EAAM,CAAC,EAAE,CAAC,CAAA;AAAA;AACvE,EAEA,MAAM,eACJ,CAAA,EAAA,EACA,mBACe,EAAA;AACf,IAAA,MAAM,SAAY,GAAA,MAAM,IAAK,CAAA,YAAA,CAAa,EAAE,CAAA;AAC5C,IAAA,IAAI,CAAC,SAAW,EAAA;AACd,MAAA,MAAM,IAAIA,oBAAA,CAAc,CAAqB,kBAAA,EAAA,EAAE,CAAgB,cAAA,CAAA,CAAA;AAAA;AAGjE,IAAA,MAAM,IAAK,CAAA,yBAAA;AAAA,MACT,mBAAoB,CAAA,aAAA;AAAA,MACpB,mBAAoB,CAAA,YAAA;AAAA,MACpB,mBAAoB,CAAA,QAAA;AAAA,MACpB,mBAAoB,CAAA,iBAAA,CAAkB,GAAI,CAAA,CAAA,IAAA,KAAQ,KAAK,MAAM,CAAA;AAAA,MAC7D;AAAA,KACF;AAEA,IAAM,MAAA,YAAA,GAAe,IAAK,CAAA,KAAA,CAAM,mBAAmB,CAAA;AACnD,IAAA,YAAA,CAAa,EAAK,GAAA,EAAA;AAClB,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,IACvB,CAAA,KAAA,CAAM,iBAAiB,CACvB,CAAA,KAAA,CAAM,IAAM,EAAA,YAAA,CAAa,EAAE,CAC3B,CAAA,MAAA,CAAqC,YAAY,CAAA,CACjD,UAAU,IAAI,CAAA;AAEjB,IAAA,IAAI,CAAC,MAAA,IAAU,MAAO,CAAA,MAAA,KAAW,CAAG,EAAA;AAClC,MAAA,MAAM,IAAI,KAAA,CAAM,CAA2C,wCAAA,EAAA,EAAE,CAAG,CAAA,CAAA,CAAA;AAAA;AAClE;AACF,EAEQ,MACN,mBAC8B,EAAA;AAC9B,IAAM,MAAA;AAAA,MACJ,MAAA;AAAA,MACA,QAAA;AAAA,MACA,YAAA;AAAA,MACA,UAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA,KACE,GAAA,mBAAA;AACJ,IAAM,MAAA,cAAA,GAAiB,IAAK,CAAA,SAAA,CAAU,UAAU,CAAA;AAChD,IAAO,OAAA;AAAA,MACL,MAAA;AAAA,MACA,QAAA;AAAA,MACA,YAAA;AAAA,MACA,cAAA;AAAA,MACA,aAAA;AAAA,MACA,WAAA,EAAa,IAAK,CAAA,SAAA,CAAU,iBAAiB;AAAA,KAC/C;AAAA;AACF,EAEQ,yBACN,GAC+C,EAAA;AAC/C,IAAI,IAAA,CAAC,IAAI,EAAI,EAAA;AACX,MAAA,MAAM,IAAIC,iBAAA,CAAW,CAAgC,6BAAA,EAAA,GAAG,CAAE,CAAA,CAAA;AAAA;AAE5D,IAAM,MAAA;AAAA,MACJ,EAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA;AAAA,MACA,YAAA;AAAA,MACA,cAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA,KACE,GAAA,GAAA;AAEJ,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,KAAA,CAAM,cAAc,CAAA;AAC5C,IAAO,OAAA;AAAA,MACL,EAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA;AAAA,MACA,YAAA;AAAA,MACA,UAAA;AAAA,MACA,aAAA;AAAA,MACA,iBAAA,EAAmB,IAAK,CAAA,KAAA,CAAM,WAAW;AAAA,KAC3C;AAAA;AAEJ;;;;;"}
|
package/dist/helper.cjs.js
CHANGED
|
@@ -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 ===
|
|
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(
|
package/dist/helper.cjs.js.map
CHANGED
|
@@ -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":";;;;;AAiDO,SAAS,eAAe,MAA0B,EAAA;AACvD,EAAA,OAAO,CAAI,CAAA,EAAA,MAAA,CAAO,IAAK,CAAA,IAAI,CAAC,CAAA,CAAA,CAAA;AAC9B;AAEgB,SAAA,mBAAA,CAAoB,QAAkB,IAAsB,EAAA;AAC1E,EAAA,OAAO,GAAG,IAAI,CAAA,EAAA,EAAK,MAAO,CAAA,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AACtC;AAEO,SAAS,iBAAiB,QAA8B,EAAA;AAC7D,EAAM,MAAA,cAAA,GAAiB,SACpB,GAAI,CAAA,CAAA,MAAA,KAAU,eAAe,MAAM,CAAC,CACpC,CAAA,IAAA,CAAK,GAAG,CAAA;AACX,EAAA,OAAO,IAAI,cAAc,CAAA,CAAA,CAAA;AAC3B;AAEgB,SAAA,qBAAA,CACd,UACA,IACQ,EAAA;AACR,EAAM,MAAA,cAAA,GAAiB,QACpB,CAAA,GAAA,CAAI,CAAU,MAAA,KAAA;AACb,IAAA,OAAO,OAAO,MAAW,KAAA,CAAA,GAAI,mBAAoB,CAAA,MAAA,EAAQ,IAAI,CAAI,GAAA,EAAA;AAAA,GAClE,CACA,CAAA,IAAA,CAAK,IAAI,CAAA;AACZ,EAAO,OAAA;AAAA,IAAA,EACH,cAAc;AAAA,EAAA,CAAA;AAEpB;AAEO,SAAS,uBAAuB,MAA0B,EAAA;AAC/D,EAAO,OAAA,MAAA,CAAO,OAAQ,CAAA,GAAA,EAAK,EAAE,CAAA,CAAE,QAAQ,GAAK,EAAA,EAAE,CAAE,CAAA,KAAA,CAAM,IAAI,CAAA;AAC5D;AAEA,eAAsB,oBACpB,aACA,EAAA,UAAA,EACA,QACA,aACA,EAAA,GAAA,EACAA,eACA,UACe,EAAA;AACf,EAAA,aAAA,CAAc,KAAK,CAAC,CAAA,EAAG,MAAM,CAAE,CAAA,aAAA,CAAc,CAAC,CAAC,CAAA;AAC/C,EAAA,UAAA,CAAW,KAAK,CAAC,CAAA,EAAG,MAAM,CAAE,CAAA,aAAA,CAAc,CAAC,CAAC,CAAA;AAC5C,EAAM,MAAA,OAAA,GAAUC,iBAAW,CAAA,aAAA,EAAe,UAAU,CAAA;AAEpD,EAAA,MAAM,gBAA4B,EAAC;AACnC,EAAA,KAAA,MAAW,eAAe,OAAS,EAAA;AACjC,IAAA,aAAA,CAAc,IAAK,CAAA,CAAC,WAAa,EAAA,aAAa,CAAC,CAAA;AAAA;AAGjD,EAAI,IAAA,aAAA,CAAc,WAAW,CAAG,EAAA;AAC9B,IAAA;AAAA;AAGF,EAAA,MAAM,YAAe,GAAA,EAAE,MAAQ,EAAA,UAAA,EAAY,aAAc,EAAA;AACzD,EAAA,MAAM,GAAI,CAAA,sBAAA,CAAuB,aAAe,EAAA,YAAA,EAAc,KAAK,CAAA;AAEnE,EAAM,MAAA,gBAAA,GAAmB,MAAM,GAAI,CAAA,yBAAA;AAAA,IACjC,CAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,OACJ,GAAA,gBAAA,CAAiB,MAAS,GAAA,CAAA,GACtB,+BACA,GAAA,cAAA;AACN,EAAA,MAAM,YACJ,gBAAiB,CAAA,MAAA,GAAS,CACtB,GAAAC,sBAAA,CAAW,cACXA,sBAAW,CAAA,WAAA;AACjB,EAAA,MAAMF,cAAY,QAAwB,CAAA;AAAA,IACxC,OAAS,EAAAG,wBAAA;AAAA,IACT,OAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAU,EAAA;AAAA,MACR,GAAG,YAAA;AAAA,MACH,SAAS,aAAc,CAAA,GAAA,CAAI,CAAM,EAAA,KAAA,EAAA,CAAG,CAAC,CAAC;AAAA,KACxC;AAAA,IACA,KAAO,EAAAC,kCAAA;AAAA,IACP,MAAQ,EAAA;AAAA,GACT,CAAA;AACH;AAEO,SAAS,uBAAuB,WAAwC,EAAA;AAC7E,EAAA,MAAM,CAAC,eAAA,EAAiB,UAAY,EAAA,MAAA,EAAQ,MAAM,CAAI,GAAA,WAAA;AACtD,EAAA,OAAO,EAAE,eAAA,EAAiB,UAAY,EAAA,MAAA,EAAQ,MAAO,EAAA;AACvD;AAEO,SAAS,gCAAgC,WAAuB,EAAA;AACrE,EACE,IAAA,WAAA,CAAY,SAAS,CACrB,IAAA,WAAA,CAAY,CAAC,CAAE,CAAA,UAAA,CAAW,GAAG,CAC5B,KAAA,WAAA,CAAY,CAAC,CAAE,CAAA,UAAA,CAAW,MAAM,CAAK,IAAA,WAAA,CAAY,CAAC,CAAE,CAAA,UAAA,CAAW,OAAO,CACvE,CAAA,EAAA;AACA,IAAA,WAAA,CAAY,CAAC,CAAI,GAAA,WAAA,CAAY,CAAC,CAAA,CAAE,kBAAkB,OAAO,CAAA;AAAA;AAE7D;AAEO,SAAS,+BAA+B,KAAmB,EAAA;AAChE,EAAA,OAAO,KAAM,CAAA,GAAA;AAAA,IAAI,UACf,IAAK,CAAA,MAAA,IAAU,CACX,GAAA,CAAC,KAAK,CAAC,CAAA,CAAE,iBAAkB,CAAA,OAAO,GAAG,GAAG,IAAA,CAAK,KAAM,CAAA,CAAC,CAAC,CACrD,GAAA;AAAA,GACN;AACF;AAEgB,SAAA,eAAA,CACd,IACA,EAAA,IAAA,EACA,aACS,EAAA;AACT,EAAI,IAAA,QAAA;AACJ,EAAI,IAAA,QAAA;AACJ,EAAA,IAAI,aAAe,EAAA;AACjB,IAAM,MAAA,sBAAA,GAAgD,CAAC,MAAA,EAAQ,GAAQ,KAAA;AACrE,MAAA,KAAA,MAAW,SAAS,aAAe,EAAA;AACjC,QAAA,IAAI,QAAQ,KAAO,EAAA;AACjB,UAAO,OAAA,IAAA;AAAA;AACT;AAEF,MAAO,OAAA,KAAA;AAAA,KACT;AACA,IAAW,QAAA,GAAAC,aAAA,CAAO,MAAM,sBAAsB,CAAA;AAC9C,IAAW,QAAA,GAAAA,aAAA,CAAO,MAAM,sBAAsB,CAAA;AAAA;AAGhD,EAAM,MAAA,UAAA,GAAaC,aAAO,CAAAC,cAAA,CAAQ,QAAY,IAAA,IAAI,GAAG,CAAC,CAAC,GAAG,CAAA,KAAM,GAAG,CAAA;AACnE,EAAM,MAAA,UAAA,GAAaD,aAAO,CAAAC,cAAA,CAAQ,QAAY,IAAA,IAAI,GAAG,CAAC,CAAC,GAAG,CAAA,KAAM,GAAG,CAAA;AAEnE,EAAO,OAAAC,cAAA,CAAQ,YAAY,UAAU,CAAA;AACvC;AAEO,SAAS,mBAAmB,MAA4C,EAAA;AAC7E,EAAA,OAAO,CAAC,QAAU,EAAA,MAAA,EAAQ,QAAU,EAAA,QAAA,EAAU,KAAK,CAAE,CAAA,QAAA;AAAA,IACnD;AAAA,GACF;AACF;AAEsB,eAAA,kBAAA,CACpB,UACA,YAC0C,EAAA;AAC1C,EAAA,OAAO,MAAM,QAAS,CAAA,MAAA;AAAA,IACpB,OACE,KACA,MAC6C,KAAA;AAC7C,MAAM,MAAA,aAAA,GAAgB,OAAO,CAAC,CAAA;AAC9B,MAAA,MAAM,cAAc,MAAM,GAAA;AAC1B,MAAA,IAAI,CAAC,WAAA,CAAY,GAAI,CAAA,aAAa,CAAG,EAAA;AACnC,QAAA,MAAM,QAAW,GAAA,MAAM,YAAa,CAAA,gBAAA,CAAiB,aAAa,CAAA;AAClE,QAAY,WAAA,CAAA,GAAA,CAAI,aAAe,EAAA,QAAA,EAAU,MAAM,CAAA;AAAA;AAEjD,MAAO,OAAA,WAAA;AAAA,KACT;AAAA,IACA,OAAQ,CAAA,OAAA,iBAAY,IAAA,GAAA,EAAiC;AAAA,GACvD;AACF;AAEgB,SAAA,iBAAA,CACd,iBACA,WACiB,EAAA;AACjB,EAAM,MAAA,cAAA,GAAkC,EAAE,GAAG,eAAgB,EAAA;AAC7D,EAAA,cAAA,CAAe,eACb,WAAY,CAAA,YAAA,IAAA,iBAAoB,IAAA,IAAA,IAAO,WAAY,EAAA;AACrD,EAAA,cAAA,CAAe,aAAa,WAAY,CAAA,UAAA;AACxC,EAAe,cAAA,CAAA,WAAA,GACb,WAAY,CAAA,WAAA,IAAe,eAAgB,CAAA,WAAA;AAC7C,EAAA,cAAA,CAAe,gBAAgB,WAAY,CAAA,aAAA;AAC3C,EAAA,cAAA,CAAe,SAAS,WAAY,CAAA,MAAA;AACpC,EAAO,OAAA,cAAA;AACT;AAEsB,eAAA,uBAAA,CACpB,mBACA,EAAA,kBAAA,EACA,IACwD,EAAA;AACxD,EAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,IACjD,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,IAChD,gBAAgB,mBAAoB,CAAA;AAAA,GACrC,CAAA;AAED,EAAM,MAAA,IAAA,GACJ,MAAM,kBAAmB,CAAA,qBAAA;AAAA,IACvB,mBAAoB,CAAA,QAAA;AAAA,IACpB;AAAA,GACF;AACF,EAAI,IAAA,CAAC,MAAM,WAAa,EAAA;AACtB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,yCAAA,EAA4C,oBAAoB,QAAQ,CAAA;AAAA,KAC1E;AAAA;AAGF,EAAA,MAAM,WAA6B,EAAC;AACpC,EAAW,KAAA,MAAA,MAAA,IAAU,oBAAoB,iBAAmB,EAAA;AAC1D,IAAM,MAAA,IAAA,GAAO,KAAK,WAAY,CAAA,IAAA;AAAA,MAC5B,CACE,UAAA,KAAA,UAAA,CAAW,IAAS,KAAA,UAAA,KACnB,MAAW,KAAA,UAAA,CAAW,UAAW,CAAA,MAAA,IAC/B,MAAW,KAAA,KAAA,IAAS,UAAW,CAAA,UAAA,CAAW,MAAW,KAAA,KAAA,CAAA;AAAA,KAC5D;AACA,IAAA,IAAI,CAAC,IAAM,EAAA;AACT,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,uEACE,mBAAoB,CAAA,YACtB,gBAAgB,IAAK,CAAA,SAAA,CAAU,MAAM,CAAC,CAAA;AAAA,OACxC;AAAA;AAEF,IAAA,QAAA,CAAS,KAAK,EAAE,IAAA,EAAM,IAAK,CAAA,IAAA,EAAM,QAAQ,CAAA;AAAA;AAG3C,EAAO,OAAA;AAAA,IACL,GAAG,mBAAA;AAAA,IACH,iBAAmB,EAAA;AAAA,GACrB;AACF;AAEO,SAAS,SAAS,KAAiB,EAAA;AACxC,EAAI,IAAAC,cAAA,CAAQ,KAAK,CAAG,EAAA;AAClB,IAAA,OAAOH,aAAO,CAAA,KAAA,CAAM,GAAI,CAAA,QAAQ,CAAC,CAAA;AAAA,GACnC,MAAA,IAAWI,oBAAc,CAAA,KAAK,CAAG,EAAA;AAC/B,IAAO,OAAAC,gBAAA;AAAA,MACLL,aAAA;AAAA,QACEC,cAAQ,CAAA,KAAK,CAAE,CAAA,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAqB,CAAC,CAAA,EAAG,QAAS,CAAA,CAAC,CAAC,CAAC,CAAA;AAAA,QAC9D;AAAA;AACF,KACF;AAAA;AAEF,EAAO,OAAA,KAAA;AACT;AAEgB,SAAA,aAAA,CAAc,MAAW,IAAoB,EAAA;AAC3D,EAAA,OAAOC,eAAQ,QAAS,CAAA,IAAI,CAAG,EAAA,QAAA,CAAS,IAAI,CAAC,CAAA;AAC/C;;;;;;;;;;;;;;;;;;;"}
|
|
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":";;;;;AAiDO,SAAS,eAAe,MAA0B,EAAA;AACvD,EAAA,OAAO,CAAI,CAAA,EAAA,MAAA,CAAO,IAAK,CAAA,IAAI,CAAC,CAAA,CAAA,CAAA;AAC9B;AAEgB,SAAA,mBAAA,CAAoB,QAAkB,IAAsB,EAAA;AAC1E,EAAA,OAAO,GAAG,IAAI,CAAA,EAAA,EAAK,MAAO,CAAA,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AACtC;AAEO,SAAS,iBAAiB,QAA8B,EAAA;AAC7D,EAAM,MAAA,cAAA,GAAiB,SACpB,GAAI,CAAA,CAAA,MAAA,KAAU,eAAe,MAAM,CAAC,CACpC,CAAA,IAAA,CAAK,GAAG,CAAA;AACX,EAAA,OAAO,IAAI,cAAc,CAAA,CAAA,CAAA;AAC3B;AAEgB,SAAA,qBAAA,CACd,UACA,IACQ,EAAA;AACR,EAAM,MAAA,cAAA,GAAiB,QACpB,CAAA,GAAA,CAAI,CAAU,MAAA,KAAA;AACb,IAAA,OAAO,OAAO,MAAW,KAAA,CAAA,GAAI,mBAAoB,CAAA,MAAA,EAAQ,IAAI,CAAI,GAAA,EAAA;AAAA,GAClE,CACA,CAAA,IAAA,CAAK,IAAI,CAAA;AACZ,EAAO,OAAA;AAAA,IAAA,EACH,cAAc;AAAA,EAAA,CAAA;AAEpB;AAEO,SAAS,uBAAuB,MAA0B,EAAA;AAC/D,EAAO,OAAA,MAAA,CAAO,OAAQ,CAAA,GAAA,EAAK,EAAE,CAAA,CAAE,QAAQ,GAAK,EAAA,EAAE,CAAE,CAAA,KAAA,CAAM,IAAI,CAAA;AAC5D;AAEA,eAAsB,oBACpB,aACA,EAAA,UAAA,EACA,QACA,aACA,EAAA,GAAA,EACAA,eACA,UACe,EAAA;AACf,EAAA,aAAA,CAAc,KAAK,CAAC,CAAA,EAAG,MAAM,CAAE,CAAA,aAAA,CAAc,CAAC,CAAC,CAAA;AAC/C,EAAA,UAAA,CAAW,KAAK,CAAC,CAAA,EAAG,MAAM,CAAE,CAAA,aAAA,CAAc,CAAC,CAAC,CAAA;AAC5C,EAAM,MAAA,OAAA,GAAUC,iBAAW,CAAA,aAAA,EAAe,UAAU,CAAA;AAEpD,EAAA,MAAM,gBAA4B,EAAC;AACnC,EAAA,KAAA,MAAW,eAAe,OAAS,EAAA;AACjC,IAAA,aAAA,CAAc,IAAK,CAAA,CAAC,WAAa,EAAA,aAAa,CAAC,CAAA;AAAA;AAGjD,EAAI,IAAA,aAAA,CAAc,WAAW,CAAG,EAAA;AAC9B,IAAA;AAAA;AAGF,EAAA,MAAM,YAAe,GAAA,EAAE,MAAQ,EAAA,UAAA,EAAY,aAAc,EAAA;AACzD,EAAA,MAAM,GAAI,CAAA,sBAAA,CAAuB,aAAe,EAAA,YAAA,EAAc,KAAK,CAAA;AAEnE,EAAM,MAAA,gBAAA,GAAmB,MAAM,GAAI,CAAA,yBAAA;AAAA,IACjC,CAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,OACJ,GAAA,gBAAA,CAAiB,MAAS,GAAA,CAAA,GACtB,+BACA,GAAA,cAAA;AACN,EAAA,MAAM,YACJ,gBAAiB,CAAA,MAAA,GAAS,CACtB,GAAAC,sBAAA,CAAW,cACXA,sBAAW,CAAA,WAAA;AACjB,EAAA,MAAMF,cAAY,QAAwB,CAAA;AAAA,IACxC,OAAS,EAAAG,wBAAA;AAAA,IACT,OAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAU,EAAA;AAAA,MACR,GAAG,YAAA;AAAA,MACH,SAAS,aAAc,CAAA,GAAA,CAAI,CAAM,EAAA,KAAA,EAAA,CAAG,CAAC,CAAC;AAAA,KACxC;AAAA,IACA,KAAO,EAAAC,kCAAA;AAAA,IACP,MAAQ,EAAA;AAAA,GACT,CAAA;AACH;AAEO,SAAS,uBAAuB,WAAwC,EAAA;AAC7E,EAAA,MAAM,CAAC,eAAA,EAAiB,UAAY,EAAA,MAAA,EAAQ,MAAM,CAAI,GAAA,WAAA;AACtD,EAAA,OAAO,EAAE,eAAA,EAAiB,UAAY,EAAA,MAAA,EAAQ,MAAO,EAAA;AACvD;AAEO,SAAS,gCAAgC,WAAuB,EAAA;AACrE,EACE,IAAA,WAAA,CAAY,SAAS,CACrB,IAAA,WAAA,CAAY,CAAC,CAAE,CAAA,UAAA,CAAW,GAAG,CAC5B,KAAA,WAAA,CAAY,CAAC,CAAE,CAAA,UAAA,CAAW,MAAM,CAAK,IAAA,WAAA,CAAY,CAAC,CAAE,CAAA,UAAA,CAAW,OAAO,CACvE,CAAA,EAAA;AACA,IAAA,WAAA,CAAY,CAAC,CAAI,GAAA,WAAA,CAAY,CAAC,CAAA,CAAE,kBAAkB,OAAO,CAAA;AAAA;AAE7D;AAEO,SAAS,+BAA+B,KAAmB,EAAA;AAChE,EAAA,OAAO,KAAM,CAAA,GAAA;AAAA,IAAI,UACf,IAAK,CAAA,MAAA,IAAU,CACX,GAAA,CAAC,KAAK,CAAC,CAAA,CAAE,iBAAkB,CAAA,OAAO,GAAG,GAAG,IAAA,CAAK,KAAM,CAAA,CAAC,CAAC,CACrD,GAAA;AAAA,GACN;AACF;AAEgB,SAAA,eAAA,CACd,IACA,EAAA,IAAA,EACA,aACS,EAAA;AACT,EAAI,IAAA,QAAA;AACJ,EAAI,IAAA,QAAA;AACJ,EAAA,IAAI,aAAe,EAAA;AACjB,IAAM,MAAA,sBAAA,GAAgD,CAAC,MAAA,EAAQ,GAAQ,KAAA;AACrE,MAAA,KAAA,MAAW,SAAS,aAAe,EAAA;AACjC,QAAA,IAAI,QAAQ,KAAO,EAAA;AACjB,UAAO,OAAA,IAAA;AAAA;AACT;AAEF,MAAO,OAAA,KAAA;AAAA,KACT;AACA,IAAW,QAAA,GAAAC,aAAA,CAAO,MAAM,sBAAsB,CAAA;AAC9C,IAAW,QAAA,GAAAA,aAAA,CAAO,MAAM,sBAAsB,CAAA;AAAA;AAGhD,EAAM,MAAA,UAAA,GAAaC,aAAO,CAAAC,cAAA,CAAQ,QAAY,IAAA,IAAI,GAAG,CAAC,CAAC,GAAG,CAAA,KAAM,GAAG,CAAA;AACnE,EAAM,MAAA,UAAA,GAAaD,aAAO,CAAAC,cAAA,CAAQ,QAAY,IAAA,IAAI,GAAG,CAAC,CAAC,GAAG,CAAA,KAAM,GAAG,CAAA;AAEnE,EAAO,OAAAC,cAAA,CAAQ,YAAY,UAAU,CAAA;AACvC;AAEO,SAAS,mBAAmB,MAA4C,EAAA;AAC7E,EAAA,OAAO,CAAC,QAAU,EAAA,MAAA,EAAQ,QAAU,EAAA,QAAA,EAAU,KAAK,CAAE,CAAA,QAAA;AAAA,IACnD;AAAA,GACF;AACF;AAEsB,eAAA,kBAAA,CACpB,UACA,YAC0C,EAAA;AAC1C,EAAA,OAAO,MAAM,QAAS,CAAA,MAAA;AAAA,IACpB,OACE,KACA,MAC6C,KAAA;AAC7C,MAAM,MAAA,aAAA,GAAgB,OAAO,CAAC,CAAA;AAC9B,MAAA,MAAM,cAAc,MAAM,GAAA;AAC1B,MAAA,IAAI,CAAC,WAAA,CAAY,GAAI,CAAA,aAAa,CAAG,EAAA;AACnC,QAAA,MAAM,QAAW,GAAA,MAAM,YAAa,CAAA,gBAAA,CAAiB,aAAa,CAAA;AAClE,QAAY,WAAA,CAAA,GAAA,CAAI,aAAe,EAAA,QAAA,EAAU,MAAM,CAAA;AAAA;AAEjD,MAAO,OAAA,WAAA;AAAA,KACT;AAAA,IACA,OAAQ,CAAA,OAAA,iBAAY,IAAA,GAAA,EAAiC;AAAA,GACvD;AACF;AAEgB,SAAA,iBAAA,CACd,iBACA,WACiB,EAAA;AACjB,EAAM,MAAA,cAAA,GAAkC,EAAE,GAAG,eAAgB,EAAA;AAC7D,EAAA,cAAA,CAAe,eACb,WAAY,CAAA,YAAA,IAAA,iBAAoB,IAAA,IAAA,IAAO,WAAY,EAAA;AACrD,EAAA,cAAA,CAAe,aAAa,WAAY,CAAA,UAAA;AACxC,EAAe,cAAA,CAAA,WAAA,GACb,WAAY,CAAA,WAAA,IAAe,eAAgB,CAAA,WAAA;AAC7C,EAAA,cAAA,CAAe,gBAAgB,WAAY,CAAA,aAAA;AAC3C,EAAA,cAAA,CAAe,SAAS,WAAY,CAAA,MAAA;AACpC,EAAO,OAAA,cAAA;AACT;AAEsB,eAAA,uBAAA,CACpB,mBACA,EAAA,kBAAA,EACA,IACwD,EAAA;AACxD,EAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,qBAAsB,CAAA;AAAA,IACjD,UAAA,EAAY,MAAM,IAAA,CAAK,wBAAyB,EAAA;AAAA,IAChD,gBAAgB,mBAAoB,CAAA;AAAA,GACrC,CAAA;AAED,EAAM,MAAA,IAAA,GACJ,MAAM,kBAAmB,CAAA,qBAAA;AAAA,IACvB,mBAAoB,CAAA,QAAA;AAAA,IACpB;AAAA,GACF;AACF,EAAI,IAAA,CAAC,MAAM,WAAa,EAAA;AACtB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,yCAAA,EAA4C,oBAAoB,QAAQ,CAAA;AAAA,KAC1E;AAAA;AAGF,EAAA,MAAM,WAA6B,EAAC;AACpC,EAAW,KAAA,MAAA,MAAA,IAAU,oBAAoB,iBAAmB,EAAA;AAC1D,IAAM,MAAA,IAAA,GAAO,KAAK,WAAY,CAAA,IAAA;AAAA,MAC5B,CACE,UAAA,KAAA,UAAA,CAAW,IAAS,KAAA,UAAA,KACnB,MAAW,KAAA,UAAA,CAAW,UAAW,CAAA,MAAA,IAC/B,MAAW,KAAA,KAAA,IAAS,UAAW,CAAA,UAAA,CAAW,MAAW,KAAA,SAAA;AAAA,KAC5D;AACA,IAAA,IAAI,CAAC,IAAM,EAAA;AACT,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,uEACE,mBAAoB,CAAA,YACtB,gBAAgB,IAAK,CAAA,SAAA,CAAU,MAAM,CAAC,CAAA;AAAA,OACxC;AAAA;AAEF,IAAA,QAAA,CAAS,KAAK,EAAE,IAAA,EAAM,IAAK,CAAA,IAAA,EAAM,QAAQ,CAAA;AAAA;AAG3C,EAAO,OAAA;AAAA,IACL,GAAG,mBAAA;AAAA,IACH,iBAAmB,EAAA;AAAA,GACrB;AACF;AAEO,SAAS,SAAS,KAAiB,EAAA;AACxC,EAAI,IAAAC,cAAA,CAAQ,KAAK,CAAG,EAAA;AAClB,IAAA,OAAOH,aAAO,CAAA,KAAA,CAAM,GAAI,CAAA,QAAQ,CAAC,CAAA;AAAA,GACnC,MAAA,IAAWI,oBAAc,CAAA,KAAK,CAAG,EAAA;AAC/B,IAAO,OAAAC,gBAAA;AAAA,MACLL,aAAA;AAAA,QACEC,cAAQ,CAAA,KAAK,CAAE,CAAA,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAqB,CAAC,CAAA,EAAG,QAAS,CAAA,CAAC,CAAC,CAAC,CAAA;AAAA,QAC9D;AAAA;AACF,KACF;AAAA;AAEF,EAAO,OAAA,KAAA;AACT;AAEgB,SAAA,aAAA,CAAc,MAAW,IAAoB,EAAA;AAC3D,EAAA,OAAOC,eAAQ,QAAS,CAAA,IAAI,CAAG,EAAA,QAAA,CAAS,IAAI,CAAC,CAAA;AAC/C;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -94,7 +94,7 @@ class RBACPermissionPolicy {
|
|
|
94
94
|
userEntityRef,
|
|
95
95
|
request
|
|
96
96
|
);
|
|
97
|
-
this.auditLogger.auditLog(auditOptions);
|
|
97
|
+
await this.auditLogger.auditLog(auditOptions);
|
|
98
98
|
try {
|
|
99
99
|
let status = false;
|
|
100
100
|
const action = pluginRbacCommon.toPermissionAction(request.permission.attributes);
|
|
@@ -113,6 +113,21 @@ class RBACPermissionPolicy {
|
|
|
113
113
|
await this.auditLogger.auditLog(auditOptions);
|
|
114
114
|
return { result: pluginPermissionCommon.AuthorizeResult.DENY };
|
|
115
115
|
}
|
|
116
|
+
if (this.superUserList.includes(userEntityRef)) {
|
|
117
|
+
const msg2 = evaluatePermMsg(
|
|
118
|
+
userEntityRef,
|
|
119
|
+
pluginPermissionCommon.AuthorizeResult.ALLOW,
|
|
120
|
+
request.permission
|
|
121
|
+
);
|
|
122
|
+
auditOptions = auditLogger.createPermissionEvaluationOptions(
|
|
123
|
+
msg2,
|
|
124
|
+
userEntityRef,
|
|
125
|
+
request,
|
|
126
|
+
{ result: pluginPermissionCommon.AuthorizeResult.ALLOW }
|
|
127
|
+
);
|
|
128
|
+
await this.auditLogger.auditLog(auditOptions);
|
|
129
|
+
return { result: pluginPermissionCommon.AuthorizeResult.ALLOW };
|
|
130
|
+
}
|
|
116
131
|
const permissionName = request.permission.name;
|
|
117
132
|
await this.enforcer.loadPolicy();
|
|
118
133
|
const roles = await this.enforcer.getRolesForUser(userEntityRef);
|
|
@@ -175,9 +190,6 @@ class RBACPermissionPolicy {
|
|
|
175
190
|
return false;
|
|
176
191
|
}
|
|
177
192
|
isAuthorized = async (userIdentity, permission, action, roles) => {
|
|
178
|
-
if (this.superUserList.includes(userIdentity)) {
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
193
|
return await this.enforcer.enforce(userIdentity, permission, action, roles);
|
|
182
194
|
};
|
|
183
195
|
async handleConditions(userEntityRef, request, roles, userInfo) {
|
|
@@ -189,7 +201,7 @@ class RBACPermissionPolicy {
|
|
|
189
201
|
for (const role of roles) {
|
|
190
202
|
const conditionalDecisions = await this.conditionStorage.filterConditions(
|
|
191
203
|
role,
|
|
192
|
-
|
|
204
|
+
undefined,
|
|
193
205
|
resourceType,
|
|
194
206
|
[action],
|
|
195
207
|
[permissionName]
|
|
@@ -234,7 +246,7 @@ class RBACPermissionPolicy {
|
|
|
234
246
|
await this.auditLogger.auditLog(auditOptions);
|
|
235
247
|
return result;
|
|
236
248
|
}
|
|
237
|
-
return
|
|
249
|
+
return undefined;
|
|
238
250
|
}
|
|
239
251
|
}
|
|
240
252
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission-policy.cjs.js","sources":["../../src/policies/permission-policy.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type {\n AuthService,\n BackstageUserInfo,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport type { ConfigApi } from '@backstage/core-plugin-api';\nimport {\n AuthorizeResult,\n ConditionalPolicyDecision,\n isResourcePermission,\n Permission,\n PermissionCondition,\n PermissionCriteria,\n PermissionRuleParams,\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport type {\n PermissionPolicy,\n PolicyQuery,\n PolicyQueryUser,\n} from '@backstage/plugin-permission-node';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport type { Knex } from 'knex';\n\nimport {\n NonEmptyArray,\n toPermissionAction,\n} from '@backstage-community/plugin-rbac-common';\n\nimport {\n setAdminPermissions,\n useAdminsFromConfig,\n} from '../admin-permissions/admin-creation';\nimport {\n createPermissionEvaluationOptions,\n EVALUATE_PERMISSION_ACCESS_STAGE,\n EvaluationEvents,\n} from '../audit-log/audit-logger';\nimport { replaceAliases } from '../conditional-aliases/alias-resolver';\nimport { ConditionalStorage } from '../database/conditional-storage';\nimport { RoleMetadataStorage } from '../database/role-metadata';\nimport { CSVFileWatcher } from '../file-permissions/csv-file-watcher';\nimport { YamlConditinalPoliciesFileWatcher } from '../file-permissions/yaml-conditional-file-watcher';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from '../service/plugin-endpoints';\n\nconst evaluatePermMsg = (\n userEntityRef: string | undefined,\n result: AuthorizeResult,\n permission: Permission,\n) =>\n `${userEntityRef} is ${result} for permission '${permission.name}'${\n isResourcePermission(permission)\n ? `, resource type '${permission.resourceType}'`\n : ''\n } and action '${toPermissionAction(permission.attributes)}'`;\n\nexport class RBACPermissionPolicy implements PermissionPolicy {\n private readonly superUserList?: string[];\n\n public static async build(\n logger: LoggerService,\n auditLogger: AuditLogger,\n configApi: ConfigApi,\n conditionalStorage: ConditionalStorage,\n enforcerDelegate: EnforcerDelegate,\n roleMetadataStorage: RoleMetadataStorage,\n knex: Knex,\n pluginMetadataCollector: PluginPermissionMetadataCollector,\n auth: AuthService,\n ): Promise<RBACPermissionPolicy> {\n const superUserList: string[] = [];\n const adminUsers = configApi.getOptionalConfigArray(\n 'permission.rbac.admin.users',\n );\n\n const superUsers = configApi.getOptionalConfigArray(\n 'permission.rbac.admin.superUsers',\n );\n\n const policiesFile = configApi.getOptionalString(\n 'permission.rbac.policies-csv-file',\n );\n\n const allowReload =\n configApi.getOptionalBoolean('permission.rbac.policyFileReload') || false;\n\n const conditionalPoliciesFile = configApi.getOptionalString(\n 'permission.rbac.conditionalPoliciesFile',\n );\n\n if (superUsers && superUsers.length > 0) {\n for (const user of superUsers) {\n const userName = user.getString('name');\n superUserList.push(userName);\n }\n }\n\n await useAdminsFromConfig(\n adminUsers || [],\n enforcerDelegate,\n auditLogger,\n roleMetadataStorage,\n knex,\n );\n await setAdminPermissions(enforcerDelegate, auditLogger);\n\n if (\n (!adminUsers || adminUsers.length === 0) &&\n (!superUsers || superUsers.length === 0)\n ) {\n logger.warn(\n 'There are no admins or super admins configured for the RBAC-backend plugin.',\n );\n }\n\n const csvFile = new CSVFileWatcher(\n policiesFile,\n allowReload,\n logger,\n enforcerDelegate,\n roleMetadataStorage,\n auditLogger,\n );\n await csvFile.initialize();\n\n const conditionalFile = new YamlConditinalPoliciesFileWatcher(\n conditionalPoliciesFile,\n allowReload,\n logger,\n conditionalStorage,\n auditLogger,\n auth,\n pluginMetadataCollector,\n roleMetadataStorage,\n enforcerDelegate,\n );\n await conditionalFile.initialize();\n\n if (!conditionalPoliciesFile) {\n // clean up conditional policies corresponding to roles from csv file\n logger.info('conditional policies file feature was disabled');\n await conditionalFile.cleanUpConditionalPolicies();\n }\n if (!policiesFile) {\n // remove roles and policies from csv file\n logger.info('csv policies file feature was disabled');\n await csvFile.cleanUpRolesAndPolicies();\n }\n\n return new RBACPermissionPolicy(\n enforcerDelegate,\n auditLogger,\n conditionalStorage,\n superUserList,\n );\n }\n\n private constructor(\n private readonly enforcer: EnforcerDelegate,\n private readonly auditLogger: AuditLogger,\n private readonly conditionStorage: ConditionalStorage,\n superUserList?: string[],\n ) {\n this.superUserList = superUserList;\n }\n\n async handle(\n request: PolicyQuery,\n user?: PolicyQueryUser,\n ): Promise<PolicyDecision> {\n const userEntityRef = user?.info.userEntityRef ?? `user without entity`;\n\n let auditOptions = createPermissionEvaluationOptions(\n `Policy check for ${userEntityRef}`,\n userEntityRef,\n request,\n );\n this.auditLogger.auditLog(auditOptions);\n\n try {\n let status = false;\n\n const action = toPermissionAction(request.permission.attributes);\n if (!user) {\n const msg = evaluatePermMsg(\n userEntityRef,\n AuthorizeResult.DENY,\n request.permission,\n );\n auditOptions = createPermissionEvaluationOptions(\n msg,\n userEntityRef,\n request,\n { result: AuthorizeResult.DENY },\n );\n await this.auditLogger.auditLog(auditOptions);\n return { result: AuthorizeResult.DENY };\n }\n\n const permissionName = request.permission.name;\n 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 if (this.superUserList!.includes(userIdentity)) {\n return true;\n }\n\n return await this.enforcer.enforce(userIdentity, permission, action, roles);\n };\n\n private async handleConditions(\n userEntityRef: string,\n request: PolicyQuery,\n roles: string[],\n userInfo: BackstageUserInfo,\n ): Promise<PolicyDecision | undefined> {\n const permissionName = request.permission.name;\n const resourceType = (request.permission as ResourcePermission)\n .resourceType;\n const action = toPermissionAction(request.permission.attributes);\n\n const conditions: PermissionCriteria<\n PermissionCondition<string, PermissionRuleParams>\n >[] = [];\n let pluginId = '';\n for (const role of roles) {\n const conditionalDecisions = await this.conditionStorage.filterConditions(\n role,\n undefined,\n resourceType,\n [action],\n [permissionName],\n );\n\n if (conditionalDecisions.length === 1) {\n pluginId = conditionalDecisions[0].pluginId;\n conditions.push(conditionalDecisions[0].conditions);\n }\n\n // this error is unexpected and should not happen, but just in case handle it.\n if (conditionalDecisions.length > 1) {\n const msg = `Detected ${JSON.stringify(\n conditionalDecisions,\n )} collisions for conditional policies. Expected to find a stored single condition for permission with name ${permissionName}, resource type ${resourceType}, action ${action} for user ${userEntityRef}`;\n const auditOptions = createPermissionEvaluationOptions(\n msg,\n userEntityRef,\n request,\n { result: AuthorizeResult.DENY },\n );\n await this.auditLogger.auditLog(auditOptions);\n return {\n result: AuthorizeResult.DENY,\n };\n }\n }\n\n if (conditions.length > 0) {\n const result: ConditionalPolicyDecision = {\n pluginId,\n result: AuthorizeResult.CONDITIONAL,\n resourceType,\n conditions: {\n anyOf: conditions as NonEmptyArray<\n PermissionCriteria<\n PermissionCondition<string, PermissionRuleParams>\n >\n >,\n },\n };\n\n replaceAliases(result.conditions, userInfo);\n\n const msg = `Send condition to plugin with id ${pluginId} to evaluate permission ${permissionName} with resource type ${resourceType} and action ${action} for user ${userEntityRef}`;\n const auditOptions = createPermissionEvaluationOptions(\n msg,\n userEntityRef,\n request,\n result,\n );\n await this.auditLogger.auditLog(auditOptions);\n return result;\n }\n return undefined;\n }\n}\n"],"names":["isResourcePermission","toPermissionAction","useAdminsFromConfig","setAdminPermissions","CSVFileWatcher","YamlConditinalPoliciesFileWatcher","createPermissionEvaluationOptions","msg","AuthorizeResult","EvaluationEvents","EVALUATE_PERMISSION_ACCESS_STAGE","replaceAliases"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"permission-policy.cjs.js","sources":["../../src/policies/permission-policy.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type {\n AuthService,\n BackstageUserInfo,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport type { ConfigApi } from '@backstage/core-plugin-api';\nimport {\n AuthorizeResult,\n ConditionalPolicyDecision,\n isResourcePermission,\n Permission,\n PermissionCondition,\n PermissionCriteria,\n PermissionRuleParams,\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport type {\n PermissionPolicy,\n PolicyQuery,\n PolicyQueryUser,\n} from '@backstage/plugin-permission-node';\n\nimport type { AuditLogger } from '@janus-idp/backstage-plugin-audit-log-node';\nimport type { Knex } from 'knex';\n\nimport {\n NonEmptyArray,\n toPermissionAction,\n} from '@backstage-community/plugin-rbac-common';\n\nimport {\n setAdminPermissions,\n useAdminsFromConfig,\n} from '../admin-permissions/admin-creation';\nimport {\n createPermissionEvaluationOptions,\n EVALUATE_PERMISSION_ACCESS_STAGE,\n EvaluationEvents,\n} from '../audit-log/audit-logger';\nimport { replaceAliases } from '../conditional-aliases/alias-resolver';\nimport { ConditionalStorage } from '../database/conditional-storage';\nimport { RoleMetadataStorage } from '../database/role-metadata';\nimport { CSVFileWatcher } from '../file-permissions/csv-file-watcher';\nimport { YamlConditinalPoliciesFileWatcher } from '../file-permissions/yaml-conditional-file-watcher';\nimport { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from '../service/plugin-endpoints';\n\nconst evaluatePermMsg = (\n userEntityRef: string | undefined,\n result: AuthorizeResult,\n permission: Permission,\n) =>\n `${userEntityRef} is ${result} for permission '${permission.name}'${\n isResourcePermission(permission)\n ? `, resource type '${permission.resourceType}'`\n : ''\n } and action '${toPermissionAction(permission.attributes)}'`;\n\nexport class RBACPermissionPolicy implements PermissionPolicy {\n private readonly superUserList?: string[];\n\n public static async build(\n logger: LoggerService,\n auditLogger: AuditLogger,\n configApi: ConfigApi,\n conditionalStorage: ConditionalStorage,\n enforcerDelegate: EnforcerDelegate,\n roleMetadataStorage: RoleMetadataStorage,\n knex: Knex,\n pluginMetadataCollector: PluginPermissionMetadataCollector,\n auth: AuthService,\n ): Promise<RBACPermissionPolicy> {\n const superUserList: string[] = [];\n const adminUsers = configApi.getOptionalConfigArray(\n 'permission.rbac.admin.users',\n );\n\n const superUsers = configApi.getOptionalConfigArray(\n 'permission.rbac.admin.superUsers',\n );\n\n const policiesFile = configApi.getOptionalString(\n 'permission.rbac.policies-csv-file',\n );\n\n const allowReload =\n configApi.getOptionalBoolean('permission.rbac.policyFileReload') || false;\n\n const conditionalPoliciesFile = configApi.getOptionalString(\n 'permission.rbac.conditionalPoliciesFile',\n );\n\n if (superUsers && superUsers.length > 0) {\n for (const user of superUsers) {\n const userName = user.getString('name');\n superUserList.push(userName);\n }\n }\n\n await useAdminsFromConfig(\n adminUsers || [],\n enforcerDelegate,\n auditLogger,\n roleMetadataStorage,\n knex,\n );\n await setAdminPermissions(enforcerDelegate, auditLogger);\n\n if (\n (!adminUsers || adminUsers.length === 0) &&\n (!superUsers || superUsers.length === 0)\n ) {\n logger.warn(\n 'There are no admins or super admins configured for the RBAC-backend plugin.',\n );\n }\n\n const csvFile = new CSVFileWatcher(\n policiesFile,\n allowReload,\n logger,\n enforcerDelegate,\n roleMetadataStorage,\n auditLogger,\n );\n await csvFile.initialize();\n\n const conditionalFile = new YamlConditinalPoliciesFileWatcher(\n conditionalPoliciesFile,\n allowReload,\n logger,\n conditionalStorage,\n auditLogger,\n auth,\n pluginMetadataCollector,\n roleMetadataStorage,\n enforcerDelegate,\n );\n await conditionalFile.initialize();\n\n if (!conditionalPoliciesFile) {\n // clean up conditional policies corresponding to roles from csv file\n logger.info('conditional policies file feature was disabled');\n await conditionalFile.cleanUpConditionalPolicies();\n }\n if (!policiesFile) {\n // remove roles and policies from csv file\n logger.info('csv policies file feature was disabled');\n await csvFile.cleanUpRolesAndPolicies();\n }\n\n return new RBACPermissionPolicy(\n enforcerDelegate,\n auditLogger,\n conditionalStorage,\n superUserList,\n );\n }\n\n private constructor(\n private readonly enforcer: EnforcerDelegate,\n private readonly auditLogger: AuditLogger,\n private readonly conditionStorage: ConditionalStorage,\n superUserList?: string[],\n ) {\n this.superUserList = superUserList;\n }\n\n async handle(\n request: PolicyQuery,\n user?: PolicyQueryUser,\n ): Promise<PolicyDecision> {\n const userEntityRef = user?.info.userEntityRef ?? `user without entity`;\n\n let auditOptions = createPermissionEvaluationOptions(\n `Policy check for ${userEntityRef}`,\n userEntityRef,\n request,\n );\n 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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"}
|
|
@@ -101,7 +101,7 @@ class AncestorSearchMemo {
|
|
|
101
101
|
if (!memo.hasEntityRef(groupName)) {
|
|
102
102
|
memo.setNode(groupName);
|
|
103
103
|
}
|
|
104
|
-
if (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 !==
|
|
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":";;;;AAiCO,MAAM,kBAAmB,CAAA;AAAA,EACtB,KAAA;AAAA,EAEA,UAAA;AAAA,EACA,eAAA;AAAA,EACA,IAAA;AAAA,EAEA,aAAA;AAAA,EACA,QAAA;AAAA,EAER,WACE,CAAA,aAAA,EACA,UACA,EAAA,eAAA,EACA,MACA,QACA,EAAA;AACA,IAAA,IAAA,CAAK,QAAQ,IAAIA,cAAA,CAAM,EAAE,QAAA,EAAU,MAAM,CAAA;AACzC,IAAA,IAAA,CAAK,aAAgB,GAAA,aAAA;AACrB,IAAA,IAAA,CAAK,UAAa,GAAA,UAAA;AAClB,IAAA,IAAA,CAAK,eAAkB,GAAA,eAAA;AACvB,IAAA,IAAA,CAAK,IAAO,GAAA,IAAA;AACZ,IAAA,IAAA,CAAK,QAAW,GAAA,QAAA;AAAA;AAClB,EAEA,SAAqB,GAAA;AACnB,IAAO,OAAAC,YAAA,CAAI,SAAU,CAAA,IAAA,CAAK,KAAK,CAAA;AAAA;AACjC,EAEA,UAAyB,GAAA;AACvB,IAAO,OAAAA,YAAA,CAAI,UAAW,CAAA,IAAA,CAAK,KAAK,CAAA;AAAA;AAClC,EAEA,OAAA,CAAQ,iBAAyB,cAAwB,EAAA;AACvD,IAAK,IAAA,CAAA,KAAA,CAAM,OAAQ,CAAA,eAAA,EAAiB,cAAc,CAAA;AAAA;AACpD,EAEA,QAAQ,SAAyB,EAAA;AAC/B,IAAK,IAAA,CAAA,KAAA,CAAM,QAAQ,SAAS,CAAA;AAAA;AAC9B,EAEA,aAAa,QAA2B,EAAA;AACtC,IAAO,OAAA,IAAA,CAAK,KAAM,CAAA,OAAA,CAAQ,QAAQ,CAAA;AAAA;AACpC,EAEA,kBAAA,CAAmB,QAAuB,UAA0B,EAAA;AAClE,IAAO,MAAA,CAAA,KAAA;AAAA,MACL,CAAA,gBAAA,EAAmB,KAAK,SAAU,CAAA,IAAA,CAAK,MAAM,KAAM,EAAC,CAAC,CAAA,KAAA,EAAQ,UAAU,CAAA;AAAA,KACzE;AACA,IAAO,MAAA,CAAA,KAAA;AAAA,MACL,CAAA,gBAAA,EAAmB,KAAK,SAAU,CAAA,IAAA,CAAK,MAAM,KAAM,EAAC,CAAC,CAAA,KAAA,EAAQ,UAAU,CAAA;AAAA,KACzE;AAAA;AACF,EAEA,QAAqB,GAAA;AACnB,IAAO,OAAA,IAAA,CAAK,MAAM,KAAM,EAAA;AAAA;AAC1B,EAEA,MAAM,sBAA2C,GAAA;AAC/C,IAAI,IAAA;AACF,MAAA,OAAO,MAAM,IAAA,CAAK,eAAgB,CAAA,MAAA,CAAO,SAAS,WAAW,CAAA;AAAA,aACtD,KAAO,EAAA;AACd,MAAO,OAAA,KAAA;AAAA;AACT;AACF,EAEA,MAAM,YAAoC,GAAA;AACxC,IAAA,MAAM,EAAE,KAAM,EAAA,GAAI,MAAM,IAAA,CAAK,KAAK,qBAAsB,CAAA;AAAA,MACtD,UAAY,EAAA,MAAM,IAAK,CAAA,IAAA,CAAK,wBAAyB,EAAA;AAAA,MACrD,cAAgB,EAAA;AAAA,KACjB,CAAA;AAED,IAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,UAAW,CAAA,WAAA;AAAA,MACtC;AAAA,QACE,MAAA,EAAQ,EAAE,IAAA,EAAM,OAAQ,EAAA;AAAA,QACxB,MAAQ,EAAA,CAAC,eAAiB,EAAA,oBAAA,EAAsB,aAAa;AAAA,OAC/D;AAAA,MACA,EAAE,KAAM;AAAA,KACV;AACA,IAAO,OAAA,KAAA;AAAA;AACT,EAEA,MAAM,eAAuC,GAAA;AAC3C,IAAI,IAAA;AACF,MAAA,MAAM,IAAO,GAAA,MAAM,IAAK,CAAA,eAAA,CAAgB,WAAW,CAAA,CAChD,MAAO,CAAA,mBAAA,EAAqB,mBAAmB,CAAA,CAC/C,KAAM,CAAA,MAAA,EAAQ,SAAS,CAAA;AAC1B,MAAO,OAAA,IAAA;AAAA,aACA,KAAO,EAAA;AACd,MAAA,OAAO,EAAC;AAAA;AACV;AACF,EAEA,MAAM,aAAqC,GAAA;AACzC,IAAA,MAAM,EAAE,KAAM,EAAA,GAAI,MAAM,IAAA,CAAK,KAAK,qBAAsB,CAAA;AAAA,MACtD,UAAY,EAAA,MAAM,IAAK,CAAA,IAAA,CAAK,wBAAyB,EAAA;AAAA,MACrD,cAAgB,EAAA;AAAA,KACjB,CAAA;AACD,IAAA,MAAM,EAAE,KAAA,EAAU,GAAA,MAAM,KAAK,UAAW,CAAA,WAAA;AAAA,MACtC;AAAA,QACE,QAAQ,EAAE,IAAA,EAAM,OAAS,EAAA,qBAAA,EAAuB,KAAK,aAAc,EAAA;AAAA,QACnE,MAAQ,EAAA,CAAC,eAAiB,EAAA,oBAAA,EAAsB,aAAa;AAAA,OAC/D;AAAA,MACA,EAAE,KAAM;AAAA,KACV;AACA,IAAO,OAAA,KAAA;AAAA;AACT,EAEA,MAAM,gBAAwC,GAAA;AAC5C,IAAI,IAAA;AACF,MAAA,MAAM,OAAO,MAAM,IAAA,CAAK,eAAgB,CAAA,WAAW,EAChD,MAAO,CAAA,mBAAA,EAAqB,mBAAmB,CAAA,CAC/C,MAAM,EAAE,IAAA,EAAM,YAAY,iBAAmB,EAAA,IAAA,CAAK,eAAe,CAAA;AACpE,MAAO,OAAA,IAAA;AAAA,aACA,KAAO,EAAA;AACd,MAAA,OAAO,EAAC;AAAA;AACV;AACF,EAEA,cACE,CAAA,IAAA,EACA,KACA,EAAA,SAAA,EACA,aACA,EAAA;AACA,IAAA,MAAM,SAAY,GAAA,CAAA,MAAA,EAAS,KAAM,CAAA,QAAA,CAAS,SAAW,EAAA,iBAAA;AAAA,MACnD;AAAA,KACD,CAAI,CAAA,EAAA,KAAA,CAAM,SAAS,IAAK,CAAA,iBAAA,CAAkB,OAAO,CAAC,CAAA,CAAA;AACnD,IAAA,IAAI,CAAC,IAAA,CAAK,YAAa,CAAA,SAAS,CAAG,EAAA;AACjC,MAAA,IAAA,CAAK,QAAQ,SAAS,CAAA;AAAA;AAGxB,IAAA,IAAI,IAAK,CAAA,QAAA,KAAa,KAAa,CAAA,IAAA,aAAA,IAAiB,KAAK,QAAU,EAAA;AACjE,MAAA;AAAA;AAEF,IAAA,MAAM,QAAQ,aAAgB,GAAA,CAAA;AAE9B,IAAM,MAAA,MAAA,GAAS,MAAM,IAAM,EAAA,MAAA;AAC3B,IAAA,MAAM,cAAc,SAAU,CAAA,IAAA,CAAK,OAAK,CAAE,CAAA,QAAA,CAAS,SAAS,MAAM,CAAA;AAElE,IAAA,IAAI,WAAa,EAAA;AACf,MAAA,MAAM,UAAa,GAAA,CAAA,MAAA,EAAS,KAAM,CAAA,QAAA,CAAS,SAAW,EAAA,iBAAA;AAAA,QACpD;AAAA,OACD,CAAI,CAAA,EAAA,WAAA,CAAY,SAAS,IAAK,CAAA,iBAAA,CAAkB,OAAO,CAAC,CAAA,CAAA;AACzD,MAAK,IAAA,CAAA,OAAA,CAAQ,YAAY,SAAS,CAAA;AAElC,MAAI,IAAA,IAAA,CAAK,WAAa,EAAA;AACpB,QAAA,IAAA,CAAK,cAAe,CAAA,IAAA,EAAM,WAAa,EAAA,SAAA,EAAW,KAAK,CAAA;AAAA;AACzD;AACF;AACF,EAEA,iBACE,CAAA,IAAA,EACA,QACA,EAAA,YAAA,EACA,aACA,EAAA;AAEA,IAAA,IAAI,KAAK,QAAa,KAAA,KAAA,CAAA,IAAa,aAAiB,IAAA,IAAA,CAAK,WAAW,CAAG,EAAA;AACrE,MAAA;AAAA;AAEF,IAAA,MAAM,QAAQ,aAAgB,GAAA,CAAA;AAE9B,IAAA,IAAI,CAAC,IAAA,CAAK,YAAa,CAAA,QAAA,CAAS,iBAAiB,CAAG,EAAA;AAClD,MAAK,IAAA,CAAA,OAAA,CAAQ,SAAS,iBAAiB,CAAA;AAAA;AAGzC,IAAA,IAAA,CAAK,OAAQ,CAAA,QAAA,CAAS,iBAAmB,EAAA,QAAA,CAAS,iBAAiB,CAAA;AAEnE,IAAA,MAAM,cAAc,YAAa,CAAA,IAAA;AAAA,MAC/B,CAAA,CAAA,KAAK,CAAE,CAAA,iBAAA,KAAsB,QAAS,CAAA;AAAA,KACxC;AAEA,IAAI,IAAA,WAAA,IAAe,IAAK,CAAA,SAAA,EAAa,EAAA;AACnC,MAAA,IAAA,CAAK,iBAAkB,CAAA,IAAA,EAAM,WAAa,EAAA,YAAA,EAAc,KAAK,CAAA;AAAA;AAC/D;AACF,EAEA,MAAM,eAAe,IAA0B,EAAA;AAC7C,IAAI,IAAA,MAAM,IAAK,CAAA,sBAAA,EAA0B,EAAA;AACvC,MAAM,MAAA,aAAA,GAAgB,MAAM,IAAA,CAAK,gBAAiB,EAAA;AAClD,MAAM,MAAA,YAAA,GAAe,MAAM,IAAA,CAAK,eAAgB,EAAA;AAChD,MAAc,aAAA,CAAA,OAAA;AAAA,QAAQ,WACpB,IAAK,CAAA,iBAAA;AAAA,UACH,IAAA;AAAA,UACA,KAAA;AAAA,UACA,YAAA;AAAA,UACA;AAAA;AACF,OACF;AAAA,KACK,MAAA;AACL,MAAM,MAAA,UAAA,GAAa,MAAM,IAAA,CAAK,aAAc,EAAA;AAC5C,MAAM,MAAA,SAAA,GAAY,MAAM,IAAA,CAAK,YAAa,EAAA;AAC1C,MAAW,UAAA,CAAA,OAAA;AAAA,QAAQ,WACjB,IAAK,CAAA,cAAA,CAAe,IAAM,EAAA,KAAA,EAAiB,WAAuB,CAAC;AAAA,OACrE;AAAA;AACF;AAEJ;;;;"}
|
|
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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"}
|
|
@@ -15,7 +15,7 @@ class BackstageRoleManager {
|
|
|
15
15
|
this.allRoles = /* @__PURE__ */ new Map();
|
|
16
16
|
const rbacConfig = this.config.getOptionalConfig("permission.rbac");
|
|
17
17
|
this.maxDepth = rbacConfig?.getOptionalNumber("maxDepth");
|
|
18
|
-
if (this.maxDepth !==
|
|
18
|
+
if (this.maxDepth !== undefined && this.maxDepth < 0) {
|
|
19
19
|
throw new Error(
|
|
20
20
|
"Max Depth for RBAC group hierarchy must be greater than or equal to zero"
|
|
21
21
|
);
|
|
@@ -255,7 +255,7 @@ class BackstageRoleManager {
|
|
|
255
255
|
if (currentRole && currentRole.hasMember(name1)) {
|
|
256
256
|
return true;
|
|
257
257
|
}
|
|
258
|
-
return
|
|
258
|
+
return undefined;
|
|
259
259
|
}
|
|
260
260
|
/**
|
|
261
261
|
* hasMember checks if the members from a particular role is associated with the user
|
|
@@ -265,7 +265,7 @@ class BackstageRoleManager {
|
|
|
265
265
|
* @returns True if a member from the role is also associated with the user.
|
|
266
266
|
*/
|
|
267
267
|
hasMember(role, memo) {
|
|
268
|
-
if (role ===
|
|
268
|
+
if (role === undefined) {
|
|
269
269
|
return false;
|
|
270
270
|
}
|
|
271
271
|
for (const member of role.getMembers()) {
|