@backstage-community/plugin-rbac-backend 6.0.1 → 6.1.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 +15 -0
- package/dist/admin-permissions/admin-creation.cjs.js +28 -1
- package/dist/admin-permissions/admin-creation.cjs.js.map +1 -1
- package/dist/database/role-metadata.cjs.js +10 -0
- package/dist/database/role-metadata.cjs.js.map +1 -1
- package/dist/file-permissions/csv-file-watcher.cjs.js +10 -0
- package/dist/file-permissions/csv-file-watcher.cjs.js.map +1 -1
- package/dist/helper.cjs.js +20 -0
- package/dist/helper.cjs.js.map +1 -1
- package/dist/permissions/conditions.cjs.js +15 -0
- package/dist/permissions/conditions.cjs.js.map +1 -0
- package/dist/permissions/rules.cjs.js +41 -0
- package/dist/permissions/rules.cjs.js.map +1 -0
- package/dist/policies/permission-policy.cjs.js +13 -5
- package/dist/policies/permission-policy.cjs.js.map +1 -1
- package/dist/providers/connect-providers.cjs.js +5 -0
- package/dist/providers/connect-providers.cjs.js.map +1 -1
- package/dist/service/plugin-endpoints.cjs.js +10 -0
- package/dist/service/plugin-endpoints.cjs.js.map +1 -1
- package/dist/service/policies-rest-api.cjs.js +203 -56
- package/dist/service/policies-rest-api.cjs.js.map +1 -1
- package/dist/validation/condition-validation.cjs.js +5 -0
- package/dist/validation/condition-validation.cjs.js.map +1 -1
- package/dist/validation/policies-validation.cjs.js +12 -1
- package/dist/validation/policies-validation.cjs.js.map +1 -1
- package/migrations/20250305155143_migration.js +73 -0
- package/package.json +5 -3
|
@@ -11,6 +11,8 @@ var roleMetadata = require('../database/role-metadata.cjs.js');
|
|
|
11
11
|
var helper = require('../helper.cjs.js');
|
|
12
12
|
var conditionValidation = require('../validation/condition-validation.cjs.js');
|
|
13
13
|
var policiesValidation = require('../validation/policies-validation.cjs.js');
|
|
14
|
+
var conditions = require('../permissions/conditions.cjs.js');
|
|
15
|
+
var rules = require('../permissions/rules.cjs.js');
|
|
14
16
|
|
|
15
17
|
class PoliciesServer {
|
|
16
18
|
constructor(permissions, options, enforcer, conditionalStorage, pluginPermMetaData, roleMetadata, auditor, rbacProviders) {
|
|
@@ -23,7 +25,7 @@ class PoliciesServer {
|
|
|
23
25
|
this.auditor = auditor;
|
|
24
26
|
this.rbacProviders = rbacProviders;
|
|
25
27
|
}
|
|
26
|
-
async
|
|
28
|
+
async authorizeConditional(request, permission) {
|
|
27
29
|
const credentials = await this.options.httpAuth.credentials(request, {
|
|
28
30
|
allow: ["user", "service"]
|
|
29
31
|
});
|
|
@@ -32,31 +34,43 @@ class PoliciesServer {
|
|
|
32
34
|
`Only creadential principal with type 'user' permitted to modify permissions`
|
|
33
35
|
);
|
|
34
36
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
|
|
37
|
+
let decision;
|
|
38
|
+
if (permission.type === "resource") {
|
|
39
|
+
decision = (await this.permissions.authorizeConditional([{ permission }], {
|
|
40
|
+
credentials
|
|
41
|
+
}))[0];
|
|
42
|
+
} else {
|
|
43
|
+
decision = (await this.permissions.authorize([{ permission }], {
|
|
44
|
+
credentials
|
|
45
|
+
}))[0];
|
|
46
|
+
}
|
|
39
47
|
return decision;
|
|
40
48
|
}
|
|
41
49
|
async serve() {
|
|
42
50
|
const router = await pluginPermissionBackend.createRouter(this.options);
|
|
43
|
-
const { httpAuth } = this.options;
|
|
51
|
+
const { httpAuth, logger } = this.options;
|
|
44
52
|
if (!httpAuth) {
|
|
45
53
|
throw new errors.ServiceUnavailableError(
|
|
46
54
|
"httpAuth not found, ensure the correct configuration for the RBAC plugin"
|
|
47
55
|
);
|
|
48
56
|
}
|
|
49
|
-
const
|
|
57
|
+
const policyPermissionsIntegrationRouter = pluginPermissionNode.createPermissionIntegrationRouter({
|
|
50
58
|
resourceType: pluginRbacCommon.RESOURCE_TYPE_POLICY_ENTITY,
|
|
51
|
-
|
|
59
|
+
getResources: (resourceRefs) => Promise.all(
|
|
60
|
+
resourceRefs.map((ref) => {
|
|
61
|
+
return this.roleMetadata.findRoleMetadata(ref);
|
|
62
|
+
})
|
|
63
|
+
),
|
|
64
|
+
permissions: pluginRbacCommon.policyEntityPermissions,
|
|
65
|
+
rules: Object.values(rules.rules)
|
|
52
66
|
});
|
|
53
|
-
router.use(
|
|
67
|
+
router.use(policyPermissionsIntegrationRouter);
|
|
54
68
|
const isPluginEnabled = this.options.config.getOptionalBoolean("permission.enabled");
|
|
55
69
|
if (!isPluginEnabled) {
|
|
56
70
|
return router;
|
|
57
71
|
}
|
|
58
72
|
router.get("/", async (request, response) => {
|
|
59
|
-
const decision = await this.
|
|
73
|
+
const decision = await this.authorizeConditional(
|
|
60
74
|
request,
|
|
61
75
|
pluginRbacCommon.policyEntityReadPermission
|
|
62
76
|
);
|
|
@@ -69,25 +83,48 @@ class PoliciesServer {
|
|
|
69
83
|
"/policies",
|
|
70
84
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
71
85
|
async (request, response) => {
|
|
72
|
-
|
|
86
|
+
let conditionsFilter;
|
|
87
|
+
const decision = await this.authorizeConditional(
|
|
73
88
|
request,
|
|
74
89
|
pluginRbacCommon.policyEntityReadPermission
|
|
75
90
|
);
|
|
76
91
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
77
92
|
throw new errors.NotAllowedError();
|
|
78
93
|
}
|
|
79
|
-
|
|
94
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
95
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
96
|
+
}
|
|
97
|
+
const roleMetadata = await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);
|
|
98
|
+
let policies = [];
|
|
80
99
|
if (this.isPolicyFilterEnabled(request)) {
|
|
81
100
|
const entityRef = this.getFirstQuery(request.query.entityRef);
|
|
82
101
|
const permission = this.getFirstQuery(request.query.permission);
|
|
83
102
|
const policy = this.getFirstQuery(request.query.policy);
|
|
84
103
|
const effect = this.getFirstQuery(request.query.effect);
|
|
104
|
+
const matchedRoleName = roleMetadata.flatMap(
|
|
105
|
+
(role) => role.roleEntityRef
|
|
106
|
+
);
|
|
85
107
|
const filter = [entityRef, permission, policy, effect];
|
|
86
|
-
policies = await this.enforcer.getFilteredPolicy(0, ...filter);
|
|
108
|
+
policies = matchedRoleName.includes(entityRef) ? await this.enforcer.getFilteredPolicy(0, ...filter) : [];
|
|
87
109
|
} else {
|
|
88
|
-
|
|
110
|
+
for (const role of roleMetadata) {
|
|
111
|
+
policies.push(
|
|
112
|
+
...await this.enforcer.getFilteredPolicy(
|
|
113
|
+
0,
|
|
114
|
+
...[role.roleEntityRef]
|
|
115
|
+
)
|
|
116
|
+
);
|
|
117
|
+
}
|
|
89
118
|
}
|
|
90
119
|
const body = await this.transformPolicyArray(...policies);
|
|
120
|
+
body.map((policy) => {
|
|
121
|
+
if (policy.permission === "policy-entity" && policy.policy === "create") {
|
|
122
|
+
policy.permission = "policy.entity.create";
|
|
123
|
+
logger.warn(
|
|
124
|
+
`Permission policy with resource type 'policy-entity' and action 'create' has been removed. Please consider updating policy ${[policy.entityReference, "policy-entity", policy.policy, policy.effect]} to use 'policy.entity.create' instead of 'policy-entity' from source ${policy.metadata?.source}`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
91
128
|
response.json(body);
|
|
92
129
|
}
|
|
93
130
|
);
|
|
@@ -95,17 +132,33 @@ class PoliciesServer {
|
|
|
95
132
|
"/policies/:kind/:namespace/:name",
|
|
96
133
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
97
134
|
async (request, response) => {
|
|
98
|
-
|
|
135
|
+
let conditionsFilter;
|
|
136
|
+
const decision = await this.authorizeConditional(
|
|
99
137
|
request,
|
|
100
138
|
pluginRbacCommon.policyEntityReadPermission
|
|
101
139
|
);
|
|
102
140
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
103
141
|
throw new errors.NotAllowedError();
|
|
104
142
|
}
|
|
143
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
144
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
145
|
+
}
|
|
146
|
+
const roleMetadata = await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);
|
|
147
|
+
const matchedRoleName = roleMetadata.flatMap((role) => {
|
|
148
|
+
return role.roleEntityRef;
|
|
149
|
+
});
|
|
105
150
|
const entityRef = this.getEntityReference(request);
|
|
106
|
-
const policy = await this.enforcer.getFilteredPolicy(0, entityRef);
|
|
151
|
+
const policy = matchedRoleName.includes(entityRef) ? await this.enforcer.getFilteredPolicy(0, entityRef) : [];
|
|
107
152
|
if (policy.length !== 0) {
|
|
108
153
|
const body = await this.transformPolicyArray(...policy);
|
|
154
|
+
body.map((bodyPolicy) => {
|
|
155
|
+
if (bodyPolicy.permission === "policy-entity" && bodyPolicy.policy === "create") {
|
|
156
|
+
bodyPolicy.permission = "policy.entity.create";
|
|
157
|
+
logger.warn(
|
|
158
|
+
`Permission policy with resource type 'policy-entity' and action 'create' has been removed. Please consider updating policy ${[bodyPolicy.entityReference, "policy-entity", bodyPolicy.policy, bodyPolicy.effect]} to use 'policy.entity.create' instead of 'policy-entity' from source ${bodyPolicy.metadata?.source}`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
109
162
|
response.json(body);
|
|
110
163
|
} else {
|
|
111
164
|
throw new errors.NotFoundError();
|
|
@@ -116,13 +169,17 @@ class PoliciesServer {
|
|
|
116
169
|
"/policies/:kind/:namespace/:name",
|
|
117
170
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
118
171
|
async (request, response) => {
|
|
119
|
-
|
|
172
|
+
let conditionsFilter;
|
|
173
|
+
const decision = await this.authorizeConditional(
|
|
120
174
|
request,
|
|
121
175
|
pluginRbacCommon.policyEntityDeletePermission
|
|
122
176
|
);
|
|
123
177
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
124
178
|
throw new errors.NotAllowedError();
|
|
125
179
|
}
|
|
180
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
181
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
182
|
+
}
|
|
126
183
|
const entityRef = this.getEntityReference(request);
|
|
127
184
|
const policyRaw = request.body;
|
|
128
185
|
if (lodash.isEmpty(policyRaw)) {
|
|
@@ -131,7 +188,12 @@ class PoliciesServer {
|
|
|
131
188
|
policyRaw.forEach((element) => {
|
|
132
189
|
element.entityReference = entityRef;
|
|
133
190
|
});
|
|
134
|
-
const processedPolicies = await this.processPolicies(
|
|
191
|
+
const processedPolicies = await this.processPolicies(
|
|
192
|
+
policyRaw,
|
|
193
|
+
true,
|
|
194
|
+
undefined,
|
|
195
|
+
conditionsFilter
|
|
196
|
+
);
|
|
135
197
|
await this.enforcer.removePolicies(processedPolicies);
|
|
136
198
|
response.locals.meta = { policies: processedPolicies };
|
|
137
199
|
response.status(204).end();
|
|
@@ -141,7 +203,7 @@ class PoliciesServer {
|
|
|
141
203
|
"/policies",
|
|
142
204
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
143
205
|
async (request, response) => {
|
|
144
|
-
const decision = await this.
|
|
206
|
+
const decision = await this.authorizeConditional(
|
|
145
207
|
request,
|
|
146
208
|
pluginRbacCommon.policyEntityCreatePermission
|
|
147
209
|
);
|
|
@@ -152,7 +214,11 @@ class PoliciesServer {
|
|
|
152
214
|
if (lodash.isEmpty(policyRaw)) {
|
|
153
215
|
throw new errors.InputError(`permission policy must be present`);
|
|
154
216
|
}
|
|
155
|
-
const processedPolicies = await this.processPolicies(
|
|
217
|
+
const processedPolicies = await this.processPolicies(
|
|
218
|
+
policyRaw,
|
|
219
|
+
false,
|
|
220
|
+
undefined
|
|
221
|
+
);
|
|
156
222
|
const entityRef = processedPolicies[0][0];
|
|
157
223
|
const roleMetadata = await this.roleMetadata.findRoleMetadata(entityRef);
|
|
158
224
|
if (entityRef.startsWith("role:default") && !roleMetadata) {
|
|
@@ -167,13 +233,17 @@ class PoliciesServer {
|
|
|
167
233
|
"/policies/:kind/:namespace/:name",
|
|
168
234
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
169
235
|
async (request, response) => {
|
|
170
|
-
|
|
236
|
+
let conditionsFilter;
|
|
237
|
+
const decision = await this.authorizeConditional(
|
|
171
238
|
request,
|
|
172
239
|
pluginRbacCommon.policyEntityUpdatePermission
|
|
173
240
|
);
|
|
174
241
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
175
242
|
throw new errors.NotAllowedError();
|
|
176
243
|
}
|
|
244
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
245
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
246
|
+
}
|
|
177
247
|
const entityRef = this.getEntityReference(request);
|
|
178
248
|
const oldPolicyRaw = request.body.oldPolicy;
|
|
179
249
|
if (lodash.isEmpty(oldPolicyRaw)) {
|
|
@@ -189,7 +259,8 @@ class PoliciesServer {
|
|
|
189
259
|
const processedOldPolicy = await this.processPolicies(
|
|
190
260
|
oldPolicyRaw,
|
|
191
261
|
true,
|
|
192
|
-
"old policy"
|
|
262
|
+
"old policy",
|
|
263
|
+
conditionsFilter
|
|
193
264
|
);
|
|
194
265
|
oldPolicyRaw.sort(
|
|
195
266
|
(a, b) => a.permission === b.permission ? this.nameSort(a.policy, b.policy) : this.nameSort(a.permission, b.permission)
|
|
@@ -207,7 +278,8 @@ class PoliciesServer {
|
|
|
207
278
|
const processedNewPolicy = await this.processPolicies(
|
|
208
279
|
newPolicyRaw,
|
|
209
280
|
false,
|
|
210
|
-
"new policy"
|
|
281
|
+
"new policy",
|
|
282
|
+
conditionsFilter
|
|
211
283
|
);
|
|
212
284
|
const roleMetadata = await this.roleMetadata.findRoleMetadata(entityRef);
|
|
213
285
|
if (entityRef.startsWith("role:default") && !roleMetadata) {
|
|
@@ -225,15 +297,19 @@ class PoliciesServer {
|
|
|
225
297
|
"/roles",
|
|
226
298
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
227
299
|
async (request, response) => {
|
|
228
|
-
|
|
300
|
+
let conditionsFilter;
|
|
301
|
+
const decision = await this.authorizeConditional(
|
|
229
302
|
request,
|
|
230
303
|
pluginRbacCommon.policyEntityReadPermission
|
|
231
304
|
);
|
|
232
305
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
233
306
|
throw new errors.NotAllowedError();
|
|
234
307
|
}
|
|
308
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
309
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
310
|
+
}
|
|
235
311
|
const roles = await this.enforcer.getGroupingPolicy();
|
|
236
|
-
const body = await this.transformRoleArray(...roles);
|
|
312
|
+
const body = await this.transformRoleArray(conditionsFilter, ...roles);
|
|
237
313
|
response.json(body);
|
|
238
314
|
}
|
|
239
315
|
);
|
|
@@ -241,20 +317,24 @@ class PoliciesServer {
|
|
|
241
317
|
"/roles/:kind/:namespace/:name",
|
|
242
318
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
243
319
|
async (request, response) => {
|
|
244
|
-
|
|
320
|
+
let conditionsFilter;
|
|
321
|
+
const decision = await this.authorizeConditional(
|
|
245
322
|
request,
|
|
246
323
|
pluginRbacCommon.policyEntityReadPermission
|
|
247
324
|
);
|
|
248
325
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
249
326
|
throw new errors.NotAllowedError();
|
|
250
327
|
}
|
|
328
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
329
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
330
|
+
}
|
|
251
331
|
const roleEntityRef = this.getEntityReference(request, true);
|
|
252
332
|
const role = await this.enforcer.getFilteredGroupingPolicy(
|
|
253
333
|
1,
|
|
254
334
|
roleEntityRef
|
|
255
335
|
);
|
|
256
|
-
|
|
257
|
-
|
|
336
|
+
const body = await this.transformRoleArray(conditionsFilter, ...role);
|
|
337
|
+
if (body.length !== 0) {
|
|
258
338
|
response.json(body);
|
|
259
339
|
} else {
|
|
260
340
|
throw new errors.NotFoundError();
|
|
@@ -266,7 +346,7 @@ class PoliciesServer {
|
|
|
266
346
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
267
347
|
async (request, response) => {
|
|
268
348
|
const uniqueItems = /* @__PURE__ */ new Set();
|
|
269
|
-
const decision = await this.
|
|
349
|
+
const decision = await this.authorizeConditional(
|
|
270
350
|
request,
|
|
271
351
|
pluginRbacCommon.policyEntityCreatePermission
|
|
272
352
|
);
|
|
@@ -314,7 +394,8 @@ class PoliciesServer {
|
|
|
314
394
|
source: "rest",
|
|
315
395
|
description: roleRaw.metadata?.description ?? "",
|
|
316
396
|
author: modifiedBy,
|
|
317
|
-
modifiedBy
|
|
397
|
+
modifiedBy,
|
|
398
|
+
owner: roleRaw.metadata?.owner ?? modifiedBy
|
|
318
399
|
};
|
|
319
400
|
await this.enforcer.addGroupingPolicies(roles, metadata);
|
|
320
401
|
response.locals.meta = { ...metadata, members: roles.map((gp) => gp[0]) };
|
|
@@ -326,13 +407,17 @@ class PoliciesServer {
|
|
|
326
407
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
327
408
|
async (request, response) => {
|
|
328
409
|
const uniqueItems = /* @__PURE__ */ new Set();
|
|
329
|
-
|
|
410
|
+
let conditionsFilter;
|
|
411
|
+
const decision = await this.authorizeConditional(
|
|
330
412
|
request,
|
|
331
413
|
pluginRbacCommon.policyEntityUpdatePermission
|
|
332
414
|
);
|
|
333
415
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
334
416
|
throw new errors.NotAllowedError();
|
|
335
417
|
}
|
|
418
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
419
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
420
|
+
}
|
|
336
421
|
const roleEntityRef = this.getEntityReference(request, true);
|
|
337
422
|
const oldRoleRaw = request.body.oldRole;
|
|
338
423
|
if (!oldRoleRaw) {
|
|
@@ -368,7 +453,8 @@ class PoliciesServer {
|
|
|
368
453
|
...newRoleRaw.metadata,
|
|
369
454
|
source: newRoleRaw.metadata?.source ?? "rest",
|
|
370
455
|
roleEntityRef: newRoleRaw.name,
|
|
371
|
-
modifiedBy: credentials.principal.userEntityRef
|
|
456
|
+
modifiedBy: credentials.principal.userEntityRef,
|
|
457
|
+
owner: newRoleRaw.metadata?.owner ?? ""
|
|
372
458
|
};
|
|
373
459
|
const oldMetadata = await this.roleMetadata.findRoleMetadata(roleEntityRef);
|
|
374
460
|
if (!oldMetadata) {
|
|
@@ -380,11 +466,15 @@ class PoliciesServer {
|
|
|
380
466
|
if (err) {
|
|
381
467
|
throw new errors.NotAllowedError(`Unable to edit role: ${err.message}`);
|
|
382
468
|
}
|
|
469
|
+
if (!helper.matches(oldMetadata, conditionsFilter)) {
|
|
470
|
+
throw new errors.NotAllowedError();
|
|
471
|
+
}
|
|
383
472
|
if (lodash.isEqual(oldRole, newRole) && helper.deepSortedEqual(oldMetadata, newMetadata, [
|
|
384
473
|
"author",
|
|
385
474
|
"modifiedBy",
|
|
386
475
|
"createdAt",
|
|
387
|
-
"lastModified"
|
|
476
|
+
"lastModified",
|
|
477
|
+
"owner"
|
|
388
478
|
])) {
|
|
389
479
|
response.status(204).end();
|
|
390
480
|
return;
|
|
@@ -447,14 +537,26 @@ class PoliciesServer {
|
|
|
447
537
|
"/roles/:kind/:namespace/:name",
|
|
448
538
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
449
539
|
async (request, response) => {
|
|
450
|
-
|
|
540
|
+
let conditionsFilter;
|
|
541
|
+
const decision = await this.authorizeConditional(
|
|
451
542
|
request,
|
|
452
543
|
pluginRbacCommon.policyEntityDeletePermission
|
|
453
544
|
);
|
|
454
545
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
455
546
|
throw new errors.NotAllowedError();
|
|
456
547
|
}
|
|
548
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
549
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
550
|
+
}
|
|
457
551
|
const roleEntityRef = this.getEntityReference(request, true);
|
|
552
|
+
const currentMetadata = await this.roleMetadata.findRoleMetadata(roleEntityRef);
|
|
553
|
+
if (!helper.matches(currentMetadata, conditionsFilter)) {
|
|
554
|
+
throw new errors.NotAllowedError();
|
|
555
|
+
}
|
|
556
|
+
const err = await policiesValidation.validateSource("rest", currentMetadata);
|
|
557
|
+
if (err) {
|
|
558
|
+
throw new errors.NotAllowedError(`Unable to delete role: ${err.message}`);
|
|
559
|
+
}
|
|
458
560
|
let roleMembers = [];
|
|
459
561
|
if (request.query.memberReferences) {
|
|
460
562
|
const memberReference = this.getFirstQuery(
|
|
@@ -483,11 +585,6 @@ class PoliciesServer {
|
|
|
483
585
|
throw new errors.NotFoundError(`role member '${role[0]}' was not found`);
|
|
484
586
|
}
|
|
485
587
|
}
|
|
486
|
-
const currentMetadata = await this.roleMetadata.findRoleMetadata(roleEntityRef);
|
|
487
|
-
const err = await policiesValidation.validateSource("rest", currentMetadata);
|
|
488
|
-
if (err) {
|
|
489
|
-
throw new errors.NotAllowedError(`Unable to delete role: ${err.message}`);
|
|
490
|
-
}
|
|
491
588
|
const credentials = await httpAuth.credentials(request, {
|
|
492
589
|
allow: ["user"]
|
|
493
590
|
});
|
|
@@ -512,7 +609,7 @@ class PoliciesServer {
|
|
|
512
609
|
"/plugins/policies",
|
|
513
610
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
514
611
|
async (request, response) => {
|
|
515
|
-
const decision = await this.
|
|
612
|
+
const decision = await this.authorizeConditional(
|
|
516
613
|
request,
|
|
517
614
|
pluginRbacCommon.policyEntityReadPermission
|
|
518
615
|
);
|
|
@@ -529,7 +626,7 @@ class PoliciesServer {
|
|
|
529
626
|
"/plugins/condition-rules",
|
|
530
627
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
531
628
|
async (request, response) => {
|
|
532
|
-
const decision = await this.
|
|
629
|
+
const decision = await this.authorizeConditional(
|
|
533
630
|
request,
|
|
534
631
|
pluginRbacCommon.policyEntityReadPermission
|
|
535
632
|
);
|
|
@@ -546,26 +643,36 @@ class PoliciesServer {
|
|
|
546
643
|
"/roles/conditions",
|
|
547
644
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
548
645
|
async (request, response) => {
|
|
549
|
-
|
|
646
|
+
let conditionsFilter;
|
|
647
|
+
const decision = await this.authorizeConditional(
|
|
550
648
|
request,
|
|
551
649
|
pluginRbacCommon.policyEntityReadPermission
|
|
552
650
|
);
|
|
553
651
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
554
652
|
throw new errors.NotAllowedError();
|
|
555
653
|
}
|
|
556
|
-
|
|
654
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
655
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
656
|
+
}
|
|
657
|
+
const roleMetadata = await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);
|
|
658
|
+
const matchedRoleName = roleMetadata.flatMap((role) => {
|
|
659
|
+
return role.roleEntityRef;
|
|
660
|
+
});
|
|
661
|
+
const conditions$1 = await this.conditionalStorage.filterConditions(
|
|
557
662
|
this.getFirstQuery(request.query.roleEntityRef),
|
|
558
663
|
this.getFirstQuery(request.query.pluginId),
|
|
559
664
|
this.getFirstQuery(request.query.resourceType),
|
|
560
665
|
this.getActionQueries(request.query.actions)
|
|
561
666
|
);
|
|
562
|
-
const body = conditions.map((condition) => {
|
|
667
|
+
const body = conditions$1.map((condition) => {
|
|
563
668
|
return {
|
|
564
669
|
...condition,
|
|
565
670
|
permissionMapping: condition.permissionMapping.map(
|
|
566
671
|
(pm) => pm.action
|
|
567
672
|
)
|
|
568
673
|
};
|
|
674
|
+
}).filter((condition) => {
|
|
675
|
+
return matchedRoleName.includes(condition.roleEntityRef);
|
|
569
676
|
});
|
|
570
677
|
response.json(body);
|
|
571
678
|
}
|
|
@@ -574,7 +681,7 @@ class PoliciesServer {
|
|
|
574
681
|
"/roles/conditions",
|
|
575
682
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
576
683
|
async (request, response) => {
|
|
577
|
-
const decision = await this.
|
|
684
|
+
const decision = await this.authorizeConditional(
|
|
578
685
|
request,
|
|
579
686
|
pluginRbacCommon.policyEntityCreatePermission
|
|
580
687
|
);
|
|
@@ -598,7 +705,8 @@ class PoliciesServer {
|
|
|
598
705
|
"/roles/conditions/:id",
|
|
599
706
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
600
707
|
async (request, response) => {
|
|
601
|
-
|
|
708
|
+
let conditionsFilter;
|
|
709
|
+
const decision = await this.authorizeConditional(
|
|
602
710
|
request,
|
|
603
711
|
pluginRbacCommon.policyEntityReadPermission
|
|
604
712
|
);
|
|
@@ -613,10 +721,19 @@ class PoliciesServer {
|
|
|
613
721
|
if (!condition) {
|
|
614
722
|
throw new errors.NotFoundError();
|
|
615
723
|
}
|
|
616
|
-
|
|
724
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
725
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
726
|
+
}
|
|
727
|
+
const roleMetadata = await this.roleMetadata.filterForOwnerRoleMetadata(conditionsFilter);
|
|
728
|
+
const matchedRoleName = roleMetadata.flatMap((role) => {
|
|
729
|
+
return role.roleEntityRef;
|
|
730
|
+
});
|
|
731
|
+
const body = matchedRoleName.includes(condition.roleEntityRef) ? {
|
|
617
732
|
...condition,
|
|
618
|
-
permissionMapping: condition.permissionMapping.map(
|
|
619
|
-
|
|
733
|
+
permissionMapping: condition.permissionMapping.map(
|
|
734
|
+
(pm) => pm.action
|
|
735
|
+
)
|
|
736
|
+
} : [];
|
|
620
737
|
response.json(body);
|
|
621
738
|
}
|
|
622
739
|
);
|
|
@@ -624,13 +741,17 @@ class PoliciesServer {
|
|
|
624
741
|
"/roles/conditions/:id",
|
|
625
742
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
626
743
|
async (request, response) => {
|
|
627
|
-
|
|
744
|
+
let conditionsFilter;
|
|
745
|
+
const decision = await this.authorizeConditional(
|
|
628
746
|
request,
|
|
629
747
|
pluginRbacCommon.policyEntityDeletePermission
|
|
630
748
|
);
|
|
631
749
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
632
750
|
throw new errors.NotAllowedError();
|
|
633
751
|
}
|
|
752
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
753
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
754
|
+
}
|
|
634
755
|
const id = parseInt(request.params.id, 10);
|
|
635
756
|
if (isNaN(id)) {
|
|
636
757
|
throw new errors.InputError("Id is not a valid number.");
|
|
@@ -643,6 +764,12 @@ class PoliciesServer {
|
|
|
643
764
|
...condition,
|
|
644
765
|
permissionMapping: condition.permissionMapping.map((pm) => pm.action)
|
|
645
766
|
};
|
|
767
|
+
const roleMetadata = await this.roleMetadata.findRoleMetadata(
|
|
768
|
+
conditionToDelete.roleEntityRef
|
|
769
|
+
);
|
|
770
|
+
if (!helper.matches(roleMetadata, conditionsFilter)) {
|
|
771
|
+
throw new errors.NotAllowedError();
|
|
772
|
+
}
|
|
646
773
|
await this.conditionalStorage.deleteCondition(id);
|
|
647
774
|
response.locals.meta = { condition: conditionToDelete };
|
|
648
775
|
response.status(204).end();
|
|
@@ -652,17 +779,31 @@ class PoliciesServer {
|
|
|
652
779
|
"/roles/conditions/:id",
|
|
653
780
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
654
781
|
async (request, response) => {
|
|
655
|
-
|
|
782
|
+
let conditionsFilter;
|
|
783
|
+
const decision = await this.authorizeConditional(
|
|
656
784
|
request,
|
|
657
785
|
pluginRbacCommon.policyEntityUpdatePermission
|
|
658
786
|
);
|
|
659
787
|
if (decision.result === pluginPermissionCommon.AuthorizeResult.DENY) {
|
|
660
788
|
throw new errors.NotAllowedError();
|
|
661
789
|
}
|
|
790
|
+
if (decision.result === pluginPermissionCommon.AuthorizeResult.CONDITIONAL) {
|
|
791
|
+
conditionsFilter = conditions.transformConditions(decision.conditions);
|
|
792
|
+
}
|
|
662
793
|
const id = parseInt(request.params.id, 10);
|
|
663
794
|
if (isNaN(id)) {
|
|
664
795
|
throw new errors.InputError("Id is not a valid number.");
|
|
665
796
|
}
|
|
797
|
+
const condition = await this.conditionalStorage.getCondition(id);
|
|
798
|
+
if (!condition) {
|
|
799
|
+
throw new errors.NotFoundError(`Condition with id ${id} was not found`);
|
|
800
|
+
}
|
|
801
|
+
const roleMetadata = await this.roleMetadata.findRoleMetadata(
|
|
802
|
+
condition.roleEntityRef
|
|
803
|
+
);
|
|
804
|
+
if (!helper.matches(roleMetadata, conditionsFilter)) {
|
|
805
|
+
throw new errors.NotAllowedError();
|
|
806
|
+
}
|
|
666
807
|
const roleConditionPolicy = request.body;
|
|
667
808
|
conditionValidation.validateRoleCondition(roleConditionPolicy);
|
|
668
809
|
const conditionToUpdate = await helper.processConditionMapping(
|
|
@@ -679,7 +820,7 @@ class PoliciesServer {
|
|
|
679
820
|
"/refresh/:id",
|
|
680
821
|
restInterceptor.logAuditorEvent(this.auditor),
|
|
681
822
|
async (request, response) => {
|
|
682
|
-
const decision = await this.
|
|
823
|
+
const decision = await this.authorizeConditional(
|
|
683
824
|
request,
|
|
684
825
|
pluginRbacCommon.policyEntityCreatePermission
|
|
685
826
|
);
|
|
@@ -734,7 +875,7 @@ class PoliciesServer {
|
|
|
734
875
|
}
|
|
735
876
|
return roleBasedPolices;
|
|
736
877
|
}
|
|
737
|
-
async transformRoleArray(...roles) {
|
|
878
|
+
async transformRoleArray(filter, ...roles) {
|
|
738
879
|
const combinedRoles = {};
|
|
739
880
|
roles.forEach(([value, role]) => {
|
|
740
881
|
if (combinedRoles.hasOwnProperty(role)) {
|
|
@@ -744,7 +885,7 @@ class PoliciesServer {
|
|
|
744
885
|
}
|
|
745
886
|
});
|
|
746
887
|
const result = await Promise.all(
|
|
747
|
-
Object.entries(combinedRoles).
|
|
888
|
+
Object.entries(combinedRoles).flatMap(async ([role, value]) => {
|
|
748
889
|
const metadataDao = await this.roleMetadata.findRoleMetadata(role);
|
|
749
890
|
const metadata = metadataDao ? roleMetadata.daoToMetadata(metadataDao) : undefined;
|
|
750
891
|
return Promise.resolve({
|
|
@@ -754,7 +895,10 @@ class PoliciesServer {
|
|
|
754
895
|
});
|
|
755
896
|
})
|
|
756
897
|
);
|
|
757
|
-
|
|
898
|
+
const filteredResult = result.filter((role) => {
|
|
899
|
+
return role.metadata && helper.matches(role.metadata, filter);
|
|
900
|
+
});
|
|
901
|
+
return filteredResult;
|
|
758
902
|
}
|
|
759
903
|
transformPolicyToArray(policy) {
|
|
760
904
|
return [
|
|
@@ -818,7 +962,7 @@ class PoliciesServer {
|
|
|
818
962
|
isPolicyFilterEnabled(request) {
|
|
819
963
|
return !!request.query.entityRef || !!request.query.permission || !!request.query.policy || !!request.query.effect;
|
|
820
964
|
}
|
|
821
|
-
async processPolicies(policyArray, isOld, errorMessage) {
|
|
965
|
+
async processPolicies(policyArray, isOld, errorMessage, filter) {
|
|
822
966
|
const policies = [];
|
|
823
967
|
const uniqueItems = /* @__PURE__ */ new Set();
|
|
824
968
|
for (const policy of policyArray) {
|
|
@@ -831,6 +975,9 @@ class PoliciesServer {
|
|
|
831
975
|
const metadata = await this.roleMetadata.findRoleMetadata(
|
|
832
976
|
policy.entityReference
|
|
833
977
|
);
|
|
978
|
+
if (!helper.matches(metadata, filter)) {
|
|
979
|
+
throw new errors.NotAllowedError();
|
|
980
|
+
}
|
|
834
981
|
let action = errorMessage ? "edit" : "delete";
|
|
835
982
|
action = isOld ? action : "add";
|
|
836
983
|
err = await policiesValidation.validateSource("rest", metadata);
|