From 667b350b7d81b92264d06814f5bfc5b59ef8c296 Mon Sep 17 00:00:00 2001 From: Kenneth Trecy Tobias <19201.tobias.kennethtrecy.c@gmail.com> Date: Sun, 11 Sep 2022 15:48:31 +0800 Subject: [PATCH] intrn(policy): allow setting permission combinations that can bypass --- .../policies/belongs_to_current_user.spec.ts | 10 ++-- server/policies/belongs_to_current_user.ts | 51 +++++++++++++++++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/server/policies/belongs_to_current_user.spec.ts b/server/policies/belongs_to_current_user.spec.ts index 027cb26db..685c3b42d 100644 --- a/server/policies/belongs_to_current_user.spec.ts +++ b/server/policies/belongs_to_current_user.spec.ts @@ -40,10 +40,10 @@ describe("Policy: Belongs to current user", () => { const otherUser = await userFactory.attach(adminRole).serializedOne(true) const pageGuard = new BelongsToCurrentUserPolicy(UserManager, { "bypassNecessarilyWith": { - "group": permissions, "combinations": [ [ "readDepartmentScope" ] - ] + ], + "group": permissions } }) requester.customizeRequest({ @@ -64,12 +64,12 @@ describe("Policy: Belongs to current user", () => { const role = await new RoleFactory().userFlags(permissions.generateMask("view")).insertOne() const user = await userFactory.attach(role).serializedOne(true) const otherUser = await userFactory.serializedOne(true) - const pageGuard = new BelongsToCurrentUserPolicy(UserManager,, { + const pageGuard = new BelongsToCurrentUserPolicy(UserManager, { "bypassNecessarilyWith": { - "group": permissions, "combinations": [ [ "readDepartmentScope" ] - ] + ], + "group": permissions } }) requester.customizeRequest({ diff --git a/server/policies/belongs_to_current_user.ts b/server/policies/belongs_to_current_user.ts index e3e15f43c..1d11918eb 100644 --- a/server/policies/belongs_to_current_user.ts +++ b/server/policies/belongs_to_current_user.ts @@ -1,9 +1,11 @@ +import type { GeneralObject } from "$/types/general" import type { BaseManagerClass } from "!/types/independent" import type { AuthenticatedRequest } from "!/types/dependent" import type { DeserializedUserProfile } from "$/types/documents/user" -import isUndefined from "$/type_guards/is_undefined" import deserialize from "$/object/deserialize" +import PermissionGroup from "$/permissions/base" +import isUndefined from "$/type_guards/is_undefined" import AuthorizationError from "$!/errors/authorization" import AuthenticationBasedPolicy from "!/policies/authentication-based" @@ -15,16 +17,41 @@ import AuthenticationBasedPolicy from "!/policies/authentication-based" * Hint: If the resource can be processed by other users through advance permissions, do not use * the policy. */ -export default class extends AuthenticationBasedPolicy { +export default class < + T extends GeneralObject, + U, + V extends AuthenticatedRequest = AuthenticatedRequest +> extends AuthenticationBasedPolicy { private readonly Class: BaseManagerClass + private permissionGroup: PermissionGroup|undefined + private permissionCombinations: U[][]|undefined + private checkOthers: (request: V) => Promise - constructor(managerClass: BaseManagerClass) { + constructor( + managerClass: BaseManagerClass, + { + bypassNecessarilyWith, + checkOthers = (): Promise => { + const promise = Promise.resolve() + return promise + } + }: Partial<{ + bypassNecessarilyWith: { + group: PermissionGroup, + combinations: U[][] + }, + checkOthers: (request: V) => Promise + }> = {} + ) { super(true) this.Class = managerClass + this.permissionGroup = bypassNecessarilyWith?.group + this.permissionCombinations = bypassNecessarilyWith?.combinations + this.checkOthers = checkOthers } - async authorize(request: AuthenticatedRequest): Promise { + async authorize(request: V): Promise { await super.authorize(request) if (isUndefined(request.params.id)) { @@ -38,7 +65,21 @@ export default class extends AuthenticationBasedPolicy { Number(user.data.id), manager.modelChainToUser )) { - throw new AuthorizationError("Only the owner of the resource can do the operation.") + let isPermitted = false + + if (!isUndefined(this.permissionGroup) && !isUndefined(this.permissionCombinations)) { + const roles = user.data.roles.data as unknown as T[] + isPermitted = this.permissionGroup.hasOneRoleAllowed( + roles, + this.permissionCombinations + ) + } + + if (!isPermitted) { + throw new AuthorizationError("Only the owner of the resource can do the operation.") + } } + + await this.checkOthers(request) } }