diff --git a/apps/server/src/common/helpers/types/permission.ts b/apps/server/src/common/helpers/types/permission.ts index 5493bdb47..fd45d0fa4 100644 --- a/apps/server/src/common/helpers/types/permission.ts +++ b/apps/server/src/common/helpers/types/permission.ts @@ -4,6 +4,11 @@ export enum UserRole { MEMBER = 'member', } +export enum InviteUserRole { + ADMIN = 'admin', // can have owner permissions but cannot delete workspace + MEMBER = 'member', +} + export enum SpaceRole { ADMIN = 'admin', // can manage space settings, members, and delete space WRITER = 'writer', // can read and write pages in space diff --git a/apps/server/src/core/workspace/dto/invitation.dto.ts b/apps/server/src/core/workspace/dto/invitation.dto.ts index 187688c4d..ced007ccd 100644 --- a/apps/server/src/core/workspace/dto/invitation.dto.ts +++ b/apps/server/src/core/workspace/dto/invitation.dto.ts @@ -11,7 +11,7 @@ import { MaxLength, MinLength, } from 'class-validator'; -import { UserRole } from '../../../common/helpers/types/permission'; +import { InviteUserRole } from '../../../common/helpers/types/permission'; import { NoUrls } from '../../../common/validators/no-urls.validator'; export class InviteUserDto { @@ -32,7 +32,7 @@ export class InviteUserDto { @IsUUID('all', { each: true }) groupIds: string[]; - @IsEnum(UserRole) + @IsEnum(InviteUserRole) role: string; } diff --git a/apps/server/src/core/workspace/services/workspace-invitation.service.ts b/apps/server/src/core/workspace/services/workspace-invitation.service.ts index 50ed49f05..2bc3d2dd9 100644 --- a/apps/server/src/core/workspace/services/workspace-invitation.service.ts +++ b/apps/server/src/core/workspace/services/workspace-invitation.service.ts @@ -1,5 +1,6 @@ import { BadRequestException, + ForbiddenException, Inject, Injectable, Logger, @@ -40,6 +41,7 @@ import { AUDIT_SERVICE, IAuditService, } from '../../../integrations/audit/audit.service'; +import { isAdminActingOnOwner } from '../workspace.util'; @Injectable() export class WorkspaceInvitationService { @@ -119,6 +121,10 @@ export class WorkspaceInvitationService { ): Promise { const { emails, role, groupIds } = inviteUserDto; + if (isAdminActingOnOwner(authUser.role, role)) { + throw new ForbiddenException(); + } + let invites: WorkspaceInvitation[] = []; try { diff --git a/apps/server/src/core/workspace/services/workspace.service.ts b/apps/server/src/core/workspace/services/workspace.service.ts index 267eb13b7..f3ab78e60 100644 --- a/apps/server/src/core/workspace/services/workspace.service.ts +++ b/apps/server/src/core/workspace/services/workspace.service.ts @@ -30,6 +30,7 @@ import { DomainService } from '../../../integrations/environment/domain.service' import { jsonArrayFrom } from 'kysely/helpers/postgres'; import { addDays } from 'date-fns'; import { DISALLOWED_HOSTNAMES, WorkspaceStatus } from '../workspace.constants'; +import { isAdminActingOnOwner } from '../workspace.util'; import { v4 } from 'uuid'; import { InjectQueue } from '@nestjs/bullmq'; import { QueueJob, QueueName } from '../../../integrations/queue/constants'; @@ -590,8 +591,8 @@ export class WorkspaceService { // prevent ADMIN from managing OWNER role if ( - (authUser.role === UserRole.ADMIN && newRole === UserRole.OWNER) || - (authUser.role === UserRole.ADMIN && user.role === UserRole.OWNER) + isAdminActingOnOwner(authUser.role, newRole) || + isAdminActingOnOwner(authUser.role, user.role) ) { throw new ForbiddenException(); } @@ -695,7 +696,7 @@ export class WorkspaceService { throw new BadRequestException('You cannot deactivate yourself'); } - if (authUser.role === UserRole.ADMIN && user.role === UserRole.OWNER) { + if (isAdminActingOnOwner(authUser.role, user.role)) { throw new BadRequestException( 'You cannot deactivate a user with owner role', ); @@ -753,7 +754,7 @@ export class WorkspaceService { throw new BadRequestException('User is not deactivated'); } - if (authUser.role === UserRole.ADMIN && user.role === UserRole.OWNER) { + if (isAdminActingOnOwner(authUser.role, user.role)) { throw new BadRequestException( 'You cannot activate a user with owner role', ); @@ -805,7 +806,7 @@ export class WorkspaceService { throw new BadRequestException('You cannot delete yourself'); } - if (authUser.role === UserRole.ADMIN && user.role === UserRole.OWNER) { + if (isAdminActingOnOwner(authUser.role, user.role)) { throw new BadRequestException('You cannot delete a user with owner role'); } diff --git a/apps/server/src/core/workspace/workspace.util.ts b/apps/server/src/core/workspace/workspace.util.ts new file mode 100644 index 000000000..b22aa3ceb --- /dev/null +++ b/apps/server/src/core/workspace/workspace.util.ts @@ -0,0 +1,8 @@ +import { UserRole } from '../../common/helpers/types/permission'; + +export function isAdminActingOnOwner( + authUserRole: string, + targetRole: string, +): boolean { + return authUserRole === UserRole.ADMIN && targetRole === UserRole.OWNER; +}