@backstage-community/plugin-rbac-backend 5.2.3
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 +671 -0
- package/README.md +220 -0
- package/config.d.ts +68 -0
- package/dist/admin-permissions/admin-creation.cjs.js +117 -0
- package/dist/admin-permissions/admin-creation.cjs.js.map +1 -0
- package/dist/audit-log/audit-logger.cjs.js +108 -0
- package/dist/audit-log/audit-logger.cjs.js.map +1 -0
- package/dist/audit-log/rest-errors-interceptor.cjs.js +100 -0
- package/dist/audit-log/rest-errors-interceptor.cjs.js.map +1 -0
- package/dist/conditional-aliases/alias-resolver.cjs.js +76 -0
- package/dist/conditional-aliases/alias-resolver.cjs.js.map +1 -0
- package/dist/database/casbin-adapter-factory.cjs.js +87 -0
- package/dist/database/casbin-adapter-factory.cjs.js.map +1 -0
- package/dist/database/conditional-storage.cjs.js +172 -0
- package/dist/database/conditional-storage.cjs.js.map +1 -0
- package/dist/database/migration.cjs.js +21 -0
- package/dist/database/migration.cjs.js.map +1 -0
- package/dist/database/role-metadata.cjs.js +89 -0
- package/dist/database/role-metadata.cjs.js.map +1 -0
- package/dist/file-permissions/csv-file-watcher.cjs.js +407 -0
- package/dist/file-permissions/csv-file-watcher.cjs.js.map +1 -0
- package/dist/file-permissions/file-watcher.cjs.js +46 -0
- package/dist/file-permissions/file-watcher.cjs.js.map +1 -0
- package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js +208 -0
- package/dist/file-permissions/yaml-conditional-file-watcher.cjs.js.map +1 -0
- package/dist/helper.cjs.js +171 -0
- package/dist/helper.cjs.js.map +1 -0
- package/dist/index.cjs.js +14 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/plugin.cjs.js +79 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/policies/allow-all-policy.cjs.js +12 -0
- package/dist/policies/allow-all-policy.cjs.js.map +1 -0
- package/dist/policies/permission-policy.cjs.js +243 -0
- package/dist/policies/permission-policy.cjs.js.map +1 -0
- package/dist/providers/connect-providers.cjs.js +211 -0
- package/dist/providers/connect-providers.cjs.js.map +1 -0
- package/dist/role-manager/ancestor-search-memo.cjs.js +159 -0
- package/dist/role-manager/ancestor-search-memo.cjs.js.map +1 -0
- package/dist/role-manager/member-list.cjs.js +101 -0
- package/dist/role-manager/member-list.cjs.js.map +1 -0
- package/dist/role-manager/role-manager.cjs.js +281 -0
- package/dist/role-manager/role-manager.cjs.js.map +1 -0
- package/dist/service/enforcer-delegate.cjs.js +353 -0
- package/dist/service/enforcer-delegate.cjs.js.map +1 -0
- package/dist/service/permission-model.cjs.js +21 -0
- package/dist/service/permission-model.cjs.js.map +1 -0
- package/dist/service/plugin-endpoints.cjs.js +121 -0
- package/dist/service/plugin-endpoints.cjs.js.map +1 -0
- package/dist/service/policies-rest-api.cjs.js +949 -0
- package/dist/service/policies-rest-api.cjs.js.map +1 -0
- package/dist/service/policy-builder.cjs.js +134 -0
- package/dist/service/policy-builder.cjs.js.map +1 -0
- package/dist/service/router.cjs.js +24 -0
- package/dist/service/router.cjs.js.map +1 -0
- package/dist/validation/condition-validation.cjs.js +107 -0
- package/dist/validation/condition-validation.cjs.js.map +1 -0
- package/dist/validation/policies-validation.cjs.js +194 -0
- package/dist/validation/policies-validation.cjs.js.map +1 -0
- package/migrations/20231015161232_migrations.js +41 -0
- package/migrations/20231212224526_migrations.js +84 -0
- package/migrations/20231221113214_migrations.js +60 -0
- package/migrations/20240201144429_migrations.js +37 -0
- package/migrations/20240215154456_migrations.js +143 -0
- package/migrations/20240308134410_migrations.js +31 -0
- package/migrations/20240308134941_migrations.js +43 -0
- package/migrations/20240404111242_migrations.js +53 -0
- package/migrations/20240611092136_migrations.js +29 -0
- package/package.json +98 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var casbin = require('casbin');
|
|
4
|
+
var EventEmitter = require('events');
|
|
5
|
+
var adminCreation = require('../admin-permissions/admin-creation.cjs.js');
|
|
6
|
+
var helper = require('../helper.cjs.js');
|
|
7
|
+
var permissionModel = require('./permission-model.cjs.js');
|
|
8
|
+
|
|
9
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var EventEmitter__default = /*#__PURE__*/_interopDefaultCompat(EventEmitter);
|
|
12
|
+
|
|
13
|
+
class EnforcerDelegate {
|
|
14
|
+
constructor(enforcer, roleMetadataStorage, knex) {
|
|
15
|
+
this.enforcer = enforcer;
|
|
16
|
+
this.roleMetadataStorage = roleMetadataStorage;
|
|
17
|
+
this.knex = knex;
|
|
18
|
+
}
|
|
19
|
+
roleEventEmitter = new EventEmitter__default.default();
|
|
20
|
+
on(event, listener) {
|
|
21
|
+
this.roleEventEmitter.on(event, listener);
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
async hasPolicy(...policy) {
|
|
25
|
+
return await this.enforcer.hasPolicy(...policy);
|
|
26
|
+
}
|
|
27
|
+
async hasGroupingPolicy(...policy) {
|
|
28
|
+
return await this.enforcer.hasGroupingPolicy(...policy);
|
|
29
|
+
}
|
|
30
|
+
async getPolicy() {
|
|
31
|
+
return await this.enforcer.getPolicy();
|
|
32
|
+
}
|
|
33
|
+
async getGroupingPolicy() {
|
|
34
|
+
return await this.enforcer.getGroupingPolicy();
|
|
35
|
+
}
|
|
36
|
+
async getRolesForUser(userEntityRef) {
|
|
37
|
+
return await this.enforcer.getRolesForUser(userEntityRef);
|
|
38
|
+
}
|
|
39
|
+
async getFilteredPolicy(fieldIndex, ...filter) {
|
|
40
|
+
return await this.enforcer.getFilteredPolicy(fieldIndex, ...filter);
|
|
41
|
+
}
|
|
42
|
+
async getFilteredGroupingPolicy(fieldIndex, ...filter) {
|
|
43
|
+
return await this.enforcer.getFilteredGroupingPolicy(fieldIndex, ...filter);
|
|
44
|
+
}
|
|
45
|
+
async addPolicy(policy, externalTrx) {
|
|
46
|
+
const trx = externalTrx ?? await this.knex.transaction();
|
|
47
|
+
if (await this.enforcer.hasPolicy(...policy)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const ok = await this.enforcer.addPolicy(...policy);
|
|
52
|
+
if (!ok) {
|
|
53
|
+
throw new Error(`failed to create policy ${helper.policyToString(policy)}`);
|
|
54
|
+
}
|
|
55
|
+
if (!externalTrx) {
|
|
56
|
+
await trx.commit();
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
if (!externalTrx) {
|
|
60
|
+
await trx.rollback(err);
|
|
61
|
+
}
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async addPolicies(policies, externalTrx) {
|
|
66
|
+
if (policies.length === 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const trx = externalTrx || await this.knex.transaction();
|
|
70
|
+
try {
|
|
71
|
+
const ok = await this.enforcer.addPolicies(policies);
|
|
72
|
+
if (!ok) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Failed to store policies ${helper.policiesToString(policies)}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
if (!externalTrx) {
|
|
78
|
+
await trx.commit();
|
|
79
|
+
}
|
|
80
|
+
} catch (err) {
|
|
81
|
+
if (!externalTrx) {
|
|
82
|
+
await trx.rollback(err);
|
|
83
|
+
}
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async addGroupingPolicy(policy, roleMetadata, externalTrx) {
|
|
88
|
+
const trx = externalTrx ?? await this.knex.transaction();
|
|
89
|
+
const entityRef = roleMetadata.roleEntityRef;
|
|
90
|
+
if (await this.enforcer.hasGroupingPolicy(...policy)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
let currentMetadata;
|
|
95
|
+
if (entityRef.startsWith(`role:`)) {
|
|
96
|
+
currentMetadata = await this.roleMetadataStorage.findRoleMetadata(
|
|
97
|
+
entityRef,
|
|
98
|
+
trx
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (currentMetadata) {
|
|
102
|
+
await this.roleMetadataStorage.updateRoleMetadata(
|
|
103
|
+
helper.mergeRoleMetadata(currentMetadata, roleMetadata),
|
|
104
|
+
entityRef,
|
|
105
|
+
trx
|
|
106
|
+
);
|
|
107
|
+
} else {
|
|
108
|
+
const currentDate = /* @__PURE__ */ new Date();
|
|
109
|
+
roleMetadata.createdAt = currentDate.toUTCString();
|
|
110
|
+
roleMetadata.lastModified = currentDate.toUTCString();
|
|
111
|
+
await this.roleMetadataStorage.createRoleMetadata(roleMetadata, trx);
|
|
112
|
+
}
|
|
113
|
+
const ok = await this.enforcer.addGroupingPolicy(...policy);
|
|
114
|
+
if (!ok) {
|
|
115
|
+
throw new Error(`failed to create policy ${helper.policyToString(policy)}`);
|
|
116
|
+
}
|
|
117
|
+
if (!externalTrx) {
|
|
118
|
+
await trx.commit();
|
|
119
|
+
}
|
|
120
|
+
if (!currentMetadata) {
|
|
121
|
+
this.roleEventEmitter.emit("roleAdded", roleMetadata.roleEntityRef);
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
if (!externalTrx) {
|
|
125
|
+
await trx.rollback(err);
|
|
126
|
+
}
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async addGroupingPolicies(policies, roleMetadata, externalTrx) {
|
|
131
|
+
if (policies.length === 0) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const trx = externalTrx ?? await this.knex.transaction();
|
|
135
|
+
try {
|
|
136
|
+
const currentRoleMetadata = await this.roleMetadataStorage.findRoleMetadata(
|
|
137
|
+
roleMetadata.roleEntityRef,
|
|
138
|
+
trx
|
|
139
|
+
);
|
|
140
|
+
if (currentRoleMetadata) {
|
|
141
|
+
await this.roleMetadataStorage.updateRoleMetadata(
|
|
142
|
+
helper.mergeRoleMetadata(currentRoleMetadata, roleMetadata),
|
|
143
|
+
roleMetadata.roleEntityRef,
|
|
144
|
+
trx
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
const currentDate = /* @__PURE__ */ new Date();
|
|
148
|
+
roleMetadata.createdAt = currentDate.toUTCString();
|
|
149
|
+
roleMetadata.lastModified = currentDate.toUTCString();
|
|
150
|
+
await this.roleMetadataStorage.createRoleMetadata(roleMetadata, trx);
|
|
151
|
+
}
|
|
152
|
+
const ok = await this.enforcer.addGroupingPolicies(policies);
|
|
153
|
+
if (!ok) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Failed to store policies ${helper.policiesToString(policies)}`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (!externalTrx) {
|
|
159
|
+
await trx.commit();
|
|
160
|
+
}
|
|
161
|
+
if (!currentRoleMetadata) {
|
|
162
|
+
this.roleEventEmitter.emit("roleAdded", roleMetadata.roleEntityRef);
|
|
163
|
+
}
|
|
164
|
+
} catch (err) {
|
|
165
|
+
if (!externalTrx) {
|
|
166
|
+
await trx.rollback(err);
|
|
167
|
+
}
|
|
168
|
+
throw err;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async updateGroupingPolicies(oldRole, newRole, newRoleMetadata) {
|
|
172
|
+
const oldRoleName = oldRole.at(0)?.at(1);
|
|
173
|
+
const trx = await this.knex.transaction();
|
|
174
|
+
try {
|
|
175
|
+
const currentMetadata = await this.roleMetadataStorage.findRoleMetadata(
|
|
176
|
+
oldRoleName,
|
|
177
|
+
trx
|
|
178
|
+
);
|
|
179
|
+
if (!currentMetadata) {
|
|
180
|
+
throw new Error(`Role metadata ${oldRoleName} was not found`);
|
|
181
|
+
}
|
|
182
|
+
await this.removeGroupingPolicies(oldRole, currentMetadata, true, trx);
|
|
183
|
+
await this.addGroupingPolicies(newRole, newRoleMetadata, trx);
|
|
184
|
+
await trx.commit();
|
|
185
|
+
} catch (err) {
|
|
186
|
+
await trx.rollback(err);
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async updatePolicies(oldPolicies, newPolicies) {
|
|
191
|
+
const trx = await this.knex.transaction();
|
|
192
|
+
try {
|
|
193
|
+
await this.removePolicies(oldPolicies, trx);
|
|
194
|
+
await this.addPolicies(newPolicies, trx);
|
|
195
|
+
await trx.commit();
|
|
196
|
+
} catch (err) {
|
|
197
|
+
await trx.rollback(err);
|
|
198
|
+
throw err;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async removePolicy(policy, externalTrx) {
|
|
202
|
+
const trx = externalTrx ?? await this.knex.transaction();
|
|
203
|
+
try {
|
|
204
|
+
const ok = await this.enforcer.removePolicy(...policy);
|
|
205
|
+
if (!ok) {
|
|
206
|
+
throw new Error(`fail to delete policy ${policy}`);
|
|
207
|
+
}
|
|
208
|
+
if (!externalTrx) {
|
|
209
|
+
await trx.commit();
|
|
210
|
+
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (!externalTrx) {
|
|
213
|
+
await trx.rollback(err);
|
|
214
|
+
}
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async removePolicies(policies, externalTrx) {
|
|
219
|
+
const trx = externalTrx ?? await this.knex.transaction();
|
|
220
|
+
try {
|
|
221
|
+
const ok = await this.enforcer.removePolicies(policies);
|
|
222
|
+
if (!ok) {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`Failed to delete policies ${helper.policiesToString(policies)}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
if (!externalTrx) {
|
|
228
|
+
await trx.commit();
|
|
229
|
+
}
|
|
230
|
+
} catch (err) {
|
|
231
|
+
if (!externalTrx) {
|
|
232
|
+
await trx.rollback(err);
|
|
233
|
+
}
|
|
234
|
+
throw err;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async removeGroupingPolicy(policy, roleMetadata, isUpdate, externalTrx) {
|
|
238
|
+
const trx = externalTrx ?? await this.knex.transaction();
|
|
239
|
+
const roleEntity = policy[1];
|
|
240
|
+
try {
|
|
241
|
+
const ok = await this.enforcer.removeGroupingPolicy(...policy);
|
|
242
|
+
if (!ok) {
|
|
243
|
+
throw new Error(`Failed to delete policy ${helper.policyToString(policy)}`);
|
|
244
|
+
}
|
|
245
|
+
if (!isUpdate) {
|
|
246
|
+
const currentRoleMetadata = await this.roleMetadataStorage.findRoleMetadata(roleEntity, trx);
|
|
247
|
+
const remainingGroupPolicies = await this.enforcer.getFilteredGroupingPolicy(1, roleEntity);
|
|
248
|
+
if (currentRoleMetadata && remainingGroupPolicies.length === 0 && roleEntity !== adminCreation.ADMIN_ROLE_NAME) {
|
|
249
|
+
await this.roleMetadataStorage.removeRoleMetadata(roleEntity, trx);
|
|
250
|
+
} else if (currentRoleMetadata) {
|
|
251
|
+
await this.roleMetadataStorage.updateRoleMetadata(
|
|
252
|
+
helper.mergeRoleMetadata(currentRoleMetadata, roleMetadata),
|
|
253
|
+
roleEntity,
|
|
254
|
+
trx
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (!externalTrx) {
|
|
259
|
+
await trx.commit();
|
|
260
|
+
}
|
|
261
|
+
} catch (err) {
|
|
262
|
+
if (!externalTrx) {
|
|
263
|
+
await trx.rollback(err);
|
|
264
|
+
}
|
|
265
|
+
throw err;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async removeGroupingPolicies(policies, roleMetadata, isUpdate, externalTrx) {
|
|
269
|
+
const trx = externalTrx ?? await this.knex.transaction();
|
|
270
|
+
const roleEntity = roleMetadata.roleEntityRef;
|
|
271
|
+
try {
|
|
272
|
+
const ok = await this.enforcer.removeGroupingPolicies(policies);
|
|
273
|
+
if (!ok) {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`Failed to delete grouping policies: ${helper.policiesToString(policies)}`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
if (!isUpdate) {
|
|
279
|
+
const currentRoleMetadata = await this.roleMetadataStorage.findRoleMetadata(roleEntity, trx);
|
|
280
|
+
const remainingGroupPolicies = await this.enforcer.getFilteredGroupingPolicy(1, roleEntity);
|
|
281
|
+
if (currentRoleMetadata && remainingGroupPolicies.length === 0 && roleEntity !== adminCreation.ADMIN_ROLE_NAME) {
|
|
282
|
+
await this.roleMetadataStorage.removeRoleMetadata(roleEntity, trx);
|
|
283
|
+
} else if (currentRoleMetadata) {
|
|
284
|
+
await this.roleMetadataStorage.updateRoleMetadata(
|
|
285
|
+
helper.mergeRoleMetadata(currentRoleMetadata, roleMetadata),
|
|
286
|
+
roleEntity,
|
|
287
|
+
trx
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (!externalTrx) {
|
|
292
|
+
await trx.commit();
|
|
293
|
+
}
|
|
294
|
+
} catch (err) {
|
|
295
|
+
if (!externalTrx) {
|
|
296
|
+
await trx.rollback(err);
|
|
297
|
+
}
|
|
298
|
+
throw err;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* enforce aims to enforce a particular permission policy based on the user that it receives.
|
|
303
|
+
* Under the hood, enforce uses the `enforce` method from the enforcer`.
|
|
304
|
+
*
|
|
305
|
+
* Before enforcement, a filter is set up to reduce the number of permission policies that will
|
|
306
|
+
* be loaded in.
|
|
307
|
+
* This will reduce the amount of checks that need to be made to determine if a user is authorize
|
|
308
|
+
* to perform an action
|
|
309
|
+
*
|
|
310
|
+
* A temporary enforcer will also be used while enforcing.
|
|
311
|
+
* This is to ensure that the filter does not interact with the base enforcer.
|
|
312
|
+
* The temporary enforcer has lazy loading of the permission policies enabled to reduce the amount
|
|
313
|
+
* of time it takes to initialize the temporary enforcer.
|
|
314
|
+
* The justification for lazy loading is because permission policies are already present in the
|
|
315
|
+
* role manager / database and it will be filtered and loaded whenever `loadFilteredPolicy` is called.
|
|
316
|
+
* @param entityRef The user to enforce
|
|
317
|
+
* @param resourceType The resource type / name of the permission policy
|
|
318
|
+
* @param action The action of the permission policy
|
|
319
|
+
* @param roles Any roles that the user is directly or indirectly attached to.
|
|
320
|
+
* Used for filtering permission policies.
|
|
321
|
+
* @returns True if the user is allowed based on the particular permission
|
|
322
|
+
*/
|
|
323
|
+
async enforce(entityRef, resourceType, action, roles) {
|
|
324
|
+
const filter = [];
|
|
325
|
+
if (roles.length > 0) {
|
|
326
|
+
roles.forEach((role) => {
|
|
327
|
+
filter.push({ ptype: "p", v0: role, v1: resourceType, v2: action });
|
|
328
|
+
});
|
|
329
|
+
} else {
|
|
330
|
+
filter.push({ ptype: "p", v1: resourceType, v2: action });
|
|
331
|
+
}
|
|
332
|
+
const adapt = this.enforcer.getAdapter();
|
|
333
|
+
const roleManager = this.enforcer.getRoleManager();
|
|
334
|
+
const tempEnforcer = new casbin.Enforcer();
|
|
335
|
+
await tempEnforcer.initWithModelAndAdapter(
|
|
336
|
+
casbin.newModelFromString(permissionModel.MODEL),
|
|
337
|
+
adapt,
|
|
338
|
+
true
|
|
339
|
+
);
|
|
340
|
+
tempEnforcer.setRoleManager(roleManager);
|
|
341
|
+
await tempEnforcer.loadFilteredPolicy(filter);
|
|
342
|
+
return await tempEnforcer.enforce(entityRef, resourceType, action);
|
|
343
|
+
}
|
|
344
|
+
async getImplicitPermissionsForUser(user) {
|
|
345
|
+
return this.enforcer.getImplicitPermissionsForUser(user);
|
|
346
|
+
}
|
|
347
|
+
async getAllRoles() {
|
|
348
|
+
return this.enforcer.getAllRoles();
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
exports.EnforcerDelegate = EnforcerDelegate;
|
|
353
|
+
//# sourceMappingURL=enforcer-delegate.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enforcer-delegate.cjs.js","sources":["../../src/service/enforcer-delegate.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 { Enforcer, newModelFromString } from 'casbin';\nimport { Knex } from 'knex';\n\nimport EventEmitter from 'events';\n\nimport { ADMIN_ROLE_NAME } from '../admin-permissions/admin-creation';\nimport {\n RoleMetadataDao,\n RoleMetadataStorage,\n} from '../database/role-metadata';\nimport { mergeRoleMetadata, policiesToString, policyToString } from '../helper';\nimport { MODEL } from './permission-model';\n\nexport type RoleEvents = 'roleAdded';\nexport interface RoleEventEmitter<T extends RoleEvents> {\n on(event: T, listener: (roleEntityRef: string | string[]) => void): this;\n}\n\ntype EventMap = {\n [event in RoleEvents]: any[];\n};\n\nexport class EnforcerDelegate implements RoleEventEmitter<RoleEvents> {\n private readonly roleEventEmitter = new EventEmitter<EventMap>();\n\n constructor(\n private readonly enforcer: Enforcer,\n private readonly roleMetadataStorage: RoleMetadataStorage,\n private readonly knex: Knex,\n ) {}\n\n on(event: RoleEvents, listener: (role: string) => void): this {\n this.roleEventEmitter.on(event, listener);\n return this;\n }\n\n async hasPolicy(...policy: string[]): Promise<boolean> {\n return await this.enforcer.hasPolicy(...policy);\n }\n\n async hasGroupingPolicy(...policy: string[]): Promise<boolean> {\n return await this.enforcer.hasGroupingPolicy(...policy);\n }\n\n async getPolicy(): Promise<string[][]> {\n return await this.enforcer.getPolicy();\n }\n\n async getGroupingPolicy(): Promise<string[][]> {\n return await this.enforcer.getGroupingPolicy();\n }\n\n async getRolesForUser(userEntityRef: string): Promise<string[]> {\n return await this.enforcer.getRolesForUser(userEntityRef);\n }\n\n async getFilteredPolicy(\n fieldIndex: number,\n ...filter: string[]\n ): Promise<string[][]> {\n return await this.enforcer.getFilteredPolicy(fieldIndex, ...filter);\n }\n\n async getFilteredGroupingPolicy(\n fieldIndex: number,\n ...filter: string[]\n ): Promise<string[][]> {\n return await this.enforcer.getFilteredGroupingPolicy(fieldIndex, ...filter);\n }\n\n async addPolicy(\n policy: string[],\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n const trx = externalTrx ?? (await this.knex.transaction());\n\n if (await this.enforcer.hasPolicy(...policy)) {\n return;\n }\n try {\n const ok = await this.enforcer.addPolicy(...policy);\n if (!ok) {\n throw new Error(`failed to create policy ${policyToString(policy)}`);\n }\n if (!externalTrx) {\n await trx.commit();\n }\n } catch (err) {\n if (!externalTrx) {\n await trx.rollback(err);\n }\n throw err;\n }\n }\n\n async addPolicies(\n policies: string[][],\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n if (policies.length === 0) {\n return;\n }\n\n const trx = externalTrx || (await this.knex.transaction());\n\n try {\n const ok = await this.enforcer.addPolicies(policies);\n if (!ok) {\n throw new Error(\n `Failed to store policies ${policiesToString(policies)}`,\n );\n }\n if (!externalTrx) {\n await trx.commit();\n }\n } catch (err) {\n if (!externalTrx) {\n await trx.rollback(err);\n }\n throw err;\n }\n }\n\n async addGroupingPolicy(\n policy: string[],\n roleMetadata: RoleMetadataDao,\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n const trx = externalTrx ?? (await this.knex.transaction());\n const entityRef = roleMetadata.roleEntityRef;\n\n if (await this.enforcer.hasGroupingPolicy(...policy)) {\n return;\n }\n try {\n let currentMetadata;\n if (entityRef.startsWith(`role:`)) {\n currentMetadata = await this.roleMetadataStorage.findRoleMetadata(\n entityRef,\n trx,\n );\n }\n\n if (currentMetadata) {\n await this.roleMetadataStorage.updateRoleMetadata(\n mergeRoleMetadata(currentMetadata, roleMetadata),\n entityRef,\n trx,\n );\n } else {\n const currentDate: Date = new Date();\n roleMetadata.createdAt = currentDate.toUTCString();\n roleMetadata.lastModified = currentDate.toUTCString();\n await this.roleMetadataStorage.createRoleMetadata(roleMetadata, trx);\n }\n\n const ok = await this.enforcer.addGroupingPolicy(...policy);\n if (!ok) {\n throw new Error(`failed to create policy ${policyToString(policy)}`);\n }\n if (!externalTrx) {\n await trx.commit();\n }\n if (!currentMetadata) {\n this.roleEventEmitter.emit('roleAdded', roleMetadata.roleEntityRef);\n }\n } catch (err) {\n if (!externalTrx) {\n await trx.rollback(err);\n }\n throw err;\n }\n }\n\n async addGroupingPolicies(\n policies: string[][],\n roleMetadata: RoleMetadataDao,\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n if (policies.length === 0) {\n return;\n }\n\n const trx = externalTrx ?? (await this.knex.transaction());\n\n try {\n const currentRoleMetadata =\n await this.roleMetadataStorage.findRoleMetadata(\n roleMetadata.roleEntityRef,\n trx,\n );\n if (currentRoleMetadata) {\n await this.roleMetadataStorage.updateRoleMetadata(\n mergeRoleMetadata(currentRoleMetadata, roleMetadata),\n roleMetadata.roleEntityRef,\n trx,\n );\n } else {\n const currentDate: Date = new Date();\n roleMetadata.createdAt = currentDate.toUTCString();\n roleMetadata.lastModified = currentDate.toUTCString();\n await this.roleMetadataStorage.createRoleMetadata(roleMetadata, trx);\n }\n\n const ok = await this.enforcer.addGroupingPolicies(policies);\n if (!ok) {\n throw new Error(\n `Failed to store policies ${policiesToString(policies)}`,\n );\n }\n\n if (!externalTrx) {\n await trx.commit();\n }\n if (!currentRoleMetadata) {\n this.roleEventEmitter.emit('roleAdded', roleMetadata.roleEntityRef);\n }\n } catch (err) {\n if (!externalTrx) {\n await trx.rollback(err);\n }\n throw err;\n }\n }\n\n async updateGroupingPolicies(\n oldRole: string[][],\n newRole: string[][],\n newRoleMetadata: RoleMetadataDao,\n ): Promise<void> {\n const oldRoleName = oldRole.at(0)?.at(1)!;\n\n const trx = await this.knex.transaction();\n try {\n const currentMetadata = await this.roleMetadataStorage.findRoleMetadata(\n oldRoleName,\n trx,\n );\n if (!currentMetadata) {\n throw new Error(`Role metadata ${oldRoleName} was not found`);\n }\n\n await this.removeGroupingPolicies(oldRole, currentMetadata, true, trx);\n await this.addGroupingPolicies(newRole, newRoleMetadata, trx);\n await trx.commit();\n } catch (err) {\n await trx.rollback(err);\n throw err;\n }\n }\n\n async updatePolicies(\n oldPolicies: string[][],\n newPolicies: string[][],\n ): Promise<void> {\n const trx = await this.knex.transaction();\n\n try {\n await this.removePolicies(oldPolicies, trx);\n await this.addPolicies(newPolicies, trx);\n await trx.commit();\n } catch (err) {\n await trx.rollback(err);\n throw err;\n }\n }\n\n async removePolicy(policy: string[], externalTrx?: Knex.Transaction) {\n const trx = externalTrx ?? (await this.knex.transaction());\n\n try {\n const ok = await this.enforcer.removePolicy(...policy);\n if (!ok) {\n throw new Error(`fail to delete policy ${policy}`);\n }\n if (!externalTrx) {\n await trx.commit();\n }\n } catch (err) {\n if (!externalTrx) {\n await trx.rollback(err);\n }\n throw err;\n }\n }\n\n async removePolicies(\n policies: string[][],\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n const trx = externalTrx ?? (await this.knex.transaction());\n\n try {\n const ok = await this.enforcer.removePolicies(policies);\n if (!ok) {\n throw new Error(\n `Failed to delete policies ${policiesToString(policies)}`,\n );\n }\n\n if (!externalTrx) {\n await trx.commit();\n }\n } catch (err) {\n if (!externalTrx) {\n await trx.rollback(err);\n }\n throw err;\n }\n }\n\n async removeGroupingPolicy(\n policy: string[],\n roleMetadata: RoleMetadataDao,\n isUpdate?: boolean,\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n const trx = externalTrx ?? (await this.knex.transaction());\n const roleEntity = policy[1];\n\n try {\n const ok = await this.enforcer.removeGroupingPolicy(...policy);\n if (!ok) {\n throw new Error(`Failed to delete policy ${policyToString(policy)}`);\n }\n\n if (!isUpdate) {\n const currentRoleMetadata =\n await this.roleMetadataStorage.findRoleMetadata(roleEntity, trx);\n const remainingGroupPolicies =\n await this.enforcer.getFilteredGroupingPolicy(1, roleEntity);\n if (\n currentRoleMetadata &&\n remainingGroupPolicies.length === 0 &&\n roleEntity !== ADMIN_ROLE_NAME\n ) {\n await this.roleMetadataStorage.removeRoleMetadata(roleEntity, trx);\n } else if (currentRoleMetadata) {\n await this.roleMetadataStorage.updateRoleMetadata(\n mergeRoleMetadata(currentRoleMetadata, roleMetadata),\n roleEntity,\n trx,\n );\n }\n }\n\n if (!externalTrx) {\n await trx.commit();\n }\n } catch (err) {\n if (!externalTrx) {\n await trx.rollback(err);\n }\n throw err;\n }\n }\n\n async removeGroupingPolicies(\n policies: string[][],\n roleMetadata: RoleMetadataDao,\n isUpdate?: boolean,\n externalTrx?: Knex.Transaction,\n ): Promise<void> {\n const trx = externalTrx ?? (await this.knex.transaction());\n\n const roleEntity = roleMetadata.roleEntityRef;\n try {\n const ok = await this.enforcer.removeGroupingPolicies(policies);\n if (!ok) {\n throw new Error(\n `Failed to delete grouping policies: ${policiesToString(policies)}`,\n );\n }\n\n if (!isUpdate) {\n const currentRoleMetadata =\n await this.roleMetadataStorage.findRoleMetadata(roleEntity, trx);\n const remainingGroupPolicies =\n await this.enforcer.getFilteredGroupingPolicy(1, roleEntity);\n if (\n currentRoleMetadata &&\n remainingGroupPolicies.length === 0 &&\n roleEntity !== ADMIN_ROLE_NAME\n ) {\n await this.roleMetadataStorage.removeRoleMetadata(roleEntity, trx);\n } else if (currentRoleMetadata) {\n await this.roleMetadataStorage.updateRoleMetadata(\n mergeRoleMetadata(currentRoleMetadata, roleMetadata),\n roleEntity,\n trx,\n );\n }\n }\n\n if (!externalTrx) {\n await trx.commit();\n }\n } catch (err) {\n if (!externalTrx) {\n await trx.rollback(err);\n }\n throw err;\n }\n }\n\n /**\n * enforce aims to enforce a particular permission policy based on the user that it receives.\n * Under the hood, enforce uses the `enforce` method from the enforcer`.\n *\n * Before enforcement, a filter is set up to reduce the number of permission policies that will\n * be loaded in.\n * This will reduce the amount of checks that need to be made to determine if a user is authorize\n * to perform an action\n *\n * A temporary enforcer will also be used while enforcing.\n * This is to ensure that the filter does not interact with the base enforcer.\n * The temporary enforcer has lazy loading of the permission policies enabled to reduce the amount\n * of time it takes to initialize the temporary enforcer.\n * The justification for lazy loading is because permission policies are already present in the\n * role manager / database and it will be filtered and loaded whenever `loadFilteredPolicy` is called.\n * @param entityRef The user to enforce\n * @param resourceType The resource type / name of the permission policy\n * @param action The action of the permission policy\n * @param roles Any roles that the user is directly or indirectly attached to.\n * Used for filtering permission policies.\n * @returns True if the user is allowed based on the particular permission\n */\n async enforce(\n entityRef: string,\n resourceType: string,\n action: string,\n roles: string[],\n ): Promise<boolean> {\n const filter = [];\n if (roles.length > 0) {\n roles.forEach(role => {\n filter.push({ ptype: 'p', v0: role, v1: resourceType, v2: action });\n });\n } else {\n filter.push({ ptype: 'p', v1: resourceType, v2: action });\n }\n\n const adapt = this.enforcer.getAdapter();\n const roleManager = this.enforcer.getRoleManager();\n const tempEnforcer = new Enforcer();\n await tempEnforcer.initWithModelAndAdapter(\n newModelFromString(MODEL),\n adapt,\n true,\n );\n tempEnforcer.setRoleManager(roleManager);\n\n await tempEnforcer.loadFilteredPolicy(filter);\n\n return await tempEnforcer.enforce(entityRef, resourceType, action);\n }\n\n async getImplicitPermissionsForUser(user: string): Promise<string[][]> {\n return this.enforcer.getImplicitPermissionsForUser(user);\n }\n\n async getAllRoles(): Promise<string[]> {\n return this.enforcer.getAllRoles();\n }\n}\n"],"names":["EventEmitter","policyToString","policiesToString","mergeRoleMetadata","ADMIN_ROLE_NAME","Enforcer","newModelFromString","MODEL"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const MODEL = `
|
|
4
|
+
[request_definition]
|
|
5
|
+
r = sub, obj, act
|
|
6
|
+
|
|
7
|
+
[policy_definition]
|
|
8
|
+
p = sub, obj, act, eft
|
|
9
|
+
|
|
10
|
+
[policy_effect]
|
|
11
|
+
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
|
|
12
|
+
|
|
13
|
+
[role_definition]
|
|
14
|
+
g = _, _
|
|
15
|
+
|
|
16
|
+
[matchers]
|
|
17
|
+
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
exports.MODEL = MODEL;
|
|
21
|
+
//# sourceMappingURL=permission-model.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-model.cjs.js","sources":["../../src/service/permission-model.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 */\nexport const MODEL = `\n[request_definition]\nr = sub, obj, act\n\n[policy_definition]\np = sub, obj, act, eft\n\n[policy_effect]\ne = some(where (p.eft == allow)) && !some(where (p.eft == deny))\n\n[role_definition]\ng = _, _\n\n[matchers]\nm = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act\n`;\n"],"names":[],"mappings":";;AAeO,MAAM,KAAQ,GAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var urlReader = require('@backstage/backend-defaults/urlReader');
|
|
4
|
+
var errors = require('@backstage/errors');
|
|
5
|
+
var pluginPermissionCommon = require('@backstage/plugin-permission-common');
|
|
6
|
+
|
|
7
|
+
class PluginPermissionMetadataCollector {
|
|
8
|
+
pluginIds;
|
|
9
|
+
discovery;
|
|
10
|
+
logger;
|
|
11
|
+
urlReader;
|
|
12
|
+
constructor({
|
|
13
|
+
deps,
|
|
14
|
+
optional
|
|
15
|
+
}) {
|
|
16
|
+
const { discovery, pluginIdProvider, logger, config } = deps;
|
|
17
|
+
this.pluginIds = pluginIdProvider.getPluginIds();
|
|
18
|
+
this.discovery = discovery;
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
this.urlReader = optional?.urlReader ?? urlReader.UrlReaders.default({
|
|
21
|
+
config,
|
|
22
|
+
logger,
|
|
23
|
+
factories: [PluginPermissionMetadataCollector.permissionFactory]
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async getPluginConditionRules(auth) {
|
|
27
|
+
const pluginMetadata = await this.getPluginMetaData(auth);
|
|
28
|
+
return pluginMetadata.filter((metadata) => metadata.metaDataResponse.rules.length > 0).map((metadata) => {
|
|
29
|
+
return {
|
|
30
|
+
pluginId: metadata.pluginId,
|
|
31
|
+
rules: metadata.metaDataResponse.rules
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async getPluginPolicies(auth) {
|
|
36
|
+
const pluginMetadata = await this.getPluginMetaData(auth);
|
|
37
|
+
return pluginMetadata.filter((metadata) => metadata.metaDataResponse.permissions !== void 0).map((metadata) => {
|
|
38
|
+
return {
|
|
39
|
+
pluginId: metadata.pluginId,
|
|
40
|
+
policies: permissionsToCasbinPolicies(
|
|
41
|
+
metadata.metaDataResponse.permissions
|
|
42
|
+
)
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
static permissionFactory = () => {
|
|
47
|
+
return [{ reader: new urlReader.FetchUrlReader(), predicate: (_url) => true }];
|
|
48
|
+
};
|
|
49
|
+
async getPluginMetaData(auth) {
|
|
50
|
+
let pluginResponses = [];
|
|
51
|
+
for (const pluginId of this.pluginIds) {
|
|
52
|
+
try {
|
|
53
|
+
const { token } = await auth.getPluginRequestToken({
|
|
54
|
+
onBehalfOf: await auth.getOwnServiceCredentials(),
|
|
55
|
+
targetPluginId: pluginId
|
|
56
|
+
});
|
|
57
|
+
const permMetaData = await this.getMetadataByPluginId(pluginId, token);
|
|
58
|
+
if (permMetaData) {
|
|
59
|
+
pluginResponses = [
|
|
60
|
+
...pluginResponses,
|
|
61
|
+
{
|
|
62
|
+
metaDataResponse: permMetaData,
|
|
63
|
+
pluginId
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
this.logger.error(
|
|
69
|
+
`Failed to retrieve permission metadata for ${pluginId}. ${error}`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return pluginResponses;
|
|
74
|
+
}
|
|
75
|
+
async getMetadataByPluginId(pluginId, token) {
|
|
76
|
+
let permMetaData;
|
|
77
|
+
try {
|
|
78
|
+
const baseEndpoint = await this.discovery.getBaseUrl(pluginId);
|
|
79
|
+
const wellKnownURL = `${baseEndpoint}/.well-known/backstage/permissions/metadata`;
|
|
80
|
+
const permResp = await this.urlReader.readUrl(wellKnownURL, { token });
|
|
81
|
+
const permMetaDataRaw = (await permResp.buffer()).toString();
|
|
82
|
+
try {
|
|
83
|
+
permMetaData = JSON.parse(permMetaDataRaw);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
} catch (err) {
|
|
88
|
+
if (errors.isError(err) && err.name === "NotFoundError") {
|
|
89
|
+
this.logger.warn(
|
|
90
|
+
`No permission metadata found for ${pluginId}. ${err}`
|
|
91
|
+
);
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
94
|
+
this.logger.error(
|
|
95
|
+
`Failed to retrieve permission metadata for ${pluginId}. ${err}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return permMetaData;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function permissionsToCasbinPolicies(permissions) {
|
|
102
|
+
const policies = [];
|
|
103
|
+
for (const permission of permissions) {
|
|
104
|
+
if (pluginPermissionCommon.isResourcePermission(permission)) {
|
|
105
|
+
policies.push({
|
|
106
|
+
resourceType: permission.resourceType,
|
|
107
|
+
name: permission.name,
|
|
108
|
+
policy: permission.attributes.action || "use"
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
policies.push({
|
|
112
|
+
name: permission.name,
|
|
113
|
+
policy: permission.attributes.action || "use"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return policies;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
exports.PluginPermissionMetadataCollector = PluginPermissionMetadataCollector;
|
|
121
|
+
//# sourceMappingURL=plugin-endpoints.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-endpoints.cjs.js","sources":["../../src/service/plugin-endpoints.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 FetchUrlReader,\n ReaderFactory,\n UrlReaders,\n} from '@backstage/backend-defaults/urlReader';\nimport type {\n AuthService,\n DiscoveryService,\n LoggerService,\n UrlReaderService,\n} from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport { isError } from '@backstage/errors';\nimport {\n isResourcePermission,\n Permission,\n} from '@backstage/plugin-permission-common';\nimport type {\n MetadataResponse,\n MetadataResponseSerializedRule,\n} from '@backstage/plugin-permission-node';\n\nimport type {\n PluginPermissionMetaData,\n PolicyDetails,\n} from '@backstage-community/plugin-rbac-common';\nimport type { PluginIdProvider } from '@backstage-community/plugin-rbac-node';\n\ntype PluginMetadataResponse = {\n pluginId: string;\n metaDataResponse: MetadataResponse;\n};\n\nexport type PluginMetadataResponseSerializedRule = {\n pluginId: string;\n rules: MetadataResponseSerializedRule[];\n};\n\nexport class PluginPermissionMetadataCollector {\n private readonly pluginIds: string[];\n private readonly discovery: DiscoveryService;\n private readonly logger: LoggerService;\n private readonly urlReader: UrlReaderService;\n\n constructor({\n deps,\n optional,\n }: {\n deps: {\n discovery: DiscoveryService;\n pluginIdProvider: PluginIdProvider;\n logger: LoggerService;\n config: Config;\n };\n optional?: {\n urlReader?: UrlReaderService;\n };\n }) {\n const { discovery, pluginIdProvider, logger, config } = deps;\n this.pluginIds = pluginIdProvider.getPluginIds();\n this.discovery = discovery;\n this.logger = logger;\n this.urlReader =\n optional?.urlReader ??\n UrlReaders.default({\n config,\n logger,\n factories: [PluginPermissionMetadataCollector.permissionFactory],\n });\n }\n\n async getPluginConditionRules(\n auth: AuthService,\n ): Promise<PluginMetadataResponseSerializedRule[]> {\n const pluginMetadata = await this.getPluginMetaData(auth);\n\n return pluginMetadata\n .filter(metadata => metadata.metaDataResponse.rules.length > 0)\n .map(metadata => {\n return {\n pluginId: metadata.pluginId,\n rules: metadata.metaDataResponse.rules,\n };\n });\n }\n\n async getPluginPolicies(\n auth: AuthService,\n ): Promise<PluginPermissionMetaData[]> {\n const pluginMetadata = await this.getPluginMetaData(auth);\n\n return pluginMetadata\n .filter(metadata => metadata.metaDataResponse.permissions !== undefined)\n .map(metadata => {\n return {\n pluginId: metadata.pluginId,\n policies: permissionsToCasbinPolicies(\n metadata.metaDataResponse.permissions!,\n ),\n };\n });\n }\n\n private static permissionFactory: ReaderFactory = () => {\n return [{ reader: new FetchUrlReader(), predicate: (_url: URL) => true }];\n };\n\n private async getPluginMetaData(\n auth: AuthService,\n ): Promise<PluginMetadataResponse[]> {\n let pluginResponses: PluginMetadataResponse[] = [];\n\n for (const pluginId of this.pluginIds) {\n try {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: pluginId,\n });\n\n const permMetaData = await this.getMetadataByPluginId(pluginId, token);\n if (permMetaData) {\n pluginResponses = [\n ...pluginResponses,\n {\n metaDataResponse: permMetaData,\n pluginId,\n },\n ];\n }\n } catch (error) {\n this.logger.error(\n `Failed to retrieve permission metadata for ${pluginId}. ${error}`,\n );\n }\n }\n\n return pluginResponses;\n }\n\n async getMetadataByPluginId(\n pluginId: string,\n token: string | undefined,\n ): Promise<MetadataResponse | undefined> {\n let permMetaData: MetadataResponse | undefined;\n try {\n const baseEndpoint = await this.discovery.getBaseUrl(pluginId);\n const wellKnownURL = `${baseEndpoint}/.well-known/backstage/permissions/metadata`;\n\n const permResp = await this.urlReader.readUrl(wellKnownURL, { token });\n const permMetaDataRaw = (await permResp.buffer()).toString();\n\n try {\n permMetaData = JSON.parse(permMetaDataRaw);\n } catch (err) {\n // workaround for https://issues.redhat.com/browse/RHIDP-1456\n return undefined;\n }\n } catch (err) {\n if (isError(err) && err.name === 'NotFoundError') {\n this.logger.warn(\n `No permission metadata found for ${pluginId}. ${err}`,\n );\n return undefined;\n }\n this.logger.error(\n `Failed to retrieve permission metadata for ${pluginId}. ${err}`,\n );\n }\n return permMetaData;\n }\n}\n\nfunction permissionsToCasbinPolicies(\n permissions: Permission[],\n): PolicyDetails[] {\n const policies: PolicyDetails[] = [];\n for (const permission of permissions) {\n if (isResourcePermission(permission)) {\n policies.push({\n resourceType: permission.resourceType,\n name: permission.name,\n policy: permission.attributes.action || 'use',\n });\n } else {\n policies.push({\n name: permission.name,\n policy: permission.attributes.action || 'use',\n });\n }\n }\n\n return policies;\n}\n"],"names":["UrlReaders","FetchUrlReader","isError","isResourcePermission"],"mappings":"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"}
|