Skip to content

Commit b5f1552

Browse files
authored
feat(rbac): lazy load temporary enforcer (janus-idp#1513)
1 parent ffcd101 commit b5f1552

File tree

2 files changed

+50
-18
lines changed

2 files changed

+50
-18
lines changed

plugins/rbac-backend/src/service/enforcer-delegate.ts

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { NotAllowedError, NotFoundError } from '@backstage/errors';
22

3-
import { Enforcer, newEnforcer, newModelFromString } from 'casbin';
3+
import { Enforcer, newModelFromString } from 'casbin';
44
import { Knex } from 'knex';
55

66
import {
@@ -575,26 +575,51 @@ export class EnforcerDelegate {
575575
}
576576
}
577577

578+
/**
579+
* enforce aims to enforce a particular permission policy based on the user that it receives.
580+
* Under the hood, enforce uses the `enforce` method from the enforcer`.
581+
*
582+
* Before enforcement, a filter is set up to reduce the number of permission policies that will
583+
* be loaded in.
584+
* This will reduce the amount of checks that need to be made to determine if a user is authorize
585+
* to perform an action
586+
*
587+
* A temporary enforcer will also be used while enforcing.
588+
* This is to ensure that the filter does not interact with the base enforcer.
589+
* The temporary enforcer has lazy loading of the permission policies enabled to reduce the amount
590+
* of time it takes to initialize the temporary enforcer.
591+
* The justification for lazy loading is because permission policies are already present in the
592+
* role manager / database and it will be filtered and loaded whenever `loadFilteredPolicy` is called.
593+
* @param entityRef The user to enforce
594+
* @param resourceType The resource type / name of the permission policy
595+
* @param action The action of the permission policy
596+
* @param roles Any roles that the user is directly or indirectly attached to.
597+
* Used for filtering permission policies.
598+
* @returns True if the user is allowed based on the particular permission
599+
*/
578600
async enforce(
579601
entityRef: string,
580602
resourceType: string,
581603
action: string,
604+
roles: string[],
582605
): Promise<boolean> {
583-
const filter = [
584-
{
585-
ptype: 'p',
586-
v1: resourceType,
587-
v2: action,
588-
},
589-
{
590-
ptype: 'g',
591-
v0: entityRef,
592-
},
593-
];
606+
const filter = [];
607+
if (roles.length > 0) {
608+
roles.forEach(role => {
609+
filter.push({ ptype: 'p', v0: role, v1: resourceType, v2: action });
610+
});
611+
} else {
612+
filter.push({ ptype: 'p', v1: resourceType, v2: action });
613+
}
594614

595615
const adapt = this.enforcer.getAdapter();
596616
const roleManager = this.enforcer.getRoleManager();
597-
const tempEnforcer = await newEnforcer(newModelFromString(MODEL), adapt);
617+
const tempEnforcer = new Enforcer();
618+
await tempEnforcer.initWithModelAndAdapter(
619+
newModelFromString(MODEL),
620+
adapt,
621+
true,
622+
);
598623
tempEnforcer.setRoleManager(roleManager);
599624

600625
await tempEnforcer.loadFilteredPolicy(filter);

plugins/rbac-backend/src/service/permission-policy.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ export class RBACPermissionPolicy implements PermissionPolicy {
284284

285285
const userEntityRef = identityResp.identity.userEntityRef;
286286
const permissionName = request.permission.name;
287+
const roles = await this.enforcer.getRolesForUser(userEntityRef);
287288

288289
if (isResourcePermission(request.permission)) {
289290
const resourceType = request.permission.resourceType;
@@ -295,6 +296,7 @@ export class RBACPermissionPolicy implements PermissionPolicy {
295296
resourceType,
296297
request.permission.name,
297298
action,
299+
roles,
298300
);
299301
if (conditionResult) {
300302
return conditionResult;
@@ -311,10 +313,15 @@ export class RBACPermissionPolicy implements PermissionPolicy {
311313
// Let's set up higher priority for permission specified by name, than by resource type
312314
const obj = hasNamedPermission ? permissionName : resourceType;
313315

314-
status = await this.isAuthorized(userEntityRef, obj, action);
316+
status = await this.isAuthorized(userEntityRef, obj, action, roles);
315317
} else {
316318
// handle permission with 'basic' type
317-
status = await this.isAuthorized(userEntityRef, permissionName, action);
319+
status = await this.isAuthorized(
320+
userEntityRef,
321+
permissionName,
322+
action,
323+
roles,
324+
);
318325
}
319326

320327
const result = status ? AuthorizeResult.ALLOW : AuthorizeResult.DENY;
@@ -357,6 +364,7 @@ export class RBACPermissionPolicy implements PermissionPolicy {
357364
userIdentity: string,
358365
permission: string,
359366
action: string,
367+
roles: string[],
360368
): Promise<boolean> => {
361369
if (this.superUserList!.includes(userIdentity)) {
362370
return true;
@@ -373,17 +381,16 @@ export class RBACPermissionPolicy implements PermissionPolicy {
373381
);
374382
}
375383

376-
return await this.enforcer.enforce(userIdentity, permission, action);
384+
return await this.enforcer.enforce(userIdentity, permission, action, roles);
377385
};
378386

379387
private async handleConditions(
380388
userEntityRef: string,
381389
resourceType: string,
382390
permissionName: string,
383391
action: PermissionAction,
392+
roles: string[],
384393
): Promise<PolicyDecision | undefined> {
385-
const roles = await this.enforcer.getRolesForUser(userEntityRef);
386-
387394
const conditions: PermissionCriteria<
388395
PermissionCondition<string, PermissionRuleParams>
389396
>[] = [];

0 commit comments

Comments
 (0)