diff --git a/apps/api/src/approval/controller/approval.controller.spec.ts b/apps/api/src/approval/controller/approval.controller.spec.ts index ff475fd8..1b9025d4 100644 --- a/apps/api/src/approval/controller/approval.controller.spec.ts +++ b/apps/api/src/approval/controller/approval.controller.spec.ts @@ -14,13 +14,15 @@ import { REDIS_CLIENT } from '../../provider/redis.provider' import { RedisClientType } from 'redis' import { mockDeep } from 'jest-mock-extended' import { ProviderModule } from '../../provider/provider.module' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('ApprovalController', () => { let controller: ApprovalController beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - imports: [ProviderModule], + imports: [ProviderModule, CommonModule], controllers: [ApprovalController], providers: [ ApprovalService, @@ -34,7 +36,8 @@ describe('ApprovalController', () => { { provide: MAIL_SERVICE, useClass: MockMailService - } + }, + AuthorityCheckerService ] }) .overrideProvider(REDIS_CLIENT) diff --git a/apps/api/src/approval/service/approval.service.spec.ts b/apps/api/src/approval/service/approval.service.spec.ts index cb600041..6406b824 100644 --- a/apps/api/src/approval/service/approval.service.spec.ts +++ b/apps/api/src/approval/service/approval.service.spec.ts @@ -13,13 +13,15 @@ import { REDIS_CLIENT } from '../../provider/redis.provider' import { RedisClientType } from 'redis' import { mockDeep } from 'jest-mock-extended' import { ProviderModule } from '../../provider/provider.module' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('ApprovalService', () => { let service: ApprovalService beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - imports: [ProviderModule], + imports: [ProviderModule, CommonModule], providers: [ ApprovalService, PrismaService, @@ -32,7 +34,8 @@ describe('ApprovalService', () => { { provide: MAIL_SERVICE, useClass: MockMailService - } + }, + AuthorityCheckerService ] }) .overrideProvider(REDIS_CLIENT) diff --git a/apps/api/src/approval/service/approval.service.ts b/apps/api/src/approval/service/approval.service.ts index dfd29d83..24bc461f 100644 --- a/apps/api/src/approval/service/approval.service.ts +++ b/apps/api/src/approval/service/approval.service.ts @@ -31,7 +31,7 @@ import { UpdateVariableMetadata, UpdateWorkspaceMetadata } from '../approval.types' -import getWorkspaceWithAuthority from '../../common/get-workspace-with-authority' +import { AuthorityCheckerService } from '../../common/authority-checker.service' @Injectable() export class ApprovalService { @@ -43,7 +43,8 @@ export class ApprovalService { private readonly projectService: ProjectService, private readonly environmentService: EnvironmentService, private readonly secretService: SecretService, - private readonly variableService: VariableService + private readonly variableService: VariableService, + private readonly authorityCheckerService: AuthorityCheckerService ) {} async updateApproval(user: User, reason: string, approvalId: Approval['id']) { @@ -453,12 +454,12 @@ export class ApprovalService { actions: ApprovalAction[], statuses: ApprovalStatus[] ) { - await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.MANAGE_APPROVALS, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.MANAGE_APPROVALS, + prisma: this.prisma + }) return await this.prisma.approval.findMany({ where: { @@ -493,12 +494,12 @@ export class ApprovalService { actions: ApprovalAction[], statuses: ApprovalStatus[] ) { - await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.READ_WORKSPACE, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.READ_WORKSPACE, + prisma: this.prisma + }) return this.prisma.approval.findMany({ where: { diff --git a/apps/api/src/common/authority-checker.service.ts b/apps/api/src/common/authority-checker.service.ts new file mode 100644 index 00000000..ed93abc2 --- /dev/null +++ b/apps/api/src/common/authority-checker.service.ts @@ -0,0 +1,396 @@ +import { PrismaClient, Authority, Workspace } from '@prisma/client' +import { VariableWithProjectAndVersion } from '../variable/variable.types' +import { + BadRequestException, + Injectable, + NotFoundException, + UnauthorizedException +} from '@nestjs/common' +import getCollectiveProjectAuthorities from './get-collective-project-authorities' +import getCollectiveWorkspaceAuthorities from './get-collective-workspace-authorities' +import { EnvironmentWithProject } from '../environment/environment.types' +import { ProjectWithSecrets } from '../project/project.types' +import { SecretWithProjectAndVersion } from '../secret/secret.types' +import { CustomLoggerService } from './logger.service' + +export interface AuthorityInput { + userId: string + entity: { + id?: string + name?: string + } + authority: Authority + prisma: PrismaClient +} + +@Injectable() +export class AuthorityCheckerService { + constructor(private customLoggerService: CustomLoggerService) {} + + public async checkAuthorityOverWorkspace( + input: AuthorityInput + ): Promise { + const { userId, entity, authority, prisma } = input + + let workspace: Workspace + + try { + if (entity?.id) { + workspace = await prisma.workspace.findUnique({ + where: { + id: entity.id + } + }) + } else { + workspace = await prisma.workspace.findFirst({ + where: { + name: entity?.name, + members: { some: { userId: userId } } + } + }) + } + } catch (error) { + this.customLoggerService.error(error) + throw new Error(error) + } + + // Check if the workspace exists or not + if (!workspace) { + throw new NotFoundException(`Workspace with id ${entity.id} not found`) + } + + const permittedAuthorities = await getCollectiveWorkspaceAuthorities( + entity.id, + userId, + prisma + ) + + // Check if the user has the authority to perform the action + if ( + !permittedAuthorities.has(authority) && + !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) + ) { + throw new UnauthorizedException( + `User ${userId} does not have the required authorities to perform the action` + ) + } + + return workspace + } + + public async checkAuthorityOverProject( + input: AuthorityInput + ): Promise { + const { userId, entity, authority, prisma } = input + + // Fetch the project + let project: ProjectWithSecrets + + try { + if (entity?.id) { + project = await prisma.project.findUnique({ + where: { + id: entity.id + }, + include: { + secrets: true + } + }) + } else { + project = await prisma.project.findFirst({ + where: { + name: entity?.name, + workspace: { members: { some: { userId: userId } } } + }, + include: { + secrets: true + } + }) + } + } catch (error) { + this.customLoggerService.error(error) + throw new Error(error) + } + + // If the project is not found, throw an error + if (!project) { + throw new NotFoundException(`Project with id ${entity?.id} not found`) + } + + // Get the authorities of the user in the workspace with the project + const permittedAuthorities = await getCollectiveProjectAuthorities( + userId, + project, + prisma + ) + + // If the user does not have the required authority, or is not a workspace admin, throw an error + if ( + !permittedAuthorities.has(authority) && + !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) + ) { + throw new UnauthorizedException( + `User with id ${userId} does not have the authority in the project with id ${entity?.id}` + ) + } + + // If the project is pending creation, only the user who created the project, a workspace admin or + // a user with the MANAGE_APPROVALS authority can fetch the project + if ( + project.pendingCreation && + !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) && + !permittedAuthorities.has(Authority.MANAGE_APPROVALS) && + project.lastUpdatedById !== userId + ) { + throw new BadRequestException( + `The project with id ${entity?.id} is pending creation and cannot be fetched by the user with id ${userId}` + ) + } + + return project + } + + public async checkAuthorityOverEnvironment( + input: AuthorityInput + ): Promise { + const { userId, entity, authority, prisma } = input + + // Fetch the environment + let environment: EnvironmentWithProject + + try { + if (entity?.id) { + environment = await prisma.environment.findUnique({ + where: { + id: entity.id + }, + include: { + project: true + } + }) + } else { + environment = await prisma.environment.findFirst({ + where: { + name: entity?.name, + project: { workspace: { members: { some: { userId: userId } } } } + }, + include: { + project: true + } + }) + } + } catch (error) { + this.customLoggerService.error(error) + throw new Error(error) + } + + if (!environment) { + throw new NotFoundException(`Environment with id ${entity.id} not found`) + } + + const permittedAuthorities = await getCollectiveProjectAuthorities( + userId, + environment.project, + prisma + ) + + // Check if the user has the required authorities + if ( + !permittedAuthorities.has(authority) && + !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) + ) { + throw new UnauthorizedException( + `User ${userId} does not have the required authorities` + ) + } + + // If the environment is pending creation, only the user who created the environment, a workspace admin or + // a user with the MANAGE_APPROVALS authority can fetch the environment + if ( + environment.pendingCreation && + !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) && + !permittedAuthorities.has(Authority.MANAGE_APPROVALS) && + environment.lastUpdatedById !== userId + ) { + throw new BadRequestException( + `The environment with id ${entity.id} is pending creation and cannot be fetched by the user with id ${userId}` + ) + } + + return environment + } + + public async checkAuthorityOverVariable( + input: AuthorityInput + ): Promise { + const { userId, entity, authority, prisma } = input + + // Fetch the variable + let variable: VariableWithProjectAndVersion + + try { + if (entity?.id) { + variable = await prisma.variable.findUnique({ + where: { + id: entity.id + }, + include: { + versions: true, + project: true, + environment: { + select: { + id: true, + name: true + } + } + } + }) + } else { + variable = await prisma.variable.findFirst({ + where: { + name: entity?.name, + environment: { + project: { workspace: { members: { some: { userId: userId } } } } + } + }, + include: { + versions: true, + project: true, + environment: { + select: { + id: true, + name: true + } + } + } + }) + } + } catch (error) { + this.customLoggerService.error(error) + throw new Error(error) + } + + if (!variable) { + throw new NotFoundException(`Variable with id ${entity.id} not found`) + } + + // Check if the user has the project in their workspace role list + const permittedAuthorities = await getCollectiveProjectAuthorities( + userId, + variable.project, + prisma + ) + + // Check if the user has the required authorities + if ( + !permittedAuthorities.has(authority) && + !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) + ) { + throw new UnauthorizedException( + `User ${userId} does not have the required authorities` + ) + } + + // If the variable is pending creation, only the user who created the variable, a workspace admin or + // a user with the MANAGE_APPROVALS authority can fetch the variable + if ( + variable.pendingCreation && + !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) && + !permittedAuthorities.has(Authority.MANAGE_APPROVALS) && + variable.lastUpdatedById !== userId + ) { + throw new BadRequestException( + `The variable with id ${entity.id} is pending creation and cannot be fetched by the user with id ${userId}` + ) + } + + return variable + } + + public async checkAuthorityOverSecret( + input: AuthorityInput + ): Promise { + const { userId, entity, authority, prisma } = input + + // Fetch the secret + let secret: SecretWithProjectAndVersion + + try { + if (entity?.id) { + secret = await prisma.secret.findUnique({ + where: { + id: entity.id + }, + include: { + versions: true, + project: true, + environment: { + select: { + id: true, + name: true + } + } + } + }) + } else { + secret = await prisma.secret.findFirst({ + where: { + name: entity?.name, + environment: { + project: { workspace: { members: { some: { userId: userId } } } } + } + }, + include: { + versions: true, + project: true, + environment: { + select: { + id: true, + name: true + } + } + } + }) + } + } catch (error) { + this.customLoggerService.error(error) + throw new Error(error) + } + + if (!secret) { + throw new NotFoundException(`Secret with id ${entity.id} not found`) + } + + // Check if the user has the project in their workspace role list + const permittedAuthorities = await getCollectiveProjectAuthorities( + userId, + secret.project, + prisma + ) + + // Check if the user has the required authorities + if ( + !permittedAuthorities.has(authority) && + !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) + ) { + throw new UnauthorizedException( + `User ${userId} does not have the required authorities` + ) + } + + // If the secret is pending creation, only the user who created the secret, a workspace admin or + // a user with the MANAGE_APPROVALS authority can fetch the secret + if ( + secret.pendingCreation && + !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) && + !permittedAuthorities.has(Authority.MANAGE_APPROVALS) && + secret.lastUpdatedById !== userId + ) { + throw new BadRequestException( + `The secret with id ${entity.id} is pending creation and cannot be fetched by the user with id ${userId}` + ) + } + + return secret + } +} diff --git a/apps/api/src/common/common.module.ts b/apps/api/src/common/common.module.ts index 01be450f..29c83e0f 100644 --- a/apps/api/src/common/common.module.ts +++ b/apps/api/src/common/common.module.ts @@ -1,8 +1,11 @@ import { Global, Module } from '@nestjs/common' +import { AuthorityCheckerService } from './authority-checker.service' +import { CustomLoggerService } from './logger.service' @Global() @Module({ - providers: [], - exports: [] + imports: [], + providers: [AuthorityCheckerService, CustomLoggerService], + exports: [AuthorityCheckerService, CustomLoggerService] }) export class CommonModule {} diff --git a/apps/api/src/common/get-environment-with-authority.ts b/apps/api/src/common/get-environment-with-authority.ts deleted file mode 100644 index 93cdb3ac..00000000 --- a/apps/api/src/common/get-environment-with-authority.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - BadRequestException, - NotFoundException, - UnauthorizedException -} from '@nestjs/common' -import { Authority, Environment, PrismaClient, User } from '@prisma/client' -import getCollectiveProjectAuthorities from './get-collective-project-authorities' -import { EnvironmentWithProject } from 'src/environment/environment.types' - -export default async function getEnvironmentWithAuthority( - userId: User['id'], - environmentId: Environment['id'], - authority: Authority, - prisma: PrismaClient -): Promise { - // Fetch the environment - let environment: EnvironmentWithProject - - try { - environment = await prisma.environment.findUnique({ - where: { - id: environmentId - }, - include: { - project: true - } - }) - } catch (e) { - /* empty */ - } - - if (!environment) { - throw new NotFoundException( - `Environment with id ${environmentId} not found` - ) - } - - const permittedAuthorities = await getCollectiveProjectAuthorities( - userId, - environment.project, - prisma - ) - - // Check if the user has the required authorities - if ( - !permittedAuthorities.has(authority) && - !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) - ) { - throw new UnauthorizedException( - `User ${userId} does not have the required authorities` - ) - } - - // If the environment is pending creation, only the user who created the environment, a workspace admin or - // a user with the MANAGE_APPROVALS authority can fetch the environment - if ( - environment.pendingCreation && - !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) && - !permittedAuthorities.has(Authority.MANAGE_APPROVALS) && - environment.lastUpdatedById !== userId - ) { - throw new BadRequestException( - `The environment with id ${environmentId} is pending creation and cannot be fetched by the user with id ${userId}` - ) - } - - return environment -} diff --git a/apps/api/src/common/get-project-with-authority.ts b/apps/api/src/common/get-project-with-authority.ts deleted file mode 100644 index 9b291def..00000000 --- a/apps/api/src/common/get-project-with-authority.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { - BadRequestException, - NotFoundException, - UnauthorizedException -} from '@nestjs/common' -import { Authority, PrismaClient, Project, User } from '@prisma/client' -import getCollectiveProjectAuthorities from './get-collective-project-authorities' -import { ProjectWithSecrets } from '../project/project.types' - -export default async function getProjectWithAuthority( - userId: User['id'], - projectId: Project['id'], - authority: Authority, - prisma: PrismaClient -): Promise { - // Fetch the project - let project: ProjectWithSecrets - - // Fetch the project - try { - project = await prisma.project.findUnique({ - where: { - id: projectId - }, - include: { - secrets: true - } - }) - } catch (error) { - /* empty */ - } - - // If the project is not found, throw an error - if (!project) { - throw new NotFoundException(`Project with id ${projectId} not found`) - } - - // Get the authorities of the user in the workspace with the project - const permittedAuthorities = await getCollectiveProjectAuthorities( - userId, - project, - prisma - ) - - // If the user does not have the required authority, or is not a workspace admin, throw an error - if ( - !permittedAuthorities.has(authority) && - !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) - ) { - throw new UnauthorizedException( - `User with id ${userId} does not have the authority in the project with id ${projectId}` - ) - } - - // If the project is pending creation, only the user who created the project, a workspace admin or - // a user with the MANAGE_APPROVALS authority can fetch the project - if ( - project.pendingCreation && - !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) && - !permittedAuthorities.has(Authority.MANAGE_APPROVALS) && - project.lastUpdatedById !== userId - ) { - throw new BadRequestException( - `The project with id ${projectId} is pending creation and cannot be fetched by the user with id ${userId}` - ) - } - - return project -} diff --git a/apps/api/src/common/get-secret-with-authority.ts b/apps/api/src/common/get-secret-with-authority.ts deleted file mode 100644 index b964ad0d..00000000 --- a/apps/api/src/common/get-secret-with-authority.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Authority, PrismaClient, Secret, User } from '@prisma/client' -import { SecretWithProjectAndVersion } from '../secret/secret.types' -import getCollectiveProjectAuthorities from './get-collective-project-authorities' -import { - BadRequestException, - NotFoundException, - UnauthorizedException -} from '@nestjs/common' - -export default async function getSecretWithAuthority( - userId: User['id'], - secretId: Secret['id'], - authority: Authority, - prisma: PrismaClient -): Promise { - // Fetch the secret - let secret: SecretWithProjectAndVersion - - try { - secret = await prisma.secret.findUnique({ - where: { - id: secretId - }, - include: { - versions: true, - project: true, - environment: { - select: { - id: true, - name: true - } - } - } - }) - } catch (error) { - /* empty */ - } - - if (!secret) { - throw new NotFoundException(`Secret with id ${secretId} not found`) - } - - // Check if the user has the project in their workspace role list - const permittedAuthorities = await getCollectiveProjectAuthorities( - userId, - secret.project, - prisma - ) - - // Check if the user has the required authorities - if ( - !permittedAuthorities.has(authority) && - !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) - ) { - throw new UnauthorizedException( - `User ${userId} does not have the required authorities` - ) - } - - // If the secret is pending creation, only the user who created the secret, a workspace admin or - // a user with the MANAGE_APPROVALS authority can fetch the secret - if ( - secret.pendingCreation && - !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) && - !permittedAuthorities.has(Authority.MANAGE_APPROVALS) && - secret.lastUpdatedById !== userId - ) { - throw new BadRequestException( - `The secret with id ${secretId} is pending creation and cannot be fetched by the user with id ${userId}` - ) - } - - return secret -} diff --git a/apps/api/src/common/get-variable-with-authority.ts b/apps/api/src/common/get-variable-with-authority.ts deleted file mode 100644 index 90dac1b1..00000000 --- a/apps/api/src/common/get-variable-with-authority.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Authority, PrismaClient, User, Variable } from '@prisma/client' -import getCollectiveProjectAuthorities from './get-collective-project-authorities' -import { - BadRequestException, - NotFoundException, - UnauthorizedException -} from '@nestjs/common' -import { VariableWithProjectAndVersion } from '../variable/variable.types' - -export default async function getVariableWithAuthority( - userId: User['id'], - variableId: Variable['id'], - authority: Authority, - prisma: PrismaClient -): Promise { - // Fetch the variable - let variable: VariableWithProjectAndVersion - - try { - variable = await prisma.variable.findUnique({ - where: { - id: variableId - }, - include: { - versions: true, - project: true, - environment: { - select: { - id: true, - name: true - } - } - } - }) - } catch (error) { - /* empty */ - } - - if (!variable) { - throw new NotFoundException(`Variable with id ${variableId} not found`) - } - - // Check if the user has the project in their workspace role list - const permittedAuthorities = await getCollectiveProjectAuthorities( - userId, - variable.project, - prisma - ) - - // Check if the user has the required authorities - if ( - !permittedAuthorities.has(authority) && - !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) - ) { - throw new UnauthorizedException( - `User ${userId} does not have the required authorities` - ) - } - - // If the variable is pending creation, only the user who created the variable, a workspace admin or - // a user with the MANAGE_APPROVALS authority can fetch the variable - if ( - variable.pendingCreation && - !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) && - !permittedAuthorities.has(Authority.MANAGE_APPROVALS) && - variable.lastUpdatedById !== userId - ) { - throw new BadRequestException( - `The variable with id ${variableId} is pending creation and cannot be fetched by the user with id ${userId}` - ) - } - - return variable -} diff --git a/apps/api/src/common/get-workspace-with-authority.ts b/apps/api/src/common/get-workspace-with-authority.ts deleted file mode 100644 index e83203c5..00000000 --- a/apps/api/src/common/get-workspace-with-authority.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Authority, PrismaClient, User, Workspace } from '@prisma/client' -import getCollectiveWorkspaceAuthorities from './get-collective-workspace-authorities' -import { NotFoundException, UnauthorizedException } from '@nestjs/common' - -export default async function getWorkspaceWithAuthority( - userId: User['id'], - workspaceId: Workspace['id'], - authority: Authority, - prisma: PrismaClient -): Promise { - let workspace: Workspace - - try { - workspace = await prisma.workspace.findUnique({ - where: { - id: workspaceId - } - }) - } catch (error) { - /* empty */ - } - - // Check if the workspace exists or not - if (!workspace) { - throw new NotFoundException(`Workspace with id ${workspaceId} not found`) - } - - const permittedAuthorities = await getCollectiveWorkspaceAuthorities( - workspaceId, - userId, - prisma - ) - - // Check if the user has the authority to perform the action - if ( - !permittedAuthorities.has(authority) && - !permittedAuthorities.has(Authority.WORKSPACE_ADMIN) - ) { - throw new UnauthorizedException( - `User ${userId} does not have the required authorities to perform the action` - ) - } - - return workspace -} diff --git a/apps/api/src/common/logger.service.ts b/apps/api/src/common/logger.service.ts new file mode 100644 index 00000000..1a823bea --- /dev/null +++ b/apps/api/src/common/logger.service.ts @@ -0,0 +1,34 @@ +import { Injectable, LoggerService } from '@nestjs/common' +import * as chalk from 'chalk' +import * as moment from 'moment' + +@Injectable() +export class CustomLoggerService implements LoggerService { + log(message: string) { + this.info(message) + } + + info(message: string) { + console.info( + `${chalk.green('[INFO]')} ${chalk.green( + moment().format('YYYY-MM-DD HH:mm:ss') + )} - ${message}` + ) + } + + error(message: string) { + console.error( + `${chalk.red('[ERROR]')} ${chalk.red( + moment().format('YYYY-MM-DD HH:mm:ss') + )} - ${message}` + ) + } + + warn(message: string) { + console.warn( + `${chalk.yellow('[WARN]')} ${chalk.yellow( + moment().format('YYYY-MM-DD HH:mm:ss') + )} - ${message}` + ) + } +} diff --git a/apps/api/src/environment/controller/environment.controller.spec.ts b/apps/api/src/environment/controller/environment.controller.spec.ts index 0b5d37c6..09e073e1 100644 --- a/apps/api/src/environment/controller/environment.controller.spec.ts +++ b/apps/api/src/environment/controller/environment.controller.spec.ts @@ -3,14 +3,17 @@ import { EnvironmentController } from './environment.controller' import { EnvironmentService } from '../service/environment.service' import { PrismaService } from '../../prisma/prisma.service' import { mockDeep } from 'jest-mock-extended' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('EnvironmentController', () => { let controller: EnvironmentController beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [CommonModule], controllers: [EnvironmentController], - providers: [EnvironmentService, PrismaService] + providers: [EnvironmentService, PrismaService, AuthorityCheckerService] }) .overrideProvider(PrismaService) .useValue(mockDeep()) diff --git a/apps/api/src/environment/service/environment.service.spec.ts b/apps/api/src/environment/service/environment.service.spec.ts index 8e3c2731..bc6eec84 100644 --- a/apps/api/src/environment/service/environment.service.spec.ts +++ b/apps/api/src/environment/service/environment.service.spec.ts @@ -2,13 +2,16 @@ import { Test, TestingModule } from '@nestjs/testing' import { EnvironmentService } from './environment.service' import { PrismaService } from '../../prisma/prisma.service' import { mockDeep } from 'jest-mock-extended' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('EnvironmentService', () => { let service: EnvironmentService beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [EnvironmentService, PrismaService] + imports: [CommonModule], + providers: [EnvironmentService, PrismaService, AuthorityCheckerService] }) .overrideProvider(PrismaService) .useValue(mockDeep()) diff --git a/apps/api/src/environment/service/environment.service.ts b/apps/api/src/environment/service/environment.service.ts index dcb92c08..1a0eb800 100644 --- a/apps/api/src/environment/service/environment.service.ts +++ b/apps/api/src/environment/service/environment.service.ts @@ -17,17 +17,19 @@ import { import { CreateEnvironment } from '../dto/create.environment/create.environment' import { UpdateEnvironment } from '../dto/update.environment/update.environment' import { PrismaService } from '../../prisma/prisma.service' -import getProjectWithAuthority from '../../common/get-project-with-authority' -import getEnvironmentWithAuthority from '../../common/get-environment-with-authority' import createEvent from '../../common/create-event' import workspaceApprovalEnabled from '../../common/workspace-approval-enabled' import { EnvironmentWithProject } from '../environment.types' import createApproval from '../../common/create-approval' import { UpdateEnvironmentMetadata } from '../../approval/approval.types' +import { AuthorityCheckerService } from '../../common/authority-checker.service' @Injectable() export class EnvironmentService { - constructor(private readonly prisma: PrismaService) {} + constructor( + private readonly prisma: PrismaService, + private readonly authorityCheckerService: AuthorityCheckerService + ) {} async createEnvironment( user: User, @@ -36,12 +38,13 @@ export class EnvironmentService { reason?: string ) { // Check if the user has the required role to create an environment - const project = await getProjectWithAuthority( - user.id, - projectId, - Authority.CREATE_ENVIRONMENT, - this.prisma - ) + const project = + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.CREATE_ENVIRONMENT, + prisma: this.prisma + }) // Check if an environment with the same name already exists if (await this.environmentExists(dto.name, projectId)) { @@ -136,12 +139,13 @@ export class EnvironmentService { environmentId: Environment['id'], reason?: string ) { - const environment = await getEnvironmentWithAuthority( - user.id, - environmentId, - Authority.UPDATE_ENVIRONMENT, - this.prisma - ) + const environment = + await this.authorityCheckerService.checkAuthorityOverEnvironment({ + userId: user.id, + entity: { id: environmentId }, + authority: Authority.UPDATE_ENVIRONMENT, + prisma: this.prisma + }) // Check if an environment with the same name already exists if ( @@ -179,12 +183,13 @@ export class EnvironmentService { } async getEnvironment(user: User, environmentId: Environment['id']) { - const environment = await getEnvironmentWithAuthority( - user.id, - environmentId, - Authority.READ_ENVIRONMENT, - this.prisma - ) + const environment = + await this.authorityCheckerService.checkAuthorityOverEnvironment({ + userId: user.id, + entity: { id: environmentId }, + authority: Authority.READ_ENVIRONMENT, + prisma: this.prisma + }) return environment } @@ -198,12 +203,12 @@ export class EnvironmentService { order: string, search: string ) { - await getProjectWithAuthority( - user.id, - projectId, - Authority.READ_ENVIRONMENT, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.READ_ENVIRONMENT, + prisma: this.prisma + }) // Get the environments return await this.prisma.environment.findMany({ @@ -230,12 +235,13 @@ export class EnvironmentService { environmentId: Environment['id'], reason?: string ) { - const environment = await getEnvironmentWithAuthority( - user.id, - environmentId, - Authority.DELETE_ENVIRONMENT, - this.prisma - ) + const environment = + await this.authorityCheckerService.checkAuthorityOverEnvironment({ + userId: user.id, + entity: { id: environmentId }, + authority: Authority.DELETE_ENVIRONMENT, + prisma: this.prisma + }) // Check if the environment is the default one if (environment.isDefault) { diff --git a/apps/api/src/event/controller/event.controller.spec.ts b/apps/api/src/event/controller/event.controller.spec.ts index 94c1b6ef..9f3f9e8d 100644 --- a/apps/api/src/event/controller/event.controller.spec.ts +++ b/apps/api/src/event/controller/event.controller.spec.ts @@ -2,14 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing' import { EventController } from './event.controller' import { EventService } from '../service/event.service' import { PrismaService } from '../../prisma/prisma.service' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('EventController', () => { let controller: EventController beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [CommonModule], controllers: [EventController], - providers: [EventService, PrismaService] + providers: [EventService, PrismaService, AuthorityCheckerService] }).compile() controller = module.get(EventController) diff --git a/apps/api/src/event/event.module.ts b/apps/api/src/event/event.module.ts index c8d9823f..e716c12e 100644 --- a/apps/api/src/event/event.module.ts +++ b/apps/api/src/event/event.module.ts @@ -3,6 +3,7 @@ import { EventService } from './service/event.service' import { EventController } from './controller/event.controller' @Module({ + imports: [], providers: [EventService], controllers: [EventController] }) diff --git a/apps/api/src/event/service/event.service.spec.ts b/apps/api/src/event/service/event.service.spec.ts index 4032d3c1..9397ff59 100644 --- a/apps/api/src/event/service/event.service.spec.ts +++ b/apps/api/src/event/service/event.service.spec.ts @@ -1,13 +1,16 @@ import { Test, TestingModule } from '@nestjs/testing' import { EventService } from './event.service' import { PrismaService } from '../../prisma/prisma.service' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('EventService', () => { let service: EventService beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [EventService, PrismaService] + imports: [CommonModule], + providers: [EventService, PrismaService, AuthorityCheckerService] }).compile() service = module.get(EventService) diff --git a/apps/api/src/event/service/event.service.ts b/apps/api/src/event/service/event.service.ts index 0336cceb..9f540d9c 100644 --- a/apps/api/src/event/service/event.service.ts +++ b/apps/api/src/event/service/event.service.ts @@ -1,11 +1,14 @@ import { BadRequestException, Injectable } from '@nestjs/common' import { Authority, EventSeverity, EventSource, User } from '@prisma/client' -import getWorkspaceWithAuthority from '../../common/get-workspace-with-authority' import { PrismaService } from '../../prisma/prisma.service' +import { AuthorityCheckerService } from '../../common/authority-checker.service' @Injectable() export class EventService { - constructor(private readonly prisma: PrismaService) {} + constructor( + private readonly prisma: PrismaService, + private readonly authorityCheckerService: AuthorityCheckerService + ) {} async getEvents( user: User, @@ -25,12 +28,12 @@ export class EventService { } // Check for workspace authority - await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.READ_EVENT, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.READ_EVENT, + prisma: this.prisma + }) const query = { where: { diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index fa975202..88283299 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -3,52 +3,21 @@ * This is only a minimal backend to get started. */ -import { Logger, LoggerService, ValidationPipe } from '@nestjs/common' +import { Logger, ValidationPipe } from '@nestjs/common' import { NestFactory } from '@nestjs/core' import { AppModule } from './app/app.module' -import * as chalk from 'chalk' -import * as moment from 'moment' import { QueryTransformPipe } from './common/query.transform.pipe' import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' import * as Sentry from '@sentry/node' import { ProfilingIntegration } from '@sentry/profiling-node' import { RedisIoAdapter } from './socket/redis.adapter' +import { CustomLoggerService } from './common/logger.service' export const sentryEnv = process.env.SENTRY_ENV || 'production' -class CustomLogger implements LoggerService { - log(message: string) { - this.info(message) - } - - info(message: string) { - console.info( - `${chalk.green('[INFO]')} ${chalk.green( - moment().format('YYYY-MM-DD HH:mm:ss') - )} - ${message}` - ) - } - - error(message: string) { - console.error( - `${chalk.red('[ERROR]')} ${chalk.red( - moment().format('YYYY-MM-DD HH:mm:ss') - )} - ${message}` - ) - } - - warn(message: string) { - console.warn( - `${chalk.yellow('[WARN]')} ${chalk.yellow( - moment().format('YYYY-MM-DD HH:mm:ss') - )} - ${message}` - ) - } -} - async function initializeSentry() { - const logger = new CustomLogger() + const logger = new CustomLoggerService() if ( !process.env.SENTRY_DSN || @@ -77,7 +46,7 @@ async function initializeSentry() { } async function initializeNestApp() { - const logger = new CustomLogger() + const logger = new CustomLoggerService() const app = await NestFactory.create(AppModule, { logger, cors: { diff --git a/apps/api/src/project/controller/project.controller.spec.ts b/apps/api/src/project/controller/project.controller.spec.ts index 85ee649f..9bb3cb5c 100644 --- a/apps/api/src/project/controller/project.controller.spec.ts +++ b/apps/api/src/project/controller/project.controller.spec.ts @@ -5,17 +5,21 @@ import { MAIL_SERVICE } from '../../mail/services/interface.service' import { MockMailService } from '../../mail/services/mock.service' import { PrismaService } from '../../prisma/prisma.service' import { mockDeep } from 'jest-mock-extended' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('ProjectController', () => { let controller: ProjectController beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [CommonModule], controllers: [ProjectController], providers: [ ProjectService, PrismaService, - { provide: MAIL_SERVICE, useClass: MockMailService } + { provide: MAIL_SERVICE, useClass: MockMailService }, + AuthorityCheckerService ] }) .overrideProvider(PrismaService) diff --git a/apps/api/src/project/service/project.service.spec.ts b/apps/api/src/project/service/project.service.spec.ts index fd3d08b0..c4e24c70 100644 --- a/apps/api/src/project/service/project.service.spec.ts +++ b/apps/api/src/project/service/project.service.spec.ts @@ -4,16 +4,20 @@ import { MockMailService } from '../../mail/services/mock.service' import { MAIL_SERVICE } from '../../mail/services/interface.service' import { PrismaService } from '../../prisma/prisma.service' import { mockDeep } from 'jest-mock-extended' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('ProjectService', () => { let service: ProjectService beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [CommonModule], providers: [ ProjectService, PrismaService, - { provide: MAIL_SERVICE, useClass: MockMailService } + { provide: MAIL_SERVICE, useClass: MockMailService }, + AuthorityCheckerService ] }) .overrideProvider(PrismaService) diff --git a/apps/api/src/project/service/project.service.ts b/apps/api/src/project/service/project.service.ts index 337381b4..14c53d6f 100644 --- a/apps/api/src/project/service/project.service.ts +++ b/apps/api/src/project/service/project.service.ts @@ -18,20 +18,22 @@ import { excludeFields } from '../../common/exclude-fields' import { PrismaService } from '../../prisma/prisma.service' import { decrypt } from '../../common/decrypt' import { encrypt } from '../../common/encrypt' -import getWorkspaceWithAuthority from '../../common/get-workspace-with-authority' -import getProjectWithAuthority from '../../common/get-project-with-authority' import { v4 } from 'uuid' import createEvent from '../../common/create-event' import workspaceApprovalEnabled from '../../common/workspace-approval-enabled' import createApproval from '../../common/create-approval' import { UpdateProjectMetadata } from '../../approval/approval.types' import { ProjectWithSecrets } from '../project.types' +import { AuthorityCheckerService } from '../../common/authority-checker.service' @Injectable() export class ProjectService { private readonly log: Logger = new Logger(ProjectService.name) - constructor(private readonly prisma: PrismaService) {} + constructor( + private readonly prisma: PrismaService, + private readonly authorityCheckerService: AuthorityCheckerService + ) {} async createProject( user: User, @@ -40,12 +42,13 @@ export class ProjectService { reason?: string ) { // Check if the workspace exists or not - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.CREATE_PROJECT, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.CREATE_PROJECT, + prisma: this.prisma + }) // Check if project with this name already exists for the user if (await this.projectExists(dto.name, workspaceId)) @@ -219,12 +222,13 @@ export class ProjectService { dto: UpdateProject, reason?: string ) { - const project = await getProjectWithAuthority( - user.id, - projectId, - Authority.UPDATE_PROJECT, - this.prisma - ) + const project = + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.UPDATE_PROJECT, + prisma: this.prisma + }) // Check if project with this name already exists for the user if ( @@ -257,12 +261,13 @@ export class ProjectService { } async deleteProject(user: User, projectId: Project['id'], reason?: string) { - const project = await getProjectWithAuthority( - user.id, - projectId, - Authority.DELETE_PROJECT, - this.prisma - ) + const project = + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.DELETE_PROJECT, + prisma: this.prisma + }) if (await workspaceApprovalEnabled(project.workspaceId, this.prisma)) { return await createApproval( @@ -282,12 +287,13 @@ export class ProjectService { } async getProjectByUserAndId(user: User, projectId: Project['id']) { - const project = await getProjectWithAuthority( - user.id, - projectId, - Authority.READ_PROJECT, - this.prisma - ) + const project = + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.READ_PROJECT, + prisma: this.prisma + }) return project } @@ -301,12 +307,12 @@ export class ProjectService { order: string, search: string ) { - await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.READ_PROJECT, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.READ_PROJECT, + prisma: this.prisma + }) return ( await this.prisma.project.findMany({ diff --git a/apps/api/src/secret/controller/secret.controller.spec.ts b/apps/api/src/secret/controller/secret.controller.spec.ts index 69c1de26..946f3dc0 100644 --- a/apps/api/src/secret/controller/secret.controller.spec.ts +++ b/apps/api/src/secret/controller/secret.controller.spec.ts @@ -8,13 +8,15 @@ import { mockDeep } from 'jest-mock-extended' import { REDIS_CLIENT } from '../../provider/redis.provider' import { RedisClientType } from 'redis' import { ProviderModule } from '../../provider/provider.module' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('SecretController', () => { let controller: SecretController beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - imports: [ProviderModule], + imports: [ProviderModule, CommonModule], controllers: [SecretController], providers: [ PrismaService, @@ -22,7 +24,8 @@ describe('SecretController', () => { provide: MAIL_SERVICE, useClass: MockMailService }, - SecretService + SecretService, + AuthorityCheckerService ] }) .overrideProvider(REDIS_CLIENT) diff --git a/apps/api/src/secret/service/secret.service.spec.ts b/apps/api/src/secret/service/secret.service.spec.ts index 408b4ae6..d076d93a 100644 --- a/apps/api/src/secret/service/secret.service.spec.ts +++ b/apps/api/src/secret/service/secret.service.spec.ts @@ -7,20 +7,23 @@ import { mockDeep } from 'jest-mock-extended' import { REDIS_CLIENT } from '../../provider/redis.provider' import { RedisClientType } from 'redis' import { ProviderModule } from '../../provider/provider.module' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('SecretService', () => { let service: SecretService beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - imports: [ProviderModule], + imports: [ProviderModule, CommonModule], providers: [ PrismaService, { provide: MAIL_SERVICE, useClass: MockMailService }, - SecretService + SecretService, + AuthorityCheckerService ] }) .overrideProvider(REDIS_CLIENT) diff --git a/apps/api/src/secret/service/secret.service.ts b/apps/api/src/secret/service/secret.service.ts index ffc342c3..b28d8754 100644 --- a/apps/api/src/secret/service/secret.service.ts +++ b/apps/api/src/secret/service/secret.service.ts @@ -25,9 +25,6 @@ import { decrypt } from '../../common/decrypt' import { PrismaService } from '../../prisma/prisma.service' import { addHoursToDate } from '../../common/add-hours-to-date' import { encrypt } from '../../common/encrypt' -import getProjectWithAuthority from '../../common/get-project-with-authority' -import getEnvironmentWithAuthority from '../../common/get-environment-with-authority' -import getSecretWithAuthority from '../../common/get-secret-with-authority' import { SecretWithProject, SecretWithProjectAndVersion, @@ -41,6 +38,7 @@ import { UpdateSecretMetadata } from '../../approval/approval.types' import { RedisClientType } from 'redis' import { REDIS_CLIENT } from '../../provider/redis.provider' import { CHANGE_NOTIFIER_RSC } from '../../socket/change-notifier.socket' +import { AuthorityCheckerService } from '../../common/authority-checker.service' @Injectable() export class SecretService { @@ -52,7 +50,8 @@ export class SecretService { @Inject(REDIS_CLIENT) readonly redisClient: { publisher: RedisClientType - } + }, + private readonly authorityCheckerService: AuthorityCheckerService ) { this.redis = redisClient.publisher } @@ -65,22 +64,24 @@ export class SecretService { ) { const environmentId = dto.environmentId // Fetch the project - const project = await getProjectWithAuthority( - user.id, - projectId, - Authority.CREATE_SECRET, - this.prisma - ) + const project = + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.CREATE_SECRET, + prisma: this.prisma + }) // Check if the environment exists let environment: Environment | null = null if (environmentId) { - environment = await getEnvironmentWithAuthority( - user.id, - environmentId, - Authority.READ_ENVIRONMENT, - this.prisma - ) + environment = + await this.authorityCheckerService.checkAuthorityOverEnvironment({ + userId: user.id, + entity: { id: environmentId }, + authority: Authority.READ_ENVIRONMENT, + prisma: this.prisma + }) } if (!environment) { environment = await getDefaultEnvironmentOfProject(projectId, this.prisma) @@ -193,12 +194,12 @@ export class SecretService { dto: UpdateSecret, reason?: string ) { - const secret = await getSecretWithAuthority( - user.id, - secretId, - Authority.UPDATE_SECRET, - this.prisma - ) + const secret = await this.authorityCheckerService.checkAuthorityOverSecret({ + userId: user.id, + entity: { id: secretId }, + authority: Authority.UPDATE_SECRET, + prisma: this.prisma + }) // Check if the secret already exists in the environment if ( @@ -242,12 +243,12 @@ export class SecretService { environmentId: Environment['id'], reason?: string ) { - const secret = await getSecretWithAuthority( - user.id, - secretId, - Authority.UPDATE_SECRET, - this.prisma - ) + const secret = await this.authorityCheckerService.checkAuthorityOverSecret({ + userId: user.id, + entity: { id: secretId }, + authority: Authority.UPDATE_SECRET, + prisma: this.prisma + }) if (secret.environmentId === environmentId) { throw new BadRequestException( @@ -256,12 +257,13 @@ export class SecretService { } // Check if the environment exists - const environment = await getEnvironmentWithAuthority( - user.id, - environmentId, - Authority.READ_ENVIRONMENT, - this.prisma - ) + const environment = + await this.authorityCheckerService.checkAuthorityOverEnvironment({ + userId: user.id, + entity: { id: environmentId }, + authority: Authority.READ_ENVIRONMENT, + prisma: this.prisma + }) if (environment.projectId !== secret.projectId) { throw new BadRequestException( @@ -306,12 +308,12 @@ export class SecretService { reason?: string ) { // Fetch the secret - const secret = await getSecretWithAuthority( - user.id, - secretId, - Authority.UPDATE_SECRET, - this.prisma - ) + const secret = await this.authorityCheckerService.checkAuthorityOverSecret({ + userId: user.id, + entity: { id: secretId }, + authority: Authority.UPDATE_SECRET, + prisma: this.prisma + }) const maxVersion = secret.versions[secret.versions.length - 1].version @@ -347,12 +349,12 @@ export class SecretService { async deleteSecret(user: User, secretId: Secret['id'], reason?: string) { // Check if the user has the required role - const secret = await getSecretWithAuthority( - user.id, - secretId, - Authority.DELETE_SECRET, - this.prisma - ) + const secret = await this.authorityCheckerService.checkAuthorityOverSecret({ + userId: user.id, + entity: { id: secretId }, + authority: Authority.DELETE_SECRET, + prisma: this.prisma + }) if ( !secret.pendingCreation && @@ -380,12 +382,12 @@ export class SecretService { decryptValue: boolean ) { // Fetch the secret - const secret = await getSecretWithAuthority( - user.id, - secretId, - Authority.READ_SECRET, - this.prisma - ) + const secret = await this.authorityCheckerService.checkAuthorityOverSecret({ + userId: user.id, + entity: { id: secretId }, + authority: Authority.READ_SECRET, + prisma: this.prisma + }) const project = secret.project @@ -430,12 +432,13 @@ export class SecretService { search: string ) { // Fetch the project - const project = await getProjectWithAuthority( - user.id, - projectId, - Authority.READ_SECRET, - this.prisma - ) + const project = + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.READ_SECRET, + prisma: this.prisma + }) // Check if the project is allowed to store the private key if (decryptValue && !project.storePrivateKey) { diff --git a/apps/api/src/socket/change-notifier.socket.ts b/apps/api/src/socket/change-notifier.socket.ts index 8b817667..3b970730 100644 --- a/apps/api/src/socket/change-notifier.socket.ts +++ b/apps/api/src/socket/change-notifier.socket.ts @@ -15,10 +15,7 @@ import { ChangeNotifierRegistration } from './socket.types' import { Authority, User } from '@prisma/client' -import getWorkspaceWithAuthority from '../common/get-workspace-with-authority' import { CurrentUser } from '../decorators/user.decorator' -import getProjectWithAuthority from '../common/get-project-with-authority' -import getEnvironmentWithAuthority from '../common/get-environment-with-authority' import { PrismaService } from '../prisma/prisma.service' import { REDIS_CLIENT } from '../provider/redis.provider' import { RedisClientType } from 'redis' @@ -26,6 +23,7 @@ import { ApiKeyGuard } from '../auth/guard/api-key/api-key.guard' import { AuthGuard } from '../auth/guard/auth/auth.guard' import { RequiredApiKeyAuthorities } from '../decorators/required-api-key-authorities.decorator' import { Cron, CronExpression } from '@nestjs/schedule' +import { AuthorityCheckerService } from '../common/authority-checker.service' // The redis subscription channel for configuration updates export const CHANGE_NOTIFIER_RSC = 'configuration-updates' @@ -52,7 +50,8 @@ export default class ChangeNotifier subscriber: RedisClientType publisher: RedisClientType }, - private readonly prisma: PrismaService + private readonly prisma: PrismaService, + private readonly authorityCheckerService: AuthorityCheckerService ) { this.redis = redisClient.publisher this.redisSubscriber = redisClient.subscriber @@ -106,12 +105,12 @@ export default class ChangeNotifier name: data.workspaceName } }) - await getWorkspaceWithAuthority( - user.id, - workspace.id, - Authority.READ_WORKSPACE, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspace.id }, + authority: Authority.READ_WORKSPACE, + prisma: this.prisma + }) // Check if the user has access to the project const project = await this.prisma.project.findFirst({ @@ -119,12 +118,12 @@ export default class ChangeNotifier name: data.projectName } }) - await getProjectWithAuthority( - user.id, - project.id, - Authority.READ_PROJECT, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: project.id }, + authority: Authority.READ_PROJECT, + prisma: this.prisma + }) // Check if the user has access to the environment const environment = await this.prisma.environment.findFirst({ @@ -132,12 +131,12 @@ export default class ChangeNotifier name: data.environmentName } }) - await getEnvironmentWithAuthority( - user.id, - environment.id, - Authority.READ_ENVIRONMENT, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverEnvironment({ + userId: user.id, + entity: { id: environment.id }, + authority: Authority.READ_ENVIRONMENT, + prisma: this.prisma + }) // Add the client to the environment await this.addClientToEnvironment(client, environment.id) diff --git a/apps/api/src/socket/socket.module.ts b/apps/api/src/socket/socket.module.ts index 0171a914..dc58285b 100644 --- a/apps/api/src/socket/socket.module.ts +++ b/apps/api/src/socket/socket.module.ts @@ -1,7 +1,9 @@ import { Module } from '@nestjs/common' import ChangeNotifier from './change-notifier.socket' +import { CommonModule } from '../common/common.module' @Module({ + imports: [CommonModule], providers: [ChangeNotifier] }) export class SocketModule {} diff --git a/apps/api/src/variable/controller/variable.controller.spec.ts b/apps/api/src/variable/controller/variable.controller.spec.ts index 233655ec..b6a634da 100644 --- a/apps/api/src/variable/controller/variable.controller.spec.ts +++ b/apps/api/src/variable/controller/variable.controller.spec.ts @@ -8,20 +8,23 @@ import { REDIS_CLIENT } from '../../provider/redis.provider' import { RedisClientType } from 'redis' import { mockDeep } from 'jest-mock-extended' import { ProviderModule } from '../../provider/provider.module' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('VariableController', () => { let controller: VariableController beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - imports: [ProviderModule], + imports: [ProviderModule, CommonModule], providers: [ PrismaService, { provide: MAIL_SERVICE, useClass: MockMailService }, - VariableService + VariableService, + AuthorityCheckerService ], controllers: [VariableController] }) diff --git a/apps/api/src/variable/service/variable.service.spec.ts b/apps/api/src/variable/service/variable.service.spec.ts index da77e3de..4a97e2a3 100644 --- a/apps/api/src/variable/service/variable.service.spec.ts +++ b/apps/api/src/variable/service/variable.service.spec.ts @@ -7,20 +7,23 @@ import { REDIS_CLIENT } from '../../provider/redis.provider' import { RedisClientType } from 'redis' import { mockDeep } from 'jest-mock-extended' import { ProviderModule } from '../../provider/provider.module' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('VariableService', () => { let service: VariableService beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - imports: [ProviderModule], + imports: [ProviderModule, CommonModule], providers: [ PrismaService, { provide: MAIL_SERVICE, useClass: MockMailService }, - VariableService + VariableService, + AuthorityCheckerService ] }) .overrideProvider(REDIS_CLIENT) diff --git a/apps/api/src/variable/service/variable.service.ts b/apps/api/src/variable/service/variable.service.ts index 53534251..c0c3c517 100644 --- a/apps/api/src/variable/service/variable.service.ts +++ b/apps/api/src/variable/service/variable.service.ts @@ -21,12 +21,9 @@ import { VariableVersion } from '@prisma/client' import { CreateVariable } from '../dto/create.variable/create.variable' -import getProjectWithAuthority from '../../common/get-project-with-authority' -import getEnvironmentWithAuthority from '../../common/get-environment-with-authority' import getDefaultEnvironmentOfProject from '../../common/get-default-project-environment' import createEvent from '../../common/create-event' import { UpdateVariable } from '../dto/update.variable/update.variable' -import getVariableWithAuthority from '../../common/get-variable-with-authority' import workspaceApprovalEnabled from '../../common/workspace-approval-enabled' import createApproval from '../../common/create-approval' import { UpdateVariableMetadata } from '../../approval/approval.types' @@ -37,6 +34,7 @@ import { import { RedisClientType } from 'redis' import { REDIS_CLIENT } from '../../provider/redis.provider' import { CHANGE_NOTIFIER_RSC } from '../../socket/change-notifier.socket' +import { AuthorityCheckerService } from '../../common/authority-checker.service' @Injectable() export class VariableService { @@ -48,7 +46,8 @@ export class VariableService { @Inject(REDIS_CLIENT) readonly redisClient: { publisher: RedisClientType - } + }, + private readonly authorityCheckerService: AuthorityCheckerService ) { this.redis = redisClient.publisher } @@ -61,22 +60,24 @@ export class VariableService { ) { const environmentId = dto.environmentId // Fetch the project - const project = await getProjectWithAuthority( - user.id, - projectId, - Authority.CREATE_VARIABLE, - this.prisma - ) + const project = + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.CREATE_VARIABLE, + prisma: this.prisma + }) // Check i the environment exists let environment: Environment | null = null if (environmentId) { - environment = await getEnvironmentWithAuthority( - user.id, - environmentId, - Authority.READ_ENVIRONMENT, - this.prisma - ) + environment = + await this.authorityCheckerService.checkAuthorityOverEnvironment({ + userId: user.id, + entity: { id: environmentId }, + authority: Authority.READ_ENVIRONMENT, + prisma: this.prisma + }) } if (!environment) { environment = await getDefaultEnvironmentOfProject(projectId, this.prisma) @@ -199,12 +200,13 @@ export class VariableService { dto: UpdateVariable, reason?: string ) { - const variable = await getVariableWithAuthority( - user.id, - variableId, - Authority.UPDATE_VARIABLE, - this.prisma - ) + const variable = + await this.authorityCheckerService.checkAuthorityOverVariable({ + userId: user.id, + entity: { id: variableId }, + authority: Authority.UPDATE_VARIABLE, + prisma: this.prisma + }) // Check if the variable already exists in the environment if ( @@ -247,12 +249,13 @@ export class VariableService { environmentId: Environment['id'], reason?: string ) { - const variable = await getVariableWithAuthority( - user.id, - variableId, - Authority.UPDATE_VARIABLE, - this.prisma - ) + const variable = + await this.authorityCheckerService.checkAuthorityOverVariable({ + userId: user.id, + entity: { id: variableId }, + authority: Authority.UPDATE_VARIABLE, + prisma: this.prisma + }) if (variable.environmentId === environmentId) { throw new BadRequestException( @@ -261,12 +264,13 @@ export class VariableService { } // Check if the environment exists - const environment = await getEnvironmentWithAuthority( - user.id, - environmentId, - Authority.READ_ENVIRONMENT, - this.prisma - ) + const environment = + await this.authorityCheckerService.checkAuthorityOverEnvironment({ + userId: user.id, + entity: { id: environmentId }, + authority: Authority.READ_ENVIRONMENT, + prisma: this.prisma + }) // Check if the environment belongs to the same project if (environment.projectId !== variable.projectId) { @@ -313,12 +317,13 @@ export class VariableService { rollbackVersion: VariableVersion['version'], reason?: string ) { - const variable = await getVariableWithAuthority( - user.id, - variableId, - Authority.UPDATE_VARIABLE, - this.prisma - ) + const variable = + await this.authorityCheckerService.checkAuthorityOverVariable({ + userId: user.id, + entity: { id: variableId }, + authority: Authority.UPDATE_VARIABLE, + prisma: this.prisma + }) const maxVersion = variable.versions[variable.versions.length - 1].version @@ -360,12 +365,13 @@ export class VariableService { variableId: Variable['id'], reason?: string ) { - const variable = await getVariableWithAuthority( - user.id, - variableId, - Authority.DELETE_VARIABLE, - this.prisma - ) + const variable = + await this.authorityCheckerService.checkAuthorityOverVariable({ + userId: user.id, + entity: { id: variableId }, + authority: Authority.DELETE_VARIABLE, + prisma: this.prisma + }) if ( !variable.pendingCreation && @@ -391,12 +397,12 @@ export class VariableService { } async getVariableById(user: User, variableId: Variable['id']) { - return getVariableWithAuthority( - user.id, - variableId, - Authority.READ_VARIABLE, - this.prisma - ) + return this.authorityCheckerService.checkAuthorityOverVariable({ + userId: user.id, + entity: { id: variableId }, + authority: Authority.READ_VARIABLE, + prisma: this.prisma + }) } async getAllVariablesOfProject( @@ -409,12 +415,12 @@ export class VariableService { search: string ) { // Check if the user has the required authorities in the project - await getProjectWithAuthority( - user.id, - projectId, - Authority.READ_VARIABLE, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverProject({ + userId: user.id, + entity: { id: projectId }, + authority: Authority.READ_VARIABLE, + prisma: this.prisma + }) return await this.prisma.variable.findMany({ where: { diff --git a/apps/api/src/workspace-role/controller/workspace-role.controller.spec.ts b/apps/api/src/workspace-role/controller/workspace-role.controller.spec.ts index e879a6cd..290fb847 100644 --- a/apps/api/src/workspace-role/controller/workspace-role.controller.spec.ts +++ b/apps/api/src/workspace-role/controller/workspace-role.controller.spec.ts @@ -4,16 +4,20 @@ import { MockMailService } from '../../mail/services/mock.service' import { MAIL_SERVICE } from '../../mail/services/interface.service' import { PrismaService } from '../../prisma/prisma.service' import { WorkspaceRoleService } from '../service/workspace-role.service' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('WorkspaceRoleController', () => { let controller: WorkspaceRoleController beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [CommonModule], providers: [ WorkspaceRoleService, PrismaService, - { provide: MAIL_SERVICE, useClass: MockMailService } + { provide: MAIL_SERVICE, useClass: MockMailService }, + AuthorityCheckerService ], controllers: [WorkspaceRoleController] }).compile() diff --git a/apps/api/src/workspace-role/service/workspace-role.service.spec.ts b/apps/api/src/workspace-role/service/workspace-role.service.spec.ts index 518c24d4..40ddc523 100644 --- a/apps/api/src/workspace-role/service/workspace-role.service.spec.ts +++ b/apps/api/src/workspace-role/service/workspace-role.service.spec.ts @@ -3,16 +3,20 @@ import { WorkspaceRoleService } from './workspace-role.service' import { PrismaService } from '../../prisma/prisma.service' import { MAIL_SERVICE } from '../../mail/services/interface.service' import { MockMailService } from '../../mail/services/mock.service' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('WorkspaceRoleService', () => { let service: WorkspaceRoleService beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [CommonModule], providers: [ WorkspaceRoleService, PrismaService, - { provide: MAIL_SERVICE, useClass: MockMailService } + { provide: MAIL_SERVICE, useClass: MockMailService }, + AuthorityCheckerService ] }).compile() diff --git a/apps/api/src/workspace-role/service/workspace-role.service.ts b/apps/api/src/workspace-role/service/workspace-role.service.ts index 2ee4de70..164fc496 100644 --- a/apps/api/src/workspace-role/service/workspace-role.service.ts +++ b/apps/api/src/workspace-role/service/workspace-role.service.ts @@ -15,19 +15,22 @@ import { WorkspaceRole } from '@prisma/client' import { CreateWorkspaceRole } from '../dto/create-workspace-role/create-workspace-role' -import getWorkspaceWithAuthority from '../../common/get-workspace-with-authority' import getCollectiveWorkspaceAuthorities from '../../common/get-collective-workspace-authorities' import { UpdateWorkspaceRole } from '../dto/update-workspace-role/update-workspace-role' import { PrismaService } from '../../prisma/prisma.service' import createEvent from '../../common/create-event' import { WorkspaceRoleWithProjects } from '../workspace-role.types' import { v4 } from 'uuid' +import { AuthorityCheckerService } from '../../common/authority-checker.service' @Injectable() export class WorkspaceRoleService { private readonly logger: Logger = new Logger(WorkspaceRoleService.name) - constructor(private readonly prisma: PrismaService) {} + constructor( + private readonly prisma: PrismaService, + private readonly authorityCheckerService: AuthorityCheckerService + ) {} async createWorkspaceRole( user: User, @@ -43,12 +46,13 @@ export class WorkspaceRoleService { ) } - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.CREATE_WORKSPACE_ROLE, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.CREATE_WORKSPACE_ROLE, + prisma: this.prisma + }) if (await this.checkWorkspaceRoleExists(user, workspaceId, dto.name)) { throw new ConflictException( @@ -263,12 +267,12 @@ export class WorkspaceRoleService { workspaceId: Workspace['id'], name: string ) { - await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.READ_WORKSPACE_ROLE, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.READ_WORKSPACE_ROLE, + prisma: this.prisma + }) return ( (await this.prisma.workspaceRole.count({ @@ -300,12 +304,12 @@ export class WorkspaceRoleService { order: string, search: string ): Promise { - await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.READ_WORKSPACE_ROLE, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.READ_WORKSPACE_ROLE, + prisma: this.prisma + }) return await this.prisma.workspaceRole.findMany({ where: { diff --git a/apps/api/src/workspace-role/workspace-role.module.ts b/apps/api/src/workspace-role/workspace-role.module.ts index 09abe1e3..55690c74 100644 --- a/apps/api/src/workspace-role/workspace-role.module.ts +++ b/apps/api/src/workspace-role/workspace-role.module.ts @@ -3,6 +3,7 @@ import { WorkspaceRoleService } from './service/workspace-role.service' import { WorkspaceRoleController } from './controller/workspace-role.controller' @Module({ + imports: [], providers: [WorkspaceRoleService], controllers: [WorkspaceRoleController] }) diff --git a/apps/api/src/workspace/controller/workspace.controller.spec.ts b/apps/api/src/workspace/controller/workspace.controller.spec.ts index 38ea6d9a..22f664f9 100644 --- a/apps/api/src/workspace/controller/workspace.controller.spec.ts +++ b/apps/api/src/workspace/controller/workspace.controller.spec.ts @@ -5,12 +5,15 @@ import { PrismaService } from '../../prisma/prisma.service' import { MAIL_SERVICE } from '../../mail/services/interface.service' import { MockMailService } from '../../mail/services/mock.service' import { JwtService } from '@nestjs/jwt' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('WorkspaceController', () => { let controller: WorkspaceController beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [CommonModule], providers: [ WorkspaceService, PrismaService, @@ -18,7 +21,8 @@ describe('WorkspaceController', () => { provide: MAIL_SERVICE, useClass: MockMailService }, - JwtService + JwtService, + AuthorityCheckerService ], controllers: [WorkspaceController] }).compile() diff --git a/apps/api/src/workspace/service/workspace.service.spec.ts b/apps/api/src/workspace/service/workspace.service.spec.ts index acfa1362..8f8412c0 100644 --- a/apps/api/src/workspace/service/workspace.service.spec.ts +++ b/apps/api/src/workspace/service/workspace.service.spec.ts @@ -4,12 +4,15 @@ import { PrismaService } from '../../prisma/prisma.service' import { MAIL_SERVICE } from '../../mail/services/interface.service' import { MockMailService } from '../../mail/services/mock.service' import { JwtService } from '@nestjs/jwt' +import { AuthorityCheckerService } from '../../common/authority-checker.service' +import { CommonModule } from '../../common/common.module' describe('WorkspaceService', () => { let service: WorkspaceService beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [CommonModule], providers: [ WorkspaceService, PrismaService, @@ -17,7 +20,8 @@ describe('WorkspaceService', () => { provide: MAIL_SERVICE, useClass: MockMailService }, - JwtService + JwtService, + AuthorityCheckerService ] }).compile() diff --git a/apps/api/src/workspace/service/workspace.service.ts b/apps/api/src/workspace/service/workspace.service.ts index 6dab9958..a3174716 100644 --- a/apps/api/src/workspace/service/workspace.service.ts +++ b/apps/api/src/workspace/service/workspace.service.ts @@ -29,13 +29,13 @@ import { } from '../../mail/services/interface.service' import { JwtService } from '@nestjs/jwt' import { UpdateWorkspace } from '../dto/update.workspace/update.workspace' -import getWorkspaceWithAuthority from '../../common/get-workspace-with-authority' import { v4 } from 'uuid' import createEvent from '../../common/create-event' import { UpdateWorkspaceMetadata } from '../../approval/approval.types' import workspaceApprovalEnabled from '../../common/workspace-approval-enabled' import createApproval from '../../common/create-approval' import createWorkspace from '../../common/create-workspace' +import { AuthorityCheckerService } from '../../common/authority-checker.service' @Injectable() export class WorkspaceService { @@ -44,7 +44,8 @@ export class WorkspaceService { constructor( private readonly prisma: PrismaService, private readonly jwt: JwtService, - @Inject(MAIL_SERVICE) private readonly mailService: IMailService + @Inject(MAIL_SERVICE) private readonly mailService: IMailService, + private readonly authorityCheckerService: AuthorityCheckerService ) {} async createWorkspace(user: User, dto: CreateWorkspace) { @@ -62,12 +63,14 @@ export class WorkspaceService { reason?: string ) { // Fetch the workspace - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.UPDATE_WORKSPACE, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.UPDATE_WORKSPACE, + + prisma: this.prisma + }) // Check if a same named workspace already exists if ( @@ -102,12 +105,14 @@ export class WorkspaceService { workspaceId: Workspace['id'], userId: User['id'] ): Promise { - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.WORKSPACE_ADMIN, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.WORKSPACE_ADMIN, + + prisma: this.prisma + }) if (userId === user.id) { throw new BadRequestException( @@ -222,12 +227,13 @@ export class WorkspaceService { user: User, workspaceId: Workspace['id'] ): Promise { - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.DELETE_WORKSPACE, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.DELETE_WORKSPACE, + prisma: this.prisma + }) // We don't want the users to delete their default workspace if (workspace.isDefault) { @@ -251,12 +257,13 @@ export class WorkspaceService { workspaceId: Workspace['id'], members: WorkspaceMemberDTO[] ): Promise { - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.ADD_USER, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.ADD_USER, + prisma: this.prisma + }) // Add users to the workspace if any if (members && members.length > 0) { @@ -296,12 +303,13 @@ export class WorkspaceService { workspaceId: Workspace['id'], userIds: User['id'][] ): Promise { - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.REMOVE_USER, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.REMOVE_USER, + prisma: this.prisma + }) // Remove users from the workspace if any if (userIds && userIds.length > 0) { @@ -350,12 +358,13 @@ export class WorkspaceService { userId: User['id'], roleIds: WorkspaceRole['id'][] ): Promise { - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.UPDATE_USER_ROLE, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.UPDATE_USER_ROLE, + prisma: this.prisma + }) if (!roleIds || roleIds.length === 0) { this.log.warn( @@ -432,12 +441,12 @@ export class WorkspaceService { order: string, search: string ) { - await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.READ_USERS, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.READ_USERS, + prisma: this.prisma + }) return this.prisma.workspaceMember.findMany({ skip: page * limit, @@ -541,12 +550,13 @@ export class WorkspaceService { workspaceId: Workspace['id'], inviteeId: User['id'] ): Promise { - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.REMOVE_USER, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.REMOVE_USER, + prisma: this.prisma + }) // Check if the user has a pending invitation to the workspace if (!(await this.invitationPending(workspaceId, inviteeId))) @@ -618,12 +628,13 @@ export class WorkspaceService { user: User, workspaceId: Workspace['id'] ): Promise { - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.READ_WORKSPACE, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.READ_WORKSPACE, + prisma: this.prisma + }) const workspaceOwnerId = await this.prisma.workspace .findUnique({ @@ -670,12 +681,12 @@ export class WorkspaceService { workspaceId: Workspace['id'], otherUserId: User['id'] ): Promise { - await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.READ_USERS, - this.prisma - ) + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.READ_USERS, + prisma: this.prisma + }) return await this.memberExistsInWorkspace(workspaceId, otherUserId) } @@ -684,12 +695,12 @@ export class WorkspaceService { user: User, workspaceId: Workspace['id'] ): Promise { - return await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.READ_USERS, - this.prisma - ) + return await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.READ_USERS, + prisma: this.prisma + }) } async getWorkspacesOfUser( @@ -729,12 +740,13 @@ export class WorkspaceService { } async exportData(user: User, workspaceId: Workspace['id']) { - const workspace = await getWorkspaceWithAuthority( - user.id, - workspaceId, - Authority.WORKSPACE_ADMIN, - this.prisma - ) + const workspace = + await this.authorityCheckerService.checkAuthorityOverWorkspace({ + userId: user.id, + entity: { id: workspaceId }, + authority: Authority.WORKSPACE_ADMIN, + prisma: this.prisma + }) // eslint-disable-next-line @typescript-eslint/no-explicit-any const data: any = {}