@backstage-community/plugin-rbac-backend 6.2.6 → 7.0.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 +42 -0
- package/README.md +44 -6
- package/dist/auditor/auditor.cjs.js +5 -0
- package/dist/auditor/auditor.cjs.js.map +1 -1
- package/dist/auditor/rest-interceptor.cjs.js +5 -0
- package/dist/auditor/rest-interceptor.cjs.js.map +1 -1
- package/dist/database/extra-permission-enabled-plugins-storage.cjs.js +21 -0
- package/dist/database/extra-permission-enabled-plugins-storage.cjs.js.map +1 -0
- package/dist/index.d.ts +7 -6
- package/dist/permissions/conditions.cjs.js +6 -5
- package/dist/permissions/conditions.cjs.js.map +1 -1
- package/dist/permissions/resource.cjs.js +12 -0
- package/dist/permissions/resource.cjs.js.map +1 -0
- package/dist/permissions/rules.cjs.js +3 -4
- package/dist/permissions/rules.cjs.js.map +1 -1
- package/dist/plugin.cjs.js +12 -6
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/service/extendable-id-provider.cjs.js +32 -0
- package/dist/service/extendable-id-provider.cjs.js.map +1 -0
- package/dist/service/permission-definition-routes.cjs.js +104 -0
- package/dist/service/permission-definition-routes.cjs.js.map +1 -0
- package/dist/service/plugin-endpoints.cjs.js +5 -4
- package/dist/service/plugin-endpoints.cjs.js.map +1 -1
- package/dist/service/policies-rest-api.cjs.js +153 -147
- package/dist/service/policies-rest-api.cjs.js.map +1 -1
- package/dist/service/policy-builder.cjs.js +43 -29
- package/dist/service/policy-builder.cjs.js.map +1 -1
- package/dist/service/router.cjs.js +3 -1
- package/dist/service/router.cjs.js.map +1 -1
- package/dist/validation/plugin-validation.cjs.js +15 -0
- package/dist/validation/plugin-validation.cjs.js.map +1 -0
- package/migrations/20250509110032_migrations.js +29 -0
- package/package.json +18 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
### Dependencies
|
|
2
2
|
|
|
3
|
+
## 7.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 2e732e8: **BREAKING**: Removal of the deprecated createRouter from @backstage/plugin-permission-backend. This results in a new requirement of having the permission plugin installed alongside the RBAC backend plugin.
|
|
8
|
+
|
|
9
|
+
Recent changes to the @backstage/plugin-permission-backend resulted in the deprecating and removal of `createRouter` which was primarily used as a way to start both the permission backend plugin and the RBAC backend plugin at the same time. This removal now results in the requirement of having the permission backend plugin installed separately to ensure that the RBAC backend plugin works accordingly.
|
|
10
|
+
|
|
11
|
+
Changes required to `packages/backend/src/index.ts`
|
|
12
|
+
|
|
13
|
+
```diff
|
|
14
|
+
// permission plugin
|
|
15
|
+
+ backend.add(import('@backstage/plugin-permission-backend'));
|
|
16
|
+
backend.add(import('@backstage-community/plugin-rbac-backend'));
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- 4b58a1d: Backstage version bump to v1.39.0
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- 6a59fcf: remove support and lifecycle keywords in package.json
|
|
26
|
+
- Updated dependencies [6a59fcf]
|
|
27
|
+
- Updated dependencies [4b58a1d]
|
|
28
|
+
- @backstage-community/plugin-rbac-common@1.18.0
|
|
29
|
+
- @backstage-community/plugin-rbac-node@1.12.0
|
|
30
|
+
|
|
31
|
+
## 6.3.0
|
|
32
|
+
|
|
33
|
+
### Minor Changes
|
|
34
|
+
|
|
35
|
+
- a42945e: Introduce API to store additional plugin ID list
|
|
36
|
+
- 3e3f346: Migrate rbac-backend to use permission registry service.
|
|
37
|
+
|
|
38
|
+
### Patch Changes
|
|
39
|
+
|
|
40
|
+
- 098b200: Updated dependency `@types/express` to `4.17.22`.
|
|
41
|
+
- e958f2f: Updated dependency `@types/node` to `22.15.29`.
|
|
42
|
+
- Updated dependencies [a42945e]
|
|
43
|
+
- @backstage-community/plugin-rbac-common@1.17.0
|
|
44
|
+
|
|
3
45
|
## 6.2.6
|
|
4
46
|
|
|
5
47
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Add the RBAC plugin packages as dependencies by running the following command.
|
|
|
34
34
|
yarn workspace backend add @backstage-community/plugin-rbac-backend
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
**NOTE**: If you are using Red Hat Developer Hub backend plugin is pre-installed and you do not need this step.
|
|
37
|
+
**NOTE**: If you are using Red Hat Developer Hub, backend plugin is pre-installed and you do not need this step.
|
|
38
38
|
|
|
39
39
|
### Configuring the Backend
|
|
40
40
|
|
|
@@ -42,11 +42,11 @@ yarn workspace backend add @backstage-community/plugin-rbac-backend
|
|
|
42
42
|
|
|
43
43
|
The RBAC plugin supports the integration with the new backend system.
|
|
44
44
|
|
|
45
|
-
Add the RBAC plugin to the `packages/backend/src/index.ts` file and remove the
|
|
45
|
+
Add the RBAC plugin to the `packages/backend/src/index.ts` file and remove the Allow All Permission policy module.
|
|
46
46
|
|
|
47
47
|
```diff
|
|
48
48
|
// permission plugin
|
|
49
|
-
|
|
49
|
+
backend.add(import('@backstage/plugin-permission-backend'));
|
|
50
50
|
- backend.add(
|
|
51
51
|
- import('@backstage/plugin-permission-backend-module-allow-all-policy'),
|
|
52
52
|
- );
|
|
@@ -87,10 +87,14 @@ For more information on the available API endpoints accessible to the policy adm
|
|
|
87
87
|
|
|
88
88
|
### Configure plugins with permission
|
|
89
89
|
|
|
90
|
-
In order for the RBAC UI to display available permissions provided by installed plugins,
|
|
91
|
-
plugin IDs to the `app-config.yaml`.
|
|
90
|
+
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:
|
|
92
91
|
|
|
93
|
-
|
|
92
|
+
- Application configuration(`app-config.yaml`)
|
|
93
|
+
- REST API
|
|
94
|
+
|
|
95
|
+
#### Configure plugins with Application configuration
|
|
96
|
+
|
|
97
|
+
You can specify the plugins with permissions in your application configuration as follows:
|
|
94
98
|
|
|
95
99
|
```YAML
|
|
96
100
|
permission:
|
|
@@ -106,6 +110,40 @@ permission:
|
|
|
106
110
|
- name: group:default/admins
|
|
107
111
|
```
|
|
108
112
|
|
|
113
|
+
#### Configure plugins with REST API
|
|
114
|
+
|
|
115
|
+
You can specify the plugins with permissions using the corresponding [REST API](./docs/apis.md#plugin-ids-that-support-the-backstage-permission-framework).
|
|
116
|
+
|
|
117
|
+
Curl Examples:
|
|
118
|
+
|
|
119
|
+
Get the object containing the list of plugin IDs:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
curl -X GET "http://localhost:7007/api/permission/plugins/id" \
|
|
123
|
+
-H "Content-Type: application/json" \
|
|
124
|
+
-H "Authorization: Bearer $token" -v
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Add more plugin IDs:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
curl -X POST "http://localhost:7007/api/permission/plugins/id" \
|
|
131
|
+
-d '{ "ids": [ "permission", "scaffolder" ] }' \
|
|
132
|
+
-H "Content-Type: application/json" \
|
|
133
|
+
-H "Authorization: Bearer $token" -v
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Remove plugin IDs:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
curl -X DELETE "http://localhost:7007/api/permission/plugins/id" \
|
|
140
|
+
-d '{ "ids": [ "permission", "scaffolder" ] }' \
|
|
141
|
+
-H "Content-Type: application/json" \
|
|
142
|
+
-H "Authorization: Bearer $token" -v
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Notice: The REST API does not allow deletion of plugin IDs that were provided via application configuration, in order to prevent an inconsistent state after a deployment restart. These ID values can only be removed through the configuration file.
|
|
146
|
+
|
|
109
147
|
For more information on the available permissions, refer to the [RBAC permissions documentation](./docs/permissions.md).
|
|
110
148
|
|
|
111
149
|
### Configuring policies via file
|
|
@@ -25,6 +25,10 @@ const ListPluginPoliciesEvents = {
|
|
|
25
25
|
const ListConditionEvents = {
|
|
26
26
|
CONDITION_RULES_READ: "condition-rules-read"
|
|
27
27
|
};
|
|
28
|
+
const ListPluginIDsEvents = {
|
|
29
|
+
PLUGIN_IDS_READ: "plugin-ids-read",
|
|
30
|
+
PLUGIN_IDS_WRITE: "plugin-ids-write"
|
|
31
|
+
};
|
|
28
32
|
const PoliciesData = {
|
|
29
33
|
PERMISSIONS_READ: "permissions-read"
|
|
30
34
|
};
|
|
@@ -57,6 +61,7 @@ exports.ActionType = ActionType;
|
|
|
57
61
|
exports.ConditionEvents = ConditionEvents;
|
|
58
62
|
exports.EvaluationEvents = EvaluationEvents;
|
|
59
63
|
exports.ListConditionEvents = ListConditionEvents;
|
|
64
|
+
exports.ListPluginIDsEvents = ListPluginIDsEvents;
|
|
60
65
|
exports.ListPluginPoliciesEvents = ListPluginPoliciesEvents;
|
|
61
66
|
exports.PermissionEvents = PermissionEvents;
|
|
62
67
|
exports.PoliciesData = PoliciesData;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auditor.cjs.js","sources":["../../src/auditor/auditor.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport type { PolicyQuery } from '@backstage/plugin-permission-node';\n\nimport {\n PermissionAction,\n toPermissionAction,\n} from '@backstage-community/plugin-rbac-common';\nimport {\n AuditorService,\n AuditorServiceEvent,\n} from '@backstage/backend-plugin-api';\n\nexport const ActionType = {\n CREATE: 'create',\n CREATE_OR_UPDATE: 'create_or_update',\n UPDATE: 'update',\n DELETE: 'delete',\n};\n\nexport const RoleEvents = {\n ROLE_WRITE: 'role-write',\n ROLE_READ: 'role-read',\n} as const;\n\nexport const PermissionEvents = {\n POLICY_WRITE: 'policy-write',\n POLICY_READ: 'policy-read',\n} as const;\n\nexport const EvaluationEvents = {\n PERMISSION_EVALUATION: 'permission-evaluation',\n} as const;\n\nexport const ListPluginPoliciesEvents = {\n PLUGIN_POLICIES_READ: 'plugin-policies-read',\n};\n\nexport const ListConditionEvents = {\n CONDITION_RULES_READ: 'condition-rules-read',\n};\n\nexport type EvaluationAuditInfo = {\n userEntityRef: string;\n permissionName: string;\n action: PermissionAction;\n resourceType?: string;\n decision?: PolicyDecision;\n};\n\nexport const PoliciesData = {\n PERMISSIONS_READ: 'permissions-read',\n};\n\nexport const ConditionEvents = {\n CONDITION_WRITE: 'condition-write',\n CONDITION_READ: 'condition-read',\n CONDITIONAL_POLICIES_FILE_NOT_FOUND: 'conditional-policies-file-not-found',\n CONDITIONAL_POLICIES_FILE_CHANGE: 'conditional-policies-file-change',\n};\n\nexport async function createPermissionEvaluationAuditorEvent(\n auditor: AuditorService,\n userEntityRef: string,\n request: PolicyQuery,\n policyDecision?: PolicyDecision,\n): Promise<AuditorServiceEvent> {\n const auditInfo: EvaluationAuditInfo = {\n userEntityRef,\n permissionName: request.permission.name,\n action: toPermissionAction(request.permission.attributes),\n };\n\n const resourceType = (request.permission as ResourcePermission).resourceType;\n if (resourceType) {\n auditInfo.resourceType = resourceType;\n }\n if (policyDecision) {\n auditInfo.decision = policyDecision;\n }\n\n return await auditor.createEvent({\n eventId: EvaluationEvents.PERMISSION_EVALUATION,\n severityLevel: 'medium',\n meta: {\n ...auditInfo,\n },\n });\n}\n"],"names":["toPermissionAction"],"mappings":";;;;AA8BO,MAAM,UAAa,GAAA;AAAA,EACxB,MAAQ,EAAA,QAAA;AAAA,EACR,gBAAkB,EAAA,kBAAA;AAAA,EAClB,MAAQ,EAAA,QAAA;AAAA,EACR,MAAQ,EAAA;AACV;AAEO,MAAM,UAAa,GAAA;AAAA,EACxB,UAAY,EAAA,YAAA;AAAA,EACZ,SAAW,EAAA;AACb;AAEO,MAAM,gBAAmB,GAAA;AAAA,EAC9B,YAAc,EAAA,cAAA;AAAA,EACd,WAAa,EAAA;AACf;AAEO,MAAM,gBAAmB,GAAA;AAAA,EAC9B,qBAAuB,EAAA;AACzB;AAEO,MAAM,wBAA2B,GAAA;AAAA,EACtC,oBAAsB,EAAA;AACxB;AAEO,MAAM,mBAAsB,GAAA;AAAA,EACjC,oBAAsB,EAAA;AACxB;AAUO,MAAM,YAAe,GAAA;AAAA,EAC1B,gBAAkB,EAAA;AACpB;AAEO,MAAM,eAAkB,GAAA;AAAA,EAC7B,eAAiB,EAAA,iBAAA;AAAA,EACjB,cAAgB,EAAA,gBAAA;AAAA,EAChB,mCAAqC,EAAA,qCAAA;AAAA,EACrC,gCAAkC,EAAA;AACpC;AAEA,eAAsB,sCACpB,CAAA,OAAA,EACA,aACA,EAAA,OAAA,EACA,cAC8B,EAAA;AAC9B,EAAA,MAAM,SAAiC,GAAA;AAAA,IACrC,aAAA;AAAA,IACA,cAAA,EAAgB,QAAQ,UAAW,CAAA,IAAA;AAAA,IACnC,MAAQ,EAAAA,mCAAA,CAAmB,OAAQ,CAAA,UAAA,CAAW,UAAU;AAAA,GAC1D;AAEA,EAAM,MAAA,YAAA,GAAgB,QAAQ,UAAkC,CAAA,YAAA;AAChE,EAAA,IAAI,YAAc,EAAA;AAChB,IAAA,SAAA,CAAU,YAAe,GAAA,YAAA;AAAA;AAM3B,EAAO,OAAA,MAAM,QAAQ,WAAY,CAAA;AAAA,IAC/B,SAAS,gBAAiB,CAAA,qBAAA;AAAA,IAC1B,aAAe,EAAA,QAAA;AAAA,IACf,IAAM,EAAA;AAAA,MACJ,GAAG;AAAA;AACL,GACD,CAAA;AACH
|
|
1
|
+
{"version":3,"file":"auditor.cjs.js","sources":["../../src/auditor/auditor.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n PolicyDecision,\n ResourcePermission,\n} from '@backstage/plugin-permission-common';\nimport type { PolicyQuery } from '@backstage/plugin-permission-node';\n\nimport {\n PermissionAction,\n toPermissionAction,\n} from '@backstage-community/plugin-rbac-common';\nimport {\n AuditorService,\n AuditorServiceEvent,\n} from '@backstage/backend-plugin-api';\n\nexport const ActionType = {\n CREATE: 'create',\n CREATE_OR_UPDATE: 'create_or_update',\n UPDATE: 'update',\n DELETE: 'delete',\n};\n\nexport const RoleEvents = {\n ROLE_WRITE: 'role-write',\n ROLE_READ: 'role-read',\n} as const;\n\nexport const PermissionEvents = {\n POLICY_WRITE: 'policy-write',\n POLICY_READ: 'policy-read',\n} as const;\n\nexport const EvaluationEvents = {\n PERMISSION_EVALUATION: 'permission-evaluation',\n} as const;\n\nexport const ListPluginPoliciesEvents = {\n PLUGIN_POLICIES_READ: 'plugin-policies-read',\n};\n\nexport const ListConditionEvents = {\n CONDITION_RULES_READ: 'condition-rules-read',\n};\n\nexport const ListPluginIDsEvents = {\n PLUGIN_IDS_READ: 'plugin-ids-read',\n PLUGIN_IDS_WRITE: 'plugin-ids-write',\n};\n\nexport type EvaluationAuditInfo = {\n userEntityRef: string;\n permissionName: string;\n action: PermissionAction;\n resourceType?: string;\n decision?: PolicyDecision;\n};\n\nexport const PoliciesData = {\n PERMISSIONS_READ: 'permissions-read',\n};\n\nexport const ConditionEvents = {\n CONDITION_WRITE: 'condition-write',\n CONDITION_READ: 'condition-read',\n CONDITIONAL_POLICIES_FILE_NOT_FOUND: 'conditional-policies-file-not-found',\n CONDITIONAL_POLICIES_FILE_CHANGE: 'conditional-policies-file-change',\n};\n\nexport async function createPermissionEvaluationAuditorEvent(\n auditor: AuditorService,\n userEntityRef: string,\n request: PolicyQuery,\n policyDecision?: PolicyDecision,\n): Promise<AuditorServiceEvent> {\n const auditInfo: EvaluationAuditInfo = {\n userEntityRef,\n permissionName: request.permission.name,\n action: toPermissionAction(request.permission.attributes),\n };\n\n const resourceType = (request.permission as ResourcePermission).resourceType;\n if (resourceType) {\n auditInfo.resourceType = resourceType;\n }\n if (policyDecision) {\n auditInfo.decision = policyDecision;\n }\n\n return await auditor.createEvent({\n eventId: EvaluationEvents.PERMISSION_EVALUATION,\n severityLevel: 'medium',\n meta: {\n ...auditInfo,\n },\n });\n}\n"],"names":["toPermissionAction"],"mappings":";;;;AA8BO,MAAM,UAAa,GAAA;AAAA,EACxB,MAAQ,EAAA,QAAA;AAAA,EACR,gBAAkB,EAAA,kBAAA;AAAA,EAClB,MAAQ,EAAA,QAAA;AAAA,EACR,MAAQ,EAAA;AACV;AAEO,MAAM,UAAa,GAAA;AAAA,EACxB,UAAY,EAAA,YAAA;AAAA,EACZ,SAAW,EAAA;AACb;AAEO,MAAM,gBAAmB,GAAA;AAAA,EAC9B,YAAc,EAAA,cAAA;AAAA,EACd,WAAa,EAAA;AACf;AAEO,MAAM,gBAAmB,GAAA;AAAA,EAC9B,qBAAuB,EAAA;AACzB;AAEO,MAAM,wBAA2B,GAAA;AAAA,EACtC,oBAAsB,EAAA;AACxB;AAEO,MAAM,mBAAsB,GAAA;AAAA,EACjC,oBAAsB,EAAA;AACxB;AAEO,MAAM,mBAAsB,GAAA;AAAA,EACjC,eAAiB,EAAA,iBAAA;AAAA,EACjB,gBAAkB,EAAA;AACpB;AAUO,MAAM,YAAe,GAAA;AAAA,EAC1B,gBAAkB,EAAA;AACpB;AAEO,MAAM,eAAkB,GAAA;AAAA,EAC7B,eAAiB,EAAA,iBAAA;AAAA,EACjB,cAAgB,EAAA,gBAAA;AAAA,EAChB,mCAAqC,EAAA,qCAAA;AAAA,EACrC,gCAAkC,EAAA;AACpC;AAEA,eAAsB,sCACpB,CAAA,OAAA,EACA,aACA,EAAA,OAAA,EACA,cAC8B,EAAA;AAC9B,EAAA,MAAM,SAAiC,GAAA;AAAA,IACrC,aAAA;AAAA,IACA,cAAA,EAAgB,QAAQ,UAAW,CAAA,IAAA;AAAA,IACnC,MAAQ,EAAAA,mCAAA,CAAmB,OAAQ,CAAA,UAAA,CAAW,UAAU;AAAA,GAC1D;AAEA,EAAM,MAAA,YAAA,GAAgB,QAAQ,UAAkC,CAAA,YAAA;AAChE,EAAA,IAAI,YAAc,EAAA;AAChB,IAAA,SAAA,CAAU,YAAe,GAAA,YAAA;AAAA;AAM3B,EAAO,OAAA,MAAM,QAAQ,WAAY,CAAA;AAAA,IAC/B,SAAS,gBAAiB,CAAA,qBAAA;AAAA,IAC1B,aAAe,EAAA,QAAA;AAAA,IACf,IAAM,EAAA;AAAA,MACJ,GAAG;AAAA;AACL,GACD,CAAA;AACH;;;;;;;;;;;;;"}
|
|
@@ -26,6 +26,11 @@ const eventMap = {
|
|
|
26
26
|
},
|
|
27
27
|
"/plugins/condition-rules": {
|
|
28
28
|
GET: auditor.ListConditionEvents.CONDITION_RULES_READ
|
|
29
|
+
},
|
|
30
|
+
"/plugins/id": {
|
|
31
|
+
GET: auditor.ListPluginIDsEvents.PLUGIN_IDS_READ,
|
|
32
|
+
POST: auditor.ListPluginIDsEvents.PLUGIN_IDS_WRITE,
|
|
33
|
+
DELETE: auditor.ListPluginIDsEvents.PLUGIN_IDS_WRITE
|
|
29
34
|
}
|
|
30
35
|
};
|
|
31
36
|
const eventToActionMap = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-interceptor.cjs.js","sources":["../../src/auditor/rest-interceptor.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n type RequestHandler,\n type NextFunction,\n type Request,\n type Response,\n type ErrorRequestHandler,\n} from 'express';\n\nimport {\n ActionType,\n ConditionEvents,\n ListConditionEvents,\n ListPluginPoliciesEvents,\n PermissionEvents,\n RoleEvents,\n} from './auditor';\nimport {\n AuditorService,\n AuditorServiceEvent,\n} from '@backstage/backend-plugin-api';\nimport type { JsonObject } from '@backstage/types';\n\n// Mapping paths and methods to corresponding events and messages\nconst eventMap: {\n [key: string]: { [key: string]: string };\n} = {\n '/policies': {\n POST: PermissionEvents.POLICY_WRITE,\n PUT: PermissionEvents.POLICY_WRITE,\n DELETE: PermissionEvents.POLICY_WRITE,\n GET: PermissionEvents.POLICY_READ,\n },\n '/roles/conditions': {\n POST: ConditionEvents.CONDITION_WRITE,\n PUT: ConditionEvents.CONDITION_WRITE,\n DELETE: ConditionEvents.CONDITION_WRITE,\n GET: ConditionEvents.CONDITION_READ,\n },\n '/roles': {\n POST: RoleEvents.ROLE_WRITE,\n PUT: RoleEvents.ROLE_WRITE,\n DELETE: RoleEvents.ROLE_WRITE,\n GET: RoleEvents.ROLE_READ,\n },\n '/plugins/policies': {\n GET: ListPluginPoliciesEvents.PLUGIN_POLICIES_READ,\n },\n '/plugins/condition-rules': {\n GET: ListConditionEvents.CONDITION_RULES_READ,\n },\n};\n\nconst eventToActionMap: {\n [key: string]: string;\n} = {\n POST: ActionType.CREATE,\n PUT: ActionType.UPDATE,\n DELETE: ActionType.DELETE,\n};\n\nfunction getRequestAuditorMeta(req: Request, eventId: string): JsonObject {\n const meta = {\n ...(req.method in eventToActionMap\n ? { actionType: eventToActionMap[req.method] }\n : {}),\n source: 'rest',\n };\n\n if (req.method !== 'GET') {\n return meta;\n }\n\n let extraMeta = {};\n const hasQuery = Object.keys(req.query).length > 0;\n const hasParams = Object.keys(req.params).length > 0;\n switch (eventId) {\n case PermissionEvents.POLICY_READ:\n if (hasParams) {\n extraMeta = {\n queryType: 'by-role',\n entityRef: `${req.params.kind}:${req.params.namespace}/${req.params.name}`,\n };\n break;\n }\n extraMeta = {\n queryType: hasQuery ? 'by-query' : 'all',\n ...(hasQuery ? { query: req.query } : {}),\n };\n break;\n case RoleEvents.ROLE_READ:\n extraMeta = {\n queryType: hasParams ? 'by-role' : 'all',\n ...(hasParams\n ? {\n entityRef: `${req.params.kind}:${req.params.namespace}/${req.params.name}`,\n }\n : {}),\n };\n break;\n case ConditionEvents.CONDITION_READ:\n if (hasParams) {\n extraMeta = {\n queryType: 'by-id',\n id: req.params.id,\n };\n break;\n }\n extraMeta = {\n queryType: hasQuery ? 'by-query' : 'all',\n ...(hasQuery ? { query: req.query } : {}),\n };\n break;\n default:\n break;\n }\n return { ...meta, ...extraMeta };\n}\n\nexport function logAuditorEvent(auditor: AuditorService): RequestHandler {\n return async (req: Request, resp: Response, next: NextFunction) => {\n let auditorEvent: AuditorServiceEvent | undefined;\n const matchedPath = Object.keys(eventMap).find(path =>\n req.path.startsWith(path),\n );\n if (matchedPath) {\n const methodEvent = eventMap[matchedPath][req.method];\n if (methodEvent) {\n const meta = getRequestAuditorMeta(req, methodEvent);\n auditorEvent = await auditor.createEvent({\n eventId: methodEvent,\n severityLevel: 'medium',\n request: req,\n meta,\n });\n }\n }\n\n resp.on('finish', async () => {\n const meta = {\n response: { status: resp.statusCode },\n ...(resp.locals.meta ?? {}),\n };\n if (resp.statusCode < 400) {\n await auditorEvent?.success({ meta });\n } else {\n const error = resp.locals.error ?? new Error(resp.statusMessage);\n await auditorEvent?.fail({\n error,\n meta,\n });\n }\n });\n\n next();\n };\n}\n\nexport function setAuditorError(): ErrorRequestHandler {\n return async (\n err: Error,\n _req: Request,\n resp: Response,\n next: NextFunction,\n ) => {\n resp.locals.error = err;\n next(err);\n };\n}\n"],"names":["PermissionEvents","ConditionEvents","RoleEvents","ListPluginPoliciesEvents","ListConditionEvents","ActionType"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"rest-interceptor.cjs.js","sources":["../../src/auditor/rest-interceptor.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n type RequestHandler,\n type NextFunction,\n type Request,\n type Response,\n type ErrorRequestHandler,\n} from 'express';\n\nimport {\n ActionType,\n ConditionEvents,\n ListConditionEvents,\n ListPluginIDsEvents,\n ListPluginPoliciesEvents,\n PermissionEvents,\n RoleEvents,\n} from './auditor';\nimport {\n AuditorService,\n AuditorServiceEvent,\n} from '@backstage/backend-plugin-api';\nimport type { JsonObject } from '@backstage/types';\n\n// Mapping paths and methods to corresponding events and messages\nconst eventMap: {\n [key: string]: { [key: string]: string };\n} = {\n '/policies': {\n POST: PermissionEvents.POLICY_WRITE,\n PUT: PermissionEvents.POLICY_WRITE,\n DELETE: PermissionEvents.POLICY_WRITE,\n GET: PermissionEvents.POLICY_READ,\n },\n '/roles/conditions': {\n POST: ConditionEvents.CONDITION_WRITE,\n PUT: ConditionEvents.CONDITION_WRITE,\n DELETE: ConditionEvents.CONDITION_WRITE,\n GET: ConditionEvents.CONDITION_READ,\n },\n '/roles': {\n POST: RoleEvents.ROLE_WRITE,\n PUT: RoleEvents.ROLE_WRITE,\n DELETE: RoleEvents.ROLE_WRITE,\n GET: RoleEvents.ROLE_READ,\n },\n '/plugins/policies': {\n GET: ListPluginPoliciesEvents.PLUGIN_POLICIES_READ,\n },\n '/plugins/condition-rules': {\n GET: ListConditionEvents.CONDITION_RULES_READ,\n },\n '/plugins/id': {\n GET: ListPluginIDsEvents.PLUGIN_IDS_READ,\n POST: ListPluginIDsEvents.PLUGIN_IDS_WRITE,\n DELETE: ListPluginIDsEvents.PLUGIN_IDS_WRITE,\n },\n};\n\nconst eventToActionMap: {\n [key: string]: string;\n} = {\n POST: ActionType.CREATE,\n PUT: ActionType.UPDATE,\n DELETE: ActionType.DELETE,\n};\n\nfunction getRequestAuditorMeta(req: Request, eventId: string): JsonObject {\n const meta = {\n ...(req.method in eventToActionMap\n ? { actionType: eventToActionMap[req.method] }\n : {}),\n source: 'rest',\n };\n\n if (req.method !== 'GET') {\n return meta;\n }\n\n let extraMeta = {};\n const hasQuery = Object.keys(req.query).length > 0;\n const hasParams = Object.keys(req.params).length > 0;\n switch (eventId) {\n case PermissionEvents.POLICY_READ:\n if (hasParams) {\n extraMeta = {\n queryType: 'by-role',\n entityRef: `${req.params.kind}:${req.params.namespace}/${req.params.name}`,\n };\n break;\n }\n extraMeta = {\n queryType: hasQuery ? 'by-query' : 'all',\n ...(hasQuery ? { query: req.query } : {}),\n };\n break;\n case RoleEvents.ROLE_READ:\n extraMeta = {\n queryType: hasParams ? 'by-role' : 'all',\n ...(hasParams\n ? {\n entityRef: `${req.params.kind}:${req.params.namespace}/${req.params.name}`,\n }\n : {}),\n };\n break;\n case ConditionEvents.CONDITION_READ:\n if (hasParams) {\n extraMeta = {\n queryType: 'by-id',\n id: req.params.id,\n };\n break;\n }\n extraMeta = {\n queryType: hasQuery ? 'by-query' : 'all',\n ...(hasQuery ? { query: req.query } : {}),\n };\n break;\n default:\n break;\n }\n return { ...meta, ...extraMeta };\n}\n\nexport function logAuditorEvent(auditor: AuditorService): RequestHandler {\n return async (req: Request, resp: Response, next: NextFunction) => {\n let auditorEvent: AuditorServiceEvent | undefined;\n const matchedPath = Object.keys(eventMap).find(path =>\n req.path.startsWith(path),\n );\n if (matchedPath) {\n const methodEvent = eventMap[matchedPath][req.method];\n if (methodEvent) {\n const meta = getRequestAuditorMeta(req, methodEvent);\n auditorEvent = await auditor.createEvent({\n eventId: methodEvent,\n severityLevel: 'medium',\n request: req,\n meta,\n });\n }\n }\n\n resp.on('finish', async () => {\n const meta = {\n response: { status: resp.statusCode },\n ...(resp.locals.meta ?? {}),\n };\n if (resp.statusCode < 400) {\n await auditorEvent?.success({ meta });\n } else {\n const error = resp.locals.error ?? new Error(resp.statusMessage);\n await auditorEvent?.fail({\n error,\n meta,\n });\n }\n });\n\n next();\n };\n}\n\nexport function setAuditorError(): ErrorRequestHandler {\n return async (\n err: Error,\n _req: Request,\n resp: Response,\n next: NextFunction,\n ) => {\n resp.locals.error = err;\n next(err);\n };\n}\n"],"names":["PermissionEvents","ConditionEvents","RoleEvents","ListPluginPoliciesEvents","ListConditionEvents","ListPluginIDsEvents","ActionType"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const PLUGINS_TABLE = "extra_permission_enabled_plugins";
|
|
4
|
+
class PermissionDependentPluginDatabaseStore {
|
|
5
|
+
constructor(knex) {
|
|
6
|
+
this.knex = knex;
|
|
7
|
+
}
|
|
8
|
+
async getPlugins() {
|
|
9
|
+
return await this.knex.table(PLUGINS_TABLE).select("pluginId");
|
|
10
|
+
}
|
|
11
|
+
async addPlugins(plugins) {
|
|
12
|
+
await this.knex.table(PLUGINS_TABLE).insert(plugins);
|
|
13
|
+
}
|
|
14
|
+
async deletePlugins(pluginIds) {
|
|
15
|
+
await this.knex.table(PLUGINS_TABLE).whereIn("pluginId", pluginIds).delete();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
exports.PLUGINS_TABLE = PLUGINS_TABLE;
|
|
20
|
+
exports.PermissionDependentPluginDatabaseStore = PermissionDependentPluginDatabaseStore;
|
|
21
|
+
//# sourceMappingURL=extra-permission-enabled-plugins-storage.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extra-permission-enabled-plugins-storage.cjs.js","sources":["../../src/database/extra-permission-enabled-plugins-storage.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';\n\nexport const PLUGINS_TABLE = 'extra_permission_enabled_plugins';\n\nexport interface PermissionDependentPluginDTO {\n pluginId: string;\n}\n\n/**\n * This interface defines the methods for managing the extra permission-enabled plugins in the database.\n */\nexport interface PermissionDependentPluginStore {\n // Fetches the extra plugin list from database.\n // This list contains information about extra plugins that supports Backstage permissions framework.\n getPlugins(): Promise<PermissionDependentPluginDTO[]>;\n\n // Adds the plugins to the database.\n addPlugins(plugins: PermissionDependentPluginDTO[]): Promise<void>;\n\n // Removes plugins from the database by pluginIds.\n deletePlugins(pluginIds: string[]): Promise<void>;\n}\n\nexport class PermissionDependentPluginDatabaseStore\n implements PermissionDependentPluginStore\n{\n public constructor(private readonly knex: Knex<any, any[]>) {}\n\n async getPlugins(): Promise<PermissionDependentPluginDTO[]> {\n return await this.knex\n .table(PLUGINS_TABLE)\n .select<PermissionDependentPluginDTO[]>('pluginId');\n }\n\n async addPlugins(plugins: PermissionDependentPluginDTO[]): Promise<void> {\n await this.knex.table(PLUGINS_TABLE).insert(plugins);\n }\n\n async deletePlugins(pluginIds: string[]): Promise<void> {\n await this.knex\n .table(PLUGINS_TABLE)\n .whereIn('pluginId', pluginIds)\n .delete();\n }\n}\n"],"names":[],"mappings":";;AAiBO,MAAM,aAAgB,GAAA;AAqBtB,MAAM,sCAEb,CAAA;AAAA,EACS,YAA6B,IAAwB,EAAA;AAAxB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAAyB,EAE7D,MAAM,UAAsD,GAAA;AAC1D,IAAA,OAAO,MAAM,IAAK,CAAA,IAAA,CACf,MAAM,aAAa,CAAA,CACnB,OAAuC,UAAU,CAAA;AAAA;AACtD,EAEA,MAAM,WAAW,OAAwD,EAAA;AACvE,IAAA,MAAM,KAAK,IAAK,CAAA,KAAA,CAAM,aAAa,CAAA,CAAE,OAAO,OAAO,CAAA;AAAA;AACrD,EAEA,MAAM,cAAc,SAAoC,EAAA;AACtD,IAAM,MAAA,IAAA,CAAK,KACR,KAAM,CAAA,aAAa,EACnB,OAAQ,CAAA,UAAA,EAAY,SAAS,CAAA,CAC7B,MAAO,EAAA;AAAA;AAEd;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
2
|
-
import { LoggerService, DiscoveryService, AuthService, HttpAuthService, AuditorService,
|
|
2
|
+
import { LoggerService, DiscoveryService, AuthService, HttpAuthService, AuditorService, LifecycleService, PermissionsRegistryService, PermissionsService } from '@backstage/backend-plugin-api';
|
|
3
3
|
import { Config } from '@backstage/config';
|
|
4
4
|
import express, { Router } from 'express';
|
|
5
5
|
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
|
|
6
|
-
import { PermissionPolicy } from '@backstage/plugin-permission-node';
|
|
7
6
|
import { PluginIdProvider, RBACProvider } from '@backstage-community/plugin-rbac-node';
|
|
8
7
|
export { PluginIdProvider } from '@backstage-community/plugin-rbac-node';
|
|
8
|
+
import { PolicyExtensionPoint } from '@backstage/plugin-permission-node/alpha';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @public
|
|
@@ -30,8 +30,9 @@ type EnvOptions = {
|
|
|
30
30
|
auth: AuthService;
|
|
31
31
|
httpAuth: HttpAuthService;
|
|
32
32
|
auditor: AuditorService;
|
|
33
|
-
userInfo: UserInfoService;
|
|
34
33
|
lifecycle: LifecycleService;
|
|
34
|
+
permissionsRegistry: PermissionsRegistryService;
|
|
35
|
+
policy: PolicyExtensionPoint;
|
|
35
36
|
};
|
|
36
37
|
/**
|
|
37
38
|
* @public
|
|
@@ -39,11 +40,11 @@ type EnvOptions = {
|
|
|
39
40
|
type RBACRouterOptions = {
|
|
40
41
|
config: Config;
|
|
41
42
|
logger: LoggerService;
|
|
42
|
-
discovery: DiscoveryService;
|
|
43
|
-
policy: PermissionPolicy;
|
|
44
43
|
auth: AuthService;
|
|
45
44
|
httpAuth: HttpAuthService;
|
|
46
|
-
|
|
45
|
+
permissions: PermissionsService;
|
|
46
|
+
permissionsRegistry: PermissionsRegistryService;
|
|
47
|
+
auditor: AuditorService;
|
|
47
48
|
};
|
|
48
49
|
/**
|
|
49
50
|
* @public
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var pluginRbacCommon = require('@backstage-community/plugin-rbac-common');
|
|
4
3
|
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
4
|
var rules = require('./rules.cjs.js');
|
|
5
|
+
var resource = require('./resource.cjs.js');
|
|
6
6
|
|
|
7
7
|
pluginPermissionNode.createConditionExports({
|
|
8
|
-
|
|
9
|
-
resourceType: pluginRbacCommon.RESOURCE_TYPE_POLICY_ENTITY,
|
|
8
|
+
resourceRef: resource.permissionMetadataResourceRef,
|
|
10
9
|
rules: rules.rules
|
|
11
10
|
});
|
|
12
|
-
const
|
|
11
|
+
const conditionTransformerFunc = (permissionRegistry) => pluginPermissionNode.createConditionTransformer(
|
|
12
|
+
permissionRegistry.getPermissionRuleset(resource.permissionMetadataResourceRef)
|
|
13
|
+
);
|
|
13
14
|
|
|
14
|
-
exports.
|
|
15
|
+
exports.conditionTransformerFunc = conditionTransformerFunc;
|
|
15
16
|
//# sourceMappingURL=conditions.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conditions.cjs.js","sources":["../../src/permissions/conditions.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":"conditions.cjs.js","sources":["../../src/permissions/conditions.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 {\n ConditionTransformer,\n createConditionExports,\n createConditionTransformer,\n} from '@backstage/plugin-permission-node';\n\nimport { rules, RBACFilter } from './rules';\nimport { PermissionsRegistryService } from '@backstage/backend-plugin-api';\nimport { permissionMetadataResourceRef } from './resource';\n\nconst { conditions, createConditionalDecision } = createConditionExports({\n resourceRef: permissionMetadataResourceRef,\n rules,\n});\n\nexport const rbacConditions = conditions;\n\nexport const createRBACConditionalDecision = createConditionalDecision;\n\nexport const conditionTransformerFunc: (\n permissionRegistry: PermissionsRegistryService,\n) => ConditionTransformer<RBACFilter> = (\n permissionRegistry: PermissionsRegistryService,\n) =>\n createConditionTransformer(\n permissionRegistry.getPermissionRuleset(permissionMetadataResourceRef),\n );\n"],"names":["createConditionExports","permissionMetadataResourceRef","rules","createConditionTransformer"],"mappings":";;;;;;AAyBkDA,2CAAuB,CAAA;AAAA,EACvE,WAAa,EAAAC,sCAAA;AAAA,SACbC;AACF,CAAC;AAMY,MAAA,wBAAA,GAE2B,CACtC,kBAEA,KAAAC,+CAAA;AAAA,EACE,kBAAA,CAAmB,qBAAqBF,sCAA6B;AACvE;;;;"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pluginRbacCommon = require('@backstage-community/plugin-rbac-common');
|
|
4
|
+
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
5
|
+
|
|
6
|
+
const permissionMetadataResourceRef = pluginPermissionNode.createPermissionResourceRef().with({
|
|
7
|
+
pluginId: "permission",
|
|
8
|
+
resourceType: pluginRbacCommon.RESOURCE_TYPE_POLICY_ENTITY
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
exports.permissionMetadataResourceRef = permissionMetadataResourceRef;
|
|
12
|
+
//# sourceMappingURL=resource.cjs.js.map
|
|
@@ -0,0 +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 {\n RESOURCE_TYPE_POLICY_ENTITY,\n type RoleMetadata,\n} from '@backstage-community/plugin-rbac-common';\nimport { createPermissionResourceRef } from '@backstage/plugin-permission-node';\nimport { RBACFilter } from './rules';\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 RoleMetadata,\n RBACFilter\n>().with({\n pluginId: 'permission',\n resourceType: RESOURCE_TYPE_POLICY_ENTITY,\n});\n"],"names":["createPermissionResourceRef","RESOURCE_TYPE_POLICY_ENTITY"],"mappings":";;;;;AA2Ba,MAAA,6BAAA,GAAgCA,gDAG3C,EAAA,CAAE,IAAK,CAAA;AAAA,EACP,QAAU,EAAA,YAAA;AAAA,EACV,YAAc,EAAAC;AAChB,CAAC;;;;"}
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var pluginPermissionNode = require('@backstage/plugin-permission-node');
|
|
4
|
-
var pluginRbacCommon = require('@backstage-community/plugin-rbac-common');
|
|
5
4
|
var zod = require('zod');
|
|
6
5
|
var zodToJsonSchema = require('zod-to-json-schema');
|
|
6
|
+
var resource = require('./resource.cjs.js');
|
|
7
7
|
|
|
8
8
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
9
9
|
|
|
10
10
|
var zodToJsonSchema__default = /*#__PURE__*/_interopDefaultCompat(zodToJsonSchema);
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
const isOwner = createRBACPermissionRule({
|
|
12
|
+
const isOwner = pluginPermissionNode.createPermissionRule({
|
|
14
13
|
name: "IS_OWNER",
|
|
15
14
|
description: "Should allow access to RBAC roles and Permissions through ownership",
|
|
16
|
-
|
|
15
|
+
resourceRef: resource.permissionMetadataResourceRef,
|
|
17
16
|
paramsSchema: zod.z.object({
|
|
18
17
|
owners: zod.z.string().array().describe("List of entity refs to match against")
|
|
19
18
|
}),
|
|
@@ -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 {
|
|
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 type { RoleMetadata } from '@backstage-community/plugin-rbac-common';\nimport { z } from 'zod';\nimport zodToJsonSchema from 'zod-to-json-schema';\nimport { permissionMetadataResourceRef } from './resource';\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: RoleMetadata, { owners }) => {\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,QAAwB,EAAA,EAAE,QAAa,KAAA;AAC7C,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;;;;;"}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
4
|
var pluginRbacBackend = require('@backstage-community/plugin-rbac-backend');
|
|
5
5
|
var pluginRbacNode = require('@backstage-community/plugin-rbac-node');
|
|
6
|
+
var alpha = require('@backstage/plugin-permission-node/alpha');
|
|
6
7
|
|
|
7
|
-
const rbacPlugin = backendPluginApi.
|
|
8
|
+
const rbacPlugin = backendPluginApi.createBackendModule({
|
|
8
9
|
pluginId: "permission",
|
|
10
|
+
moduleId: "rbac",
|
|
9
11
|
register(env) {
|
|
10
12
|
const pluginIdProviderExtensionPointImpl = new class PluginIdProviderImpl {
|
|
11
13
|
pluginIdProviders = [];
|
|
@@ -34,7 +36,9 @@ const rbacPlugin = backendPluginApi.createBackendPlugin({
|
|
|
34
36
|
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
35
37
|
auditor: backendPluginApi.coreServices.auditor,
|
|
36
38
|
userInfo: backendPluginApi.coreServices.userInfo,
|
|
37
|
-
lifecycle: backendPluginApi.coreServices.lifecycle
|
|
39
|
+
lifecycle: backendPluginApi.coreServices.lifecycle,
|
|
40
|
+
permissionsRegistry: backendPluginApi.coreServices.permissionsRegistry,
|
|
41
|
+
policy: alpha.policyExtensionPoint
|
|
38
42
|
},
|
|
39
43
|
async init({
|
|
40
44
|
http,
|
|
@@ -45,8 +49,9 @@ const rbacPlugin = backendPluginApi.createBackendPlugin({
|
|
|
45
49
|
auth,
|
|
46
50
|
httpAuth,
|
|
47
51
|
auditor,
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
lifecycle,
|
|
53
|
+
permissionsRegistry,
|
|
54
|
+
policy
|
|
50
55
|
}) {
|
|
51
56
|
http.use(
|
|
52
57
|
await pluginRbacBackend.PolicyBuilder.build(
|
|
@@ -58,8 +63,9 @@ const rbacPlugin = backendPluginApi.createBackendPlugin({
|
|
|
58
63
|
auth,
|
|
59
64
|
httpAuth,
|
|
60
65
|
auditor,
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
lifecycle,
|
|
67
|
+
permissionsRegistry,
|
|
68
|
+
policy
|
|
63
69
|
},
|
|
64
70
|
{
|
|
65
71
|
getPluginIds: () => Array.from(
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\n\nimport { PolicyBuilder } from '@backstage-community/plugin-rbac-backend';\nimport {\n PluginIdProvider,\n PluginIdProviderExtensionPoint,\n pluginIdProviderExtensionPoint,\n RBACProvider,\n rbacProviderExtensionPoint,\n} from '@backstage-community/plugin-rbac-node';\n\nimport { policyExtensionPoint } from '@backstage/plugin-permission-node/alpha';\n\n/**\n * @public\n * RBAC plugin\n *\n */\nexport const rbacPlugin = createBackendModule({\n pluginId: 'permission',\n moduleId: 'rbac',\n register(env) {\n const pluginIdProviderExtensionPointImpl = new (class PluginIdProviderImpl\n implements PluginIdProviderExtensionPoint\n {\n pluginIdProviders: PluginIdProvider[] = [];\n\n addPluginIdProvider(pluginIdProvider: PluginIdProvider): void {\n this.pluginIdProviders.push(pluginIdProvider);\n }\n })();\n\n env.registerExtensionPoint(\n pluginIdProviderExtensionPoint,\n pluginIdProviderExtensionPointImpl,\n );\n\n const rbacProviders = new Array<RBACProvider>();\n\n env.registerExtensionPoint(rbacProviderExtensionPoint, {\n addRBACProvider(\n ...providers: Array<RBACProvider | Array<RBACProvider>>\n ): void {\n rbacProviders.push(...providers.flat());\n },\n });\n\n env.registerInit({\n deps: {\n http: coreServices.httpRouter,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n discovery: coreServices.discovery,\n permissions: coreServices.permissions,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n auditor: coreServices.auditor,\n userInfo: coreServices.userInfo,\n lifecycle: coreServices.lifecycle,\n permissionsRegistry: coreServices.permissionsRegistry,\n policy: policyExtensionPoint,\n },\n async init({\n http,\n config,\n logger,\n discovery,\n permissions,\n auth,\n httpAuth,\n auditor,\n lifecycle,\n permissionsRegistry: permissionsRegistry,\n policy,\n }) {\n http.use(\n await PolicyBuilder.build(\n {\n config,\n logger,\n discovery,\n permissions,\n auth,\n httpAuth,\n auditor,\n lifecycle,\n permissionsRegistry: permissionsRegistry,\n policy,\n },\n {\n getPluginIds: () =>\n Array.from(\n new Set(\n pluginIdProviderExtensionPointImpl.pluginIdProviders.flatMap(\n p => p.getPluginIds(),\n ),\n ),\n ),\n },\n rbacProviders,\n ),\n );\n },\n });\n },\n});\n"],"names":["createBackendModule","pluginIdProviderExtensionPoint","rbacProviderExtensionPoint","coreServices","policyExtensionPoint","PolicyBuilder"],"mappings":";;;;;;;AAoCO,MAAM,aAAaA,oCAAoB,CAAA;AAAA,EAC5C,QAAU,EAAA,YAAA;AAAA,EACV,QAAU,EAAA,MAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAM,MAAA,kCAAA,GAAqC,IAAK,MAAM,oBAEtD,CAAA;AAAA,MACE,oBAAwC,EAAC;AAAA,MAEzC,oBAAoB,gBAA0C,EAAA;AAC5D,QAAK,IAAA,CAAA,iBAAA,CAAkB,KAAK,gBAAgB,CAAA;AAAA;AAC9C,KACC,EAAA;AAEH,IAAI,GAAA,CAAA,sBAAA;AAAA,MACFC,6CAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAM,MAAA,aAAA,GAAgB,IAAI,KAAoB,EAAA;AAE9C,IAAA,GAAA,CAAI,uBAAuBC,yCAA4B,EAAA;AAAA,MACrD,mBACK,SACG,EAAA;AACN,QAAA,aAAA,CAAc,IAAK,CAAA,GAAG,SAAU,CAAA,IAAA,EAAM,CAAA;AAAA;AACxC,KACD,CAAA;AAED,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,MAAMC,6BAAa,CAAA,UAAA;AAAA,QACnB,QAAQA,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,aAAaA,6BAAa,CAAA,WAAA;AAAA,QAC1B,MAAMA,6BAAa,CAAA,IAAA;AAAA,QACnB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,SAASA,6BAAa,CAAA,OAAA;AAAA,QACtB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,qBAAqBA,6BAAa,CAAA,mBAAA;AAAA,QAClC,MAAQ,EAAAC;AAAA,OACV;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,IAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,SAAA;AAAA,QACA,WAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,OAAA;AAAA,QACA,SAAA;AAAA,QACA,mBAAA;AAAA,QACA;AAAA,OACC,EAAA;AACD,QAAK,IAAA,CAAA,GAAA;AAAA,UACH,MAAMC,+BAAc,CAAA,KAAA;AAAA,YAClB;AAAA,cACE,MAAA;AAAA,cACA,MAAA;AAAA,cACA,SAAA;AAAA,cACA,WAAA;AAAA,cACA,IAAA;AAAA,cACA,QAAA;AAAA,cACA,OAAA;AAAA,cACA,SAAA;AAAA,cACA,mBAAA;AAAA,cACA;AAAA,aACF;AAAA,YACA;AAAA,cACE,YAAA,EAAc,MACZ,KAAM,CAAA,IAAA;AAAA,gBACJ,IAAI,GAAA;AAAA,kBACF,mCAAmC,iBAAkB,CAAA,OAAA;AAAA,oBACnD,CAAA,CAAA,KAAK,EAAE,YAAa;AAAA;AACtB;AACF;AACF,aACJ;AAAA,YACA;AAAA;AACF,SACF;AAAA;AACF,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var lodash = require('lodash');
|
|
4
|
+
|
|
5
|
+
class ExtendablePluginIdProvider {
|
|
6
|
+
constructor(pluginStore, pluginIdProvider, config) {
|
|
7
|
+
this.pluginStore = pluginStore;
|
|
8
|
+
const pluginIdsConfig = config.getOptionalStringArray(
|
|
9
|
+
"permission.rbac.pluginsWithPermission"
|
|
10
|
+
);
|
|
11
|
+
this.configurationPluginIds = pluginIdsConfig ? lodash.union(pluginIdsConfig, pluginIdProvider.getPluginIds()) : pluginIdProvider.getPluginIds();
|
|
12
|
+
}
|
|
13
|
+
// plugin ids which came from application config and PluginIdProvider
|
|
14
|
+
configurationPluginIds;
|
|
15
|
+
isConfiguredPluginId(pluginId) {
|
|
16
|
+
return this.configurationPluginIds.includes(pluginId);
|
|
17
|
+
}
|
|
18
|
+
async handleConflictedPluginIds() {
|
|
19
|
+
const conflictedIds = await (await this.pluginStore.getPlugins()).filter((pId) => this.configurationPluginIds.includes(pId.pluginId));
|
|
20
|
+
await this.pluginStore.deletePlugins(conflictedIds.map((p) => p.pluginId));
|
|
21
|
+
}
|
|
22
|
+
async getPluginIds() {
|
|
23
|
+
const extraPlugins = await this.pluginStore.getPlugins();
|
|
24
|
+
return lodash.union(
|
|
25
|
+
this.configurationPluginIds,
|
|
26
|
+
extraPlugins.map((plugin) => plugin.pluginId)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
exports.ExtendablePluginIdProvider = ExtendablePluginIdProvider;
|
|
32
|
+
//# sourceMappingURL=extendable-id-provider.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extendable-id-provider.cjs.js","sources":["../../src/service/extendable-id-provider.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 { PluginIdProvider } from '@backstage-community/plugin-rbac-node';\nimport { PermissionDependentPluginStore } from '../database/extra-permission-enabled-plugins-storage';\nimport type { Config } from '@backstage/config';\nimport { union } from 'lodash';\n\nexport class ExtendablePluginIdProvider {\n // plugin ids which came from application config and PluginIdProvider\n private readonly configurationPluginIds: string[];\n\n constructor(\n private readonly pluginStore: PermissionDependentPluginStore,\n pluginIdProvider: PluginIdProvider,\n config: Config,\n ) {\n const pluginIdsConfig = config.getOptionalStringArray(\n 'permission.rbac.pluginsWithPermission',\n );\n this.configurationPluginIds = pluginIdsConfig\n ? union(pluginIdsConfig, pluginIdProvider.getPluginIds())\n : pluginIdProvider.getPluginIds();\n }\n\n isConfiguredPluginId(pluginId: string): boolean {\n return this.configurationPluginIds.includes(pluginId);\n }\n\n async handleConflictedPluginIds(): Promise<void> {\n const conflictedIds = await (\n await this.pluginStore.getPlugins()\n ).filter(pId => this.configurationPluginIds.includes(pId.pluginId));\n await this.pluginStore.deletePlugins(conflictedIds.map(p => p.pluginId));\n }\n\n async getPluginIds(): Promise<string[]> {\n const extraPlugins = await this.pluginStore.getPlugins();\n return union(\n this.configurationPluginIds,\n extraPlugins.map(plugin => plugin.pluginId),\n );\n }\n}\n"],"names":["union"],"mappings":";;;;AAoBO,MAAM,0BAA2B,CAAA;AAAA,EAItC,WAAA,CACmB,WACjB,EAAA,gBAAA,EACA,MACA,EAAA;AAHiB,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAIjB,IAAA,MAAM,kBAAkB,MAAO,CAAA,sBAAA;AAAA,MAC7B;AAAA,KACF;AACA,IAAK,IAAA,CAAA,sBAAA,GAAyB,kBAC1BA,YAAM,CAAA,eAAA,EAAiB,iBAAiB,YAAa,EAAC,CACtD,GAAA,gBAAA,CAAiB,YAAa,EAAA;AAAA;AACpC;AAAA,EAbiB,sBAAA;AAAA,EAejB,qBAAqB,QAA2B,EAAA;AAC9C,IAAO,OAAA,IAAA,CAAK,sBAAuB,CAAA,QAAA,CAAS,QAAQ,CAAA;AAAA;AACtD,EAEA,MAAM,yBAA2C,GAAA;AAC/C,IAAA,MAAM,aAAgB,GAAA,MAAA,CACpB,MAAM,IAAA,CAAK,YAAY,UAAW,EAAA,EAClC,MAAO,CAAA,CAAA,GAAA,KAAO,IAAK,CAAA,sBAAA,CAAuB,QAAS,CAAA,GAAA,CAAI,QAAQ,CAAC,CAAA;AAClE,IAAM,MAAA,IAAA,CAAK,YAAY,aAAc,CAAA,aAAA,CAAc,IAAI,CAAK,CAAA,KAAA,CAAA,CAAE,QAAQ,CAAC,CAAA;AAAA;AACzE,EAEA,MAAM,YAAkC,GAAA;AACtC,IAAA,MAAM,YAAe,GAAA,MAAM,IAAK,CAAA,WAAA,CAAY,UAAW,EAAA;AACvD,IAAO,OAAAA,YAAA;AAAA,MACL,IAAK,CAAA,sBAAA;AAAA,MACL,YAAa,CAAA,GAAA,CAAI,CAAU,MAAA,KAAA,MAAA,CAAO,QAAQ;AAAA,KAC5C;AAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pluginRbacCommon = require('@backstage-community/plugin-rbac-common');
|
|
4
|
+
var restInterceptor = require('../auditor/rest-interceptor.cjs.js');
|
|
5
|
+
var policiesRestApi = require('./policies-rest-api.cjs.js');
|
|
6
|
+
var errors = require('@backstage/errors');
|
|
7
|
+
var pluginValidation = require('../validation/plugin-validation.cjs.js');
|
|
8
|
+
|
|
9
|
+
function registerPermissionDefinitionRoutes(router, pluginPermMetaData, pluginIdProvider, extraPluginsIdStorage, deps) {
|
|
10
|
+
const { auth, auditor } = deps;
|
|
11
|
+
router.get(
|
|
12
|
+
"/plugins/policies",
|
|
13
|
+
restInterceptor.logAuditorEvent(auditor),
|
|
14
|
+
async (request, response) => {
|
|
15
|
+
await policiesRestApi.authorizeConditional(request, pluginRbacCommon.policyEntityReadPermission, deps);
|
|
16
|
+
const body = await pluginPermMetaData.getPluginPolicies(auth);
|
|
17
|
+
response.json(body);
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
router.get(
|
|
21
|
+
"/plugins/condition-rules",
|
|
22
|
+
restInterceptor.logAuditorEvent(auditor),
|
|
23
|
+
async (request, response) => {
|
|
24
|
+
await policiesRestApi.authorizeConditional(request, pluginRbacCommon.policyEntityReadPermission, deps);
|
|
25
|
+
const body = await pluginPermMetaData.getPluginConditionRules(auth);
|
|
26
|
+
response.json(body);
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
router.get(
|
|
30
|
+
"/plugins/id",
|
|
31
|
+
restInterceptor.logAuditorEvent(auditor),
|
|
32
|
+
async (request, response) => {
|
|
33
|
+
await policiesRestApi.authorizeConditional(request, pluginRbacCommon.policyEntityReadPermission, deps);
|
|
34
|
+
const actualPluginIds = await pluginIdProvider.getPluginIds();
|
|
35
|
+
response.status(200).json(pluginIdsToResponse(actualPluginIds));
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
router.post(
|
|
39
|
+
"/plugins/id",
|
|
40
|
+
restInterceptor.logAuditorEvent(auditor),
|
|
41
|
+
async (request, response) => {
|
|
42
|
+
await policiesRestApi.authorizeConditional(request, pluginRbacCommon.policyEntityCreatePermission, deps);
|
|
43
|
+
const pluginIds = request.body;
|
|
44
|
+
pluginValidation.validatePermissionDependentPlugin(pluginIds);
|
|
45
|
+
const pluginDtos = permissionDependentPluginListToDTO(pluginIds);
|
|
46
|
+
let actualPluginIds = await pluginIdProvider.getPluginIds();
|
|
47
|
+
const conflictedIds = pluginIds.ids.filter(
|
|
48
|
+
(id) => actualPluginIds.includes(id)
|
|
49
|
+
);
|
|
50
|
+
if (conflictedIds.length > 0) {
|
|
51
|
+
throw new errors.ConflictError(
|
|
52
|
+
`Plugin IDs ${JSON.stringify(conflictedIds)} already exist in the system. Please use a different set of plugin ids.`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
await extraPluginsIdStorage.addPlugins(pluginDtos);
|
|
56
|
+
response.locals.meta = pluginIds;
|
|
57
|
+
actualPluginIds = await pluginIdProvider.getPluginIds();
|
|
58
|
+
response.status(200).json(pluginIdsToResponse(actualPluginIds));
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
router.delete(
|
|
62
|
+
"/plugins/id",
|
|
63
|
+
restInterceptor.logAuditorEvent(auditor),
|
|
64
|
+
async (request, response) => {
|
|
65
|
+
await policiesRestApi.authorizeConditional(request, pluginRbacCommon.policyEntityDeletePermission, deps);
|
|
66
|
+
const pluginIds = request.body;
|
|
67
|
+
pluginValidation.validatePermissionDependentPlugin(pluginIds);
|
|
68
|
+
const configuredPluginIds = pluginIds.ids.filter(
|
|
69
|
+
(pluginId) => pluginIdProvider.isConfiguredPluginId(pluginId)
|
|
70
|
+
);
|
|
71
|
+
if (configuredPluginIds.length > 0) {
|
|
72
|
+
throw new errors.NotAllowedError(
|
|
73
|
+
`Plugin IDs ${JSON.stringify(pluginIds.ids)} can be removed only with help of configuration.`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
let actualPluginIds = await pluginIdProvider.getPluginIds();
|
|
77
|
+
const notFoundPlugins = pluginIds.ids.filter(
|
|
78
|
+
(pluginToDel) => !actualPluginIds.includes(pluginToDel)
|
|
79
|
+
);
|
|
80
|
+
if (notFoundPlugins.length > 0) {
|
|
81
|
+
throw new errors.NotFoundError(
|
|
82
|
+
`Plugin IDs ${JSON.stringify(notFoundPlugins)} were not found.`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
await extraPluginsIdStorage.deletePlugins(pluginIds.ids);
|
|
86
|
+
response.locals.meta = pluginIds;
|
|
87
|
+
actualPluginIds = await pluginIdProvider.getPluginIds();
|
|
88
|
+
response.status(200).json(pluginIdsToResponse(actualPluginIds));
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
function pluginIdsToResponse(pluginIds) {
|
|
93
|
+
return { ids: pluginIds };
|
|
94
|
+
}
|
|
95
|
+
function permissionDependentPluginListToDTO(pluginList) {
|
|
96
|
+
return pluginList.ids.map((pluginId) => {
|
|
97
|
+
return { pluginId };
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
exports.permissionDependentPluginListToDTO = permissionDependentPluginListToDTO;
|
|
102
|
+
exports.pluginIdsToResponse = pluginIdsToResponse;
|
|
103
|
+
exports.registerPermissionDefinitionRoutes = registerPermissionDefinitionRoutes;
|
|
104
|
+
//# sourceMappingURL=permission-definition-routes.cjs.js.map
|