@backstage-community/plugin-rbac-backend 6.2.2 → 6.2.4
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 +12 -0
- package/dist/role-manager/ancestor-search-factory.cjs.js +26 -0
- package/dist/role-manager/ancestor-search-factory.cjs.js.map +1 -0
- package/dist/role-manager/ancestor-search-memo-pg.cjs.js +54 -0
- package/dist/role-manager/ancestor-search-memo-pg.cjs.js.map +1 -0
- package/dist/role-manager/ancestor-search-memo-sqlite.cjs.js +74 -0
- package/dist/role-manager/ancestor-search-memo-sqlite.cjs.js.map +1 -0
- package/dist/role-manager/ancestor-search-memo.cjs.js +1 -121
- package/dist/role-manager/ancestor-search-memo.cjs.js.map +1 -1
- package/dist/role-manager/role-manager.cjs.js +7 -5
- package/dist/role-manager/role-manager.cjs.js.map +1 -1
- package/dist/service/policies-rest-api.cjs.js +29 -113
- package/dist/service/policies-rest-api.cjs.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
### Dependencies
|
|
2
2
|
|
|
3
|
+
## 6.2.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 298b1d4: Avoid unnecessary query to check 'relations' table in the role manager
|
|
8
|
+
|
|
9
|
+
## 6.2.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 9436665: Reduce rbac-backend requests to credentials API.
|
|
14
|
+
|
|
3
15
|
## 6.2.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ancestorSearchMemoPg = require('./ancestor-search-memo-pg.cjs.js');
|
|
4
|
+
var ancestorSearchMemoSqlite = require('./ancestor-search-memo-sqlite.cjs.js');
|
|
5
|
+
|
|
6
|
+
class AncestorSearchFactory {
|
|
7
|
+
static async createAncestorSearchMemo(userEntityRef, config, catalogAPI, catalogDBClient, authService, maxDepth) {
|
|
8
|
+
const databaseConfig = config.getOptionalConfig("backend.database");
|
|
9
|
+
const client = databaseConfig?.getOptionalString("client");
|
|
10
|
+
if (client === "pg") {
|
|
11
|
+
return new ancestorSearchMemoPg.AncestorSearchMemoPG(userEntityRef, catalogDBClient, maxDepth);
|
|
12
|
+
}
|
|
13
|
+
if (client === "better-sqlite3") {
|
|
14
|
+
return new ancestorSearchMemoSqlite.AncestorSearchMemoSQLite(
|
|
15
|
+
userEntityRef,
|
|
16
|
+
catalogAPI,
|
|
17
|
+
authService,
|
|
18
|
+
maxDepth
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`Unsupported database: ${client}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
exports.AncestorSearchFactory = AncestorSearchFactory;
|
|
26
|
+
//# sourceMappingURL=ancestor-search-factory.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ancestor-search-factory.cjs.js","sources":["../../src/role-manager/ancestor-search-factory.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Knex } from 'knex';\nimport { AncestorSearchMemo, ASMGroup } from './ancestor-search-memo';\nimport { AncestorSearchMemoPG } from './ancestor-search-memo-pg';\nimport { AncestorSearchMemoSQLite } from './ancestor-search-memo-sqlite';\nimport type { AuthService } from '@backstage/backend-plugin-api';\nimport type { CatalogApi } from '@backstage/catalog-client';\nimport type { Config } from '@backstage/config';\n\nexport class AncestorSearchFactory {\n static async createAncestorSearchMemo(\n userEntityRef: string,\n config: Config,\n catalogAPI: CatalogApi,\n catalogDBClient: Knex,\n authService: AuthService,\n maxDepth?: number,\n ): Promise<AncestorSearchMemo<ASMGroup>> {\n const databaseConfig = config.getOptionalConfig('backend.database');\n const client = databaseConfig?.getOptionalString('client');\n\n if (client === 'pg') {\n return new AncestorSearchMemoPG(userEntityRef, catalogDBClient, maxDepth);\n }\n\n if (client === 'better-sqlite3') {\n return new AncestorSearchMemoSQLite(\n userEntityRef,\n catalogAPI,\n authService,\n maxDepth,\n );\n }\n\n throw new Error(`Unsupported database: ${client}`);\n }\n}\n"],"names":["AncestorSearchMemoPG","AncestorSearchMemoSQLite"],"mappings":";;;;;AAuBO,MAAM,qBAAsB,CAAA;AAAA,EACjC,aAAa,wBACX,CAAA,aAAA,EACA,QACA,UACA,EAAA,eAAA,EACA,aACA,QACuC,EAAA;AACvC,IAAM,MAAA,cAAA,GAAiB,MAAO,CAAA,iBAAA,CAAkB,kBAAkB,CAAA;AAClE,IAAM,MAAA,MAAA,GAAS,cAAgB,EAAA,iBAAA,CAAkB,QAAQ,CAAA;AAEzD,IAAA,IAAI,WAAW,IAAM,EAAA;AACnB,MAAA,OAAO,IAAIA,yCAAA,CAAqB,aAAe,EAAA,eAAA,EAAiB,QAAQ,CAAA;AAAA;AAG1E,IAAA,IAAI,WAAW,gBAAkB,EAAA;AAC/B,MAAA,OAAO,IAAIC,iDAAA;AAAA,QACT,aAAA;AAAA,QACA,UAAA;AAAA,QACA,WAAA;AAAA,QACA;AAAA,OACF;AAAA;AAGF,IAAA,MAAM,IAAI,KAAA,CAAM,CAAyB,sBAAA,EAAA,MAAM,CAAE,CAAA,CAAA;AAAA;AAErD;;;;"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ancestorSearchMemo = require('./ancestor-search-memo.cjs.js');
|
|
4
|
+
|
|
5
|
+
class AncestorSearchMemoPG extends ancestorSearchMemo.AncestorSearchMemo {
|
|
6
|
+
constructor(userEntityRef, catalogDBClient, maxDepth) {
|
|
7
|
+
super();
|
|
8
|
+
this.userEntityRef = userEntityRef;
|
|
9
|
+
this.catalogDBClient = catalogDBClient;
|
|
10
|
+
this.maxDepth = maxDepth;
|
|
11
|
+
}
|
|
12
|
+
async getAllASMGroups() {
|
|
13
|
+
try {
|
|
14
|
+
const rows = await this.catalogDBClient("relations").select("source_entity_ref", "target_entity_ref").where("type", "childOf");
|
|
15
|
+
return rows;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async getUserASMGroups() {
|
|
21
|
+
try {
|
|
22
|
+
const rows = await this.catalogDBClient("relations").select("source_entity_ref", "target_entity_ref").where({ type: "memberOf", source_entity_ref: this.userEntityRef });
|
|
23
|
+
return rows;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
traverse(relation, allRelations, current_depth) {
|
|
29
|
+
if (this.maxDepth !== undefined && current_depth >= this.maxDepth + 1) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const depth = current_depth + 1;
|
|
33
|
+
if (!super.hasEntityRef(relation.source_entity_ref)) {
|
|
34
|
+
super.setNode(relation.source_entity_ref);
|
|
35
|
+
}
|
|
36
|
+
super.setEdge(relation.target_entity_ref, relation.source_entity_ref);
|
|
37
|
+
const parentGroup = allRelations.find(
|
|
38
|
+
(g) => g.source_entity_ref === relation.target_entity_ref
|
|
39
|
+
);
|
|
40
|
+
if (parentGroup && super.isAcyclic()) {
|
|
41
|
+
this.traverse(parentGroup, allRelations, depth);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async buildUserGraph() {
|
|
45
|
+
const userRelations = await this.getUserASMGroups();
|
|
46
|
+
const allRelations = await this.getAllASMGroups();
|
|
47
|
+
userRelations.forEach(
|
|
48
|
+
(group) => this.traverse(group, allRelations, 0)
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
exports.AncestorSearchMemoPG = AncestorSearchMemoPG;
|
|
54
|
+
//# sourceMappingURL=ancestor-search-memo-pg.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ancestor-search-memo-pg.cjs.js","sources":["../../src/role-manager/ancestor-search-memo-pg.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 */\n\nimport { Knex } from 'knex';\nimport { AncestorSearchMemo, Relation } from './ancestor-search-memo';\n\nexport class AncestorSearchMemoPG extends AncestorSearchMemo<Relation> {\n constructor(\n private readonly userEntityRef: string,\n private readonly catalogDBClient: Knex,\n private readonly maxDepth?: number,\n ) {\n super();\n }\n\n async getAllASMGroups(): Promise<Relation[]> {\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 getUserASMGroups(): Promise<Relation[]> {\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 traverse(\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 (!super.hasEntityRef(relation.source_entity_ref)) {\n super.setNode(relation.source_entity_ref);\n }\n\n super.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 && super.isAcyclic()) {\n this.traverse(parentGroup, allRelations, depth);\n }\n }\n\n async buildUserGraph() {\n const userRelations = await this.getUserASMGroups();\n const allRelations = await this.getAllASMGroups();\n userRelations.forEach(group =>\n this.traverse(group as Relation, allRelations as Relation[], 0),\n );\n }\n}\n"],"names":["AncestorSearchMemo"],"mappings":";;;;AAmBO,MAAM,6BAA6BA,qCAA6B,CAAA;AAAA,EACrE,WAAA,CACmB,aACA,EAAA,eAAA,EACA,QACjB,EAAA;AACA,IAAM,KAAA,EAAA;AAJW,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA;AACA,IAAA,IAAA,CAAA,eAAA,GAAA,eAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA;AAGnB,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,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,QAAA,CACE,QACA,EAAA,YAAA,EACA,aACA,EAAA;AAEA,IAAA,IAAI,KAAK,QAAa,KAAA,SAAA,IAAa,aAAiB,IAAA,IAAA,CAAK,WAAW,CAAG,EAAA;AACrE,MAAA;AAAA;AAEF,IAAA,MAAM,QAAQ,aAAgB,GAAA,CAAA;AAE9B,IAAA,IAAI,CAAC,KAAA,CAAM,YAAa,CAAA,QAAA,CAAS,iBAAiB,CAAG,EAAA;AACnD,MAAM,KAAA,CAAA,OAAA,CAAQ,SAAS,iBAAiB,CAAA;AAAA;AAG1C,IAAA,KAAA,CAAM,OAAQ,CAAA,QAAA,CAAS,iBAAmB,EAAA,QAAA,CAAS,iBAAiB,CAAA;AAEpE,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,KAAM,CAAA,SAAA,EAAa,EAAA;AACpC,MAAK,IAAA,CAAA,QAAA,CAAS,WAAa,EAAA,YAAA,EAAc,KAAK,CAAA;AAAA;AAChD;AACF,EAEA,MAAM,cAAiB,GAAA;AACrB,IAAM,MAAA,aAAA,GAAgB,MAAM,IAAA,CAAK,gBAAiB,EAAA;AAClD,IAAM,MAAA,YAAA,GAAe,MAAM,IAAA,CAAK,eAAgB,EAAA;AAChD,IAAc,aAAA,CAAA,OAAA;AAAA,MAAQ,CACpB,KAAA,KAAA,IAAA,CAAK,QAAS,CAAA,KAAA,EAAmB,cAA4B,CAAC;AAAA,KAChE;AAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ancestorSearchMemo = require('./ancestor-search-memo.cjs.js');
|
|
4
|
+
|
|
5
|
+
class AncestorSearchMemoSQLite extends ancestorSearchMemo.AncestorSearchMemo {
|
|
6
|
+
constructor(userEntityRef, catalogApi, auth, maxDepth) {
|
|
7
|
+
super();
|
|
8
|
+
this.userEntityRef = userEntityRef;
|
|
9
|
+
this.catalogApi = catalogApi;
|
|
10
|
+
this.auth = auth;
|
|
11
|
+
this.maxDepth = maxDepth;
|
|
12
|
+
}
|
|
13
|
+
async getAllASMGroups() {
|
|
14
|
+
const { token } = await this.auth.getPluginRequestToken({
|
|
15
|
+
onBehalfOf: await this.auth.getOwnServiceCredentials(),
|
|
16
|
+
targetPluginId: "catalog"
|
|
17
|
+
});
|
|
18
|
+
const { items } = await this.catalogApi.getEntities(
|
|
19
|
+
{
|
|
20
|
+
filter: { kind: "Group" },
|
|
21
|
+
fields: ["metadata.name", "metadata.namespace", "spec.parent"]
|
|
22
|
+
},
|
|
23
|
+
{ token }
|
|
24
|
+
);
|
|
25
|
+
return items;
|
|
26
|
+
}
|
|
27
|
+
async getUserASMGroups() {
|
|
28
|
+
const { token } = await this.auth.getPluginRequestToken({
|
|
29
|
+
onBehalfOf: await this.auth.getOwnServiceCredentials(),
|
|
30
|
+
targetPluginId: "catalog"
|
|
31
|
+
});
|
|
32
|
+
const { items } = await this.catalogApi.getEntities(
|
|
33
|
+
{
|
|
34
|
+
filter: { kind: "Group", "relations.hasMember": this.userEntityRef },
|
|
35
|
+
fields: ["metadata.name", "metadata.namespace", "spec.parent"]
|
|
36
|
+
},
|
|
37
|
+
{ token }
|
|
38
|
+
);
|
|
39
|
+
return items;
|
|
40
|
+
}
|
|
41
|
+
traverse(group, allGroups, current_depth) {
|
|
42
|
+
const groupName = `group:${group.metadata.namespace?.toLocaleLowerCase(
|
|
43
|
+
"en-US"
|
|
44
|
+
)}/${group.metadata.name.toLocaleLowerCase("en-US")}`;
|
|
45
|
+
if (!super.hasEntityRef(groupName)) {
|
|
46
|
+
super.setNode(groupName);
|
|
47
|
+
}
|
|
48
|
+
if (this.maxDepth !== undefined && current_depth >= this.maxDepth) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const depth = current_depth + 1;
|
|
52
|
+
const parent = group.spec?.parent;
|
|
53
|
+
const parentGroup = allGroups.find((g) => g.metadata.name === parent);
|
|
54
|
+
if (parentGroup) {
|
|
55
|
+
const parentName = `group:${group.metadata.namespace?.toLocaleLowerCase(
|
|
56
|
+
"en-US"
|
|
57
|
+
)}/${parentGroup.metadata.name.toLocaleLowerCase("en-US")}`;
|
|
58
|
+
super.setEdge(parentName, groupName);
|
|
59
|
+
if (super.isAcyclic()) {
|
|
60
|
+
this.traverse(parentGroup, allGroups, depth);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async buildUserGraph() {
|
|
65
|
+
const userGroups = await this.getUserASMGroups();
|
|
66
|
+
const allGroups = await this.getAllASMGroups();
|
|
67
|
+
userGroups.forEach(
|
|
68
|
+
(group) => this.traverse(group, allGroups, 0)
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
exports.AncestorSearchMemoSQLite = AncestorSearchMemoSQLite;
|
|
74
|
+
//# sourceMappingURL=ancestor-search-memo-sqlite.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ancestor-search-memo-sqlite.cjs.js","sources":["../../src/role-manager/ancestor-search-memo-sqlite.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 } from '@backstage/backend-plugin-api';\nimport type { CatalogApi } from '@backstage/catalog-client';\nimport type { Entity } from '@backstage/catalog-model';\n\nimport { AncestorSearchMemo } from './ancestor-search-memo';\n\nexport class AncestorSearchMemoSQLite extends AncestorSearchMemo<Entity> {\n constructor(\n private readonly userEntityRef: string,\n private readonly catalogApi: CatalogApi,\n private readonly auth: AuthService,\n private readonly maxDepth?: number,\n ) {\n super();\n }\n\n async getAllASMGroups(): Promise<Entity[]> {\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 getUserASMGroups(): Promise<Entity[]> {\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 traverse(group: Entity, allGroups: Entity[], current_depth: number) {\n const groupName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${group.metadata.name.toLocaleLowerCase('en-US')}`;\n if (!super.hasEntityRef(groupName)) {\n super.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 super.setEdge(parentName, groupName);\n\n if (super.isAcyclic()) {\n this.traverse(parentGroup, allGroups, depth);\n }\n }\n }\n\n async buildUserGraph() {\n const userGroups = await this.getUserASMGroups();\n const allGroups = await this.getAllASMGroups();\n userGroups.forEach(group =>\n this.traverse(group as Entity, allGroups as Entity[], 0),\n );\n }\n}\n"],"names":["AncestorSearchMemo"],"mappings":"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"}
|
|
@@ -4,18 +4,8 @@ var graphlib = require('@dagrejs/graphlib');
|
|
|
4
4
|
|
|
5
5
|
class AncestorSearchMemo {
|
|
6
6
|
graph;
|
|
7
|
-
|
|
8
|
-
catalogDBClient;
|
|
9
|
-
auth;
|
|
10
|
-
userEntityRef;
|
|
11
|
-
maxDepth;
|
|
12
|
-
constructor(userEntityRef, catalogApi, catalogDBClient, auth, maxDepth) {
|
|
7
|
+
constructor() {
|
|
13
8
|
this.graph = new graphlib.Graph({ directed: true });
|
|
14
|
-
this.userEntityRef = userEntityRef;
|
|
15
|
-
this.catalogApi = catalogApi;
|
|
16
|
-
this.catalogDBClient = catalogDBClient;
|
|
17
|
-
this.auth = auth;
|
|
18
|
-
this.maxDepth = maxDepth;
|
|
19
9
|
}
|
|
20
10
|
isAcyclic() {
|
|
21
11
|
return graphlib.alg.isAcyclic(this.graph);
|
|
@@ -43,116 +33,6 @@ class AncestorSearchMemo {
|
|
|
43
33
|
getNodes() {
|
|
44
34
|
return this.graph.nodes();
|
|
45
35
|
}
|
|
46
|
-
async doesRelationTableExist() {
|
|
47
|
-
try {
|
|
48
|
-
return await this.catalogDBClient.schema.hasTable("relations");
|
|
49
|
-
} catch (error) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
async getAllGroups() {
|
|
54
|
-
const { token } = await this.auth.getPluginRequestToken({
|
|
55
|
-
onBehalfOf: await this.auth.getOwnServiceCredentials(),
|
|
56
|
-
targetPluginId: "catalog"
|
|
57
|
-
});
|
|
58
|
-
const { items } = await this.catalogApi.getEntities(
|
|
59
|
-
{
|
|
60
|
-
filter: { kind: "Group" },
|
|
61
|
-
fields: ["metadata.name", "metadata.namespace", "spec.parent"]
|
|
62
|
-
},
|
|
63
|
-
{ token }
|
|
64
|
-
);
|
|
65
|
-
return items;
|
|
66
|
-
}
|
|
67
|
-
async getAllRelations() {
|
|
68
|
-
try {
|
|
69
|
-
const rows = await this.catalogDBClient("relations").select("source_entity_ref", "target_entity_ref").where("type", "childOf");
|
|
70
|
-
return rows;
|
|
71
|
-
} catch (error) {
|
|
72
|
-
return [];
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
async getUserGroups() {
|
|
76
|
-
const { token } = await this.auth.getPluginRequestToken({
|
|
77
|
-
onBehalfOf: await this.auth.getOwnServiceCredentials(),
|
|
78
|
-
targetPluginId: "catalog"
|
|
79
|
-
});
|
|
80
|
-
const { items } = await this.catalogApi.getEntities(
|
|
81
|
-
{
|
|
82
|
-
filter: { kind: "Group", "relations.hasMember": this.userEntityRef },
|
|
83
|
-
fields: ["metadata.name", "metadata.namespace", "spec.parent"]
|
|
84
|
-
},
|
|
85
|
-
{ token }
|
|
86
|
-
);
|
|
87
|
-
return items;
|
|
88
|
-
}
|
|
89
|
-
async getUserRelations() {
|
|
90
|
-
try {
|
|
91
|
-
const rows = await this.catalogDBClient("relations").select("source_entity_ref", "target_entity_ref").where({ type: "memberOf", source_entity_ref: this.userEntityRef });
|
|
92
|
-
return rows;
|
|
93
|
-
} catch (error) {
|
|
94
|
-
return [];
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
traverseGroups(memo, group, allGroups, current_depth) {
|
|
98
|
-
const groupName = `group:${group.metadata.namespace?.toLocaleLowerCase(
|
|
99
|
-
"en-US"
|
|
100
|
-
)}/${group.metadata.name.toLocaleLowerCase("en-US")}`;
|
|
101
|
-
if (!memo.hasEntityRef(groupName)) {
|
|
102
|
-
memo.setNode(groupName);
|
|
103
|
-
}
|
|
104
|
-
if (this.maxDepth !== undefined && current_depth >= this.maxDepth) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const depth = current_depth + 1;
|
|
108
|
-
const parent = group.spec?.parent;
|
|
109
|
-
const parentGroup = allGroups.find((g) => g.metadata.name === parent);
|
|
110
|
-
if (parentGroup) {
|
|
111
|
-
const parentName = `group:${group.metadata.namespace?.toLocaleLowerCase(
|
|
112
|
-
"en-US"
|
|
113
|
-
)}/${parentGroup.metadata.name.toLocaleLowerCase("en-US")}`;
|
|
114
|
-
memo.setEdge(parentName, groupName);
|
|
115
|
-
if (memo.isAcyclic()) {
|
|
116
|
-
this.traverseGroups(memo, parentGroup, allGroups, depth);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
traverseRelations(memo, relation, allRelations, current_depth) {
|
|
121
|
-
if (this.maxDepth !== undefined && current_depth >= this.maxDepth + 1) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
const depth = current_depth + 1;
|
|
125
|
-
if (!memo.hasEntityRef(relation.source_entity_ref)) {
|
|
126
|
-
memo.setNode(relation.source_entity_ref);
|
|
127
|
-
}
|
|
128
|
-
memo.setEdge(relation.target_entity_ref, relation.source_entity_ref);
|
|
129
|
-
const parentGroup = allRelations.find(
|
|
130
|
-
(g) => g.source_entity_ref === relation.target_entity_ref
|
|
131
|
-
);
|
|
132
|
-
if (parentGroup && memo.isAcyclic()) {
|
|
133
|
-
this.traverseRelations(memo, parentGroup, allRelations, depth);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async buildUserGraph(memo) {
|
|
137
|
-
if (await this.doesRelationTableExist()) {
|
|
138
|
-
const userRelations = await this.getUserRelations();
|
|
139
|
-
const allRelations = await this.getAllRelations();
|
|
140
|
-
userRelations.forEach(
|
|
141
|
-
(group) => this.traverseRelations(
|
|
142
|
-
memo,
|
|
143
|
-
group,
|
|
144
|
-
allRelations,
|
|
145
|
-
0
|
|
146
|
-
)
|
|
147
|
-
);
|
|
148
|
-
} else {
|
|
149
|
-
const userGroups = await this.getUserGroups();
|
|
150
|
-
const allGroups = await this.getAllGroups();
|
|
151
|
-
userGroups.forEach(
|
|
152
|
-
(group) => this.traverseGroups(memo, group, allGroups, 0)
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
36
|
}
|
|
157
37
|
|
|
158
38
|
exports.AncestorSearchMemo = AncestorSearchMemo;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ancestor-search-memo.cjs.js","sources":["../../src/role-manager/ancestor-search-memo.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport type { CatalogApi } from '@backstage/catalog-client';\nimport type { Entity } from '@backstage/catalog-model';\n\nimport { alg, Graph } from '@dagrejs/graphlib';\nimport { Knex } from 'knex';\n\nexport interface Relation {\n source_entity_ref: string;\n target_entity_ref: string;\n}\n\nexport type ASMGroup = Relation | Entity;\n\n// AncestorSearchMemo - should be used to build group hierarchy graph for User entity reference.\n// It supports search group entity reference link in the graph.\n// Also AncestorSearchMemo supports detection cycle dependencies between groups in the graph.\n//\nexport class AncestorSearchMemo {\n private graph: Graph;\n\n private catalogApi: CatalogApi;\n private catalogDBClient: Knex;\n private auth: AuthService;\n\n private userEntityRef: string;\n private maxDepth?: number;\n\n constructor(\n userEntityRef: string,\n catalogApi: CatalogApi,\n catalogDBClient: Knex,\n auth: AuthService,\n maxDepth?: number,\n ) {\n this.graph = new Graph({ directed: true });\n this.userEntityRef = userEntityRef;\n this.catalogApi = catalogApi;\n this.catalogDBClient = catalogDBClient;\n this.auth = auth;\n this.maxDepth = maxDepth;\n }\n\n isAcyclic(): boolean {\n return alg.isAcyclic(this.graph);\n }\n\n findCycles(): string[][] {\n return alg.findCycles(this.graph);\n }\n\n setEdge(parentEntityRef: string, childEntityRef: string) {\n this.graph.setEdge(parentEntityRef, childEntityRef);\n }\n\n setNode(entityRef: string): void {\n this.graph.setNode(entityRef);\n }\n\n hasEntityRef(groupRef: string): boolean {\n return this.graph.hasNode(groupRef);\n }\n\n debugNodesAndEdges(logger: LoggerService, userEntity: string): void {\n logger.debug(\n `SubGraph edges: ${JSON.stringify(this.graph.edges())} for ${userEntity}`,\n );\n logger.debug(\n `SubGraph nodes: ${JSON.stringify(this.graph.nodes())} for ${userEntity}`,\n );\n }\n\n getNodes(): string[] {\n return this.graph.nodes();\n }\n\n async doesRelationTableExist(): Promise<boolean> {\n try {\n return await this.catalogDBClient.schema.hasTable('relations');\n } catch (error) {\n return false;\n }\n }\n\n async getAllGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group' },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getAllRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where('type', 'childOf');\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n async getUserGroups(): Promise<ASMGroup[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const { items } = await this.catalogApi.getEntities(\n {\n filter: { kind: 'Group', 'relations.hasMember': this.userEntityRef },\n fields: ['metadata.name', 'metadata.namespace', 'spec.parent'],\n },\n { token },\n );\n return items;\n }\n\n async getUserRelations(): Promise<ASMGroup[]> {\n try {\n const rows = await this.catalogDBClient('relations')\n .select('source_entity_ref', 'target_entity_ref')\n .where({ type: 'memberOf', source_entity_ref: this.userEntityRef });\n return rows;\n } catch (error) {\n return [];\n }\n }\n\n traverseGroups(\n memo: AncestorSearchMemo,\n group: Entity,\n allGroups: Entity[],\n current_depth: number,\n ) {\n const groupName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${group.metadata.name.toLocaleLowerCase('en-US')}`;\n if (!memo.hasEntityRef(groupName)) {\n memo.setNode(groupName);\n }\n\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth) {\n return;\n }\n const depth = current_depth + 1;\n\n const parent = group.spec?.parent as string;\n const parentGroup = allGroups.find(g => g.metadata.name === parent);\n\n if (parentGroup) {\n const parentName = `group:${group.metadata.namespace?.toLocaleLowerCase(\n 'en-US',\n )}/${parentGroup.metadata.name.toLocaleLowerCase('en-US')}`;\n memo.setEdge(parentName, groupName);\n\n if (memo.isAcyclic()) {\n this.traverseGroups(memo, parentGroup, allGroups, depth);\n }\n }\n }\n\n traverseRelations(\n memo: AncestorSearchMemo,\n relation: Relation,\n allRelations: Relation[],\n current_depth: number,\n ) {\n // We add one to the maxDepth here because the user is considered the starting node\n if (this.maxDepth !== undefined && current_depth >= this.maxDepth + 1) {\n return;\n }\n const depth = current_depth + 1;\n\n if (!memo.hasEntityRef(relation.source_entity_ref)) {\n memo.setNode(relation.source_entity_ref);\n }\n\n memo.setEdge(relation.target_entity_ref, relation.source_entity_ref);\n\n const parentGroup = allRelations.find(\n g => g.source_entity_ref === relation.target_entity_ref,\n );\n\n if (parentGroup && memo.isAcyclic()) {\n this.traverseRelations(memo, parentGroup, allRelations, depth);\n }\n }\n\n async buildUserGraph(memo: AncestorSearchMemo) {\n if (await this.doesRelationTableExist()) {\n const userRelations = await this.getUserRelations();\n const allRelations = await this.getAllRelations();\n userRelations.forEach(group =>\n this.traverseRelations(\n memo,\n group as Relation,\n allRelations as Relation[],\n 0,\n ),\n );\n } else {\n const userGroups = await this.getUserGroups();\n const allGroups = await this.getAllGroups();\n userGroups.forEach(group =>\n this.traverseGroups(memo, group as Entity, allGroups as Entity[], 0),\n );\n }\n }\n}\n"],"names":["Graph","alg"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ancestor-search-memo.cjs.js","sources":["../../src/role-manager/ancestor-search-memo.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport type { LoggerService } from '@backstage/backend-plugin-api';\nimport type { Entity } from '@backstage/catalog-model';\n\nimport { alg, Graph } from '@dagrejs/graphlib';\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 abstract class AncestorSearchMemo<T extends ASMGroup> {\n protected graph: Graph;\n\n constructor() {\n this.graph = new Graph({ directed: true });\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 abstract traverse(\n relation: T,\n allRelations: T[],\n current_depth: number,\n ): void;\n\n abstract buildUserGraph(): Promise<void>;\n\n abstract getUserASMGroups(): Promise<T[]>;\n\n abstract getAllASMGroups(): Promise<T[]>;\n}\n"],"names":["Graph","alg"],"mappings":";;;;AA+BO,MAAe,kBAAuC,CAAA;AAAA,EACjD,KAAA;AAAA,EAEV,WAAc,GAAA;AACZ,IAAA,IAAA,CAAK,QAAQ,IAAIA,cAAA,CAAM,EAAE,QAAA,EAAU,MAAM,CAAA;AAAA;AAC3C,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;AAc5B;;;;"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var catalogModel = require('@backstage/catalog-model');
|
|
4
|
-
var ancestorSearchMemo = require('./ancestor-search-memo.cjs.js');
|
|
5
4
|
var memberList = require('./member-list.cjs.js');
|
|
5
|
+
var ancestorSearchFactory = require('./ancestor-search-factory.cjs.js');
|
|
6
6
|
|
|
7
7
|
class BackstageRoleManager {
|
|
8
8
|
constructor(catalogApi, logger, catalogDBClient, rbacDBClient, config, auth) {
|
|
@@ -97,14 +97,15 @@ class BackstageRoleManager {
|
|
|
97
97
|
return false;
|
|
98
98
|
}
|
|
99
99
|
if (kind.toLocaleLowerCase() === "group") {
|
|
100
|
-
const memo =
|
|
100
|
+
const memo = await ancestorSearchFactory.AncestorSearchFactory.createAncestorSearchMemo(
|
|
101
101
|
name1,
|
|
102
|
+
this.config,
|
|
102
103
|
this.catalogApi,
|
|
103
104
|
this.catalogDBClient,
|
|
104
105
|
this.auth,
|
|
105
106
|
this.maxDepth
|
|
106
107
|
);
|
|
107
|
-
await memo.buildUserGraph(
|
|
108
|
+
await memo.buildUserGraph();
|
|
108
109
|
memo.debugNodesAndEdges(this.logger, name1);
|
|
109
110
|
if (!memo.isAcyclic()) {
|
|
110
111
|
const cycles = memo.findCycles();
|
|
@@ -157,14 +158,15 @@ class BackstageRoleManager {
|
|
|
157
158
|
async getRoles(name, ..._domain) {
|
|
158
159
|
const { kind } = catalogModel.parseEntityRef(name);
|
|
159
160
|
if (kind === "user") {
|
|
160
|
-
const memo =
|
|
161
|
+
const memo = await ancestorSearchFactory.AncestorSearchFactory.createAncestorSearchMemo(
|
|
161
162
|
name,
|
|
163
|
+
this.config,
|
|
162
164
|
this.catalogApi,
|
|
163
165
|
this.catalogDBClient,
|
|
164
166
|
this.auth,
|
|
165
167
|
this.maxDepth
|
|
166
168
|
);
|
|
167
|
-
await memo.buildUserGraph(
|
|
169
|
+
await memo.buildUserGraph();
|
|
168
170
|
memo.debugNodesAndEdges(this.logger, name);
|
|
169
171
|
memo.setNode(name);
|
|
170
172
|
if (!memo.isAcyclic()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-manager.cjs.js","sources":["../../src/role-manager/role-manager.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 { parseEntityRef } from '@backstage/catalog-model';\nimport type { Config } from '@backstage/config';\n\nimport { RoleManager } from 'casbin';\nimport { Knex } from 'knex';\n\nimport { AncestorSearchMemo } from './ancestor-search-memo';\nimport { RoleMemberList } from './member-list';\n\nexport class BackstageRoleManager implements RoleManager {\n private allRoles: Map<string, RoleMemberList>;\n private maxDepth?: number;\n constructor(\n private readonly catalogApi: CatalogApi,\n private readonly logger: LoggerService,\n private readonly catalogDBClient: Knex,\n private readonly rbacDBClient: Knex,\n private readonly config: Config,\n private readonly auth: AuthService,\n ) {\n this.allRoles = new Map<string, RoleMemberList>();\n const rbacConfig = this.config.getOptionalConfig('permission.rbac');\n this.maxDepth = rbacConfig?.getOptionalNumber('maxDepth');\n if (this.maxDepth !== undefined && this.maxDepth! < 0) {\n throw new Error(\n 'Max Depth for RBAC group hierarchy must be greater than or equal to zero',\n );\n }\n }\n\n /**\n * clear clears all stored data and resets the role manager to the initial state.\n */\n async clear(): Promise<void> {\n // do nothing\n }\n\n /**\n * addLink adds the inheritance link between name1 and role: name2.\n * aka name1 inherits role: name2.\n * The link that is established is based on the defined grouping policies that are added by the enforcer.\n *\n * ex. `g, name1, name2`.\n * @param name1 User or group that will be assigned to a role.\n * @param name2 The role that will be created or updated.\n * @param _domain Unimplemented prefix to the role.\n */\n async addLink(\n name1: string,\n name2: string,\n ..._domain: string[]\n ): Promise<void> {\n if (!this.isPGClient()) {\n const role1 = this.getOrCreateRole(name2);\n role1.addMember(name1);\n }\n }\n\n /**\n * deleteLink deletes the inheritance link between name1 and role: name2.\n * aka name1 does not inherit role: name2 any more.\n * The link that is deleted is based on the defined grouping policies that are removed by the enforcer.\n *\n * ex. `g, name1, name2`.\n * @param name1 User or group that will be removed from assignment of a role.\n * @param name2 The role that will be deleted or updated.\n * @param _domain Unimplemented.\n */\n async deleteLink(\n name1: string,\n name2: string,\n ..._domain: string[]\n ): Promise<void> {\n if (!this.isPGClient()) {\n const role1 = this.getOrCreateRole(name2);\n role1.deleteMember(name1);\n\n // Clean up in the event that there are no more members in the role\n if (role1.getMembers().length === 0) {\n this.allRoles.delete(name2);\n }\n }\n }\n\n /**\n * hasLink determines whether name1 inherits role: name2.\n * Before this check is called in the background by the enforcer,\n * we filter out all roles that the user is not connected to\n * directly or indirectly through the use of retrieving roles through\n * enforcer.getRolesForUser and apply those roles to a tempEnforcer.\n *\n * This means that hasLink will almost always be true in the event that a user\n * is assigned to a role (either directly or indirectly)\n *\n * In the event that a user or group is not assigned to a role and instead\n * are assigned directly to permissions, then name2 will become either that\n * user or group through the filtering. In this case we will build the graph\n * if necessary for name2 group presence or evaulate based on the names matching.\n * @param name1 The user that we are authorizing.\n * @param name2 The name of the role that we are checking against.\n * @param domain Unimplemented.\n * @returns True if the user is directly or indirectly attached to the role.\n */\n async hasLink(\n name1: string,\n name2: string,\n ...domain: string[]\n ): Promise<boolean> {\n if (domain.length > 0) {\n throw new Error('domain argument is not supported.');\n }\n\n // Name2 can be an empty string in the event that there is not a role associated with the user\n // This happens because of the filtering of the roles reduces the number of roles that we iterate through.\n if (name2.length === 0) {\n return false;\n }\n\n if (name1 === name2) {\n return true;\n }\n\n // name1 is always user in our case.\n // name2 is user or group.\n // user(name1) couldn't inherit user(name2).\n // We can use this fact for optimization.\n const { kind } = parseEntityRef(name2);\n if (kind.toLocaleLowerCase() === 'user') {\n return false;\n }\n\n // if it is a group, then we will have to build the graph,\n if (kind.toLocaleLowerCase() === 'group') {\n const memo = new AncestorSearchMemo(\n name1,\n this.catalogApi,\n this.catalogDBClient,\n this.auth,\n this.maxDepth,\n );\n await memo.buildUserGraph(memo);\n memo.debugNodesAndEdges(this.logger, name1);\n\n if (!memo.isAcyclic()) {\n const cycles = memo.findCycles();\n\n this.logger.warn(\n `Detected cycle dependencies in the Group graph: ${JSON.stringify(\n cycles,\n )}. Admin/(catalog owner) have to fix it to make RBAC permission evaluation correct for groups: ${JSON.stringify(\n cycles,\n )}`,\n );\n return false;\n }\n\n return memo.hasEntityRef(name2);\n }\n\n return true;\n }\n\n /**\n * syncedHasLink determines whether role: name1 inherits role: name2.\n * domain is a prefix to the roles.\n */\n syncedHasLink?(\n _name1: string,\n _name2: string,\n ..._domain: string[]\n ): boolean {\n throw new Error('Method \"syncedHasLink\" not implemented.');\n }\n\n /**\n * getRoles gets the roles that a subject inherits.\n *\n * name - is a string entity reference, for example: user:default/tom, role:default/dev,\n * so format is <kind>:<namespace>/<entity-name>.\n * GetRoles method supports only two kind values: 'user' and 'role'.\n *\n * domain - is a prefix to the roles, unused parameter.\n *\n * If name's kind === 'user' we return all inherited roles from groups and roles directly assigned to the user.\n * if name's kind === 'role' we return empty array, because we don't support role inheritance.\n * Case kind === 'group' - should not happen, because:\n * 1) Method getRoles returns only role entity references, so casbin engine doesn't call this\n * method again to ask about name with kind \"group\".\n * 2) We implemented getRoles method only to use:\n * 'await enforcer.getImplicitPermissionsForUser(userEntityRef)',\n * so name argument can be only with kind 'user' or 'role'.\n *\n * Info: when we call 'await enforcer.getImplicitPermissionsForUser(userEntityRef)',\n * then casbin engine executes 'getRoles' method few times.\n * Firstly casbin asks about roles for 'userEntityRef'.\n * Let's imagine, that 'getRoles' returned two roles for userEntityRef.\n * Then casbin calls 'getRoles' two more times to\n * find parent roles. But we return empty array for each such call,\n * because we don't support role inheritance and we notify casbin about end of the role sub-tree.\n */\n async getRoles(name: string, ..._domain: string[]): Promise<string[]> {\n const { kind } = parseEntityRef(name);\n if (kind === 'user') {\n const memo = new AncestorSearchMemo(\n name,\n this.catalogApi,\n this.catalogDBClient,\n this.auth,\n this.maxDepth,\n );\n await memo.buildUserGraph(memo);\n memo.debugNodesAndEdges(this.logger, name);\n\n // Account for the user not being in the graph (this can happen during direct assignment to roles)\n memo.setNode(name);\n\n if (!memo.isAcyclic()) {\n const cycles = memo.findCycles();\n\n this.logger.warn(\n `Detected cycle dependencies in the Group graph: ${JSON.stringify(\n cycles,\n )}. Admin/(catalog owner) have to fix it to make RBAC permission evaluation correct for groups: ${JSON.stringify(\n cycles,\n )}`,\n );\n return Promise.resolve([]);\n }\n\n if (this.isPGClient()) {\n const currentRole = new RoleMemberList(name);\n await currentRole.buildRoles(\n currentRole,\n memo.getNodes(),\n this.rbacDBClient,\n );\n return Promise.resolve(currentRole.getRoles());\n }\n\n const allRoles: string[] = [];\n for (const value of this.allRoles.values()) {\n if (this.hasMember(value, memo)) {\n allRoles.push(value.name);\n }\n }\n\n return Promise.resolve(allRoles);\n }\n\n return [];\n }\n\n /**\n * getUsers gets the users that inherits a subject.\n * domain is an unreferenced parameter here, may be used in other implementations.\n */\n async getUsers(_name: string, ..._domain: string[]): Promise<string[]> {\n throw new Error('Method \"getUsers\" not implemented.');\n }\n\n /**\n * printRoles prints all the roles to log.\n */\n async printRoles(): Promise<void> {\n // do nothing\n }\n\n /**\n * getOrCreateRole will get a role if it has already been cached\n * or it will create a new role to be cached.\n * This cache is a simple tree that is used to quickly compare\n * users and groups to roles.\n * @param name The user or group whose cache we will be getting / creating.\n * @returns The cached role as a RoleList.\n */\n private getOrCreateRole(name: string): RoleMemberList {\n const role = this.allRoles.get(name);\n if (role) {\n return role;\n }\n const newRole = new RoleMemberList(name);\n this.allRoles.set(name, newRole);\n\n return newRole;\n }\n\n /**\n * isPGClient checks what the current database client is at them time.\n * This is to ensure that we are querying the database in the event of postgres\n * or using in memory cache for better sqlite3.\n * @returns True if the database client is pg.\n */\n isPGClient(): boolean {\n const client = this.rbacDBClient.client.config.client;\n return client === 'pg';\n }\n\n /**\n * hasMember checks if the members from a particular role is associated with the user\n * that the AncestorSearchMemo graph is built for.\n * @param role The role that we are getting the members from.\n * @param memo The user graph that we are comparing members with.\n * @returns True if a member from the role is also associated with the user.\n */\n private hasMember(\n role: RoleMemberList | undefined,\n memo: AncestorSearchMemo,\n ): boolean {\n if (role === undefined) {\n return false;\n }\n\n for (const member of role.getMembers()) {\n if (memo.hasEntityRef(member)) {\n return true;\n }\n }\n return false;\n }\n}\n"],"names":["parseEntityRef","AncestorSearchMemo","RoleMemberList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"role-manager.cjs.js","sources":["../../src/role-manager/role-manager.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 { parseEntityRef } from '@backstage/catalog-model';\nimport type { Config } from '@backstage/config';\n\nimport { RoleManager } from 'casbin';\nimport { Knex } from 'knex';\n\nimport { AncestorSearchMemo, ASMGroup } from './ancestor-search-memo';\nimport { RoleMemberList } from './member-list';\nimport { AncestorSearchFactory } from './ancestor-search-factory';\n\nexport class BackstageRoleManager implements RoleManager {\n private allRoles: Map<string, RoleMemberList>;\n private maxDepth?: number;\n constructor(\n private readonly catalogApi: CatalogApi,\n private readonly logger: LoggerService,\n private readonly catalogDBClient: Knex,\n private readonly rbacDBClient: Knex,\n private readonly config: Config,\n private readonly auth: AuthService,\n ) {\n this.allRoles = new Map<string, RoleMemberList>();\n const rbacConfig = this.config.getOptionalConfig('permission.rbac');\n this.maxDepth = rbacConfig?.getOptionalNumber('maxDepth');\n if (this.maxDepth !== undefined && this.maxDepth! < 0) {\n throw new Error(\n 'Max Depth for RBAC group hierarchy must be greater than or equal to zero',\n );\n }\n }\n\n /**\n * clear clears all stored data and resets the role manager to the initial state.\n */\n async clear(): Promise<void> {\n // do nothing\n }\n\n /**\n * addLink adds the inheritance link between name1 and role: name2.\n * aka name1 inherits role: name2.\n * The link that is established is based on the defined grouping policies that are added by the enforcer.\n *\n * ex. `g, name1, name2`.\n * @param name1 User or group that will be assigned to a role.\n * @param name2 The role that will be created or updated.\n * @param _domain Unimplemented prefix to the role.\n */\n async addLink(\n name1: string,\n name2: string,\n ..._domain: string[]\n ): Promise<void> {\n if (!this.isPGClient()) {\n const role1 = this.getOrCreateRole(name2);\n role1.addMember(name1);\n }\n }\n\n /**\n * deleteLink deletes the inheritance link between name1 and role: name2.\n * aka name1 does not inherit role: name2 any more.\n * The link that is deleted is based on the defined grouping policies that are removed by the enforcer.\n *\n * ex. `g, name1, name2`.\n * @param name1 User or group that will be removed from assignment of a role.\n * @param name2 The role that will be deleted or updated.\n * @param _domain Unimplemented.\n */\n async deleteLink(\n name1: string,\n name2: string,\n ..._domain: string[]\n ): Promise<void> {\n if (!this.isPGClient()) {\n const role1 = this.getOrCreateRole(name2);\n role1.deleteMember(name1);\n\n // Clean up in the event that there are no more members in the role\n if (role1.getMembers().length === 0) {\n this.allRoles.delete(name2);\n }\n }\n }\n\n /**\n * hasLink determines whether name1 inherits role: name2.\n * Before this check is called in the background by the enforcer,\n * we filter out all roles that the user is not connected to\n * directly or indirectly through the use of retrieving roles through\n * enforcer.getRolesForUser and apply those roles to a tempEnforcer.\n *\n * This means that hasLink will almost always be true in the event that a user\n * is assigned to a role (either directly or indirectly)\n *\n * In the event that a user or group is not assigned to a role and instead\n * are assigned directly to permissions, then name2 will become either that\n * user or group through the filtering. In this case we will build the graph\n * if necessary for name2 group presence or evaulate based on the names matching.\n * @param name1 The user that we are authorizing.\n * @param name2 The name of the role that we are checking against.\n * @param domain Unimplemented.\n * @returns True if the user is directly or indirectly attached to the role.\n */\n async hasLink(\n name1: string,\n name2: string,\n ...domain: string[]\n ): Promise<boolean> {\n if (domain.length > 0) {\n throw new Error('domain argument is not supported.');\n }\n\n // Name2 can be an empty string in the event that there is not a role associated with the user\n // This happens because of the filtering of the roles reduces the number of roles that we iterate through.\n if (name2.length === 0) {\n return false;\n }\n\n if (name1 === name2) {\n return true;\n }\n\n // name1 is always user in our case.\n // name2 is user or group.\n // user(name1) couldn't inherit user(name2).\n // We can use this fact for optimization.\n const { kind } = parseEntityRef(name2);\n if (kind.toLocaleLowerCase() === 'user') {\n return false;\n }\n\n // if it is a group, then we will have to build the graph,\n if (kind.toLocaleLowerCase() === 'group') {\n const memo = await AncestorSearchFactory.createAncestorSearchMemo(\n name1,\n this.config,\n this.catalogApi,\n this.catalogDBClient,\n this.auth,\n this.maxDepth,\n );\n\n await memo.buildUserGraph();\n memo.debugNodesAndEdges(this.logger, name1);\n\n if (!memo.isAcyclic()) {\n const cycles = memo.findCycles();\n\n this.logger.warn(\n `Detected cycle dependencies in the Group graph: ${JSON.stringify(\n cycles,\n )}. Admin/(catalog owner) have to fix it to make RBAC permission evaluation correct for groups: ${JSON.stringify(\n cycles,\n )}`,\n );\n return false;\n }\n\n return memo.hasEntityRef(name2);\n }\n\n return true;\n }\n\n /**\n * syncedHasLink determines whether role: name1 inherits role: name2.\n * domain is a prefix to the roles.\n */\n syncedHasLink?(\n _name1: string,\n _name2: string,\n ..._domain: string[]\n ): boolean {\n throw new Error('Method \"syncedHasLink\" not implemented.');\n }\n\n /**\n * getRoles gets the roles that a subject inherits.\n *\n * name - is a string entity reference, for example: user:default/tom, role:default/dev,\n * so format is <kind>:<namespace>/<entity-name>.\n * GetRoles method supports only two kind values: 'user' and 'role'.\n *\n * domain - is a prefix to the roles, unused parameter.\n *\n * If name's kind === 'user' we return all inherited roles from groups and roles directly assigned to the user.\n * if name's kind === 'role' we return empty array, because we don't support role inheritance.\n * Case kind === 'group' - should not happen, because:\n * 1) Method getRoles returns only role entity references, so casbin engine doesn't call this\n * method again to ask about name with kind \"group\".\n * 2) We implemented getRoles method only to use:\n * 'await enforcer.getImplicitPermissionsForUser(userEntityRef)',\n * so name argument can be only with kind 'user' or 'role'.\n *\n * Info: when we call 'await enforcer.getImplicitPermissionsForUser(userEntityRef)',\n * then casbin engine executes 'getRoles' method few times.\n * Firstly casbin asks about roles for 'userEntityRef'.\n * Let's imagine, that 'getRoles' returned two roles for userEntityRef.\n * Then casbin calls 'getRoles' two more times to\n * find parent roles. But we return empty array for each such call,\n * because we don't support role inheritance and we notify casbin about end of the role sub-tree.\n */\n async getRoles(name: string, ..._domain: string[]): Promise<string[]> {\n const { kind } = parseEntityRef(name);\n if (kind === 'user') {\n const memo = await AncestorSearchFactory.createAncestorSearchMemo(\n name,\n this.config,\n this.catalogApi,\n this.catalogDBClient,\n this.auth,\n this.maxDepth,\n );\n await memo.buildUserGraph();\n memo.debugNodesAndEdges(this.logger, name);\n\n // Account for the user not being in the graph (this can happen during direct assignment to roles)\n memo.setNode(name);\n\n if (!memo.isAcyclic()) {\n const cycles = memo.findCycles();\n\n this.logger.warn(\n `Detected cycle dependencies in the Group graph: ${JSON.stringify(\n cycles,\n )}. Admin/(catalog owner) have to fix it to make RBAC permission evaluation correct for groups: ${JSON.stringify(\n cycles,\n )}`,\n );\n return Promise.resolve([]);\n }\n\n if (this.isPGClient()) {\n const currentRole = new RoleMemberList(name);\n await currentRole.buildRoles(\n currentRole,\n memo.getNodes(),\n this.rbacDBClient,\n );\n return Promise.resolve(currentRole.getRoles());\n }\n\n const allRoles: string[] = [];\n for (const value of this.allRoles.values()) {\n if (this.hasMember(value, memo)) {\n allRoles.push(value.name);\n }\n }\n\n return Promise.resolve(allRoles);\n }\n\n return [];\n }\n\n /**\n * getUsers gets the users that inherits a subject.\n * domain is an unreferenced parameter here, may be used in other implementations.\n */\n async getUsers(_name: string, ..._domain: string[]): Promise<string[]> {\n throw new Error('Method \"getUsers\" not implemented.');\n }\n\n /**\n * printRoles prints all the roles to log.\n */\n async printRoles(): Promise<void> {\n // do nothing\n }\n\n /**\n * getOrCreateRole will get a role if it has already been cached\n * or it will create a new role to be cached.\n * This cache is a simple tree that is used to quickly compare\n * users and groups to roles.\n * @param name The user or group whose cache we will be getting / creating.\n * @returns The cached role as a RoleList.\n */\n private getOrCreateRole(name: string): RoleMemberList {\n const role = this.allRoles.get(name);\n if (role) {\n return role;\n }\n const newRole = new RoleMemberList(name);\n this.allRoles.set(name, newRole);\n\n return newRole;\n }\n\n /**\n * isPGClient checks what the current database client is at them time.\n * This is to ensure that we are querying the database in the event of postgres\n * or using in memory cache for better sqlite3.\n * @returns True if the database client is pg.\n */\n isPGClient(): boolean {\n const client = this.rbacDBClient.client.config.client;\n return client === 'pg';\n }\n\n /**\n * hasMember checks if the members from a particular role is associated with the user\n * that the AncestorSearchMemo graph is built for.\n * @param role The role that we are getting the members from.\n * @param memo The user graph that we are comparing members with.\n * @returns True if a member from the role is also associated with the user.\n */\n private hasMember(\n role: RoleMemberList | undefined,\n memo: AncestorSearchMemo<ASMGroup>,\n ): boolean {\n if (role === undefined) {\n return false;\n }\n\n for (const member of role.getMembers()) {\n if (memo.hasEntityRef(member)) {\n return true;\n }\n }\n return false;\n }\n}\n"],"names":["parseEntityRef","AncestorSearchFactory","RoleMemberList"],"mappings":"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"}
|
|
@@ -31,7 +31,7 @@ class PoliciesServer {
|
|
|
31
31
|
});
|
|
32
32
|
if (this.options.auth.isPrincipal(credentials, "service") && permission !== pluginRbacCommon.policyEntityReadPermission) {
|
|
33
33
|
throw new errors.NotAllowedError(
|
|
34
|
-
`Only
|
|
34
|
+
`Only credential principal with type 'user' permitted to modify permissions`
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
let decision;
|
|
@@ -44,16 +44,14 @@ class PoliciesServer {
|
|
|
44
44
|
credentials
|
|
45
45
|
}))[0];
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
48
|
+
throw new errors.NotAllowedError();
|
|
49
|
+
}
|
|
50
|
+
return { decision, credentials };
|
|
48
51
|
}
|
|
49
52
|
async serve() {
|
|
50
53
|
const router = await pluginPermissionBackend.createRouter(this.options);
|
|
51
|
-
const {
|
|
52
|
-
if (!httpAuth) {
|
|
53
|
-
throw new errors.ServiceUnavailableError(
|
|
54
|
-
"httpAuth not found, ensure the correct configuration for the RBAC plugin"
|
|
55
|
-
);
|
|
56
|
-
}
|
|
54
|
+
const { logger } = this.options;
|
|
57
55
|
const policyPermissionsIntegrationRouter = pluginPermissionNode.createPermissionIntegrationRouter({
|
|
58
56
|
resourceType: pluginRbacCommon.RESOURCE_TYPE_POLICY_ENTITY,
|
|
59
57
|
getResources: (resourceRefs) => Promise.all(
|
|
@@ -70,13 +68,7 @@ class PoliciesServer {
|
|
|
70
68
|
return router;
|
|
71
69
|
}
|
|
72
70
|
router.get("/", async (request, response) => {
|
|
73
|
-
|
|
74
|
-
request,
|
|
75
|
-
pluginRbacCommon.policyEntityReadPermission
|
|
76
|
-
);
|
|
77
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
78
|
-
throw new errors.NotAllowedError();
|
|
79
|
-
}
|
|
71
|
+
await this.authorizeConditional(request, pluginRbacCommon.policyEntityReadPermission);
|
|
80
72
|
response.send({ status: "Authorized" });
|
|
81
73
|
});
|
|
82
74
|
router.get(
|
|
@@ -84,13 +76,10 @@ class PoliciesServer {
|
|
|
84
76
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
85
77
|
async (request, response) => {
|
|
86
78
|
let conditionsFilter;
|
|
87
|
-
const decision = await this.authorizeConditional(
|
|
79
|
+
const { decision } = await this.authorizeConditional(
|
|
88
80
|
request,
|
|
89
81
|
pluginRbacCommon.policyEntityReadPermission
|
|
90
82
|
);
|
|
91
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
92
|
-
throw new errors.NotAllowedError();
|
|
93
|
-
}
|
|
94
83
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
95
84
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
96
85
|
}
|
|
@@ -133,13 +122,10 @@ class PoliciesServer {
|
|
|
133
122
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
134
123
|
async (request, response) => {
|
|
135
124
|
let conditionsFilter;
|
|
136
|
-
const decision = await this.authorizeConditional(
|
|
125
|
+
const { decision } = await this.authorizeConditional(
|
|
137
126
|
request,
|
|
138
127
|
pluginRbacCommon.policyEntityReadPermission
|
|
139
128
|
);
|
|
140
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
141
|
-
throw new errors.NotAllowedError();
|
|
142
|
-
}
|
|
143
129
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
144
130
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
145
131
|
}
|
|
@@ -170,13 +156,10 @@ class PoliciesServer {
|
|
|
170
156
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
171
157
|
async (request, response) => {
|
|
172
158
|
let conditionsFilter;
|
|
173
|
-
const decision = await this.authorizeConditional(
|
|
159
|
+
const { decision } = await this.authorizeConditional(
|
|
174
160
|
request,
|
|
175
161
|
pluginRbacCommon.policyEntityDeletePermission
|
|
176
162
|
);
|
|
177
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
178
|
-
throw new errors.NotAllowedError();
|
|
179
|
-
}
|
|
180
163
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
181
164
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
182
165
|
}
|
|
@@ -203,13 +186,7 @@ class PoliciesServer {
|
|
|
203
186
|
"/policies",
|
|
204
187
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
205
188
|
async (request, response) => {
|
|
206
|
-
|
|
207
|
-
request,
|
|
208
|
-
pluginRbacCommon.policyEntityCreatePermission
|
|
209
|
-
);
|
|
210
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
211
|
-
throw new errors.NotAllowedError();
|
|
212
|
-
}
|
|
189
|
+
await this.authorizeConditional(request, pluginRbacCommon.policyEntityCreatePermission);
|
|
213
190
|
const policyRaw = request.body;
|
|
214
191
|
if (lodash.isEmpty(policyRaw)) {
|
|
215
192
|
throw new errors.InputError(`permission policy must be present`);
|
|
@@ -234,13 +211,10 @@ class PoliciesServer {
|
|
|
234
211
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
235
212
|
async (request, response) => {
|
|
236
213
|
let conditionsFilter;
|
|
237
|
-
const decision = await this.authorizeConditional(
|
|
214
|
+
const { decision } = await this.authorizeConditional(
|
|
238
215
|
request,
|
|
239
216
|
pluginRbacCommon.policyEntityUpdatePermission
|
|
240
217
|
);
|
|
241
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
242
|
-
throw new errors.NotAllowedError();
|
|
243
|
-
}
|
|
244
218
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
245
219
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
246
220
|
}
|
|
@@ -298,13 +272,10 @@ class PoliciesServer {
|
|
|
298
272
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
299
273
|
async (request, response) => {
|
|
300
274
|
let conditionsFilter;
|
|
301
|
-
const decision = await this.authorizeConditional(
|
|
275
|
+
const { decision } = await this.authorizeConditional(
|
|
302
276
|
request,
|
|
303
277
|
pluginRbacCommon.policyEntityReadPermission
|
|
304
278
|
);
|
|
305
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
306
|
-
throw new errors.NotAllowedError();
|
|
307
|
-
}
|
|
308
279
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
309
280
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
310
281
|
}
|
|
@@ -318,13 +289,10 @@ class PoliciesServer {
|
|
|
318
289
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
319
290
|
async (request, response) => {
|
|
320
291
|
let conditionsFilter;
|
|
321
|
-
const decision = await this.authorizeConditional(
|
|
292
|
+
const { decision } = await this.authorizeConditional(
|
|
322
293
|
request,
|
|
323
294
|
pluginRbacCommon.policyEntityReadPermission
|
|
324
295
|
);
|
|
325
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
326
|
-
throw new errors.NotAllowedError();
|
|
327
|
-
}
|
|
328
296
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
329
297
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
330
298
|
}
|
|
@@ -346,13 +314,10 @@ class PoliciesServer {
|
|
|
346
314
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
347
315
|
async (request, response) => {
|
|
348
316
|
const uniqueItems = /* @__PURE__ */ new Set();
|
|
349
|
-
const
|
|
317
|
+
const { credentials } = await this.authorizeConditional(
|
|
350
318
|
request,
|
|
351
319
|
pluginRbacCommon.policyEntityCreatePermission
|
|
352
320
|
);
|
|
353
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
354
|
-
throw new errors.NotAllowedError();
|
|
355
|
-
}
|
|
356
321
|
const roleRaw = request.body;
|
|
357
322
|
let err = policiesValidation.validateRole(roleRaw);
|
|
358
323
|
if (err) {
|
|
@@ -385,9 +350,6 @@ class PoliciesServer {
|
|
|
385
350
|
uniqueItems.add(roleString);
|
|
386
351
|
}
|
|
387
352
|
}
|
|
388
|
-
const credentials = await httpAuth.credentials(request, {
|
|
389
|
-
allow: ["user"]
|
|
390
|
-
});
|
|
391
353
|
const modifiedBy = credentials.principal.userEntityRef;
|
|
392
354
|
const metadata = {
|
|
393
355
|
roleEntityRef: roleRaw.name,
|
|
@@ -408,13 +370,10 @@ class PoliciesServer {
|
|
|
408
370
|
async (request, response) => {
|
|
409
371
|
const uniqueItems = /* @__PURE__ */ new Set();
|
|
410
372
|
let conditionsFilter;
|
|
411
|
-
const decision = await this.authorizeConditional(
|
|
373
|
+
const { decision, credentials } = await this.authorizeConditional(
|
|
412
374
|
request,
|
|
413
375
|
pluginRbacCommon.policyEntityUpdatePermission
|
|
414
376
|
);
|
|
415
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
416
|
-
throw new errors.NotAllowedError();
|
|
417
|
-
}
|
|
418
377
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
419
378
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
420
379
|
}
|
|
@@ -446,14 +405,12 @@ class PoliciesServer {
|
|
|
446
405
|
this.transformMemberReferencesToLowercase(newRoleRaw);
|
|
447
406
|
const oldRole = this.transformRoleToArray(oldRoleRaw);
|
|
448
407
|
const newRole = this.transformRoleToArray(newRoleRaw);
|
|
449
|
-
const
|
|
450
|
-
allow: ["user"]
|
|
451
|
-
});
|
|
408
|
+
const modifiedBy = credentials.principal.userEntityRef;
|
|
452
409
|
const newMetadata = {
|
|
453
410
|
...newRoleRaw.metadata,
|
|
454
411
|
source: newRoleRaw.metadata?.source ?? "rest",
|
|
455
412
|
roleEntityRef: newRoleRaw.name,
|
|
456
|
-
modifiedBy
|
|
413
|
+
modifiedBy,
|
|
457
414
|
owner: newRoleRaw.metadata?.owner ?? ""
|
|
458
415
|
};
|
|
459
416
|
const oldMetadata = await this.roleMetadata.findRoleMetadata(roleEntityRef);
|
|
@@ -538,13 +495,10 @@ class PoliciesServer {
|
|
|
538
495
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
539
496
|
async (request, response) => {
|
|
540
497
|
let conditionsFilter;
|
|
541
|
-
const decision = await this.authorizeConditional(
|
|
498
|
+
const { decision, credentials } = await this.authorizeConditional(
|
|
542
499
|
request,
|
|
543
500
|
pluginRbacCommon.policyEntityDeletePermission
|
|
544
501
|
);
|
|
545
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
546
|
-
throw new errors.NotAllowedError();
|
|
547
|
-
}
|
|
548
502
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
549
503
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
550
504
|
}
|
|
@@ -585,13 +539,11 @@ class PoliciesServer {
|
|
|
585
539
|
throw new errors.NotFoundError(`role member '${role[0]}' was not found`);
|
|
586
540
|
}
|
|
587
541
|
}
|
|
588
|
-
const
|
|
589
|
-
allow: ["user"]
|
|
590
|
-
});
|
|
542
|
+
const modifiedBy = credentials.principal.userEntityRef;
|
|
591
543
|
const metadata = {
|
|
592
544
|
roleEntityRef,
|
|
593
545
|
source: "rest",
|
|
594
|
-
modifiedBy
|
|
546
|
+
modifiedBy
|
|
595
547
|
};
|
|
596
548
|
await this.enforcer.removeGroupingPolicies(
|
|
597
549
|
roleMembers,
|
|
@@ -609,13 +561,7 @@ class PoliciesServer {
|
|
|
609
561
|
"/plugins/policies",
|
|
610
562
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
611
563
|
async (request, response) => {
|
|
612
|
-
|
|
613
|
-
request,
|
|
614
|
-
pluginRbacCommon.policyEntityReadPermission
|
|
615
|
-
);
|
|
616
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
617
|
-
throw new errors.NotAllowedError();
|
|
618
|
-
}
|
|
564
|
+
await this.authorizeConditional(request, pluginRbacCommon.policyEntityReadPermission);
|
|
619
565
|
const body = await this.pluginPermMetaData.getPluginPolicies(
|
|
620
566
|
this.options.auth
|
|
621
567
|
);
|
|
@@ -626,13 +572,7 @@ class PoliciesServer {
|
|
|
626
572
|
"/plugins/condition-rules",
|
|
627
573
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
628
574
|
async (request, response) => {
|
|
629
|
-
|
|
630
|
-
request,
|
|
631
|
-
pluginRbacCommon.policyEntityReadPermission
|
|
632
|
-
);
|
|
633
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
634
|
-
throw new errors.NotAllowedError();
|
|
635
|
-
}
|
|
575
|
+
await this.authorizeConditional(request, pluginRbacCommon.policyEntityReadPermission);
|
|
636
576
|
const body = await this.pluginPermMetaData.getPluginConditionRules(
|
|
637
577
|
this.options.auth
|
|
638
578
|
);
|
|
@@ -644,13 +584,10 @@ class PoliciesServer {
|
|
|
644
584
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
645
585
|
async (request, response) => {
|
|
646
586
|
let conditionsFilter;
|
|
647
|
-
const decision = await this.authorizeConditional(
|
|
587
|
+
const { decision } = await this.authorizeConditional(
|
|
648
588
|
request,
|
|
649
589
|
pluginRbacCommon.policyEntityReadPermission
|
|
650
590
|
);
|
|
651
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
652
|
-
throw new errors.NotAllowedError();
|
|
653
|
-
}
|
|
654
591
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
655
592
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
656
593
|
}
|
|
@@ -681,13 +618,7 @@ class PoliciesServer {
|
|
|
681
618
|
"/roles/conditions",
|
|
682
619
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
683
620
|
async (request, response) => {
|
|
684
|
-
|
|
685
|
-
request,
|
|
686
|
-
pluginRbacCommon.policyEntityCreatePermission
|
|
687
|
-
);
|
|
688
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
689
|
-
throw new errors.NotAllowedError();
|
|
690
|
-
}
|
|
621
|
+
await this.authorizeConditional(request, pluginRbacCommon.policyEntityCreatePermission);
|
|
691
622
|
const roleConditionPolicy = request.body;
|
|
692
623
|
conditionValidation.validateRoleCondition(roleConditionPolicy);
|
|
693
624
|
const conditionToCreate = await helper.processConditionMapping(
|
|
@@ -706,13 +637,10 @@ class PoliciesServer {
|
|
|
706
637
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
707
638
|
async (request, response) => {
|
|
708
639
|
let conditionsFilter;
|
|
709
|
-
const decision = await this.authorizeConditional(
|
|
640
|
+
const { decision } = await this.authorizeConditional(
|
|
710
641
|
request,
|
|
711
642
|
pluginRbacCommon.policyEntityReadPermission
|
|
712
643
|
);
|
|
713
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
714
|
-
throw new errors.NotAllowedError();
|
|
715
|
-
}
|
|
716
644
|
const id = parseInt(request.params.id, 10);
|
|
717
645
|
if (isNaN(id)) {
|
|
718
646
|
throw new errors.InputError("Id is not a valid number.");
|
|
@@ -742,13 +670,10 @@ class PoliciesServer {
|
|
|
742
670
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
743
671
|
async (request, response) => {
|
|
744
672
|
let conditionsFilter;
|
|
745
|
-
const decision = await this.authorizeConditional(
|
|
673
|
+
const { decision } = await this.authorizeConditional(
|
|
746
674
|
request,
|
|
747
675
|
pluginRbacCommon.policyEntityDeletePermission
|
|
748
676
|
);
|
|
749
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
750
|
-
throw new errors.NotAllowedError();
|
|
751
|
-
}
|
|
752
677
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
753
678
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
754
679
|
}
|
|
@@ -780,13 +705,10 @@ class PoliciesServer {
|
|
|
780
705
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
781
706
|
async (request, response) => {
|
|
782
707
|
let conditionsFilter;
|
|
783
|
-
const decision = await this.authorizeConditional(
|
|
708
|
+
const { decision } = await this.authorizeConditional(
|
|
784
709
|
request,
|
|
785
710
|
pluginRbacCommon.policyEntityUpdatePermission
|
|
786
711
|
);
|
|
787
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
788
|
-
throw new errors.NotAllowedError();
|
|
789
|
-
}
|
|
790
712
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
791
713
|
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
792
714
|
}
|
|
@@ -820,13 +742,7 @@ class PoliciesServer {
|
|
|
820
742
|
"/refresh/:id",
|
|
821
743
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
822
744
|
async (request, response) => {
|
|
823
|
-
|
|
824
|
-
request,
|
|
825
|
-
pluginRbacCommon.policyEntityCreatePermission
|
|
826
|
-
);
|
|
827
|
-
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
828
|
-
throw new errors.NotAllowedError();
|
|
829
|
-
}
|
|
745
|
+
await this.authorizeConditional(request, pluginRbacCommon.policyEntityCreatePermission);
|
|
830
746
|
if (!this.rbacProviders) {
|
|
831
747
|
throw new errors.NotFoundError(`No RBAC providers were found`);
|
|
832
748
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policies-rest-api.cjs.js","sources":["../../src/service/policies-rest-api.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 AuditorService,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\nimport {\n ConflictError,\n InputError,\n NotAllowedError,\n NotFoundError,\n ServiceUnavailableError,\n} from '@backstage/errors';\nimport { createRouter } from '@backstage/plugin-permission-backend';\nimport {\n AuthorizeResult,\n BasicPermission,\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\n\nimport express from 'express';\nimport type { Request } from 'express-serve-static-core';\nimport { isEmpty, isEqual } from 'lodash';\nimport type { ParsedQs } from 'qs';\n\nimport {\n PermissionAction,\n policyEntityCreatePermission,\n policyEntityDeletePermission,\n policyEntityPermissions,\n policyEntityReadPermission,\n policyEntityUpdatePermission,\n RESOURCE_TYPE_POLICY_ENTITY,\n Role,\n RoleBasedPolicy,\n RoleConditionalPolicyDecision,\n} from '@backstage-community/plugin-rbac-common';\nimport type { RBACProvider } from '@backstage-community/plugin-rbac-node';\n\nimport { setAuditorError, logAuditorEvent } from '../auditor/rest-interceptor';\nimport { ConditionalStorage } from '../database/conditional-storage';\nimport {\n daoToMetadata,\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport {\n buildRoleSourceMap,\n deepSortedEqual,\n isPermissionAction,\n policyToString,\n processConditionMapping,\n matches,\n} from '../helper';\nimport { validateRoleCondition } from '../validation/condition-validation';\nimport {\n validateEntityReference,\n validatePolicy,\n validateRole,\n validateSource,\n} from '../validation/policies-validation';\nimport { EnforcerDelegate } from './enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from './plugin-endpoints';\nimport { RBACRouterOptions } from './policy-builder';\nimport { RBACFilters, rules, transformConditions } from '../permissions';\n\nexport class PoliciesServer {\n constructor(\n private readonly permissions: PermissionsService,\n private readonly options: RBACRouterOptions,\n private readonly enforcer: EnforcerDelegate,\n private readonly conditionalStorage: ConditionalStorage,\n private readonly pluginPermMetaData: PluginPermissionMetadataCollector,\n private readonly roleMetadata: RoleMetadataStorage,\n private readonly auditor: AuditorService,\n private readonly rbacProviders?: RBACProvider[],\n ) {}\n\n private async authorizeConditional(\n request: Request,\n permission: ResourcePermission<'policy-entity'> | BasicPermission,\n ): Promise<PolicyDecision> {\n const credentials = await this.options.httpAuth.credentials(request, {\n allow: ['user', 'service'],\n });\n\n // allow service to service communication, but only with read permission\n if (\n this.options.auth.isPrincipal(credentials, 'service') &&\n permission !== policyEntityReadPermission\n ) {\n throw new NotAllowedError(\n `Only creadential principal with type 'user' permitted to modify permissions`,\n );\n }\n\n let decision: PolicyDecision;\n if (permission.type === 'resource') {\n decision = (\n await this.permissions.authorizeConditional([{ permission }], {\n credentials,\n })\n )[0];\n } else {\n decision = (\n await this.permissions.authorize([{ permission }], {\n credentials,\n })\n )[0];\n }\n\n return decision;\n }\n\n async serve(): Promise<express.Router> {\n const router = await createRouter(this.options);\n\n const { httpAuth, logger } = this.options;\n\n if (!httpAuth) {\n throw new ServiceUnavailableError(\n 'httpAuth not found, ensure the correct configuration for the RBAC plugin',\n );\n }\n\n const policyPermissionsIntegrationRouter =\n createPermissionIntegrationRouter({\n resourceType: RESOURCE_TYPE_POLICY_ENTITY,\n getResources: resourceRefs =>\n Promise.all(\n resourceRefs.map(ref => {\n return this.roleMetadata.findRoleMetadata(ref);\n }),\n ),\n permissions: policyEntityPermissions,\n rules: Object.values(rules),\n });\n\n router.use(policyPermissionsIntegrationRouter);\n\n const isPluginEnabled =\n this.options.config.getOptionalBoolean('permission.enabled');\n if (!isPluginEnabled) {\n return router;\n }\n\n router.get('/', async (request, response) => {\n const decision = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n response.send({ status: 'Authorized' });\n });\n\n // Policy CRUD\n\n router.get(\n '/policies',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleMetadata =\n await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);\n\n let policies: string[][] = [];\n if (this.isPolicyFilterEnabled(request)) {\n const entityRef = this.getFirstQuery(request.query.entityRef);\n const permission = this.getFirstQuery(request.query.permission);\n const policy = this.getFirstQuery(request.query.policy);\n const effect = this.getFirstQuery(request.query.effect);\n\n const matchedRoleName = roleMetadata.flatMap(\n role => role.roleEntityRef,\n );\n\n const filter: string[] = [entityRef, permission, policy, effect];\n policies = matchedRoleName.includes(entityRef)\n ? await this.enforcer.getFilteredPolicy(0, ...filter)\n : [];\n } else {\n for (const role of roleMetadata) {\n policies.push(\n ...(await this.enforcer.getFilteredPolicy(\n 0,\n ...[role.roleEntityRef],\n )),\n );\n }\n }\n\n const body = await this.transformPolicyArray(...policies);\n // TODO: Temporary workaround to prevent breakages after the removal of the resource type `policy-entity` from the permission `policy.entity.create`\n body.map(policy => {\n if (\n policy.permission === 'policy-entity' &&\n policy.policy === 'create'\n ) {\n policy.permission = 'policy.entity.create';\n logger.warn(\n `Permission policy with resource type 'policy-entity' and action 'create' has been removed. Please consider updating policy ${[policy.entityReference, 'policy-entity', policy.policy, policy.effect]} to use 'policy.entity.create' instead of 'policy-entity' from source ${policy.metadata?.source}`,\n );\n }\n });\n\n response.json(body);\n },\n );\n\n router.get(\n '/policies/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleMetadata =\n await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);\n\n const matchedRoleName = roleMetadata.flatMap(role => {\n return role.roleEntityRef;\n });\n\n const entityRef = this.getEntityReference(request);\n\n const policy = matchedRoleName.includes(entityRef)\n ? await this.enforcer.getFilteredPolicy(0, entityRef)\n : [];\n if (policy.length !== 0) {\n const body = await this.transformPolicyArray(...policy);\n // TODO: Temporary workaround to prevent breakages after the removal of the resource type `policy-entity` from the permission `policy.entity.create`\n body.map(bodyPolicy => {\n if (\n bodyPolicy.permission === 'policy-entity' &&\n bodyPolicy.policy === 'create'\n ) {\n bodyPolicy.permission = 'policy.entity.create';\n logger.warn(\n `Permission policy with resource type 'policy-entity' and action 'create' has been removed. Please consider updating policy ${[bodyPolicy.entityReference, 'policy-entity', bodyPolicy.policy, bodyPolicy.effect]} to use 'policy.entity.create' instead of 'policy-entity' from source ${bodyPolicy.metadata?.source}`,\n );\n }\n });\n\n response.json(body);\n } else {\n throw new NotFoundError(); // 404\n }\n },\n );\n\n router.delete(\n '/policies/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityDeletePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const entityRef = this.getEntityReference(request);\n\n const policyRaw: RoleBasedPolicy[] = request.body;\n if (isEmpty(policyRaw)) {\n throw new InputError(`permission policy must be present`); // 400\n }\n\n policyRaw.forEach(element => {\n element.entityReference = entityRef;\n });\n\n const processedPolicies = await this.processPolicies(\n policyRaw,\n true,\n undefined,\n conditionsFilter,\n );\n\n await this.enforcer.removePolicies(processedPolicies);\n\n response.locals.meta = { policies: processedPolicies }; // auditor\n\n response.status(204).end();\n },\n );\n\n router.post(\n '/policies',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n const decision = await this.authorizeConditional(\n request,\n policyEntityCreatePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n const policyRaw: RoleBasedPolicy[] = request.body;\n\n if (isEmpty(policyRaw)) {\n throw new InputError(`permission policy must be present`); // 400\n }\n\n const processedPolicies = await this.processPolicies(\n policyRaw,\n false,\n undefined,\n );\n\n const entityRef = processedPolicies[0][0];\n const roleMetadata =\n await this.roleMetadata.findRoleMetadata(entityRef);\n if (entityRef.startsWith('role:default') && !roleMetadata) {\n throw new Error(`Corresponding role ${entityRef} was not found`);\n }\n\n await this.enforcer.addPolicies(processedPolicies);\n\n response.locals.meta = { policies: processedPolicies }; // auditor\n\n response.status(201).end();\n },\n );\n\n router.put(\n '/policies/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityUpdatePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const entityRef = this.getEntityReference(request);\n\n const oldPolicyRaw: RoleBasedPolicy[] = request.body.oldPolicy;\n if (isEmpty(oldPolicyRaw)) {\n throw new InputError(`'oldPolicy' object must be present`); // 400\n }\n const newPolicyRaw: RoleBasedPolicy[] = request.body.newPolicy;\n if (isEmpty(newPolicyRaw)) {\n throw new InputError(`'newPolicy' object must be present`); // 400\n }\n\n [...oldPolicyRaw, ...newPolicyRaw].forEach(element => {\n element.entityReference = entityRef;\n });\n\n const processedOldPolicy = await this.processPolicies(\n oldPolicyRaw,\n true,\n 'old policy',\n conditionsFilter,\n );\n\n oldPolicyRaw.sort((a, b) =>\n a.permission === b.permission\n ? this.nameSort(a.policy!, b.policy!)\n : this.nameSort(a.permission!, b.permission!),\n );\n\n newPolicyRaw.sort((a, b) =>\n a.permission === b.permission\n ? this.nameSort(a.policy!, b.policy!)\n : this.nameSort(a.permission!, b.permission!),\n );\n\n if (\n isEqual(oldPolicyRaw, newPolicyRaw) &&\n !oldPolicyRaw.some(isEmpty)\n ) {\n response.status(204).end();\n } else if (oldPolicyRaw.length > newPolicyRaw.length) {\n throw new InputError(\n `'oldPolicy' object has more permission policies compared to 'newPolicy' object`,\n );\n }\n\n const processedNewPolicy = await this.processPolicies(\n newPolicyRaw,\n false,\n 'new policy',\n conditionsFilter,\n );\n\n const roleMetadata =\n await this.roleMetadata.findRoleMetadata(entityRef);\n if (entityRef.startsWith('role:default') && !roleMetadata) {\n throw new Error(`Corresponding role ${entityRef} was not found`);\n }\n\n await this.enforcer.updatePolicies(\n processedOldPolicy,\n processedNewPolicy,\n );\n\n response.locals.meta = { policies: processedNewPolicy }; // auditor\n\n response.status(200).end();\n },\n );\n\n // Role CRUD\n\n router.get(\n '/roles',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roles = await this.enforcer.getGroupingPolicy();\n const body = await this.transformRoleArray(conditionsFilter, ...roles);\n\n response.json(body);\n },\n );\n\n router.get(\n '/roles/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleEntityRef = this.getEntityReference(request, true);\n\n const role = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n\n const body = await this.transformRoleArray(conditionsFilter, ...role);\n if (body.length !== 0) {\n response.json(body);\n } else {\n throw new NotFoundError(); // 404\n }\n },\n );\n\n router.post(\n '/roles',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n const uniqueItems = new Set<string>();\n const decision = await this.authorizeConditional(\n request,\n policyEntityCreatePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n const roleRaw: Role = request.body;\n let err = validateRole(roleRaw);\n if (err) {\n throw new InputError( // 400\n `Invalid role definition. Cause: ${err.message}`,\n );\n }\n this.transformMemberReferencesToLowercase(roleRaw);\n\n const rMetadata = await this.roleMetadata.findRoleMetadata(\n roleRaw.name,\n );\n\n err = await validateSource('rest', rMetadata);\n if (err) {\n throw new NotAllowedError(`Unable to add role: ${err.message}`);\n }\n\n const roles = this.transformRoleToArray(roleRaw);\n\n for (const role of roles) {\n if (await this.enforcer.hasGroupingPolicy(...role)) {\n throw new ConflictError(); // 409\n }\n const roleString = JSON.stringify(role);\n\n if (uniqueItems.has(roleString)) {\n throw new ConflictError(\n `Duplicate role members found; ${role.at(0)}, ${role.at(\n 1,\n )} is a duplicate`,\n );\n } else {\n uniqueItems.add(roleString);\n }\n }\n\n const credentials = await httpAuth.credentials(request, {\n allow: ['user'],\n });\n const modifiedBy = credentials.principal.userEntityRef;\n const metadata: RoleMetadataDao = {\n roleEntityRef: roleRaw.name,\n source: 'rest',\n description: roleRaw.metadata?.description ?? '',\n author: modifiedBy,\n modifiedBy,\n owner: roleRaw.metadata?.owner ?? modifiedBy,\n };\n\n await this.enforcer.addGroupingPolicies(roles, metadata);\n\n response.locals.meta = { ...metadata, members: roles.map(gp => gp[0]) }; // auditor\n\n response.status(201).end();\n },\n );\n\n router.put(\n '/roles/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n const uniqueItems = new Set<string>();\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityUpdatePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleEntityRef = this.getEntityReference(request, true);\n\n const oldRoleRaw: Role = request.body.oldRole;\n\n if (!oldRoleRaw) {\n throw new InputError(`'oldRole' object must be present`); // 400\n }\n const newRoleRaw: Role = request.body.newRole;\n if (!newRoleRaw) {\n throw new InputError(`'newRole' object must be present`); // 400\n }\n\n oldRoleRaw.name = roleEntityRef;\n let err = validateRole(oldRoleRaw);\n if (err) {\n throw new InputError( // 400\n `Invalid old role object. Cause: ${err.message}`,\n );\n }\n err = validateRole(newRoleRaw);\n if (err) {\n throw new InputError( // 400\n `Invalid new role object. Cause: ${err.message}`,\n );\n }\n this.transformMemberReferencesToLowercase(oldRoleRaw);\n this.transformMemberReferencesToLowercase(newRoleRaw);\n\n const oldRole = this.transformRoleToArray(oldRoleRaw);\n const newRole = this.transformRoleToArray(newRoleRaw);\n // todo shell we allow newRole with an empty array?...\n\n const credentials = await httpAuth.credentials(request, {\n allow: ['user'],\n });\n\n const newMetadata: RoleMetadataDao = {\n ...newRoleRaw.metadata,\n source: newRoleRaw.metadata?.source ?? 'rest',\n roleEntityRef: newRoleRaw.name,\n modifiedBy: credentials.principal.userEntityRef,\n owner: newRoleRaw.metadata?.owner ?? '',\n };\n\n const oldMetadata =\n await this.roleMetadata.findRoleMetadata(roleEntityRef);\n if (!oldMetadata) {\n throw new NotFoundError(\n `Unable to find metadata for ${roleEntityRef}`,\n );\n }\n\n err = await validateSource('rest', oldMetadata);\n if (err) {\n throw new NotAllowedError(`Unable to edit role: ${err.message}`);\n }\n\n if (!matches(oldMetadata, conditionsFilter)) {\n throw new NotAllowedError(); // 403\n }\n\n if (\n isEqual(oldRole, newRole) &&\n deepSortedEqual(oldMetadata, newMetadata, [\n 'author',\n 'modifiedBy',\n 'createdAt',\n 'lastModified',\n 'owner',\n ])\n ) {\n // no content: old role and new role are equal and their metadata too\n response.status(204).end();\n return;\n }\n\n for (const role of newRole) {\n const hasRole = oldRole.some(element => {\n return isEqual(element, role);\n });\n // if the role is already part of old role and is a grouping policy we want to skip returning a conflict error\n // to allow for other roles to be checked and added\n if (await this.enforcer.hasGroupingPolicy(...role)) {\n if (!hasRole) {\n throw new ConflictError(); // 409\n }\n }\n const roleString = JSON.stringify(role);\n\n if (uniqueItems.has(roleString)) {\n throw new ConflictError(\n `Duplicate role members found; ${role.at(0)}, ${role.at(\n 1,\n )} is a duplicate`,\n );\n } else {\n uniqueItems.add(roleString);\n }\n }\n\n uniqueItems.clear();\n for (const role of oldRole) {\n if (!(await this.enforcer.hasGroupingPolicy(...role))) {\n throw new NotFoundError(\n `Member reference: ${role[0]} was not found for role ${roleEntityRef}`,\n ); // 404\n }\n const roleString = JSON.stringify(role);\n\n if (uniqueItems.has(roleString)) {\n throw new ConflictError(\n `Duplicate role members found; ${role.at(0)}, ${role.at(\n 1,\n )} is a duplicate`,\n );\n } else {\n uniqueItems.add(roleString);\n }\n }\n\n await this.enforcer.updateGroupingPolicies(\n oldRole,\n newRole,\n newMetadata,\n );\n\n let message = `Updated ${oldMetadata.roleEntityRef}.`;\n if (newMetadata.roleEntityRef !== oldMetadata.roleEntityRef) {\n message = `${message}. Role entity reference renamed to ${newMetadata.roleEntityRef}`;\n }\n response.locals.meta = {\n ...newMetadata,\n members: newRole.map(gp => gp[0]),\n }; // auditor\n\n response.status(200).end();\n },\n );\n\n router.delete(\n '/roles/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityDeletePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleEntityRef = this.getEntityReference(request, true);\n\n const currentMetadata =\n await this.roleMetadata.findRoleMetadata(roleEntityRef);\n\n if (!matches(currentMetadata, conditionsFilter)) {\n throw new NotAllowedError(); // 403\n }\n\n const err = await validateSource('rest', currentMetadata);\n if (err) {\n throw new NotAllowedError(`Unable to delete role: ${err.message}`);\n }\n\n let roleMembers = [];\n if (request.query.memberReferences) {\n const memberReference = this.getFirstQuery(\n request.query.memberReferences!,\n ).toLocaleLowerCase('en-US');\n const gp = await this.enforcer.getFilteredGroupingPolicy(\n 0,\n memberReference,\n roleEntityRef,\n );\n if (gp.length > 0) {\n roleMembers.push(gp[0]);\n } else {\n throw new NotFoundError(\n `role member '${memberReference}' was not found`,\n ); // 404\n }\n } else {\n roleMembers = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n }\n\n for (const role of roleMembers) {\n if (!(await this.enforcer.hasGroupingPolicy(...role))) {\n throw new NotFoundError(`role member '${role[0]}' was not found`);\n }\n }\n\n const credentials = await httpAuth.credentials(request, {\n allow: ['user'],\n });\n\n const metadata: RoleMetadataDao = {\n roleEntityRef,\n source: 'rest',\n modifiedBy: credentials.principal.userEntityRef,\n };\n\n await this.enforcer.removeGroupingPolicies(\n roleMembers,\n metadata,\n false,\n );\n\n response.locals.meta = {\n ...metadata,\n members: roleMembers.map(gp => gp[0]),\n }; // auditor\n\n response.status(204).end();\n },\n );\n\n router.get(\n '/plugins/policies',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n const decision = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n const body = await this.pluginPermMetaData.getPluginPolicies(\n this.options.auth,\n );\n\n response.json(body);\n },\n );\n\n router.get(\n '/plugins/condition-rules',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n const decision = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n const body = await this.pluginPermMetaData.getPluginConditionRules(\n this.options.auth,\n );\n\n response.json(body);\n },\n );\n\n router.get(\n '/roles/conditions',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleMetadata =\n await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);\n\n const matchedRoleName = roleMetadata.flatMap(role => {\n return role.roleEntityRef;\n });\n\n const conditions = await this.conditionalStorage.filterConditions(\n this.getFirstQuery(request.query.roleEntityRef),\n this.getFirstQuery(request.query.pluginId),\n this.getFirstQuery(request.query.resourceType),\n this.getActionQueries(request.query.actions),\n );\n\n const body: RoleConditionalPolicyDecision<PermissionAction>[] =\n conditions\n .map(condition => {\n return {\n ...condition,\n permissionMapping: condition.permissionMapping.map(\n pm => pm.action,\n ),\n };\n })\n .filter(condition => {\n return matchedRoleName.includes(condition.roleEntityRef);\n });\n\n response.json(body);\n },\n );\n\n router.post(\n '/roles/conditions',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n const decision = await this.authorizeConditional(\n request,\n policyEntityCreatePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n const roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction> =\n request.body;\n validateRoleCondition(roleConditionPolicy);\n\n const conditionToCreate = await processConditionMapping(\n roleConditionPolicy,\n this.pluginPermMetaData,\n this.options.auth,\n );\n\n const id =\n await this.conditionalStorage.createCondition(conditionToCreate);\n\n const body = { id: id };\n\n response.locals.meta = { condition: roleConditionPolicy }; // auditor\n\n response.status(201).json(body);\n },\n );\n\n router.get(\n '/roles/conditions/:id',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n const id: number = parseInt(request.params.id, 10);\n if (isNaN(id)) {\n throw new InputError('Id is not a valid number.');\n }\n\n const condition = await this.conditionalStorage.getCondition(id);\n if (!condition) {\n throw new NotFoundError();\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleMetadata =\n await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);\n\n const matchedRoleName = roleMetadata.flatMap(role => {\n return role.roleEntityRef;\n });\n\n const body: RoleConditionalPolicyDecision<PermissionAction> | [] =\n matchedRoleName.includes(condition.roleEntityRef)\n ? {\n ...condition,\n permissionMapping: condition.permissionMapping.map(\n pm => pm.action,\n ),\n }\n : [];\n\n response.json(body);\n },\n );\n\n router.delete(\n '/roles/conditions/:id',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityDeletePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const id: number = parseInt(request.params.id, 10);\n if (isNaN(id)) {\n throw new InputError('Id is not a valid number.');\n }\n\n const condition = await this.conditionalStorage.getCondition(id);\n if (!condition) {\n throw new NotFoundError(`Condition with id ${id} was not found`);\n }\n const conditionToDelete: RoleConditionalPolicyDecision<PermissionAction> =\n {\n ...condition,\n permissionMapping: condition.permissionMapping.map(pm => pm.action),\n };\n\n const roleMetadata = await this.roleMetadata.findRoleMetadata(\n conditionToDelete.roleEntityRef,\n );\n\n if (!matches(roleMetadata, conditionsFilter)) {\n throw new NotAllowedError(); // 403\n }\n\n await this.conditionalStorage.deleteCondition(id);\n response.locals.meta = { condition: conditionToDelete }; // auditor\n\n response.status(204).end();\n },\n );\n\n router.put(\n '/roles/conditions/:id',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const decision = await this.authorizeConditional(\n request,\n policyEntityUpdatePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const id: number = parseInt(request.params.id, 10);\n if (isNaN(id)) {\n throw new InputError('Id is not a valid number.');\n }\n\n const condition = await this.conditionalStorage.getCondition(id);\n\n if (!condition) {\n throw new NotFoundError(`Condition with id ${id} was not found`);\n }\n\n const roleMetadata = await this.roleMetadata.findRoleMetadata(\n condition.roleEntityRef,\n );\n\n if (!matches(roleMetadata, conditionsFilter)) {\n throw new NotAllowedError(); // 403\n }\n\n const roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction> =\n request.body;\n\n validateRoleCondition(roleConditionPolicy);\n\n const conditionToUpdate = await processConditionMapping(\n roleConditionPolicy,\n this.pluginPermMetaData,\n this.options.auth,\n );\n\n await this.conditionalStorage.updateCondition(id, conditionToUpdate);\n\n response.locals.meta = { condition: roleConditionPolicy }; // auditor\n\n response.status(200).end();\n },\n );\n\n router.post(\n '/refresh/:id',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n const decision = await this.authorizeConditional(\n request,\n policyEntityCreatePermission,\n );\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n if (!this.rbacProviders) {\n throw new NotFoundError(`No RBAC providers were found`);\n }\n\n const idProvider = this.rbacProviders.find(provider => {\n const id = provider.getProviderName();\n return id === request.params.id;\n });\n\n if (!idProvider) {\n throw new NotFoundError(\n `The RBAC provider ${request.params.id} was not found`,\n );\n }\n\n await idProvider.refresh();\n response.status(200).end();\n },\n );\n\n router.use(setAuditorError());\n\n return router;\n }\n\n getEntityReference(request: Request, role?: boolean): string {\n const kind = request.params.kind;\n const namespace = request.params.namespace;\n const name = request.params.name;\n const entityRef = `${kind}:${namespace}/${name}`;\n\n const err = validateEntityReference(entityRef, role);\n if (err) {\n throw new InputError(err.message);\n }\n\n return entityRef;\n }\n\n async transformPolicyArray(\n ...policies: string[][]\n ): Promise<RoleBasedPolicy[]> {\n const roleToSourceMap = await buildRoleSourceMap(\n policies,\n this.roleMetadata,\n );\n\n const roleBasedPolices: RoleBasedPolicy[] = [];\n for (const p of policies) {\n const [entityReference, permission, policy, effect] = p;\n roleBasedPolices.push({\n entityReference,\n permission,\n policy,\n effect,\n metadata: { source: roleToSourceMap.get(entityReference)! },\n });\n }\n\n return roleBasedPolices;\n }\n\n async transformRoleArray(\n filter?: RBACFilters,\n ...roles: string[][]\n ): Promise<Role[]> {\n const combinedRoles: { [key: string]: string[] } = {};\n\n roles.forEach(([value, role]) => {\n if (combinedRoles.hasOwnProperty(role)) {\n combinedRoles[role].push(value);\n } else {\n combinedRoles[role] = [value];\n }\n });\n\n const result: Role[] = await Promise.all(\n Object.entries(combinedRoles).flatMap(async ([role, value]) => {\n const metadataDao = await this.roleMetadata.findRoleMetadata(role);\n const metadata = metadataDao ? daoToMetadata(metadataDao) : undefined;\n return Promise.resolve({\n memberReferences: value,\n name: role,\n metadata,\n });\n }),\n );\n\n const filteredResult = result.filter(role => {\n return role.metadata && matches(role.metadata, filter);\n });\n\n return filteredResult;\n }\n\n transformPolicyToArray(policy: RoleBasedPolicy): string[] {\n return [\n policy.entityReference!,\n policy.permission!,\n policy.policy!,\n policy.effect!,\n ];\n }\n\n transformRoleToArray(role: Role): string[][] {\n const roles: string[][] = [];\n for (const entity of role.memberReferences) {\n roles.push([entity, role.name]);\n }\n return roles;\n }\n\n transformMemberReferencesToLowercase(role: Role) {\n role.memberReferences = role.memberReferences.map(member =>\n member.toLocaleLowerCase('en-US'),\n );\n }\n\n getActionQueries(\n queryValue: string | string[] | ParsedQs | ParsedQs[] | undefined,\n ): PermissionAction[] | undefined {\n if (!queryValue) {\n return undefined;\n }\n if (Array.isArray(queryValue)) {\n const permissionNames: PermissionAction[] = [];\n for (const permissionQuery of queryValue) {\n if (\n typeof permissionQuery === 'string' &&\n isPermissionAction(permissionQuery)\n ) {\n permissionNames.push(permissionQuery);\n } else {\n throw new InputError(\n `Invalid permission action query value: ${permissionQuery}. Permission name should be string.`,\n );\n }\n }\n return permissionNames;\n }\n\n if (typeof queryValue === 'string' && isPermissionAction(queryValue)) {\n return [queryValue];\n }\n throw new InputError(\n `Invalid permission action query value: ${queryValue}. Permission name should be string.`,\n );\n }\n\n getFirstQuery(\n queryValue: string | string[] | ParsedQs | ParsedQs[] | undefined,\n ): string {\n if (!queryValue) {\n return '';\n }\n if (Array.isArray(queryValue)) {\n if (typeof queryValue[0] === 'string') {\n return queryValue[0].toString();\n }\n throw new InputError(`This api doesn't support nested query`);\n }\n\n if (typeof queryValue === 'string') {\n return queryValue;\n }\n throw new InputError(`This api doesn't support nested query`);\n }\n\n isPolicyFilterEnabled(request: Request): boolean {\n return (\n !!request.query.entityRef ||\n !!request.query.permission ||\n !!request.query.policy ||\n !!request.query.effect\n );\n }\n\n async processPolicies(\n policyArray: RoleBasedPolicy[],\n isOld?: boolean,\n errorMessage?: string,\n filter?: RBACFilters,\n ): Promise<string[][]> {\n const policies: string[][] = [];\n const uniqueItems = new Set<string>();\n for (const policy of policyArray) {\n let err = validatePolicy(policy);\n if (err) {\n throw new InputError(\n `Invalid ${errorMessage ?? 'policy'} definition. Cause: ${\n err.message\n }`,\n ); // 400\n }\n\n const metadata = await this.roleMetadata.findRoleMetadata(\n policy.entityReference!,\n );\n\n if (!matches(metadata, filter)) {\n throw new NotAllowedError(); // 403\n }\n\n let action = errorMessage ? 'edit' : 'delete';\n action = isOld ? action : 'add';\n\n err = await validateSource('rest', metadata);\n if (err) {\n throw new NotAllowedError(\n `Unable to ${action} policy ${policy.entityReference},${policy.permission},${policy.policy},${policy.effect}: ${err.message}`,\n );\n }\n\n const transformedPolicy = this.transformPolicyToArray(policy);\n if (isOld && !(await this.enforcer.hasPolicy(...transformedPolicy))) {\n throw new NotFoundError(\n `Policy '${policyToString(transformedPolicy)}' not found`,\n ); // 404\n }\n\n if (!isOld && (await this.enforcer.hasPolicy(...transformedPolicy))) {\n throw new ConflictError(\n `Policy '${policyToString(\n transformedPolicy,\n )}' has been already stored`,\n ); // 409\n }\n\n // We want to ensure that there are not duplicate permission policies\n const rowString = JSON.stringify(transformedPolicy);\n if (uniqueItems.has(rowString)) {\n throw new ConflictError(\n `Duplicate polices found; ${policy.entityReference}, ${policy.permission}, ${policy.policy}, ${policy.effect} is a duplicate`,\n );\n } else {\n uniqueItems.add(rowString);\n policies.push(transformedPolicy);\n }\n }\n return policies;\n }\n\n nameSort(nameA: string, nameB: string): number {\n if (nameA.toLocaleUpperCase('en-US') < nameB.toLocaleUpperCase('en-US')) {\n return -1;\n }\n if (nameA.toLocaleUpperCase('en-US') > nameB.toLocaleUpperCase('en-US')) {\n return 1;\n }\n return 0;\n }\n}\n"],"names":["policyEntityReadPermission","NotAllowedError","createRouter","ServiceUnavailableError","createPermissionIntegrationRouter","RESOURCE_TYPE_POLICY_ENTITY","policyEntityPermissions","rules","AuthorizeResult","logAuditorEvent","transformConditions","NotFoundError","policyEntityDeletePermission","isEmpty","InputError","policyEntityCreatePermission","policyEntityUpdatePermission","isEqual","validateRole","validateSource","ConflictError","matches","deepSortedEqual","conditions","validateRoleCondition","processConditionMapping","setAuditorError","validateEntityReference","buildRoleSourceMap","daoToMetadata","isPermissionAction","validatePolicy","policyToString"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"policies-rest-api.cjs.js","sources":["../../src/service/policies-rest-api.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 AuditorService,\n BackstageCredentials,\n BackstageServicePrincipal,\n BackstageUserPrincipal,\n PermissionsService,\n} from '@backstage/backend-plugin-api';\nimport {\n ConflictError,\n InputError,\n NotAllowedError,\n NotFoundError,\n} from '@backstage/errors';\nimport { createRouter } from '@backstage/plugin-permission-backend';\nimport {\n AuthorizeResult,\n BasicPermission,\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node';\n\nimport express from 'express';\nimport type { Request } from 'express-serve-static-core';\nimport { isEmpty, isEqual } from 'lodash';\nimport type { ParsedQs } from 'qs';\n\nimport {\n PermissionAction,\n policyEntityCreatePermission,\n policyEntityDeletePermission,\n policyEntityPermissions,\n policyEntityReadPermission,\n policyEntityUpdatePermission,\n RESOURCE_TYPE_POLICY_ENTITY,\n Role,\n RoleBasedPolicy,\n RoleConditionalPolicyDecision,\n} from '@backstage-community/plugin-rbac-common';\nimport type { RBACProvider } from '@backstage-community/plugin-rbac-node';\n\nimport { setAuditorError, logAuditorEvent } from '../auditor/rest-interceptor';\nimport { ConditionalStorage } from '../database/conditional-storage';\nimport {\n daoToMetadata,\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport {\n buildRoleSourceMap,\n deepSortedEqual,\n isPermissionAction,\n policyToString,\n processConditionMapping,\n matches,\n} from '../helper';\nimport { validateRoleCondition } from '../validation/condition-validation';\nimport {\n validateEntityReference,\n validatePolicy,\n validateRole,\n validateSource,\n} from '../validation/policies-validation';\nimport { EnforcerDelegate } from './enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from './plugin-endpoints';\nimport { RBACRouterOptions } from './policy-builder';\nimport { RBACFilters, rules, transformConditions } from '../permissions';\n\nexport class PoliciesServer {\n constructor(\n private readonly permissions: PermissionsService,\n private readonly options: RBACRouterOptions,\n private readonly enforcer: EnforcerDelegate,\n private readonly conditionalStorage: ConditionalStorage,\n private readonly pluginPermMetaData: PluginPermissionMetadataCollector,\n private readonly roleMetadata: RoleMetadataStorage,\n private readonly auditor: AuditorService,\n private readonly rbacProviders?: RBACProvider[],\n ) {}\n\n private async authorizeConditional(\n request: Request,\n permission: ResourcePermission<'policy-entity'> | BasicPermission,\n ): Promise<{\n decision: PolicyDecision;\n credentials: BackstageCredentials<\n BackstageUserPrincipal | BackstageServicePrincipal\n >;\n }> {\n const credentials = await this.options.httpAuth.credentials(request, {\n allow: ['user', 'service'],\n });\n\n // allow service to service communication, but only with read permission\n if (\n this.options.auth.isPrincipal(credentials, 'service') &&\n permission !== policyEntityReadPermission\n ) {\n throw new NotAllowedError(\n `Only credential principal with type 'user' permitted to modify permissions`,\n );\n }\n\n let decision: PolicyDecision;\n if (permission.type === 'resource') {\n decision = (\n await this.permissions.authorizeConditional([{ permission }], {\n credentials,\n })\n )[0];\n } else {\n decision = (\n await this.permissions.authorize([{ permission }], {\n credentials,\n })\n )[0];\n }\n\n if (decision.result === AuthorizeResult.DENY) {\n throw new NotAllowedError(); // 403\n }\n\n return { decision, credentials };\n }\n\n async serve(): Promise<express.Router> {\n const router = await createRouter(this.options);\n\n const { logger } = this.options;\n\n const policyPermissionsIntegrationRouter =\n createPermissionIntegrationRouter({\n resourceType: RESOURCE_TYPE_POLICY_ENTITY,\n getResources: resourceRefs =>\n Promise.all(\n resourceRefs.map(ref => {\n return this.roleMetadata.findRoleMetadata(ref);\n }),\n ),\n permissions: policyEntityPermissions,\n rules: Object.values(rules),\n });\n\n router.use(policyPermissionsIntegrationRouter);\n\n const isPluginEnabled =\n this.options.config.getOptionalBoolean('permission.enabled');\n if (!isPluginEnabled) {\n return router;\n }\n\n router.get('/', async (request, response) => {\n await this.authorizeConditional(request, policyEntityReadPermission);\n\n response.send({ status: 'Authorized' });\n });\n\n // Policy CRUD\n\n router.get(\n '/policies',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleMetadata =\n await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);\n\n let policies: string[][] = [];\n if (this.isPolicyFilterEnabled(request)) {\n const entityRef = this.getFirstQuery(request.query.entityRef);\n const permission = this.getFirstQuery(request.query.permission);\n const policy = this.getFirstQuery(request.query.policy);\n const effect = this.getFirstQuery(request.query.effect);\n\n const matchedRoleName = roleMetadata.flatMap(\n role => role.roleEntityRef,\n );\n\n const filter: string[] = [entityRef, permission, policy, effect];\n policies = matchedRoleName.includes(entityRef)\n ? await this.enforcer.getFilteredPolicy(0, ...filter)\n : [];\n } else {\n for (const role of roleMetadata) {\n policies.push(\n ...(await this.enforcer.getFilteredPolicy(\n 0,\n ...[role.roleEntityRef],\n )),\n );\n }\n }\n\n const body = await this.transformPolicyArray(...policies);\n // TODO: Temporary workaround to prevent breakages after the removal of the resource type `policy-entity` from the permission `policy.entity.create`\n body.map(policy => {\n if (\n policy.permission === 'policy-entity' &&\n policy.policy === 'create'\n ) {\n policy.permission = 'policy.entity.create';\n logger.warn(\n `Permission policy with resource type 'policy-entity' and action 'create' has been removed. Please consider updating policy ${[policy.entityReference, 'policy-entity', policy.policy, policy.effect]} to use 'policy.entity.create' instead of 'policy-entity' from source ${policy.metadata?.source}`,\n );\n }\n });\n\n response.json(body);\n },\n );\n\n router.get(\n '/policies/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleMetadata =\n await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);\n\n const matchedRoleName = roleMetadata.flatMap(role => {\n return role.roleEntityRef;\n });\n\n const entityRef = this.getEntityReference(request);\n\n const policy = matchedRoleName.includes(entityRef)\n ? await this.enforcer.getFilteredPolicy(0, entityRef)\n : [];\n if (policy.length !== 0) {\n const body = await this.transformPolicyArray(...policy);\n // TODO: Temporary workaround to prevent breakages after the removal of the resource type `policy-entity` from the permission `policy.entity.create`\n body.map(bodyPolicy => {\n if (\n bodyPolicy.permission === 'policy-entity' &&\n bodyPolicy.policy === 'create'\n ) {\n bodyPolicy.permission = 'policy.entity.create';\n logger.warn(\n `Permission policy with resource type 'policy-entity' and action 'create' has been removed. Please consider updating policy ${[bodyPolicy.entityReference, 'policy-entity', bodyPolicy.policy, bodyPolicy.effect]} to use 'policy.entity.create' instead of 'policy-entity' from source ${bodyPolicy.metadata?.source}`,\n );\n }\n });\n\n response.json(body);\n } else {\n throw new NotFoundError(); // 404\n }\n },\n );\n\n router.delete(\n '/policies/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityDeletePermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const entityRef = this.getEntityReference(request);\n\n const policyRaw: RoleBasedPolicy[] = request.body;\n if (isEmpty(policyRaw)) {\n throw new InputError(`permission policy must be present`); // 400\n }\n\n policyRaw.forEach(element => {\n element.entityReference = entityRef;\n });\n\n const processedPolicies = await this.processPolicies(\n policyRaw,\n true,\n undefined,\n conditionsFilter,\n );\n\n await this.enforcer.removePolicies(processedPolicies);\n\n response.locals.meta = { policies: processedPolicies }; // auditor\n\n response.status(204).end();\n },\n );\n\n router.post(\n '/policies',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n await this.authorizeConditional(request, policyEntityCreatePermission);\n\n const policyRaw: RoleBasedPolicy[] = request.body;\n\n if (isEmpty(policyRaw)) {\n throw new InputError(`permission policy must be present`); // 400\n }\n\n const processedPolicies = await this.processPolicies(\n policyRaw,\n false,\n undefined,\n );\n\n const entityRef = processedPolicies[0][0];\n const roleMetadata =\n await this.roleMetadata.findRoleMetadata(entityRef);\n if (entityRef.startsWith('role:default') && !roleMetadata) {\n throw new Error(`Corresponding role ${entityRef} was not found`);\n }\n\n await this.enforcer.addPolicies(processedPolicies);\n\n response.locals.meta = { policies: processedPolicies }; // auditor\n\n response.status(201).end();\n },\n );\n\n router.put(\n '/policies/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityUpdatePermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const entityRef = this.getEntityReference(request);\n\n const oldPolicyRaw: RoleBasedPolicy[] = request.body.oldPolicy;\n if (isEmpty(oldPolicyRaw)) {\n throw new InputError(`'oldPolicy' object must be present`); // 400\n }\n const newPolicyRaw: RoleBasedPolicy[] = request.body.newPolicy;\n if (isEmpty(newPolicyRaw)) {\n throw new InputError(`'newPolicy' object must be present`); // 400\n }\n\n [...oldPolicyRaw, ...newPolicyRaw].forEach(element => {\n element.entityReference = entityRef;\n });\n\n const processedOldPolicy = await this.processPolicies(\n oldPolicyRaw,\n true,\n 'old policy',\n conditionsFilter,\n );\n\n oldPolicyRaw.sort((a, b) =>\n a.permission === b.permission\n ? this.nameSort(a.policy!, b.policy!)\n : this.nameSort(a.permission!, b.permission!),\n );\n\n newPolicyRaw.sort((a, b) =>\n a.permission === b.permission\n ? this.nameSort(a.policy!, b.policy!)\n : this.nameSort(a.permission!, b.permission!),\n );\n\n if (\n isEqual(oldPolicyRaw, newPolicyRaw) &&\n !oldPolicyRaw.some(isEmpty)\n ) {\n response.status(204).end();\n } else if (oldPolicyRaw.length > newPolicyRaw.length) {\n throw new InputError(\n `'oldPolicy' object has more permission policies compared to 'newPolicy' object`,\n );\n }\n\n const processedNewPolicy = await this.processPolicies(\n newPolicyRaw,\n false,\n 'new policy',\n conditionsFilter,\n );\n\n const roleMetadata =\n await this.roleMetadata.findRoleMetadata(entityRef);\n if (entityRef.startsWith('role:default') && !roleMetadata) {\n throw new Error(`Corresponding role ${entityRef} was not found`);\n }\n\n await this.enforcer.updatePolicies(\n processedOldPolicy,\n processedNewPolicy,\n );\n\n response.locals.meta = { policies: processedNewPolicy }; // auditor\n\n response.status(200).end();\n },\n );\n\n // Role CRUD\n\n router.get(\n '/roles',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roles = await this.enforcer.getGroupingPolicy();\n const body = await this.transformRoleArray(conditionsFilter, ...roles);\n\n response.json(body);\n },\n );\n\n router.get(\n '/roles/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleEntityRef = this.getEntityReference(request, true);\n\n const role = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n\n const body = await this.transformRoleArray(conditionsFilter, ...role);\n if (body.length !== 0) {\n response.json(body);\n } else {\n throw new NotFoundError(); // 404\n }\n },\n );\n\n router.post(\n '/roles',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n const uniqueItems = new Set<string>();\n const { credentials } = await this.authorizeConditional(\n request,\n policyEntityCreatePermission,\n );\n\n const roleRaw: Role = request.body;\n let err = validateRole(roleRaw);\n if (err) {\n throw new InputError( // 400\n `Invalid role definition. Cause: ${err.message}`,\n );\n }\n this.transformMemberReferencesToLowercase(roleRaw);\n\n const rMetadata = await this.roleMetadata.findRoleMetadata(\n roleRaw.name,\n );\n\n err = await validateSource('rest', rMetadata);\n if (err) {\n throw new NotAllowedError(`Unable to add role: ${err.message}`);\n }\n\n const roles = this.transformRoleToArray(roleRaw);\n\n for (const role of roles) {\n if (await this.enforcer.hasGroupingPolicy(...role)) {\n throw new ConflictError(); // 409\n }\n const roleString = JSON.stringify(role);\n\n if (uniqueItems.has(roleString)) {\n throw new ConflictError(\n `Duplicate role members found; ${role.at(0)}, ${role.at(\n 1,\n )} is a duplicate`,\n );\n } else {\n uniqueItems.add(roleString);\n }\n }\n\n const modifiedBy = (\n credentials as BackstageCredentials<BackstageUserPrincipal>\n ).principal.userEntityRef;\n const metadata: RoleMetadataDao = {\n roleEntityRef: roleRaw.name,\n source: 'rest',\n description: roleRaw.metadata?.description ?? '',\n author: modifiedBy,\n modifiedBy,\n owner: roleRaw.metadata?.owner ?? modifiedBy,\n };\n\n await this.enforcer.addGroupingPolicies(roles, metadata);\n\n response.locals.meta = { ...metadata, members: roles.map(gp => gp[0]) }; // auditor\n\n response.status(201).end();\n },\n );\n\n router.put(\n '/roles/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n const uniqueItems = new Set<string>();\n let conditionsFilter: RBACFilters | undefined;\n const { decision, credentials } = await this.authorizeConditional(\n request,\n policyEntityUpdatePermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleEntityRef = this.getEntityReference(request, true);\n\n const oldRoleRaw: Role = request.body.oldRole;\n\n if (!oldRoleRaw) {\n throw new InputError(`'oldRole' object must be present`); // 400\n }\n const newRoleRaw: Role = request.body.newRole;\n if (!newRoleRaw) {\n throw new InputError(`'newRole' object must be present`); // 400\n }\n\n oldRoleRaw.name = roleEntityRef;\n let err = validateRole(oldRoleRaw);\n if (err) {\n throw new InputError( // 400\n `Invalid old role object. Cause: ${err.message}`,\n );\n }\n err = validateRole(newRoleRaw);\n if (err) {\n throw new InputError( // 400\n `Invalid new role object. Cause: ${err.message}`,\n );\n }\n this.transformMemberReferencesToLowercase(oldRoleRaw);\n this.transformMemberReferencesToLowercase(newRoleRaw);\n\n const oldRole = this.transformRoleToArray(oldRoleRaw);\n const newRole = this.transformRoleToArray(newRoleRaw);\n // todo shell we allow newRole with an empty array?...\n\n const modifiedBy = (\n credentials as BackstageCredentials<BackstageUserPrincipal>\n ).principal.userEntityRef;\n const newMetadata: RoleMetadataDao = {\n ...newRoleRaw.metadata,\n source: newRoleRaw.metadata?.source ?? 'rest',\n roleEntityRef: newRoleRaw.name,\n modifiedBy,\n owner: newRoleRaw.metadata?.owner ?? '',\n };\n\n const oldMetadata =\n await this.roleMetadata.findRoleMetadata(roleEntityRef);\n if (!oldMetadata) {\n throw new NotFoundError(\n `Unable to find metadata for ${roleEntityRef}`,\n );\n }\n\n err = await validateSource('rest', oldMetadata);\n if (err) {\n throw new NotAllowedError(`Unable to edit role: ${err.message}`);\n }\n\n if (!matches(oldMetadata, conditionsFilter)) {\n throw new NotAllowedError(); // 403\n }\n\n if (\n isEqual(oldRole, newRole) &&\n deepSortedEqual(oldMetadata, newMetadata, [\n 'author',\n 'modifiedBy',\n 'createdAt',\n 'lastModified',\n 'owner',\n ])\n ) {\n // no content: old role and new role are equal and their metadata too\n response.status(204).end();\n return;\n }\n\n for (const role of newRole) {\n const hasRole = oldRole.some(element => {\n return isEqual(element, role);\n });\n // if the role is already part of old role and is a grouping policy we want to skip returning a conflict error\n // to allow for other roles to be checked and added\n if (await this.enforcer.hasGroupingPolicy(...role)) {\n if (!hasRole) {\n throw new ConflictError(); // 409\n }\n }\n const roleString = JSON.stringify(role);\n\n if (uniqueItems.has(roleString)) {\n throw new ConflictError(\n `Duplicate role members found; ${role.at(0)}, ${role.at(\n 1,\n )} is a duplicate`,\n );\n } else {\n uniqueItems.add(roleString);\n }\n }\n\n uniqueItems.clear();\n for (const role of oldRole) {\n if (!(await this.enforcer.hasGroupingPolicy(...role))) {\n throw new NotFoundError(\n `Member reference: ${role[0]} was not found for role ${roleEntityRef}`,\n ); // 404\n }\n const roleString = JSON.stringify(role);\n\n if (uniqueItems.has(roleString)) {\n throw new ConflictError(\n `Duplicate role members found; ${role.at(0)}, ${role.at(\n 1,\n )} is a duplicate`,\n );\n } else {\n uniqueItems.add(roleString);\n }\n }\n\n await this.enforcer.updateGroupingPolicies(\n oldRole,\n newRole,\n newMetadata,\n );\n\n let message = `Updated ${oldMetadata.roleEntityRef}.`;\n if (newMetadata.roleEntityRef !== oldMetadata.roleEntityRef) {\n message = `${message}. Role entity reference renamed to ${newMetadata.roleEntityRef}`;\n }\n response.locals.meta = {\n ...newMetadata,\n members: newRole.map(gp => gp[0]),\n }; // auditor\n\n response.status(200).end();\n },\n );\n\n router.delete(\n '/roles/:kind/:namespace/:name',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision, credentials } = await this.authorizeConditional(\n request,\n policyEntityDeletePermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleEntityRef = this.getEntityReference(request, true);\n\n const currentMetadata =\n await this.roleMetadata.findRoleMetadata(roleEntityRef);\n\n if (!matches(currentMetadata, conditionsFilter)) {\n throw new NotAllowedError(); // 403\n }\n\n const err = await validateSource('rest', currentMetadata);\n if (err) {\n throw new NotAllowedError(`Unable to delete role: ${err.message}`);\n }\n\n let roleMembers = [];\n if (request.query.memberReferences) {\n const memberReference = this.getFirstQuery(\n request.query.memberReferences!,\n ).toLocaleLowerCase('en-US');\n const gp = await this.enforcer.getFilteredGroupingPolicy(\n 0,\n memberReference,\n roleEntityRef,\n );\n if (gp.length > 0) {\n roleMembers.push(gp[0]);\n } else {\n throw new NotFoundError(\n `role member '${memberReference}' was not found`,\n ); // 404\n }\n } else {\n roleMembers = await this.enforcer.getFilteredGroupingPolicy(\n 1,\n roleEntityRef,\n );\n }\n\n for (const role of roleMembers) {\n if (!(await this.enforcer.hasGroupingPolicy(...role))) {\n throw new NotFoundError(`role member '${role[0]}' was not found`);\n }\n }\n\n const modifiedBy = (\n credentials as BackstageCredentials<BackstageUserPrincipal>\n ).principal.userEntityRef;\n const metadata: RoleMetadataDao = {\n roleEntityRef,\n source: 'rest',\n modifiedBy,\n };\n\n await this.enforcer.removeGroupingPolicies(\n roleMembers,\n metadata,\n false,\n );\n\n response.locals.meta = {\n ...metadata,\n members: roleMembers.map(gp => gp[0]),\n }; // auditor\n\n response.status(204).end();\n },\n );\n\n router.get(\n '/plugins/policies',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n await this.authorizeConditional(request, policyEntityReadPermission);\n\n const body = await this.pluginPermMetaData.getPluginPolicies(\n this.options.auth,\n );\n\n response.json(body);\n },\n );\n\n router.get(\n '/plugins/condition-rules',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n await this.authorizeConditional(request, policyEntityReadPermission);\n\n const body = await this.pluginPermMetaData.getPluginConditionRules(\n this.options.auth,\n );\n\n response.json(body);\n },\n );\n\n router.get(\n '/roles/conditions',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleMetadata =\n await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);\n\n const matchedRoleName = roleMetadata.flatMap(role => {\n return role.roleEntityRef;\n });\n\n const conditions = await this.conditionalStorage.filterConditions(\n this.getFirstQuery(request.query.roleEntityRef),\n this.getFirstQuery(request.query.pluginId),\n this.getFirstQuery(request.query.resourceType),\n this.getActionQueries(request.query.actions),\n );\n\n const body: RoleConditionalPolicyDecision<PermissionAction>[] =\n conditions\n .map(condition => {\n return {\n ...condition,\n permissionMapping: condition.permissionMapping.map(\n pm => pm.action,\n ),\n };\n })\n .filter(condition => {\n return matchedRoleName.includes(condition.roleEntityRef);\n });\n\n response.json(body);\n },\n );\n\n router.post(\n '/roles/conditions',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n await this.authorizeConditional(request, policyEntityCreatePermission);\n\n const roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction> =\n request.body;\n validateRoleCondition(roleConditionPolicy);\n\n const conditionToCreate = await processConditionMapping(\n roleConditionPolicy,\n this.pluginPermMetaData,\n this.options.auth,\n );\n\n const id =\n await this.conditionalStorage.createCondition(conditionToCreate);\n\n const body = { id: id };\n\n response.locals.meta = { condition: roleConditionPolicy }; // auditor\n\n response.status(201).json(body);\n },\n );\n\n router.get(\n '/roles/conditions/:id',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityReadPermission,\n );\n\n const id: number = parseInt(request.params.id, 10);\n if (isNaN(id)) {\n throw new InputError('Id is not a valid number.');\n }\n\n const condition = await this.conditionalStorage.getCondition(id);\n if (!condition) {\n throw new NotFoundError();\n }\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const roleMetadata =\n await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);\n\n const matchedRoleName = roleMetadata.flatMap(role => {\n return role.roleEntityRef;\n });\n\n const body: RoleConditionalPolicyDecision<PermissionAction> | [] =\n matchedRoleName.includes(condition.roleEntityRef)\n ? {\n ...condition,\n permissionMapping: condition.permissionMapping.map(\n pm => pm.action,\n ),\n }\n : [];\n\n response.json(body);\n },\n );\n\n router.delete(\n '/roles/conditions/:id',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityDeletePermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const id: number = parseInt(request.params.id, 10);\n if (isNaN(id)) {\n throw new InputError('Id is not a valid number.');\n }\n\n const condition = await this.conditionalStorage.getCondition(id);\n if (!condition) {\n throw new NotFoundError(`Condition with id ${id} was not found`);\n }\n const conditionToDelete: RoleConditionalPolicyDecision<PermissionAction> =\n {\n ...condition,\n permissionMapping: condition.permissionMapping.map(pm => pm.action),\n };\n\n const roleMetadata = await this.roleMetadata.findRoleMetadata(\n conditionToDelete.roleEntityRef,\n );\n\n if (!matches(roleMetadata, conditionsFilter)) {\n throw new NotAllowedError(); // 403\n }\n\n await this.conditionalStorage.deleteCondition(id);\n response.locals.meta = { condition: conditionToDelete }; // auditor\n\n response.status(204).end();\n },\n );\n\n router.put(\n '/roles/conditions/:id',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n let conditionsFilter: RBACFilters | undefined;\n const { decision } = await this.authorizeConditional(\n request,\n policyEntityUpdatePermission,\n );\n\n if (decision.result === AuthorizeResult.CONDITIONAL) {\n conditionsFilter = transformConditions(decision.conditions);\n }\n\n const id: number = parseInt(request.params.id, 10);\n if (isNaN(id)) {\n throw new InputError('Id is not a valid number.');\n }\n\n const condition = await this.conditionalStorage.getCondition(id);\n\n if (!condition) {\n throw new NotFoundError(`Condition with id ${id} was not found`);\n }\n\n const roleMetadata = await this.roleMetadata.findRoleMetadata(\n condition.roleEntityRef,\n );\n\n if (!matches(roleMetadata, conditionsFilter)) {\n throw new NotAllowedError(); // 403\n }\n\n const roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction> =\n request.body;\n\n validateRoleCondition(roleConditionPolicy);\n\n const conditionToUpdate = await processConditionMapping(\n roleConditionPolicy,\n this.pluginPermMetaData,\n this.options.auth,\n );\n\n await this.conditionalStorage.updateCondition(id, conditionToUpdate);\n\n response.locals.meta = { condition: roleConditionPolicy }; // auditor\n\n response.status(200).end();\n },\n );\n\n router.post(\n '/refresh/:id',\n logAuditorEvent(this.auditor),\n async (request, response) => {\n await this.authorizeConditional(request, policyEntityCreatePermission);\n\n if (!this.rbacProviders) {\n throw new NotFoundError(`No RBAC providers were found`);\n }\n\n const idProvider = this.rbacProviders.find(provider => {\n const id = provider.getProviderName();\n return id === request.params.id;\n });\n\n if (!idProvider) {\n throw new NotFoundError(\n `The RBAC provider ${request.params.id} was not found`,\n );\n }\n\n await idProvider.refresh();\n response.status(200).end();\n },\n );\n\n router.use(setAuditorError());\n\n return router;\n }\n\n getEntityReference(request: Request, role?: boolean): string {\n const kind = request.params.kind;\n const namespace = request.params.namespace;\n const name = request.params.name;\n const entityRef = `${kind}:${namespace}/${name}`;\n\n const err = validateEntityReference(entityRef, role);\n if (err) {\n throw new InputError(err.message);\n }\n\n return entityRef;\n }\n\n async transformPolicyArray(\n ...policies: string[][]\n ): Promise<RoleBasedPolicy[]> {\n const roleToSourceMap = await buildRoleSourceMap(\n policies,\n this.roleMetadata,\n );\n\n const roleBasedPolices: RoleBasedPolicy[] = [];\n for (const p of policies) {\n const [entityReference, permission, policy, effect] = p;\n roleBasedPolices.push({\n entityReference,\n permission,\n policy,\n effect,\n metadata: { source: roleToSourceMap.get(entityReference)! },\n });\n }\n\n return roleBasedPolices;\n }\n\n async transformRoleArray(\n filter?: RBACFilters,\n ...roles: string[][]\n ): Promise<Role[]> {\n const combinedRoles: { [key: string]: string[] } = {};\n\n roles.forEach(([value, role]) => {\n if (combinedRoles.hasOwnProperty(role)) {\n combinedRoles[role].push(value);\n } else {\n combinedRoles[role] = [value];\n }\n });\n\n const result: Role[] = await Promise.all(\n Object.entries(combinedRoles).flatMap(async ([role, value]) => {\n const metadataDao = await this.roleMetadata.findRoleMetadata(role);\n const metadata = metadataDao ? daoToMetadata(metadataDao) : undefined;\n return Promise.resolve({\n memberReferences: value,\n name: role,\n metadata,\n });\n }),\n );\n\n const filteredResult = result.filter(role => {\n return role.metadata && matches(role.metadata, filter);\n });\n\n return filteredResult;\n }\n\n transformPolicyToArray(policy: RoleBasedPolicy): string[] {\n return [\n policy.entityReference!,\n policy.permission!,\n policy.policy!,\n policy.effect!,\n ];\n }\n\n transformRoleToArray(role: Role): string[][] {\n const roles: string[][] = [];\n for (const entity of role.memberReferences) {\n roles.push([entity, role.name]);\n }\n return roles;\n }\n\n transformMemberReferencesToLowercase(role: Role) {\n role.memberReferences = role.memberReferences.map(member =>\n member.toLocaleLowerCase('en-US'),\n );\n }\n\n getActionQueries(\n queryValue: string | string[] | ParsedQs | ParsedQs[] | undefined,\n ): PermissionAction[] | undefined {\n if (!queryValue) {\n return undefined;\n }\n if (Array.isArray(queryValue)) {\n const permissionNames: PermissionAction[] = [];\n for (const permissionQuery of queryValue) {\n if (\n typeof permissionQuery === 'string' &&\n isPermissionAction(permissionQuery)\n ) {\n permissionNames.push(permissionQuery);\n } else {\n throw new InputError(\n `Invalid permission action query value: ${permissionQuery}. Permission name should be string.`,\n );\n }\n }\n return permissionNames;\n }\n\n if (typeof queryValue === 'string' && isPermissionAction(queryValue)) {\n return [queryValue];\n }\n throw new InputError(\n `Invalid permission action query value: ${queryValue}. Permission name should be string.`,\n );\n }\n\n getFirstQuery(\n queryValue: string | string[] | ParsedQs | ParsedQs[] | undefined,\n ): string {\n if (!queryValue) {\n return '';\n }\n if (Array.isArray(queryValue)) {\n if (typeof queryValue[0] === 'string') {\n return queryValue[0].toString();\n }\n throw new InputError(`This api doesn't support nested query`);\n }\n\n if (typeof queryValue === 'string') {\n return queryValue;\n }\n throw new InputError(`This api doesn't support nested query`);\n }\n\n isPolicyFilterEnabled(request: Request): boolean {\n return (\n !!request.query.entityRef ||\n !!request.query.permission ||\n !!request.query.policy ||\n !!request.query.effect\n );\n }\n\n async processPolicies(\n policyArray: RoleBasedPolicy[],\n isOld?: boolean,\n errorMessage?: string,\n filter?: RBACFilters,\n ): Promise<string[][]> {\n const policies: string[][] = [];\n const uniqueItems = new Set<string>();\n for (const policy of policyArray) {\n let err = validatePolicy(policy);\n if (err) {\n throw new InputError(\n `Invalid ${errorMessage ?? 'policy'} definition. Cause: ${\n err.message\n }`,\n ); // 400\n }\n\n const metadata = await this.roleMetadata.findRoleMetadata(\n policy.entityReference!,\n );\n\n if (!matches(metadata, filter)) {\n throw new NotAllowedError(); // 403\n }\n\n let action = errorMessage ? 'edit' : 'delete';\n action = isOld ? action : 'add';\n\n err = await validateSource('rest', metadata);\n if (err) {\n throw new NotAllowedError(\n `Unable to ${action} policy ${policy.entityReference},${policy.permission},${policy.policy},${policy.effect}: ${err.message}`,\n );\n }\n\n const transformedPolicy = this.transformPolicyToArray(policy);\n if (isOld && !(await this.enforcer.hasPolicy(...transformedPolicy))) {\n throw new NotFoundError(\n `Policy '${policyToString(transformedPolicy)}' not found`,\n ); // 404\n }\n\n if (!isOld && (await this.enforcer.hasPolicy(...transformedPolicy))) {\n throw new ConflictError(\n `Policy '${policyToString(\n transformedPolicy,\n )}' has been already stored`,\n ); // 409\n }\n\n // We want to ensure that there are not duplicate permission policies\n const rowString = JSON.stringify(transformedPolicy);\n if (uniqueItems.has(rowString)) {\n throw new ConflictError(\n `Duplicate polices found; ${policy.entityReference}, ${policy.permission}, ${policy.policy}, ${policy.effect} is a duplicate`,\n );\n } else {\n uniqueItems.add(rowString);\n policies.push(transformedPolicy);\n }\n }\n return policies;\n }\n\n nameSort(nameA: string, nameB: string): number {\n if (nameA.toLocaleUpperCase('en-US') < nameB.toLocaleUpperCase('en-US')) {\n return -1;\n }\n if (nameA.toLocaleUpperCase('en-US') > nameB.toLocaleUpperCase('en-US')) {\n return 1;\n }\n return 0;\n }\n}\n"],"names":["policyEntityReadPermission","NotAllowedError","AuthorizeResult","createRouter","createPermissionIntegrationRouter","RESOURCE_TYPE_POLICY_ENTITY","policyEntityPermissions","rules","logAuditorEvent","transformConditions","NotFoundError","policyEntityDeletePermission","isEmpty","InputError","policyEntityCreatePermission","policyEntityUpdatePermission","isEqual","validateRole","validateSource","ConflictError","matches","deepSortedEqual","conditions","validateRoleCondition","processConditionMapping","setAuditorError","validateEntityReference","buildRoleSourceMap","daoToMetadata","isPermissionAction","validatePolicy","policyToString"],"mappings":"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"}
|