From 6b9a12048dab5dcdda9f96623e66957a11f811ed Mon Sep 17 00:00:00 2001 From: "j.dev" Date: Tue, 5 Dec 2023 15:22:26 -0800 Subject: [PATCH] feat: add wirte base filter in security layer --- core/modelService.ts | 14 ++++++----- core/services/privateCloudProject.ts | 14 ++++++++++- core/services/privateCloudRequest.ts | 14 ++++++++++- core/services/privateCloudRequestedProject.ts | 14 ++++++++++- core/services/publicCloudProject.ts | 14 ++++++++++- core/services/publicCloudRequest.ts | 14 ++++++++++- core/services/publicCloudRequestedProject.ts | 14 ++++++++++- core/services/user.ts | 14 ++++++++++- lib/prisma.ts | 25 ++++++++++++++----- 9 files changed, 118 insertions(+), 19 deletions(-) diff --git a/core/modelService.ts b/core/modelService.ts index 8b5c6512d..f99a614b9 100644 --- a/core/modelService.ts +++ b/core/modelService.ts @@ -2,7 +2,8 @@ import { Prisma, PrismaClient, $Enums } from '@prisma/client'; import { Session } from 'next-auth'; export abstract class ModelService { - abstract secureFilter(): Promise; + abstract readFilter(): Promise; + abstract writeFilter(): Promise; abstract decorate(doc: any): Promise; protected client!: PrismaClient; @@ -14,16 +15,17 @@ export abstract class ModelService { this.session = session; } - async genFilter(where: TFilter) { + async genFilter(where: TFilter, mode: 'read' | 'write') { let filter = where; - const secureFilter = await this.secureFilter(); + const baseFilterFn = mode === 'read' ? this.readFilter : this.writeFilter; + const baseFilter = await baseFilterFn(); - if (secureFilter) { + if (baseFilter) { if (where) { - filter = { AND: [secureFilter, where] } as TFilter; + filter = { AND: [baseFilter, where] } as TFilter; } else { - filter = secureFilter; + filter = baseFilter; } } diff --git a/core/services/privateCloudProject.ts b/core/services/privateCloudProject.ts index 85d7df15e..1fe7c6fff 100644 --- a/core/services/privateCloudProject.ts +++ b/core/services/privateCloudProject.ts @@ -3,7 +3,7 @@ import { Session } from 'next-auth'; import { ModelService } from '../modelService'; export class PrivateCloudProjectService extends ModelService { - async secureFilter() { + async readFilter() { let baseFilter!: Prisma.PrivateCloudProjectWhereInput; if (!this.session.isAdmin) { baseFilter = { @@ -20,6 +20,18 @@ export class PrivateCloudProjectService extends ModelService { - async secureFilter() { + async readFilter() { let baseFilter!: Prisma.PrivateCloudRequestWhereInput; if (!this.session.isAdmin) { const res = await this.client.privateCloudRequestedProject.findMany({ @@ -20,6 +20,18 @@ export class PrivateCloudRequestService extends ModelService { - async secureFilter() { + async readFilter() { let baseFilter!: Prisma.PrivateCloudRequestedProjectWhereInput; if (!this.session.isAdmin) { baseFilter = { @@ -20,6 +20,18 @@ export class PrivateCloudRequestedProjectService extends ModelService { - async secureFilter() { + async readFilter() { let baseFilter!: Prisma.PublicCloudProjectWhereInput; if (!this.session.isAdmin) { baseFilter = { @@ -20,6 +20,18 @@ export class PublicCloudProjectService extends ModelService { - async secureFilter() { + async readFilter() { let baseFilter!: Prisma.PublicCloudRequestWhereInput; if (!this.session.isAdmin) { const res = await this.client.publicCloudRequestedProject.findMany({ @@ -21,6 +21,18 @@ export class PublicCloudRequestService extends ModelService { - async secureFilter() { + async readFilter() { let baseFilter!: Prisma.PublicCloudRequestedProjectWhereInput; if (!this.session.isAdmin) { baseFilter = { @@ -20,6 +20,18 @@ export class PublicCloudRequestedProjectService extends ModelService { - async secureFilter() { + async readFilter() { let baseFilter!: Prisma.UserWhereInput; if (!this.session.isAdmin) { baseFilter = { @@ -18,6 +18,18 @@ export class UserService extends ModelService { return baseFilter; } + async writeFilter() { + let baseFilter!: Prisma.UserWhereInput; + if (!this.session.isAdmin) { + baseFilter = { + // Adding a dummy query to ensure no documents match + created: new Date(), + }; + } + + return baseFilter; + } + async decorate(doc: any) { doc._permissions = { view: true, diff --git a/lib/prisma.ts b/lib/prisma.ts index 6a8cfce9d..6219eb550 100644 --- a/lib/prisma.ts +++ b/lib/prisma.ts @@ -16,7 +16,16 @@ const prisma = async $allOperations({ model, operation, args, query }) { if ( !model || - !['findUnique', 'findUniqueOrThrow', 'findFirst', 'findFirstOrThrow', 'findMany'].includes(operation) + ![ + 'findUnique', + 'findUniqueOrThrow', + 'findFirst', + 'findFirstOrThrow', + 'findMany', + 'update', + 'upsert', + 'updateMany', + ].includes(operation) ) { return query(args); } @@ -24,16 +33,20 @@ const prisma = const { session, ...validArgs } = args; if (session === undefined) return query(validArgs); - const multi = ['findMany'].includes(operation); - if (!session?.userId) return multi ? [] : null; - const svc = getService(model, prisma, session); - if (!svc) return query(args); + if (!svc) return query(validArgs); + + const multi = ['findMany'].includes(operation); + const writeOp = ['update', 'upsert', 'updateMany'].includes(operation); const { where, ...otherArgs } = validArgs; - const filter = await svc.genFilter(where); + const filter = await svc.genFilter(where, writeOp ? 'write' : 'read'); const result = await query({ ...otherArgs, where: filter }); + if (operation === 'updateMany') { + return result; + } + const decorated = multi ? await Promise.all(result.map(svc.decorate)) : await svc.decorate(result); return decorated; },