@backstage-community/plugin-rbac-backend 7.9.1 → 7.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +24 -0
- package/config.d.ts +23 -0
- package/dist/database/role-metadata.cjs.js +49 -3
- package/dist/database/role-metadata.cjs.js.map +1 -1
- package/dist/default-permissions/default-permissions.cjs.js +133 -0
- package/dist/default-permissions/default-permissions.cjs.js.map +1 -0
- package/dist/helper.cjs.js +19 -0
- package/dist/helper.cjs.js.map +1 -1
- package/dist/permissions/resource.cjs.js.map +1 -1
- package/dist/permissions/rules.cjs.js +3 -0
- package/dist/permissions/rules.cjs.js.map +1 -1
- package/dist/providers/connect-providers.cjs.js +82 -2
- package/dist/providers/connect-providers.cjs.js.map +1 -1
- package/dist/role-manager/role-manager.cjs.js +7 -2
- package/dist/role-manager/role-manager.cjs.js.map +1 -1
- package/dist/service/policies-rest-api.cjs.js +29 -12
- package/dist/service/policies-rest-api.cjs.js.map +1 -1
- package/dist/service/policy-builder.cjs.js +14 -1
- package/dist/service/policy-builder.cjs.js.map +1 -1
- package/migrations/20260216100000_add_is_default_to_role_metadata.js +43 -0
- package/package.json +4 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @backstage-community/plugin-rbac-backend
|
|
2
2
|
|
|
3
|
+
## 7.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 50e194d: Add support for a default role and permissions for authenticated users in RBAC backend
|
|
8
|
+
|
|
9
|
+
- Introduced a new `defaultRole` and `basicPermissions` configuration options to assign a default role to all authenticated users.
|
|
10
|
+
|
|
11
|
+
```diff
|
|
12
|
+
permission:
|
|
13
|
+
rbac:
|
|
14
|
+
+ defaultPermissions:
|
|
15
|
+
+ defaultRole: role:default/my-default-role
|
|
16
|
+
+ basicPermissions:
|
|
17
|
+
+ - permission: catalog.entity.read
|
|
18
|
+
+ action: read
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- Updated the RBAC permission policy to include the default role in user roles if not already present.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [50e194d]
|
|
26
|
+
- @backstage-community/plugin-rbac-common@1.25.0
|
|
27
|
+
- @backstage-community/plugin-rbac-node@1.19.1
|
|
28
|
+
|
|
29
|
+
## 7.10.0
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- 133eae6: Add support for loading conditional permissions from a remote provider (fix #6412)
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- Updated dependencies [133eae6]
|
|
38
|
+
- @backstage-community/plugin-rbac-node@1.19.0
|
|
39
|
+
|
|
3
40
|
## 7.9.1
|
|
4
41
|
|
|
5
42
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -88,6 +88,30 @@ permission:
|
|
|
88
88
|
|
|
89
89
|
For more information on the available API endpoints accessible to the policy administrators, refer to the [API documentation](./docs/apis.md).
|
|
90
90
|
|
|
91
|
+
### Configure default role
|
|
92
|
+
|
|
93
|
+
You can optionally assign a default role to all authenticated users by using `defaultPermissions.defaultRole`.
|
|
94
|
+
This ensures that every authenticated user receives the specified role in addition to any other roles they may have.
|
|
95
|
+
You can also define baseline permissions for that role using `defaultPermissions.basicPermissions`.
|
|
96
|
+
This is especially useful when using [Sign-In without Users in the Catalog](https://backstage.io/docs/auth/identity-resolver/#sign-in-without-users-in-the-catalog).
|
|
97
|
+
|
|
98
|
+
```YAML
|
|
99
|
+
permission:
|
|
100
|
+
rbac:
|
|
101
|
+
defaultPermissions:
|
|
102
|
+
defaultRole: role:default/my-default-role
|
|
103
|
+
basicPermissions:
|
|
104
|
+
- permission: catalog.entity.read
|
|
105
|
+
action: read
|
|
106
|
+
- permission: catalog-entity
|
|
107
|
+
action: read
|
|
108
|
+
- permission: catalog.entity.create
|
|
109
|
+
action: create
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
If configured, the RBAC backend will automatically include the default role in each authenticated user's roles and evaluate the configured `basicPermissions` for that role.
|
|
113
|
+
When `defaultPermissions.defaultRole` is set, `defaultPermissions.basicPermissions` must contain at least one permission entry.
|
|
114
|
+
|
|
91
115
|
### Configure plugins with permission
|
|
92
116
|
|
|
93
117
|
In order for the RBAC UI to display the available permissions provided by installed plugins, you must supply the corresponding list of plugin IDs. There are two ways to achieve this:
|
package/config.d.ts
CHANGED
|
@@ -71,6 +71,29 @@ export interface Config {
|
|
|
71
71
|
* @visibility frontend
|
|
72
72
|
*/
|
|
73
73
|
policyDecisionPrecedence?: 'basic' | 'conditional';
|
|
74
|
+
/**
|
|
75
|
+
* Configuration for assigning a default role with permissions
|
|
76
|
+
* to all authenticated users.
|
|
77
|
+
*/
|
|
78
|
+
defaultPermissions?: {
|
|
79
|
+
/**
|
|
80
|
+
* The default role to assign to all authenticated users.
|
|
81
|
+
*/
|
|
82
|
+
defaultRole: string;
|
|
83
|
+
/**
|
|
84
|
+
* The list of baseline basic permissions assigned to the default role.
|
|
85
|
+
*/
|
|
86
|
+
basicPermissions: Array<{
|
|
87
|
+
/**
|
|
88
|
+
* Permission name or resource type, for example `catalog.entity.read` or `catalog-entity`.
|
|
89
|
+
*/
|
|
90
|
+
permission: string;
|
|
91
|
+
/**
|
|
92
|
+
* Action for the permission. Defaults to `use` when omitted.
|
|
93
|
+
*/
|
|
94
|
+
action: 'create' | 'read' | 'update' | 'delete' | 'use';
|
|
95
|
+
}>;
|
|
96
|
+
};
|
|
74
97
|
};
|
|
75
98
|
};
|
|
76
99
|
}
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
var errors = require('@backstage/errors');
|
|
4
4
|
var helper = require('../helper.cjs.js');
|
|
5
|
+
var defaultPermissions = require('../default-permissions/default-permissions.cjs.js');
|
|
6
|
+
var policiesValidation = require('../validation/policies-validation.cjs.js');
|
|
5
7
|
|
|
6
8
|
const ROLE_METADATA_TABLE = "role-metadata";
|
|
7
9
|
class DataBaseRoleMetadataStorage {
|
|
8
10
|
constructor(knex) {
|
|
9
11
|
this.knex = knex;
|
|
10
12
|
}
|
|
13
|
+
cachedDefaultRoleMeta;
|
|
11
14
|
async filterRoleMetadata(source) {
|
|
12
15
|
return await this.knex.table(ROLE_METADATA_TABLE).where((builder) => {
|
|
13
16
|
if (source) {
|
|
@@ -15,12 +18,50 @@ class DataBaseRoleMetadataStorage {
|
|
|
15
18
|
}
|
|
16
19
|
});
|
|
17
20
|
}
|
|
21
|
+
async syncDefaultRoleMetadata(actualDefRoleRef) {
|
|
22
|
+
if (!actualDefRoleRef) {
|
|
23
|
+
await this.knex(ROLE_METADATA_TABLE).where("isDefault", true).delete();
|
|
24
|
+
this.cachedDefaultRoleMeta = undefined;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
await this.knex.transaction(async (trx) => {
|
|
28
|
+
const currentDefaultRole = await this.getDefaultRole(trx);
|
|
29
|
+
if (currentDefaultRole && currentDefaultRole.roleEntityRef !== actualDefRoleRef) {
|
|
30
|
+
await trx(ROLE_METADATA_TABLE).where("isDefault", true).delete();
|
|
31
|
+
}
|
|
32
|
+
const existing = await this.findRoleMetadata(actualDefRoleRef, trx);
|
|
33
|
+
if (!existing) {
|
|
34
|
+
const newDefaultRole = defaultPermissions.buildDefaultRoleMetadata(actualDefRoleRef);
|
|
35
|
+
await this.createRoleMetadata(newDefaultRole, trx);
|
|
36
|
+
} else {
|
|
37
|
+
const err = await policiesValidation.validateSource("configuration", existing);
|
|
38
|
+
if (err) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Role '${actualDefRoleRef}' has incompatible source. Expected 'configuration' source value. Cause: ${err.message}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const row = await this.findRoleMetadata(actualDefRoleRef);
|
|
46
|
+
this.cachedDefaultRoleMeta = row;
|
|
47
|
+
}
|
|
48
|
+
getCachedDefaultRoleMetadata() {
|
|
49
|
+
return this.cachedDefaultRoleMeta;
|
|
50
|
+
}
|
|
51
|
+
async getDefaultRole(trx) {
|
|
52
|
+
const db = trx || this.knex;
|
|
53
|
+
return await db(ROLE_METADATA_TABLE).where("isDefault", true).first();
|
|
54
|
+
}
|
|
18
55
|
async filterForOwnerRoleMetadata(filter) {
|
|
19
56
|
const roleMetadata = await this.knex.table(ROLE_METADATA_TABLE);
|
|
20
57
|
if (filter) {
|
|
21
|
-
|
|
58
|
+
const ownerRoles = roleMetadata.filter((role) => {
|
|
22
59
|
return helper.matches(role, filter);
|
|
23
60
|
});
|
|
61
|
+
if (this.cachedDefaultRoleMeta) {
|
|
62
|
+
ownerRoles.push(this.cachedDefaultRoleMeta);
|
|
63
|
+
}
|
|
64
|
+
return ownerRoles;
|
|
24
65
|
}
|
|
25
66
|
return roleMetadata;
|
|
26
67
|
}
|
|
@@ -71,7 +112,8 @@ class DataBaseRoleMetadataStorage {
|
|
|
71
112
|
);
|
|
72
113
|
}
|
|
73
114
|
}
|
|
74
|
-
async removeRoleMetadata(roleEntityRef,
|
|
115
|
+
async removeRoleMetadata(roleEntityRef, externalTrx) {
|
|
116
|
+
const trx = externalTrx ?? await this.knex.transaction();
|
|
75
117
|
const metadataDao = await this.findRoleMetadata(roleEntityRef, trx);
|
|
76
118
|
if (!metadataDao) {
|
|
77
119
|
throw new errors.NotFoundError(
|
|
@@ -79,6 +121,9 @@ class DataBaseRoleMetadataStorage {
|
|
|
79
121
|
);
|
|
80
122
|
}
|
|
81
123
|
await trx(ROLE_METADATA_TABLE).delete().whereIn("id", [metadataDao.id]);
|
|
124
|
+
if (!externalTrx) {
|
|
125
|
+
await trx.commit();
|
|
126
|
+
}
|
|
82
127
|
}
|
|
83
128
|
}
|
|
84
129
|
function daoToMetadata(dao) {
|
|
@@ -89,7 +134,8 @@ function daoToMetadata(dao) {
|
|
|
89
134
|
author: dao.author,
|
|
90
135
|
modifiedBy: dao.modifiedBy,
|
|
91
136
|
createdAt: dao.createdAt,
|
|
92
|
-
lastModified: dao.lastModified
|
|
137
|
+
lastModified: dao.lastModified,
|
|
138
|
+
isDefault: dao.isDefault === true || dao.isDefault === 1 ? true : false
|
|
93
139
|
};
|
|
94
140
|
}
|
|
95
141
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role-metadata.cjs.js","sources":["../../src/database/role-metadata.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ConflictError, InputError, NotFoundError } from '@backstage/errors';\n\nimport { Knex } from 'knex';\n\nimport type {\n RoleMetadata,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport { deepSortedEqual } from '../helper';\nimport { RBACFilters } from '../permissions';\nimport { matches } from '../helper';\n\nexport const ROLE_METADATA_TABLE = 'role-metadata';\n\nexport interface RoleMetadataDao extends RoleMetadata {\n id?: number;\n roleEntityRef: string;\n source: Source;\n modifiedBy: string;\n}\n\nexport interface RoleMetadataStorage {\n filterRoleMetadata(source?: Source): Promise<RoleMetadataDao[]>;\n filterForOwnerRoleMetadata(filter?: RBACFilters): Promise<RoleMetadataDao[]>;\n findRoleMetadata(\n roleEntityRef: string,\n trx?: Knex.Transaction,\n ): Promise<RoleMetadataDao | undefined>;\n createRoleMetadata(\n roleMetadata: RoleMetadataDao,\n trx: Knex.Transaction,\n ): Promise<number>;\n updateRoleMetadata(\n roleMetadata: RoleMetadataDao,\n oldRoleEntityRef: string,\n externalTrx?: Knex.Transaction,\n ): Promise<void>;\n removeRoleMetadata(\n roleEntityRef: string,\n trx: Knex.Transaction,\n ): Promise<void>;\n}\n\nexport class DataBaseRoleMetadataStorage implements RoleMetadataStorage {\n constructor(private readonly knex: Knex<any, any[]>) {}\n\n async filterRoleMetadata(source?: Source): Promise<RoleMetadataDao[]> {\n return await this.knex.table(ROLE_METADATA_TABLE).where(builder => {\n if (source) {\n builder.where('source', source);\n }\n });\n }\n\n async filterForOwnerRoleMetadata(\n filter?: RBACFilters,\n ): Promise<RoleMetadataDao[]> {\n const roleMetadata: RoleMetadataDao[] =\n await this.knex.table(ROLE_METADATA_TABLE);\n\n if (filter) {\n return roleMetadata.filter(role => {\n return matches(role as RoleMetadata, filter);\n });\n }\n\n return roleMetadata;\n }\n\n async findRoleMetadata(\n roleEntityRef: string,\n trx?: Knex.Transaction,\n ): Promise<RoleMetadataDao | undefined> {\n const db = trx || this.knex;\n return await db\n .table(ROLE_METADATA_TABLE)\n .where('roleEntityRef', roleEntityRef)\n // roleEntityRef should be unique.\n .first();\n }\n\n async createRoleMetadata(\n metadata: RoleMetadataDao,\n trx: Knex.Transaction,\n ): Promise<number> {\n if (await this.findRoleMetadata(metadata.roleEntityRef, trx)) {\n throw new ConflictError(\n `A metadata for role ${metadata.roleEntityRef} has already been stored`,\n );\n }\n\n const result = await trx<RoleMetadataDao>(ROLE_METADATA_TABLE)\n .insert(metadata)\n .returning<[{ id: number }]>('id');\n if (result && result?.length > 0) {\n return result[0].id;\n }\n\n throw new Error(\n `Failed to create the role metadata: '${JSON.stringify(metadata)}'.`,\n );\n }\n\n async updateRoleMetadata(\n newRoleMetadata: RoleMetadataDao,\n oldRoleEntityRef: string,\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n const trx = externalTrx ?? (await this.knex.transaction());\n const currentMetadataDao = await this.findRoleMetadata(\n oldRoleEntityRef,\n trx,\n );\n\n if (!currentMetadataDao) {\n throw new NotFoundError(\n `A metadata for role '${oldRoleEntityRef}' was not found`,\n );\n }\n\n if (\n currentMetadataDao.source !== 'legacy' &&\n currentMetadataDao.source !== newRoleMetadata.source\n ) {\n throw new InputError(`The RoleMetadata.source field is 'read-only'.`);\n }\n\n if (deepSortedEqual(currentMetadataDao, newRoleMetadata)) {\n return;\n }\n\n const result = await trx<RoleMetadataDao>(ROLE_METADATA_TABLE)\n .where('id', currentMetadataDao.id)\n .update(newRoleMetadata)\n .returning('id');\n\n if (!externalTrx) {\n await trx.commit();\n }\n\n if (!result || result.length === 0) {\n throw new Error(\n `Failed to update the role metadata '${JSON.stringify(\n currentMetadataDao,\n )}' with new value: '${JSON.stringify(newRoleMetadata)}'.`,\n );\n }\n }\n\n async removeRoleMetadata(\n roleEntityRef: string,\n trx: Knex.Transaction,\n ): Promise<void> {\n const metadataDao = await this.findRoleMetadata(roleEntityRef, trx);\n if (!metadataDao) {\n throw new NotFoundError(\n `A metadata for role '${roleEntityRef}' was not found`,\n );\n }\n\n await trx<RoleMetadataDao>(ROLE_METADATA_TABLE)\n .delete()\n .whereIn('id', [metadataDao.id!]);\n }\n}\n\nexport function daoToMetadata(dao: RoleMetadataDao): RoleMetadata {\n return {\n source: dao.source,\n description: dao.description,\n owner: dao.owner,\n author: dao.author,\n modifiedBy: dao.modifiedBy,\n createdAt: dao.createdAt,\n lastModified: dao.lastModified,\n };\n}\n"],"names":["matches","ConflictError","NotFoundError","InputError","deepSortedEqual"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"role-metadata.cjs.js","sources":["../../src/database/role-metadata.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { ConflictError, InputError, NotFoundError } from '@backstage/errors';\n\nimport { Knex } from 'knex';\n\nimport type {\n RoleMetadata,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport { deepSortedEqual } from '../helper';\nimport { RBACFilters } from '../permissions';\nimport { matches } from '../helper';\nimport { buildDefaultRoleMetadata } from '../default-permissions/default-permissions';\nimport { validateSource } from '../validation/policies-validation';\n\nexport const ROLE_METADATA_TABLE = 'role-metadata';\n\nexport interface RoleMetadataDao {\n id?: number;\n roleEntityRef: string;\n source: Source;\n modifiedBy: string;\n description?: string;\n author?: string;\n lastModified?: string;\n createdAt?: string;\n owner?: string;\n /** Postgres has a real boolean type; SQLite stores and may return 0/1. Optional when creating. */\n isDefault?: boolean | 0 | 1;\n}\n\nexport interface RoleMetadataStorage {\n filterRoleMetadata(source?: Source): Promise<RoleMetadataDao[]>;\n filterForOwnerRoleMetadata(filter?: RBACFilters): Promise<RoleMetadataDao[]>;\n findRoleMetadata(\n roleEntityRef: string,\n trx?: Knex.Transaction,\n ): Promise<RoleMetadataDao | undefined>;\n createRoleMetadata(\n roleMetadata: RoleMetadataDao,\n trx: Knex.Transaction,\n ): Promise<number>;\n updateRoleMetadata(\n roleMetadata: RoleMetadataDao,\n oldRoleEntityRef: string,\n externalTrx?: Knex.Transaction,\n ): Promise<void>;\n removeRoleMetadata(\n roleEntityRef: string,\n trx?: Knex.Transaction,\n ): Promise<void>;\n getCachedDefaultRoleMetadata(): RoleMetadataDao | undefined;\n /** Returns the default role from the database (isDefault = true), if any. */\n getDefaultRole(trx?: Knex.Transaction): Promise<RoleMetadataDao | undefined>;\n syncDefaultRoleMetadata(actualDefRoleRef?: string): Promise<void>;\n}\n\nexport class DataBaseRoleMetadataStorage implements RoleMetadataStorage {\n private cachedDefaultRoleMeta: RoleMetadataDao | undefined;\n constructor(private readonly knex: Knex<any, any[]>) {}\n\n async filterRoleMetadata(source?: Source): Promise<RoleMetadataDao[]> {\n return await this.knex.table(ROLE_METADATA_TABLE).where(builder => {\n if (source) {\n builder.where('source', source);\n }\n });\n }\n\n async syncDefaultRoleMetadata(actualDefRoleRef?: string): Promise<void> {\n if (!actualDefRoleRef) {\n await this.knex(ROLE_METADATA_TABLE).where('isDefault', true).delete();\n this.cachedDefaultRoleMeta = undefined;\n return;\n }\n await this.knex.transaction(async trx => {\n const currentDefaultRole = await this.getDefaultRole(trx);\n if (\n currentDefaultRole &&\n currentDefaultRole.roleEntityRef !== actualDefRoleRef\n ) {\n await trx(ROLE_METADATA_TABLE).where('isDefault', true).delete();\n }\n const existing = await this.findRoleMetadata(actualDefRoleRef, trx);\n if (!existing) {\n const newDefaultRole = buildDefaultRoleMetadata(actualDefRoleRef);\n await this.createRoleMetadata(newDefaultRole, trx);\n } else {\n const err = await validateSource('configuration', existing);\n if (err) {\n throw new Error(\n `Role '${actualDefRoleRef}' has incompatible source. Expected 'configuration' source value. Cause: ${err.message}`,\n );\n }\n }\n });\n const row = await this.findRoleMetadata(actualDefRoleRef);\n this.cachedDefaultRoleMeta = row;\n }\n\n getCachedDefaultRoleMetadata(): RoleMetadataDao | undefined {\n return this.cachedDefaultRoleMeta;\n }\n\n async getDefaultRole(\n trx?: Knex.Transaction,\n ): Promise<RoleMetadataDao | undefined> {\n const db = trx || this.knex;\n return await db<RoleMetadataDao>(ROLE_METADATA_TABLE)\n .where('isDefault', true)\n .first();\n }\n\n async filterForOwnerRoleMetadata(\n filter?: RBACFilters,\n ): Promise<RoleMetadataDao[]> {\n const roleMetadata =\n await this.knex.table<RoleMetadataDao>(ROLE_METADATA_TABLE);\n\n if (filter) {\n const ownerRoles = roleMetadata.filter(role => {\n return matches(role as RoleMetadata, filter);\n });\n if (this.cachedDefaultRoleMeta) {\n ownerRoles.push(this.cachedDefaultRoleMeta);\n }\n return ownerRoles;\n }\n\n return roleMetadata;\n }\n\n async findRoleMetadata(\n roleEntityRef: string,\n trx?: Knex.Transaction,\n ): Promise<RoleMetadataDao | undefined> {\n const db = trx || this.knex;\n return await db\n .table<RoleMetadataDao>(ROLE_METADATA_TABLE)\n .where('roleEntityRef', roleEntityRef)\n // roleEntityRef should be unique.\n .first();\n }\n\n async createRoleMetadata(\n metadata: RoleMetadataDao,\n trx: Knex.Transaction,\n ): Promise<number> {\n if (await this.findRoleMetadata(metadata.roleEntityRef, trx)) {\n throw new ConflictError(\n `A metadata for role ${metadata.roleEntityRef} has already been stored`,\n );\n }\n\n const result = await trx(ROLE_METADATA_TABLE)\n .insert<RoleMetadataDao>(metadata)\n .returning<[{ id: number }]>('id');\n if (result && result?.length > 0) {\n return result[0].id;\n }\n\n throw new Error(\n `Failed to create the role metadata: '${JSON.stringify(metadata)}'.`,\n );\n }\n\n async updateRoleMetadata(\n newRoleMetadata: RoleMetadataDao,\n oldRoleEntityRef: string,\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n const trx = externalTrx ?? (await this.knex.transaction());\n const currentMetadataDao = await this.findRoleMetadata(\n oldRoleEntityRef,\n trx,\n );\n\n if (!currentMetadataDao) {\n throw new NotFoundError(\n `A metadata for role '${oldRoleEntityRef}' was not found`,\n );\n }\n\n if (\n currentMetadataDao.source !== 'legacy' &&\n currentMetadataDao.source !== newRoleMetadata.source\n ) {\n throw new InputError(`The RoleMetadata.source field is 'read-only'.`);\n }\n\n if (deepSortedEqual(currentMetadataDao, newRoleMetadata)) {\n return;\n }\n\n const result = await trx<RoleMetadataDao>(ROLE_METADATA_TABLE)\n .where('id', currentMetadataDao.id)\n .update(newRoleMetadata)\n .returning('id');\n\n if (!externalTrx) {\n await trx.commit();\n }\n\n if (!result || result.length === 0) {\n throw new Error(\n `Failed to update the role metadata '${JSON.stringify(\n currentMetadataDao,\n )}' with new value: '${JSON.stringify(newRoleMetadata)}'.`,\n );\n }\n }\n\n async removeRoleMetadata(\n roleEntityRef: string,\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n const trx = externalTrx ?? (await this.knex.transaction());\n const metadataDao = await this.findRoleMetadata(roleEntityRef, trx);\n if (!metadataDao) {\n throw new NotFoundError(\n `A metadata for role '${roleEntityRef}' was not found`,\n );\n }\n\n await trx<RoleMetadataDao>(ROLE_METADATA_TABLE)\n .delete()\n .whereIn('id', [metadataDao.id!]);\n\n if (!externalTrx) {\n await trx.commit();\n }\n }\n}\n\nexport function daoToMetadata(dao: RoleMetadataDao): RoleMetadata {\n return {\n source: dao.source,\n description: dao.description,\n owner: dao.owner,\n author: dao.author,\n modifiedBy: dao.modifiedBy,\n createdAt: dao.createdAt,\n lastModified: dao.lastModified,\n isDefault: dao.isDefault === true || dao.isDefault === 1 ? true : false,\n };\n}\n"],"names":["buildDefaultRoleMetadata","validateSource","matches","ConflictError","NotFoundError","InputError","deepSortedEqual"],"mappings":";;;;;;;AA8BO,MAAM,mBAAsB,GAAA;AA0C5B,MAAM,2BAA2D,CAAA;AAAA,EAEtE,YAA6B,IAAwB,EAAA;AAAxB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAyB,EAD9C,qBAAA;AAAA,EAGR,MAAM,mBAAmB,MAA6C,EAAA;AACpE,IAAA,OAAO,MAAM,IAAK,CAAA,IAAA,CAAK,MAAM,mBAAmB,CAAA,CAAE,MAAM,CAAW,OAAA,KAAA;AACjE,MAAA,IAAI,MAAQ,EAAA;AACV,QAAQ,OAAA,CAAA,KAAA,CAAM,UAAU,MAAM,CAAA;AAAA;AAChC,KACD,CAAA;AAAA;AACH,EAEA,MAAM,wBAAwB,gBAA0C,EAAA;AACtE,IAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,MAAM,MAAA,IAAA,CAAK,KAAK,mBAAmB,CAAA,CAAE,MAAM,WAAa,EAAA,IAAI,EAAE,MAAO,EAAA;AACrE,MAAA,IAAA,CAAK,qBAAwB,GAAA,SAAA;AAC7B,MAAA;AAAA;AAEF,IAAA,MAAM,IAAK,CAAA,IAAA,CAAK,WAAY,CAAA,OAAM,GAAO,KAAA;AACvC,MAAA,MAAM,kBAAqB,GAAA,MAAM,IAAK,CAAA,cAAA,CAAe,GAAG,CAAA;AACxD,MACE,IAAA,kBAAA,IACA,kBAAmB,CAAA,aAAA,KAAkB,gBACrC,EAAA;AACA,QAAA,MAAM,IAAI,mBAAmB,CAAA,CAAE,MAAM,WAAa,EAAA,IAAI,EAAE,MAAO,EAAA;AAAA;AAEjE,MAAA,MAAM,QAAW,GAAA,MAAM,IAAK,CAAA,gBAAA,CAAiB,kBAAkB,GAAG,CAAA;AAClE,MAAA,IAAI,CAAC,QAAU,EAAA;AACb,QAAM,MAAA,cAAA,GAAiBA,4CAAyB,gBAAgB,CAAA;AAChE,QAAM,MAAA,IAAA,CAAK,kBAAmB,CAAA,cAAA,EAAgB,GAAG,CAAA;AAAA,OAC5C,MAAA;AACL,QAAA,MAAM,GAAM,GAAA,MAAMC,iCAAe,CAAA,eAAA,EAAiB,QAAQ,CAAA;AAC1D,QAAA,IAAI,GAAK,EAAA;AACP,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAS,MAAA,EAAA,gBAAgB,CAA4E,yEAAA,EAAA,GAAA,CAAI,OAAO,CAAA;AAAA,WAClH;AAAA;AACF;AACF,KACD,CAAA;AACD,IAAA,MAAM,GAAM,GAAA,MAAM,IAAK,CAAA,gBAAA,CAAiB,gBAAgB,CAAA;AACxD,IAAA,IAAA,CAAK,qBAAwB,GAAA,GAAA;AAAA;AAC/B,EAEA,4BAA4D,GAAA;AAC1D,IAAA,OAAO,IAAK,CAAA,qBAAA;AAAA;AACd,EAEA,MAAM,eACJ,GACsC,EAAA;AACtC,IAAM,MAAA,EAAA,GAAK,OAAO,IAAK,CAAA,IAAA;AACvB,IAAO,OAAA,MAAM,GAAoB,mBAAmB,CAAA,CACjD,MAAM,WAAa,EAAA,IAAI,EACvB,KAAM,EAAA;AAAA;AACX,EAEA,MAAM,2BACJ,MAC4B,EAAA;AAC5B,IAAA,MAAM,YACJ,GAAA,MAAM,IAAK,CAAA,IAAA,CAAK,MAAuB,mBAAmB,CAAA;AAE5D,IAAA,IAAI,MAAQ,EAAA;AACV,MAAM,MAAA,UAAA,GAAa,YAAa,CAAA,MAAA,CAAO,CAAQ,IAAA,KAAA;AAC7C,QAAO,OAAAC,cAAA,CAAQ,MAAsB,MAAM,CAAA;AAAA,OAC5C,CAAA;AACD,MAAA,IAAI,KAAK,qBAAuB,EAAA;AAC9B,QAAW,UAAA,CAAA,IAAA,CAAK,KAAK,qBAAqB,CAAA;AAAA;AAE5C,MAAO,OAAA,UAAA;AAAA;AAGT,IAAO,OAAA,YAAA;AAAA;AACT,EAEA,MAAM,gBACJ,CAAA,aAAA,EACA,GACsC,EAAA;AACtC,IAAM,MAAA,EAAA,GAAK,OAAO,IAAK,CAAA,IAAA;AACvB,IAAO,OAAA,MAAM,GACV,KAAuB,CAAA,mBAAmB,EAC1C,KAAM,CAAA,eAAA,EAAiB,aAAa,CAAA,CAEpC,KAAM,EAAA;AAAA;AACX,EAEA,MAAM,kBACJ,CAAA,QAAA,EACA,GACiB,EAAA;AACjB,IAAA,IAAI,MAAM,IAAK,CAAA,gBAAA,CAAiB,QAAS,CAAA,aAAA,EAAe,GAAG,CAAG,EAAA;AAC5D,MAAA,MAAM,IAAIC,oBAAA;AAAA,QACR,CAAA,oBAAA,EAAuB,SAAS,aAAa,CAAA,wBAAA;AAAA,OAC/C;AAAA;AAGF,IAAM,MAAA,MAAA,GAAS,MAAM,GAAI,CAAA,mBAAmB,EACzC,MAAwB,CAAA,QAAQ,CAChC,CAAA,SAAA,CAA4B,IAAI,CAAA;AACnC,IAAI,IAAA,MAAA,IAAU,MAAQ,EAAA,MAAA,GAAS,CAAG,EAAA;AAChC,MAAO,OAAA,MAAA,CAAO,CAAC,CAAE,CAAA,EAAA;AAAA;AAGnB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAwC,qCAAA,EAAA,IAAA,CAAK,SAAU,CAAA,QAAQ,CAAC,CAAA,EAAA;AAAA,KAClE;AAAA;AACF,EAEA,MAAM,kBAAA,CACJ,eACA,EAAA,gBAAA,EACA,WACe,EAAA;AACf,IAAA,MAAM,GAAM,GAAA,WAAA,IAAgB,MAAM,IAAA,CAAK,KAAK,WAAY,EAAA;AACxD,IAAM,MAAA,kBAAA,GAAqB,MAAM,IAAK,CAAA,gBAAA;AAAA,MACpC,gBAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,IAAI,CAAC,kBAAoB,EAAA;AACvB,MAAA,MAAM,IAAIC,oBAAA;AAAA,QACR,wBAAwB,gBAAgB,CAAA,eAAA;AAAA,OAC1C;AAAA;AAGF,IAAA,IACE,mBAAmB,MAAW,KAAA,QAAA,IAC9B,kBAAmB,CAAA,MAAA,KAAW,gBAAgB,MAC9C,EAAA;AACA,MAAM,MAAA,IAAIC,kBAAW,CAA+C,6CAAA,CAAA,CAAA;AAAA;AAGtE,IAAI,IAAAC,sBAAA,CAAgB,kBAAoB,EAAA,eAAe,CAAG,EAAA;AACxD,MAAA;AAAA;AAGF,IAAA,MAAM,MAAS,GAAA,MAAM,GAAqB,CAAA,mBAAmB,EAC1D,KAAM,CAAA,IAAA,EAAM,kBAAmB,CAAA,EAAE,CACjC,CAAA,MAAA,CAAO,eAAe,CAAA,CACtB,UAAU,IAAI,CAAA;AAEjB,IAAA,IAAI,CAAC,WAAa,EAAA;AAChB,MAAA,MAAM,IAAI,MAAO,EAAA;AAAA;AAGnB,IAAA,IAAI,CAAC,MAAA,IAAU,MAAO,CAAA,MAAA,KAAW,CAAG,EAAA;AAClC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,uCAAuC,IAAK,CAAA,SAAA;AAAA,UAC1C;AAAA,SACD,CAAA,mBAAA,EAAsB,IAAK,CAAA,SAAA,CAAU,eAAe,CAAC,CAAA,EAAA;AAAA,OACxD;AAAA;AACF;AACF,EAEA,MAAM,kBACJ,CAAA,aAAA,EACA,WACe,EAAA;AACf,IAAA,MAAM,GAAM,GAAA,WAAA,IAAgB,MAAM,IAAA,CAAK,KAAK,WAAY,EAAA;AACxD,IAAA,MAAM,WAAc,GAAA,MAAM,IAAK,CAAA,gBAAA,CAAiB,eAAe,GAAG,CAAA;AAClE,IAAA,IAAI,CAAC,WAAa,EAAA;AAChB,MAAA,MAAM,IAAIF,oBAAA;AAAA,QACR,wBAAwB,aAAa,CAAA,eAAA;AAAA,OACvC;AAAA;AAGF,IAAM,MAAA,GAAA,CAAqB,mBAAmB,CAAA,CAC3C,MAAO,EAAA,CACP,QAAQ,IAAM,EAAA,CAAC,WAAY,CAAA,EAAG,CAAC,CAAA;AAElC,IAAA,IAAI,CAAC,WAAa,EAAA;AAChB,MAAA,MAAM,IAAI,MAAO,EAAA;AAAA;AACnB;AAEJ;AAEO,SAAS,cAAc,GAAoC,EAAA;AAChE,EAAO,OAAA;AAAA,IACL,QAAQ,GAAI,CAAA,MAAA;AAAA,IACZ,aAAa,GAAI,CAAA,WAAA;AAAA,IACjB,OAAO,GAAI,CAAA,KAAA;AAAA,IACX,QAAQ,GAAI,CAAA,MAAA;AAAA,IACZ,YAAY,GAAI,CAAA,UAAA;AAAA,IAChB,WAAW,GAAI,CAAA,SAAA;AAAA,IACf,cAAc,GAAI,CAAA,YAAA;AAAA,IAClB,WAAW,GAAI,CAAA,SAAA,KAAc,QAAQ,GAAI,CAAA,SAAA,KAAc,IAAI,IAAO,GAAA;AAAA,GACpE;AACF;;;;;;"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pluginRbacCommon = require('@backstage-community/plugin-rbac-common');
|
|
4
|
+
var helper = require('../helper.cjs.js');
|
|
5
|
+
var adminCreation = require('../admin-permissions/admin-creation.cjs.js');
|
|
6
|
+
var policiesValidation = require('../validation/policies-validation.cjs.js');
|
|
7
|
+
|
|
8
|
+
const DEFAULT_ROLE_DESCRIPTION = "Role with default permissions for all users and groups.";
|
|
9
|
+
const DEFAULT_PERMISSIONS_CONF = "permission.rbac.defaultPermissions";
|
|
10
|
+
class DefaultPermissionsReader {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
}
|
|
14
|
+
readRole() {
|
|
15
|
+
const defPermissionsConfig = this.config.getOptionalConfig(
|
|
16
|
+
DEFAULT_PERMISSIONS_CONF
|
|
17
|
+
);
|
|
18
|
+
let role;
|
|
19
|
+
if (defPermissionsConfig) {
|
|
20
|
+
role = defPermissionsConfig.getOptionalString("defaultRole");
|
|
21
|
+
if (!role) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
"Default role is mandatory for defaultPermissions configuration. Please set a valid default role in the configuration."
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
const validationError = policiesValidation.validateEntityReference(role, true);
|
|
27
|
+
if (validationError) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Invalid default role '${role}': ${validationError.message}`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return role;
|
|
34
|
+
}
|
|
35
|
+
readPolicies() {
|
|
36
|
+
const defPermissionsConfig = this.config.getOptionalConfig(
|
|
37
|
+
DEFAULT_PERMISSIONS_CONF
|
|
38
|
+
);
|
|
39
|
+
const role = this.readRole();
|
|
40
|
+
let policies = [];
|
|
41
|
+
if (defPermissionsConfig) {
|
|
42
|
+
const basicPermissions = defPermissionsConfig.getOptionalConfigArray("basicPermissions");
|
|
43
|
+
if (!basicPermissions || basicPermissions.length === 0) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`The default role '${role}' requires at least one entry in permission.rbac.defaultPermissions.basicPermissions.`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
policies = basicPermissions.map((permission) => {
|
|
49
|
+
const permissionName = permission.getString("permission");
|
|
50
|
+
const action = permission.getOptionalString("action");
|
|
51
|
+
if (action && !pluginRbacCommon.isValidPermissionAction(action)) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Invalid action '${action}' for permission '${permissionName}'.`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
entityReference: role,
|
|
58
|
+
permission: permissionName,
|
|
59
|
+
policy: action || "use",
|
|
60
|
+
effect: "allow"
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return policies;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
class DefaultPermissionsSyncher {
|
|
68
|
+
constructor(roleMetadataStorage, enforcer, defaultPermissionsReader) {
|
|
69
|
+
this.roleMetadataStorage = roleMetadataStorage;
|
|
70
|
+
this.enforcer = enforcer;
|
|
71
|
+
this.defaultPermissionsReader = defaultPermissionsReader;
|
|
72
|
+
}
|
|
73
|
+
async sync() {
|
|
74
|
+
const policies = this.defaultPermissionsReader.readPolicies();
|
|
75
|
+
const roleEntityRef = this.defaultPermissionsReader.readRole();
|
|
76
|
+
const prevDefRole = await this.roleMetadataStorage.getDefaultRole();
|
|
77
|
+
const err = await policiesValidation.validateSource("configuration", prevDefRole);
|
|
78
|
+
if (err) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Detected previous default role with incompatible source: ${err.message}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (!roleEntityRef) {
|
|
84
|
+
if (prevDefRole) {
|
|
85
|
+
const pls = await this.enforcer.getFilteredPolicy(
|
|
86
|
+
0,
|
|
87
|
+
prevDefRole.roleEntityRef
|
|
88
|
+
);
|
|
89
|
+
if (pls.length > 0) {
|
|
90
|
+
await this.enforcer.removePolicies(pls);
|
|
91
|
+
}
|
|
92
|
+
await this.roleMetadataStorage.removeRoleMetadata(
|
|
93
|
+
prevDefRole.roleEntityRef
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (prevDefRole && prevDefRole.roleEntityRef !== roleEntityRef) {
|
|
99
|
+
const oldPolicies = await this.enforcer.getFilteredPolicy(
|
|
100
|
+
0,
|
|
101
|
+
prevDefRole.roleEntityRef
|
|
102
|
+
);
|
|
103
|
+
if (oldPolicies.length > 0) {
|
|
104
|
+
await this.enforcer.removePolicies(oldPolicies);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const casbinPolicies = policies.map((p) => [
|
|
108
|
+
p.entityReference,
|
|
109
|
+
p.permission,
|
|
110
|
+
p.policy,
|
|
111
|
+
p.effect
|
|
112
|
+
]);
|
|
113
|
+
await this.roleMetadataStorage.syncDefaultRoleMetadata(roleEntityRef);
|
|
114
|
+
await helper.syncRolePolicies(this.enforcer, roleEntityRef, casbinPolicies);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function buildDefaultRoleMetadata(defaultRoleRef) {
|
|
118
|
+
return {
|
|
119
|
+
roleEntityRef: defaultRoleRef,
|
|
120
|
+
author: adminCreation.ADMIN_ROLE_AUTHOR,
|
|
121
|
+
source: "configuration",
|
|
122
|
+
isDefault: true,
|
|
123
|
+
description: DEFAULT_ROLE_DESCRIPTION,
|
|
124
|
+
modifiedBy: adminCreation.ADMIN_ROLE_AUTHOR,
|
|
125
|
+
lastModified: (/* @__PURE__ */ new Date()).toUTCString(),
|
|
126
|
+
createdAt: (/* @__PURE__ */ new Date()).toUTCString()
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
exports.DefaultPermissionsReader = DefaultPermissionsReader;
|
|
131
|
+
exports.DefaultPermissionsSyncher = DefaultPermissionsSyncher;
|
|
132
|
+
exports.buildDefaultRoleMetadata = buildDefaultRoleMetadata;
|
|
133
|
+
//# sourceMappingURL=default-permissions.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-permissions.cjs.js","sources":["../../src/default-permissions/default-permissions.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 { Config } from '@backstage/config';\n\nimport {\n RoleBasedPolicy,\n isValidPermissionAction,\n} from '@backstage-community/plugin-rbac-common';\n\nimport type {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport type { EnforcerDelegate } from '../service/enforcer-delegate';\nimport { syncRolePolicies } from '../helper';\nimport { ADMIN_ROLE_AUTHOR } from '../admin-permissions/admin-creation';\nimport {\n validateSource,\n validateEntityReference,\n} from '../validation/policies-validation';\n\nconst DEFAULT_ROLE_DESCRIPTION =\n 'Role with default permissions for all users and groups.';\n\nconst DEFAULT_PERMISSIONS_CONF = 'permission.rbac.defaultPermissions';\n\nexport class DefaultPermissionsReader {\n constructor(private readonly config: Config) {}\n\n readRole(): string | undefined {\n const defPermissionsConfig = this.config.getOptionalConfig(\n DEFAULT_PERMISSIONS_CONF,\n );\n let role: string | undefined;\n\n if (defPermissionsConfig) {\n role = defPermissionsConfig.getOptionalString('defaultRole');\n\n if (!role) {\n throw new Error(\n 'Default role is mandatory for defaultPermissions configuration. Please set a valid default role in the configuration.',\n );\n }\n\n const validationError = validateEntityReference(role, true);\n if (validationError) {\n throw new Error(\n `Invalid default role '${role}': ${validationError.message}`,\n );\n }\n }\n\n return role;\n }\n\n readPolicies(): RoleBasedPolicy[] {\n const defPermissionsConfig = this.config.getOptionalConfig(\n DEFAULT_PERMISSIONS_CONF,\n );\n const role = this.readRole();\n\n let policies: RoleBasedPolicy[] = [];\n if (defPermissionsConfig) {\n const basicPermissions =\n defPermissionsConfig.getOptionalConfigArray('basicPermissions');\n if (!basicPermissions || basicPermissions.length === 0) {\n throw new Error(\n `The default role '${role}' requires at least one entry in permission.rbac.defaultPermissions.basicPermissions.`,\n );\n }\n\n policies = basicPermissions.map(permission => {\n const permissionName = permission.getString('permission');\n const action = permission.getOptionalString('action');\n\n if (action && !isValidPermissionAction(action)) {\n throw new Error(\n `Invalid action '${action}' for permission '${permissionName}'.`,\n );\n }\n\n return {\n entityReference: role,\n permission: permissionName,\n policy: action || 'use',\n effect: 'allow',\n };\n });\n }\n\n return policies;\n }\n}\n\nexport class DefaultPermissionsSyncher {\n constructor(\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly enforcer: EnforcerDelegate,\n private readonly defaultPermissionsReader: DefaultPermissionsReader,\n ) {}\n\n public async sync() {\n const policies = this.defaultPermissionsReader.readPolicies();\n const roleEntityRef = this.defaultPermissionsReader.readRole();\n\n const prevDefRole = await this.roleMetadataStorage.getDefaultRole();\n\n const err = await validateSource('configuration', prevDefRole);\n if (err) {\n throw new Error(\n `Detected previous default role with incompatible source: ${err.message}`,\n );\n }\n\n if (!roleEntityRef) {\n if (prevDefRole) {\n const pls = await this.enforcer.getFilteredPolicy(\n 0,\n prevDefRole.roleEntityRef,\n );\n if (pls.length > 0) {\n await this.enforcer.removePolicies(pls);\n }\n await this.roleMetadataStorage.removeRoleMetadata(\n prevDefRole.roleEntityRef,\n );\n }\n\n return;\n }\n\n // Clean up orphaned permissions if role name changed\n if (prevDefRole && prevDefRole.roleEntityRef !== roleEntityRef) {\n const oldPolicies = await this.enforcer.getFilteredPolicy(\n 0,\n prevDefRole.roleEntityRef,\n );\n if (oldPolicies.length > 0) {\n await this.enforcer.removePolicies(oldPolicies);\n }\n }\n\n const casbinPolicies: string[][] = policies.map(p => [\n p.entityReference!,\n p.permission!,\n p.policy!,\n p.effect!,\n ]);\n\n await this.roleMetadataStorage.syncDefaultRoleMetadata(roleEntityRef);\n await syncRolePolicies(this.enforcer, roleEntityRef, casbinPolicies);\n }\n}\n\nexport function buildDefaultRoleMetadata(\n defaultRoleRef: string,\n): RoleMetadataDao {\n return {\n roleEntityRef: defaultRoleRef,\n author: ADMIN_ROLE_AUTHOR,\n source: 'configuration',\n isDefault: true,\n description: DEFAULT_ROLE_DESCRIPTION,\n modifiedBy: ADMIN_ROLE_AUTHOR,\n lastModified: new Date().toUTCString(),\n createdAt: new Date().toUTCString(),\n };\n}\n"],"names":["validateEntityReference","isValidPermissionAction","validateSource","syncRolePolicies","ADMIN_ROLE_AUTHOR"],"mappings":"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"}
|
package/dist/helper.cjs.js
CHANGED
|
@@ -24,6 +24,24 @@ function typedPoliciesToString(policies, type) {
|
|
|
24
24
|
function metadataStringToPolicy(policy) {
|
|
25
25
|
return policy.replace("[", "").replace("]", "").split(", ");
|
|
26
26
|
}
|
|
27
|
+
function policyArraysEqual(a, b) {
|
|
28
|
+
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
29
|
+
}
|
|
30
|
+
async function syncRolePolicies(enforcerDelegate, roleEntityRef, desiredPolicies) {
|
|
31
|
+
const current = await enforcerDelegate.getFilteredPolicy(0, roleEntityRef);
|
|
32
|
+
const toAdd = desiredPolicies.filter(
|
|
33
|
+
(d) => !current.some((c) => policyArraysEqual(c, d))
|
|
34
|
+
);
|
|
35
|
+
const toRemove = current.filter(
|
|
36
|
+
(c) => !desiredPolicies.some((d) => policyArraysEqual(c, d))
|
|
37
|
+
);
|
|
38
|
+
if (toAdd.length > 0) {
|
|
39
|
+
await enforcerDelegate.addPolicies(toAdd);
|
|
40
|
+
}
|
|
41
|
+
if (toRemove.length > 0) {
|
|
42
|
+
await enforcerDelegate.removePolicies(toRemove);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
27
45
|
async function removeTheDifference(originalGroup, addedGroup, source, roleEntityRef, enf, auditor$1, modifiedBy) {
|
|
28
46
|
originalGroup.sort((a, b) => a.localeCompare(b));
|
|
29
47
|
addedGroup.sort((a, b) => a.localeCompare(b));
|
|
@@ -204,6 +222,7 @@ exports.policiesToString = policiesToString;
|
|
|
204
222
|
exports.policyToString = policyToString;
|
|
205
223
|
exports.processConditionMapping = processConditionMapping;
|
|
206
224
|
exports.removeTheDifference = removeTheDifference;
|
|
225
|
+
exports.syncRolePolicies = syncRolePolicies;
|
|
207
226
|
exports.transformArrayToPolicy = transformArrayToPolicy;
|
|
208
227
|
exports.transformPolicyGroupToLowercase = transformPolicyGroupToLowercase;
|
|
209
228
|
exports.transformRolesGroupToLowercase = transformRolesGroupToLowercase;
|
package/dist/helper.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.cjs.js","sources":["../src/helper.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AuditorService, AuthService } from '@backstage/backend-plugin-api';\nimport type { MetadataResponse } from '@backstage/plugin-permission-common';\n\nimport {\n difference,\n fromPairs,\n isArray,\n isEqual,\n isPlainObject,\n omitBy,\n sortBy,\n toPairs,\n ValueKeyIteratee,\n} from 'lodash';\n\nimport {\n PermissionAction,\n PermissionInfo,\n RoleBasedPolicy,\n RoleConditionalPolicyDecision,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport { ActionType, RoleEvents } from './auditor/auditor';\nimport { RoleMetadataDao, RoleMetadataStorage } from './database/role-metadata';\nimport { EnforcerDelegate } from './service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from './service/plugin-endpoints';\nimport { RoleMetadata } from '@backstage-community/plugin-rbac-common';\nimport { RBACFilters } from './permissions';\n\nexport function policyToString(policy: string[]): string {\n return `[${policy.join(', ')}]`;\n}\n\nexport function typedPolicyToString(policy: string[], type: string): string {\n return `${type}, ${policy.join(', ')}`;\n}\n\nexport function policiesToString(policies: string[][]): string {\n const policiesString = policies\n .map(policy => policyToString(policy))\n .join(',');\n return `[${policiesString}]`;\n}\n\nexport function typedPoliciesToString(\n policies: string[][],\n type: string,\n): string {\n const policiesString = policies\n .map(policy => {\n return policy.length !== 0 ? typedPolicyToString(policy, type) : '';\n })\n .join('\\n');\n return `\n ${policiesString}\n `;\n}\n\nexport function metadataStringToPolicy(policy: string): string[] {\n return policy.replace('[', '').replace(']', '').split(', ');\n}\n\nexport async function removeTheDifference(\n originalGroup: string[],\n addedGroup: string[],\n source: Source,\n roleEntityRef: string,\n enf: EnforcerDelegate,\n auditor: AuditorService,\n modifiedBy: string,\n): Promise<void> {\n originalGroup.sort((a, b) => a.localeCompare(b));\n addedGroup.sort((a, b) => a.localeCompare(b));\n const missing = difference(originalGroup, addedGroup);\n\n const groupPolicies: string[][] = [];\n for (const missingRole of missing) {\n groupPolicies.push([missingRole, roleEntityRef]);\n }\n\n if (groupPolicies.length === 0) {\n return;\n }\n\n const roleMetadata = { source, modifiedBy, roleEntityRef };\n const existingMembers = await enf.getFilteredGroupingPolicy(1, roleEntityRef);\n const actionType =\n existingMembers.length === missing.length\n ? ActionType.DELETE\n : ActionType.UPDATE;\n const auditorMeta = {\n ...roleMetadata,\n members: groupPolicies.map(gp => gp[0]),\n };\n const auditorEvent = await auditor.createEvent({\n eventId: RoleEvents.ROLE_WRITE,\n severityLevel: 'medium',\n meta: { actionType, source: auditorMeta.source },\n });\n\n try {\n await enf.removeGroupingPolicies(groupPolicies, roleMetadata, false);\n await auditorEvent.success({ meta: auditorMeta });\n } catch (error) {\n await auditorEvent.fail({\n error,\n meta: auditorMeta,\n });\n throw error;\n }\n}\n\nexport function transformArrayToPolicy(policyArray: string[]): RoleBasedPolicy {\n const [entityReference, permission, policy, effect] = policyArray;\n return { entityReference, permission, policy, effect };\n}\n\nexport function transformPolicyGroupToLowercase(policyArray: string[]) {\n if (\n policyArray.length > 1 &&\n policyArray[0].startsWith('g') &&\n (policyArray[1].startsWith('user') || policyArray[1].startsWith('group'))\n ) {\n policyArray[1] = policyArray[1].toLocaleLowerCase('en-US');\n }\n}\n\nexport function transformRolesGroupToLowercase(roles: string[][]) {\n return roles.map(role =>\n role.length >= 1\n ? [role[0].toLocaleLowerCase('en-US'), ...role.slice(1)]\n : role,\n );\n}\n\nexport function deepSortedEqual(\n obj1: Record<string, any>,\n obj2: Record<string, any>,\n excludeFields?: string[],\n): boolean {\n let copyObj1;\n let copyObj2;\n if (excludeFields) {\n const excludeFieldsPredicate: ValueKeyIteratee<any> = (_value, key) => {\n for (const field of excludeFields) {\n if (key === field) {\n return true;\n }\n }\n return false;\n };\n copyObj1 = omitBy(obj1, excludeFieldsPredicate);\n copyObj2 = omitBy(obj2, excludeFieldsPredicate);\n }\n\n const sortedObj1 = sortBy(toPairs(copyObj1 || obj1), ([key]) => key);\n const sortedObj2 = sortBy(toPairs(copyObj2 || obj2), ([key]) => key);\n\n return isEqual(sortedObj1, sortedObj2);\n}\n\nexport function isPermissionAction(action: string): action is PermissionAction {\n return ['create', 'read', 'update', 'delete', 'use'].includes(\n action as PermissionAction,\n );\n}\n\nexport async function buildRoleSourceMap(\n policies: string[][],\n roleMetadata: RoleMetadataStorage,\n): Promise<Map<string, Source | undefined>> {\n return await policies.reduce(\n async (\n acc: Promise<Map<string, Source | undefined>>,\n policy: string[],\n ): Promise<Map<string, Source | undefined>> => {\n const roleEntityRef = policy[0];\n const acummulator = await acc;\n if (!acummulator.has(roleEntityRef)) {\n const metadata = await roleMetadata.findRoleMetadata(roleEntityRef);\n acummulator.set(roleEntityRef, metadata?.source);\n }\n return acummulator;\n },\n Promise.resolve(new Map<string, Source | undefined>()),\n );\n}\n\nexport function mergeRoleMetadata(\n currentMetadata: RoleMetadataDao,\n newMetadata: RoleMetadataDao,\n): RoleMetadataDao {\n const mergedMetaData: RoleMetadataDao = { ...currentMetadata };\n mergedMetaData.lastModified =\n newMetadata.lastModified ?? new Date().toUTCString();\n mergedMetaData.modifiedBy = newMetadata.modifiedBy;\n mergedMetaData.description =\n newMetadata.description ?? currentMetadata.description;\n mergedMetaData.roleEntityRef = newMetadata.roleEntityRef;\n mergedMetaData.source = newMetadata.source;\n mergedMetaData.owner = newMetadata.owner ?? currentMetadata.owner;\n return mergedMetaData;\n}\n\nexport async function processConditionMapping(\n roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction>,\n pluginPermMetaData: PluginPermissionMetadataCollector,\n auth: AuthService,\n): Promise<RoleConditionalPolicyDecision<PermissionInfo>> {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: roleConditionPolicy.pluginId,\n });\n\n const rule: MetadataResponse | undefined =\n await pluginPermMetaData.getMetadataByPluginId(\n roleConditionPolicy.pluginId,\n token,\n );\n if (!rule?.permissions) {\n throw new Error(\n `Unable to get permission list for plugin ${roleConditionPolicy.pluginId}`,\n );\n }\n\n const permInfo: PermissionInfo[] = [];\n for (const action of roleConditionPolicy.permissionMapping) {\n const perm = rule.permissions.find(permission => {\n if (permission.type === 'resource') {\n const isCorrectResourceType =\n permission.resourceType === roleConditionPolicy.resourceType;\n const isCorrectAction = action === permission.attributes.action;\n const undefinedAction =\n action === 'use' && permission.attributes.action === undefined;\n\n return isCorrectResourceType && (isCorrectAction || undefinedAction);\n }\n return false;\n });\n\n if (!perm) {\n throw new Error(\n `Unable to find permission to get permission name for resource type '${\n roleConditionPolicy.resourceType\n }' and action ${JSON.stringify(action)}`,\n );\n }\n permInfo.push({ name: perm.name, action });\n }\n\n return {\n ...roleConditionPolicy,\n permissionMapping: permInfo,\n };\n}\n\nexport function deepSort(value: any): any {\n if (isArray(value)) {\n return sortBy(value.map(deepSort));\n } else if (isPlainObject(value)) {\n return fromPairs(\n sortBy(\n toPairs(value).map(([k, v]: [string, any]) => [k, deepSort(v)]),\n 0,\n ),\n );\n }\n return value;\n}\n\nexport function deepSortEqual(obj1: any, obj2: any): boolean {\n return isEqual(deepSort(obj1), deepSort(obj2));\n}\n\nexport const matches = (\n role?: RoleMetadata,\n filters?: RBACFilters,\n): boolean => {\n if (!filters) {\n return true;\n }\n\n if (!role) {\n return false;\n }\n\n if ('allOf' in filters) {\n return filters.allOf.every(filter => matches(role, filter));\n }\n\n if ('anyOf' in filters) {\n return filters.anyOf.some(filter => matches(role, filter));\n }\n\n if ('not' in filters) {\n return !matches(role, filters.not);\n }\n\n return filters.values.includes(role.owner);\n};\n"],"names":["auditor","difference","ActionType","RoleEvents","omitBy","sortBy","toPairs","isEqual","isArray","isPlainObject","fromPairs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"helper.cjs.js","sources":["../src/helper.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { AuditorService, AuthService } from '@backstage/backend-plugin-api';\nimport type { MetadataResponse } from '@backstage/plugin-permission-common';\n\nimport {\n difference,\n fromPairs,\n isArray,\n isEqual,\n isPlainObject,\n omitBy,\n sortBy,\n toPairs,\n ValueKeyIteratee,\n} from 'lodash';\n\nimport {\n PermissionAction,\n PermissionInfo,\n RoleBasedPolicy,\n RoleConditionalPolicyDecision,\n Source,\n} from '@backstage-community/plugin-rbac-common';\n\nimport { ActionType, RoleEvents } from './auditor/auditor';\nimport { RoleMetadataDao, RoleMetadataStorage } from './database/role-metadata';\nimport { EnforcerDelegate } from './service/enforcer-delegate';\nimport { PluginPermissionMetadataCollector } from './service/plugin-endpoints';\nimport { RoleMetadata } from '@backstage-community/plugin-rbac-common';\nimport { RBACFilters } from './permissions';\n\nexport function policyToString(policy: string[]): string {\n return `[${policy.join(', ')}]`;\n}\n\nexport function typedPolicyToString(policy: string[], type: string): string {\n return `${type}, ${policy.join(', ')}`;\n}\n\nexport function policiesToString(policies: string[][]): string {\n const policiesString = policies\n .map(policy => policyToString(policy))\n .join(',');\n return `[${policiesString}]`;\n}\n\nexport function typedPoliciesToString(\n policies: string[][],\n type: string,\n): string {\n const policiesString = policies\n .map(policy => {\n return policy.length !== 0 ? typedPolicyToString(policy, type) : '';\n })\n .join('\\n');\n return `\n ${policiesString}\n `;\n}\n\nexport function metadataStringToPolicy(policy: string): string[] {\n return policy.replace('[', '').replace(']', '').split(', ');\n}\n\n/**\n * Compares two policy arrays (e.g. [entityRef, permission, policy, effect]) for equality.\n */\nfunction policyArraysEqual(a: string[], b: string[]): boolean {\n return a.length === b.length && a.every((v, i) => v === b[i]);\n}\n\n/**\n * Syncs permission policies for a role to match a desired set.\n * - Adds policies that are in desired but not in the enforcer (addPolicies skips existing via hasPolicy).\n * - Removes policies that are in the enforcer but not in desired.\n *\n * @param enforcerDelegate - Enforcer to read from and write to\n * @param roleEntityRef - Role to sync (used to load current policies via getFilteredPolicy(0, roleEntityRef))\n * @param desiredPolicies - Desired policies in casbin format string[][]\n */\nexport async function syncRolePolicies(\n enforcerDelegate: EnforcerDelegate,\n roleEntityRef: string,\n desiredPolicies: string[][],\n): Promise<void> {\n const current = await enforcerDelegate.getFilteredPolicy(0, roleEntityRef);\n\n const toAdd = desiredPolicies.filter(\n d => !current.some(c => policyArraysEqual(c, d)),\n );\n const toRemove = current.filter(\n c => !desiredPolicies.some(d => policyArraysEqual(c, d)),\n );\n\n if (toAdd.length > 0) {\n await enforcerDelegate.addPolicies(toAdd);\n }\n if (toRemove.length > 0) {\n await enforcerDelegate.removePolicies(toRemove);\n }\n}\n\nexport async function removeTheDifference(\n originalGroup: string[],\n addedGroup: string[],\n source: Source,\n roleEntityRef: string,\n enf: EnforcerDelegate,\n auditor: AuditorService,\n modifiedBy: string,\n): Promise<void> {\n originalGroup.sort((a, b) => a.localeCompare(b));\n addedGroup.sort((a, b) => a.localeCompare(b));\n const missing = difference(originalGroup, addedGroup);\n\n const groupPolicies: string[][] = [];\n for (const missingRole of missing) {\n groupPolicies.push([missingRole, roleEntityRef]);\n }\n\n if (groupPolicies.length === 0) {\n return;\n }\n\n const roleMetadata = { source, modifiedBy, roleEntityRef };\n const existingMembers = await enf.getFilteredGroupingPolicy(1, roleEntityRef);\n const actionType =\n existingMembers.length === missing.length\n ? ActionType.DELETE\n : ActionType.UPDATE;\n const auditorMeta = {\n ...roleMetadata,\n members: groupPolicies.map(gp => gp[0]),\n };\n const auditorEvent = await auditor.createEvent({\n eventId: RoleEvents.ROLE_WRITE,\n severityLevel: 'medium',\n meta: { actionType, source: auditorMeta.source },\n });\n\n try {\n await enf.removeGroupingPolicies(groupPolicies, roleMetadata, false);\n await auditorEvent.success({ meta: auditorMeta });\n } catch (error) {\n await auditorEvent.fail({\n error,\n meta: auditorMeta,\n });\n throw error;\n }\n}\n\nexport function transformArrayToPolicy(policyArray: string[]): RoleBasedPolicy {\n const [entityReference, permission, policy, effect] = policyArray;\n return { entityReference, permission, policy, effect };\n}\n\nexport function transformPolicyGroupToLowercase(policyArray: string[]) {\n if (\n policyArray.length > 1 &&\n policyArray[0].startsWith('g') &&\n (policyArray[1].startsWith('user') || policyArray[1].startsWith('group'))\n ) {\n policyArray[1] = policyArray[1].toLocaleLowerCase('en-US');\n }\n}\n\nexport function transformRolesGroupToLowercase(roles: string[][]) {\n return roles.map(role =>\n role.length >= 1\n ? [role[0].toLocaleLowerCase('en-US'), ...role.slice(1)]\n : role,\n );\n}\n\nexport function deepSortedEqual(\n obj1: Record<string, any>,\n obj2: Record<string, any>,\n excludeFields?: string[],\n): boolean {\n let copyObj1;\n let copyObj2;\n if (excludeFields) {\n const excludeFieldsPredicate: ValueKeyIteratee<any> = (_value, key) => {\n for (const field of excludeFields) {\n if (key === field) {\n return true;\n }\n }\n return false;\n };\n copyObj1 = omitBy(obj1, excludeFieldsPredicate);\n copyObj2 = omitBy(obj2, excludeFieldsPredicate);\n }\n\n const sortedObj1 = sortBy(toPairs(copyObj1 || obj1), ([key]) => key);\n const sortedObj2 = sortBy(toPairs(copyObj2 || obj2), ([key]) => key);\n\n return isEqual(sortedObj1, sortedObj2);\n}\n\nexport function isPermissionAction(action: string): action is PermissionAction {\n return ['create', 'read', 'update', 'delete', 'use'].includes(\n action as PermissionAction,\n );\n}\n\nexport async function buildRoleSourceMap(\n policies: string[][],\n roleMetadata: RoleMetadataStorage,\n): Promise<Map<string, Source | undefined>> {\n return await policies.reduce(\n async (\n acc: Promise<Map<string, Source | undefined>>,\n policy: string[],\n ): Promise<Map<string, Source | undefined>> => {\n const roleEntityRef = policy[0];\n const acummulator = await acc;\n if (!acummulator.has(roleEntityRef)) {\n const metadata = await roleMetadata.findRoleMetadata(roleEntityRef);\n acummulator.set(roleEntityRef, metadata?.source);\n }\n return acummulator;\n },\n Promise.resolve(new Map<string, Source | undefined>()),\n );\n}\n\nexport function mergeRoleMetadata(\n currentMetadata: RoleMetadataDao,\n newMetadata: RoleMetadataDao,\n): RoleMetadataDao {\n const mergedMetaData: RoleMetadataDao = { ...currentMetadata };\n mergedMetaData.lastModified =\n newMetadata.lastModified ?? new Date().toUTCString();\n mergedMetaData.modifiedBy = newMetadata.modifiedBy;\n mergedMetaData.description =\n newMetadata.description ?? currentMetadata.description;\n mergedMetaData.roleEntityRef = newMetadata.roleEntityRef;\n mergedMetaData.source = newMetadata.source;\n mergedMetaData.owner = newMetadata.owner ?? currentMetadata.owner;\n return mergedMetaData;\n}\n\nexport async function processConditionMapping(\n roleConditionPolicy: RoleConditionalPolicyDecision<PermissionAction>,\n pluginPermMetaData: PluginPermissionMetadataCollector,\n auth: AuthService,\n): Promise<RoleConditionalPolicyDecision<PermissionInfo>> {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: roleConditionPolicy.pluginId,\n });\n\n const rule: MetadataResponse | undefined =\n await pluginPermMetaData.getMetadataByPluginId(\n roleConditionPolicy.pluginId,\n token,\n );\n if (!rule?.permissions) {\n throw new Error(\n `Unable to get permission list for plugin ${roleConditionPolicy.pluginId}`,\n );\n }\n\n const permInfo: PermissionInfo[] = [];\n for (const action of roleConditionPolicy.permissionMapping) {\n const perm = rule.permissions.find(permission => {\n if (permission.type === 'resource') {\n const isCorrectResourceType =\n permission.resourceType === roleConditionPolicy.resourceType;\n const isCorrectAction = action === permission.attributes.action;\n const undefinedAction =\n action === 'use' && permission.attributes.action === undefined;\n\n return isCorrectResourceType && (isCorrectAction || undefinedAction);\n }\n return false;\n });\n\n if (!perm) {\n throw new Error(\n `Unable to find permission to get permission name for resource type '${\n roleConditionPolicy.resourceType\n }' and action ${JSON.stringify(action)}`,\n );\n }\n permInfo.push({ name: perm.name, action });\n }\n\n return {\n ...roleConditionPolicy,\n permissionMapping: permInfo,\n };\n}\n\nexport function deepSort(value: any): any {\n if (isArray(value)) {\n return sortBy(value.map(deepSort));\n } else if (isPlainObject(value)) {\n return fromPairs(\n sortBy(\n toPairs(value).map(([k, v]: [string, any]) => [k, deepSort(v)]),\n 0,\n ),\n );\n }\n return value;\n}\n\nexport function deepSortEqual(obj1: any, obj2: any): boolean {\n return isEqual(deepSort(obj1), deepSort(obj2));\n}\n\nexport const matches = (\n role?: RoleMetadata,\n filters?: RBACFilters,\n): boolean => {\n if (!filters) {\n return true;\n }\n\n if (!role) {\n return false;\n }\n\n if ('allOf' in filters) {\n return filters.allOf.every(filter => matches(role, filter));\n }\n\n if ('anyOf' in filters) {\n return filters.anyOf.some(filter => matches(role, filter));\n }\n\n if ('not' in filters) {\n return !matches(role, filters.not);\n }\n\n return filters.values.includes(role.owner);\n};\n"],"names":["auditor","difference","ActionType","RoleEvents","omitBy","sortBy","toPairs","isEqual","isArray","isPlainObject","fromPairs"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.cjs.js","sources":["../../src/permissions/resource.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 {
|
|
1
|
+
{"version":3,"file":"resource.cjs.js","sources":["../../src/permissions/resource.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 { RESOURCE_TYPE_POLICY_ENTITY } from '@backstage-community/plugin-rbac-common';\nimport { createPermissionResourceRef } from '@backstage/plugin-permission-node';\nimport { RBACFilter } from './rules';\nimport { RoleMetadataDao } from '../database/role-metadata';\n\n/**\n * Reference to the RBAC permission metadata resource.\n * This is used to create RBAC permissions and conditions.\n *\n */\nexport const permissionMetadataResourceRef = createPermissionResourceRef<\n RoleMetadataDao,\n RBACFilter\n>().with({\n pluginId: 'permission',\n resourceType: RESOURCE_TYPE_POLICY_ENTITY,\n});\n"],"names":["createPermissionResourceRef","RESOURCE_TYPE_POLICY_ENTITY"],"mappings":";;;;;AAyBa,MAAA,6BAAA,GAAgCA,gDAG3C,EAAA,CAAE,IAAK,CAAA;AAAA,EACP,QAAU,EAAA,YAAA;AAAA,EACV,YAAc,EAAAC;AAChB,CAAC;;;;"}
|
|
@@ -17,6 +17,9 @@ const isOwner = pluginPermissionNode.createPermissionRule({
|
|
|
17
17
|
owners: zod.z.string().array().describe("List of entity refs to match against")
|
|
18
18
|
}),
|
|
19
19
|
apply: (roleMeta, { owners }) => {
|
|
20
|
+
if (roleMeta.isDefault) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
20
23
|
if (!roleMeta.owner) {
|
|
21
24
|
return false;
|
|
22
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.cjs.js","sources":["../../src/permissions/rules.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 { createPermissionRule } from '@backstage/plugin-permission-node';\nimport
|
|
1
|
+
{"version":3,"file":"rules.cjs.js","sources":["../../src/permissions/rules.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 { createPermissionRule } from '@backstage/plugin-permission-node';\nimport { z } from 'zod';\nimport zodToJsonSchema from 'zod-to-json-schema';\nimport { permissionMetadataResourceRef } from './resource';\nimport { RoleMetadataDao } from '../database/role-metadata';\n\n/**\n * The RBACFilter is a simple filter without any conditional criteria.\n *\n */\nexport type RBACFilter = {\n key: string;\n values: any[];\n};\n\n/**\n * The RBACFilters type is a recursive type that can be used to create complex filter structures.\n * It can be used to create filters that are a combination of other filters, or a negation of a filter.\n *\n */\nexport type RBACFilters =\n | { anyOf: RBACFilters[] }\n | { allOf: RBACFilters[] }\n | { not: RBACFilters }\n | RBACFilter;\n\nconst isOwner = createPermissionRule({\n name: 'IS_OWNER',\n description:\n 'Should allow access to RBAC roles and Permissions through ownership',\n resourceRef: permissionMetadataResourceRef,\n paramsSchema: z.object({\n owners: z.string().array().describe('List of entity refs to match against'),\n }),\n apply: (roleMeta: RoleMetadataDao, { owners }) => {\n if (roleMeta.isDefault) {\n return true;\n }\n if (!roleMeta.owner) {\n return false;\n }\n return owners.includes(roleMeta.owner);\n },\n toQuery: ({ owners }) => ({\n key: 'owners',\n values: owners,\n }),\n});\n\nexport const rbacRules = {\n name: isOwner.name,\n description: isOwner.description,\n resourceType: isOwner.resourceType,\n paramsSchema: zodToJsonSchema(isOwner.paramsSchema ?? z.object({})),\n};\n\nexport const rules = { isOwner };\n"],"names":["createPermissionRule","permissionMetadataResourceRef","z","zodToJsonSchema"],"mappings":";;;;;;;;;;;AAyCA,MAAM,UAAUA,yCAAqB,CAAA;AAAA,EACnC,IAAM,EAAA,UAAA;AAAA,EACN,WACE,EAAA,qEAAA;AAAA,EACF,WAAa,EAAAC,sCAAA;AAAA,EACb,YAAA,EAAcC,MAAE,MAAO,CAAA;AAAA,IACrB,QAAQA,KAAE,CAAA,MAAA,GAAS,KAAM,EAAA,CAAE,SAAS,sCAAsC;AAAA,GAC3E,CAAA;AAAA,EACD,KAAO,EAAA,CAAC,QAA2B,EAAA,EAAE,QAAa,KAAA;AAChD,IAAA,IAAI,SAAS,SAAW,EAAA;AACtB,MAAO,OAAA,IAAA;AAAA;AAET,IAAI,IAAA,CAAC,SAAS,KAAO,EAAA;AACnB,MAAO,OAAA,KAAA;AAAA;AAET,IAAO,OAAA,MAAA,CAAO,QAAS,CAAA,QAAA,CAAS,KAAK,CAAA;AAAA,GACvC;AAAA,EACA,OAAS,EAAA,CAAC,EAAE,MAAA,EAAc,MAAA;AAAA,IACxB,GAAK,EAAA,QAAA;AAAA,IACL,MAAQ,EAAA;AAAA,GACV;AACF,CAAC,CAAA;AAEM,MAAM,SAAY,GAAA;AAAA,EACvB,MAAM,OAAQ,CAAA,IAAA;AAAA,EACd,aAAa,OAAQ,CAAA,WAAA;AAAA,EACrB,cAAc,OAAQ,CAAA,YAAA;AAAA,EACtB,YAAA,EAAcC,iCAAgB,OAAQ,CAAA,YAAA,IAAgBD,MAAE,MAAO,CAAA,EAAE,CAAC;AACpE;AAEa,MAAA,KAAA,GAAQ,EAAE,OAAQ;;;;;"}
|