From 06919a020509733190143cd83dadc327ff78f691 Mon Sep 17 00:00:00 2001 From: Methapon Metanipat Date: Tue, 10 Sep 2024 13:23:03 +0700 Subject: [PATCH] refactor: use helper function instead for easier fix --- src/controllers/01-branch-controller.ts | 47 +----- src/controllers/02-user-controller.ts | 186 +++------------------- src/controllers/03-customer-controller.ts | 57 +------ src/controllers/03-employee-controller.ts | 172 +++++--------------- 4 files changed, 77 insertions(+), 385 deletions(-) diff --git a/src/controllers/01-branch-controller.ts b/src/controllers/01-branch-controller.ts index b141239..6db3b60 100644 --- a/src/controllers/01-branch-controller.ts +++ b/src/controllers/01-branch-controller.ts @@ -21,7 +21,7 @@ import { RequestWithUser } from "../interfaces/user"; import minio from "../services/minio"; import { isSystem } from "../utils/keycloak"; import { deleteFile, deleteFolder, fileLocation, listFile } from "../utils/minio"; -import { createPermCheck } from "../services/permission"; +import { createPermCheck, createPermCondition } from "../services/permission"; import { filterStatus } from "../services/prisma"; if (!process.env.MINIO_BUCKET) { @@ -105,6 +105,7 @@ type BranchUpdate = { }[]; }; +const permissionCond = createPermCondition(globalAllow); const permissionCheck = createPermCheck(globalAllow); @Route("api/v1/branch") @@ -114,26 +115,9 @@ export class BranchController extends Controller { @Security("keycloak") async getStats(@Request() req: RequestWithUser, @Query() headOfficeId?: string) { const where = { - AND: isSystem(req.user) - ? undefined - : [ - { - user: { some: { userId: req.user.sub } }, - }, - { - branch: { some: { user: { some: { userId: req.user.sub } } } }, - }, - { - headOffice: globalAllow(req.user) - ? { branch: { some: { user: { some: { userId: req.user.sub } } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { user: { some: { userId: req.user.sub } } } - : undefined, - }, - ], + AND: { + OR: permissionCond(req.user), + }, }; const [hq, br, virtual] = await prisma.$transaction([ @@ -231,26 +215,7 @@ export class BranchController extends Controller { zipCode, headOfficeId: headOfficeId ?? (filter === "head" || tree ? null : undefined), NOT: { headOfficeId: filter === "sub" && !headOfficeId ? null : undefined }, - OR: isSystem(req.user) - ? undefined - : [ - { - user: { some: { userId: req.user.sub } }, - }, - { - branch: { some: { user: { some: { userId: req.user.sub } } } }, - }, - { - headOffice: globalAllow(req.user) - ? { branch: { some: { user: { some: { userId: req.user.sub } } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { user: { some: { userId: req.user.sub } } } - : undefined, - }, - ], + OR: permissionCond(req.user), }, OR: [ { nameEN: { contains: query } }, diff --git a/src/controllers/02-user-controller.ts b/src/controllers/02-user-controller.ts index f04b7ce..4403b01 100644 --- a/src/controllers/02-user-controller.ts +++ b/src/controllers/02-user-controller.ts @@ -31,6 +31,11 @@ import { import { isSystem } from "../utils/keycloak"; import { fileLocation, listFile } from "../utils/minio"; import { filterStatus } from "../services/prisma"; +import { + branchRelationPermInclude, + createPermCheck, + createPermCondition, +} from "../services/permission"; if (!process.env.MINIO_BUCKET) { throw Error("Require MinIO bucket."); @@ -138,7 +143,10 @@ type UserUpdate = { branchId?: string | string[]; }; -async function permissionCheck(user: RequestWithUser["user"], userId: string) { +const permissionCond = createPermCondition(globalAllow); +const permissionCheck = createPermCheck(globalAllow); + +async function permissionCheckGetUser(user: RequestWithUser["user"], userId: string) { const record = await prisma.user.findFirst({ include: { province: true, @@ -245,26 +253,7 @@ export class UserController extends Controller { : { some: { branch: { - OR: [ - { - user: { some: { userId: req.user.sub } }, - }, - { - branch: globalAllow(req.user) - ? { some: { user: { some: { userId: req.user.sub } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { branch: { some: { user: { some: { userId: req.user.sub } } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { user: { some: { userId: req.user.sub } } } - : undefined, - }, - ], + OR: permissionCond(req.user), }, }, }, @@ -313,26 +302,7 @@ export class UserController extends Controller { : { some: { branch: { - OR: [ - { - user: { some: { userId: req.user.sub } }, - }, - { - branch: globalAllow(req.user) - ? { some: { user: { some: { userId: req.user.sub } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { branch: { some: { user: { some: { userId: req.user.sub } } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { user: { some: { userId: req.user.sub } } } - : undefined, - }, - ], + OR: permissionCond(req.user), }, }, }, @@ -395,15 +365,7 @@ export class UserController extends Controller { prisma.district.findFirst({ where: { id: body.districtId ?? undefined } }), prisma.subDistrict.findFirst({ where: { id: body.subDistrictId ?? undefined } }), prisma.branch.findMany({ - include: { - headOffice: { - include: { - branch: { where: { user: { some: { userId: req.user.sub } } } }, - user: { where: { userId: req.user.sub } }, - }, - }, - user: { where: { userId: req.user.sub } }, - }, + include: branchRelationPermInclude(req.user), where: { id: { in: Array.isArray(body.branchId) ? body.branchId : [body.branchId] } }, }), prisma.user.findFirst({ @@ -439,28 +401,7 @@ export class UserController extends Controller { ); } - if (!isSystem(req.user)) { - branch.forEach((v) => { - if (!globalAllow(req.user) && v.user.length === 0) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } else { - if ( - (v.user.length === 0 && !v.headOffice) || - (v.headOffice && v.headOffice.user.length === 0 && v.headOffice.branch.length === 0) - ) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } - } - }); - } + await Promise.all([...branch.map(async (branch) => await permissionCheck(req.user, branch))]); if (user) { throw new HttpError(HttpStatus.BAD_REQUEST, "User exists.", "userExists"); @@ -576,15 +517,7 @@ export class UserController extends Controller { branch: { include: { branch: { - include: { - headOffice: { - include: { - branch: { where: { user: { some: { userId: req.user.sub } } } }, - user: { where: { userId: req.user.sub } }, - }, - }, - user: { where: { userId: req.user.sub } }, - }, + include: branchRelationPermInclude(req.user), }, }, }, @@ -592,15 +525,7 @@ export class UserController extends Controller { where: { id: userId }, }), prisma.branch.findMany({ - include: { - headOffice: { - include: { - branch: { where: { user: { some: { userId: req.user.sub } } } }, - user: { where: { userId: req.user.sub } }, - }, - }, - user: { where: { userId: req.user.sub } }, - }, + include: branchRelationPermInclude(req.user), where: { id: { in: Array.isArray(body.branchId) ? body.branchId : body.branchId ? [body.branchId] : [], @@ -636,48 +561,10 @@ export class UserController extends Controller { "minimumBranchNotMet", ); } - if (!isSystem(req.user)) { - user.branch.forEach(({ branch: v }) => { - if (!globalAllow(req.user) && v.user.length === 0) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } else { - if ( - (v.user.length === 0 && !v.headOffice) || - (v.headOffice && v.headOffice.user.length === 0 && v.headOffice.branch.length === 0) - ) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } - } - }); - branch.forEach((v) => { - if (!globalAllow(req.user) && v.user.length === 0) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } else { - if ( - (v.user.length === 0 && !v.headOffice) || - (v.headOffice && v.headOffice.user.length === 0 && v.headOffice.branch.length === 0) - ) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } - } - }); - } + await Promise.all([ + ...user.branch.map(async ({ branch }) => await permissionCheck(req.user, branch)), + ...branch.map(async (branch) => await permissionCheck(req.user, branch)), + ]); const setRoleIndex = MANAGE_ROLES.findIndex((v) => v === body.userRole); const userRoleIndex = MANAGE_ROLES.reduce( (a, c, i) => (req.user.roles?.includes(c) ? i : a), @@ -839,30 +726,9 @@ export class UserController extends Controller { throw new HttpError(HttpStatus.NOT_FOUND, "User cannot be found.", "userNotFound"); } - if (!isSystem(req.user)) { - record.branch.forEach(({ branch }) => { - if (!globalAllow(req.user) && branch.user.length === 0) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } else { - if ( - (branch.user.length === 0 && !branch.headOffice) || - (branch.headOffice && - branch.headOffice.user.length === 0 && - branch.headOffice.branch.length === 0) - ) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } - } - }); - } + await Promise.all([ + ...record.branch.map(async ({ branch }) => await permissionCheck(req.user, branch)), + ]); if (record.status !== Status.CREATED) { throw new HttpError(HttpStatus.FORBIDDEN, "User is in used.", "userInUsed"); @@ -916,7 +782,7 @@ export class UserProfileController extends Controller { @Put("{name}") @Security("keycloak") async putImage(@Request() req: RequestWithUser, @Path() userId: string, @Path() name: string) { - await permissionCheck(req.user, userId); + await permissionCheckGetUser(req.user, userId); return req.res?.redirect( await minio.presignedPutObject( MINIO_BUCKET, @@ -929,7 +795,7 @@ export class UserProfileController extends Controller { @Delete("{name}") @Security("keycloak") async deleteImage(@Request() req: RequestWithUser, @Path() userId: string, @Path() name: string) { - await permissionCheck(req.user, userId); + await permissionCheckGetUser(req.user, userId); await minio.removeObject(MINIO_BUCKET, fileLocation.user.profile(userId, name), { forceDelete: true, }); @@ -970,7 +836,7 @@ export class UserAttachmentController extends Controller { @Path() userId: string, @Body() payload: { file: string[] }, ) { - await permissionCheck(req.user, userId); + await permissionCheckGetUser(req.user, userId); return await Promise.all( payload.file.map(async (v) => ({ @@ -991,7 +857,7 @@ export class UserAttachmentController extends Controller { @Path() userId: string, @Body() payload: { file: string[] }, ) { - await permissionCheck(req.user, userId); + await permissionCheckGetUser(req.user, userId); await Promise.all( payload.file.map(async (v) => { await minio.removeObject(MINIO_BUCKET, fileLocation.user.attachment(userId, v), { diff --git a/src/controllers/03-customer-controller.ts b/src/controllers/03-customer-controller.ts index a92c677..1ee60d2 100644 --- a/src/controllers/03-customer-controller.ts +++ b/src/controllers/03-customer-controller.ts @@ -19,7 +19,11 @@ import minio, { deleteFolder } from "../services/minio"; import HttpStatus from "../interfaces/http-status"; import HttpError from "../interfaces/http-error"; import { isSystem } from "../utils/keycloak"; -import { branchRelationPermInclude, createPermCheck } from "../services/permission"; +import { + branchRelationPermInclude, + createPermCheck, + createPermCondition, +} from "../services/permission"; import { filterStatus } from "../services/prisma"; import { fileLocation, listFile } from "../utils/minio"; @@ -43,6 +47,7 @@ function globalAllow(user: RequestWithUser["user"]) { return allowList.some((v) => user.roles?.includes(v)); } +const permissionCond = createPermCondition(globalAllow); const permissionCheck = createPermCheck(globalAllow); export type CustomerCreate = { @@ -85,30 +90,7 @@ export class CustomerController extends Controller { by: "customerType", _count: true, where: { - registeredBranch: isSystem(req.user) - ? undefined - : { - OR: [ - { - user: { some: { userId: req.user.sub } }, - }, - { - branch: globalAllow(req.user) - ? { some: { user: { some: { userId: req.user.sub } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { branch: { some: { user: { some: { userId: req.user.sub } } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { user: { some: { userId: req.user.sub } } } - : undefined, - }, - ], - }, + registeredBranch: isSystem(req.user) ? undefined : { OR: permissionCond(req.user) }, }, }); @@ -144,30 +126,7 @@ export class CustomerController extends Controller { AND: { customerType, ...filterStatus(status), - registeredBranch: isSystem(req.user) - ? undefined - : { - OR: [ - { - user: { some: { userId: req.user.sub } }, - }, - { - branch: globalAllow(req.user) - ? { some: { user: { some: { userId: req.user.sub } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { branch: { some: { user: { some: { userId: req.user.sub } } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { user: { some: { userId: req.user.sub } } } - : undefined, - }, - ], - }, + registeredBranch: isSystem(req.user) ? undefined : { OR: permissionCond(req.user) }, }, } satisfies Prisma.CustomerWhereInput; diff --git a/src/controllers/03-employee-controller.ts b/src/controllers/03-employee-controller.ts index 307249a..46f3fd5 100644 --- a/src/controllers/03-employee-controller.ts +++ b/src/controllers/03-employee-controller.ts @@ -20,6 +20,11 @@ import HttpError from "../interfaces/http-error"; import minio, { presignedGetObjectIfExist } from "../services/minio"; import { isSystem } from "../utils/keycloak"; import { filterStatus } from "../services/prisma"; +import { + branchRelationPermInclude, + createPermCheck, + createPermCondition, +} from "../services/permission"; if (!process.env.MINIO_BUCKET) { throw Error("Require MinIO bucket."); @@ -40,6 +45,9 @@ function globalAllow(user: RequestWithUser["user"]) { return allowList.some((v) => user.roles?.includes(v)); } +const permissionCond = createPermCondition(globalAllow); +const permissionCheck = createPermCheck(globalAllow); + function imageLocation(id: string) { return `employee/${id}/profile-image`; } @@ -258,26 +266,7 @@ export class EmployeeController extends Controller { customer: isSystem(req.user) ? undefined : { - registeredBranch: { - OR: [ - { - user: { some: { userId: req.user.sub } }, - }, - { - branch: { some: { user: { some: { userId: req.user.sub } } } }, - }, - { - headOffice: globalAllow(req.user) - ? { branch: { some: { user: { some: { userId: req.user.sub } } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { user: { some: { userId: req.user.sub } } } - : undefined, - }, - ], - }, + registeredBranch: { OR: permissionCond(req.user) }, }, }, }, @@ -319,24 +308,7 @@ export class EmployeeController extends Controller { ? undefined : { registeredBranch: { - OR: [ - { - user: { some: { userId: req.user.sub } }, - }, - { - branch: { some: { user: { some: { userId: req.user.sub } } } }, - }, - { - headOffice: globalAllow(req.user) - ? { branch: { some: { user: { some: { userId: req.user.sub } } } } } - : undefined, - }, - { - headOffice: globalAllow(req.user) - ? { user: { some: { userId: req.user.sub } } } - : undefined, - }, - ], + OR: permissionCond(req.user), }, }, }, @@ -416,15 +388,7 @@ export class EmployeeController extends Controller { customer: { include: { registeredBranch: { - include: { - headOffice: { - include: { - branch: { where: { user: { some: { userId: req.user.sub } } } }, - user: { where: { userId: req.user.sub } }, - }, - }, - user: { where: { userId: req.user.sub } }, - }, + include: branchRelationPermInclude(req.user), }, }, }, @@ -455,29 +419,8 @@ export class EmployeeController extends Controller { "Customer Branch cannot be found.", "relationCustomerBranchNotFound", ); - if (!isSystem(req.user)) { - const _record = customerBranch.customer.registeredBranch; - if (!globalAllow(req.user) && _record?.user.length === 0) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } else { - if ( - (_record?.user.length === 0 && !_record.headOffice) || - (_record?.headOffice && - _record.headOffice.user.length === 0 && - _record.headOffice.branch.length === 0) - ) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } - } - } + + await permissionCheck(req.user, customerBranch.customer.registeredBranch); const { provinceId, @@ -607,15 +550,7 @@ export class EmployeeController extends Controller { customer: { include: { registeredBranch: { - include: { - headOffice: { - include: { - branch: { where: { user: { some: { userId: req.user.sub } } } }, - user: { where: { userId: req.user.sub } }, - }, - }, - user: { where: { userId: req.user.sub } }, - }, + include: branchRelationPermInclude(req.user), }, }, }, @@ -629,15 +564,7 @@ export class EmployeeController extends Controller { customer: { include: { registeredBranch: { - include: { - headOffice: { - include: { - branch: { where: { user: { some: { userId: req.user.sub } } } }, - user: { where: { userId: req.user.sub } }, - }, - }, - user: { where: { userId: req.user.sub } }, - }, + include: branchRelationPermInclude(req.user), }, }, }, @@ -674,51 +601,9 @@ export class EmployeeController extends Controller { throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound"); } - if (!isSystem(req.user)) { - const _record = employee.customerBranch.customer.registeredBranch; - if (!globalAllow(req.user) && _record?.user.length === 0) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } else { - if ( - (_record?.user.length === 0 && !_record.headOffice) || - (_record?.headOffice && - _record.headOffice.user.length === 0 && - _record.headOffice.branch.length === 0) - ) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } - } - } - if (!isSystem(req.user) && body.customerBranchId && customerBranch) { - const _record = customerBranch.customer.registeredBranch; - if (!globalAllow(req.user) && _record?.user.length === 0) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } else { - if ( - (_record?.user.length === 0 && !_record.headOffice) || - (_record?.headOffice && - _record.headOffice.user.length === 0 && - _record.headOffice.branch.length === 0) - ) { - throw new HttpError( - HttpStatus.FORBIDDEN, - "You do not have permission to perform this action.", - "noPermission", - ); - } - } + await permissionCheck(req.user, employee.customerBranch.customer.registeredBranch); + if (body.customerBranchId && customerBranch) { + await permissionCheck(req.user, customerBranch.customer.registeredBranch); } const { @@ -897,13 +782,30 @@ export class EmployeeController extends Controller { @Delete("{employeeId}") @Security("keycloak", MANAGE_ROLES) - async delete(@Path() employeeId: string) { - const record = await prisma.employee.findFirst({ where: { id: employeeId } }); + async delete(@Request() req: RequestWithUser, @Path() employeeId: string) { + const record = await prisma.employee.findFirst({ + where: { id: employeeId }, + include: { + customerBranch: { + include: { + customer: { + include: { + registeredBranch: { + include: branchRelationPermInclude(req.user), + }, + }, + }, + }, + }, + }, + }); if (!record) { throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound"); } + await permissionCheck(req.user, record.customerBranch.customer.registeredBranch); + if (record.status !== Status.CREATED) { throw new HttpError(HttpStatus.FORBIDDEN, "Employee is in used.", "employeeInUsed"); }