From f2a84717e52966f851b890b6b5f2e13e3d2dca56 Mon Sep 17 00:00:00 2001 From: juancarmore Date: Fri, 9 Jan 2026 13:36:10 +0100 Subject: [PATCH] backend: enhance bulk room and recording deletion logic to check for permissions on each item to delete --- .../backend/src/services/recording.service.ts | 20 +++++++++++++++++-- meet-ce/backend/src/services/room.service.ts | 14 ++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/meet-ce/backend/src/services/recording.service.ts b/meet-ce/backend/src/services/recording.service.ts index 9a86ad6a..f656a02f 100644 --- a/meet-ce/backend/src/services/recording.service.ts +++ b/meet-ce/backend/src/services/recording.service.ts @@ -4,6 +4,7 @@ import { EgressStatus, EncodedFileOutput, EncodedFileType, RoomCompositeOptions import ms from 'ms'; import { Readable } from 'stream'; import { uid } from 'uid'; +import { container } from '../config/dependency-injector.config.js'; import { INTERNAL_CONFIG } from '../config/internal-config.js'; import { MEET_ENV } from '../environment.js'; import { RecordingHelper } from '../helpers/recording.helper.js'; @@ -32,6 +33,7 @@ import { LiveKitService } from './livekit.service.js'; import { LoggerService } from './logger.service.js'; import { MutexService, RedisLock } from './mutex.service.js'; import { RequestSessionService } from './request-session.service.js'; +import { RoomService } from './room.service.js'; import { BlobStorageService } from './storage/blob-storage.service.js'; @injectable() @@ -282,15 +284,17 @@ export class RecordingService { recordingIds: string[], roomId?: string ): Promise<{ deleted: string[]; failed: { recordingId: string; error: string }[] }> { + const roomService = container.get(RoomService); + const validRecordingIds: Set = new Set(); const deletedRecordings: Set = new Set(); const failedRecordings: Set<{ recordingId: string; error: string }> = new Set(); for (const recordingId of recordingIds) { + const { roomId: recRoomId } = RecordingHelper.extractInfoFromRecordingId(recordingId); + // If a roomId is provided, only process recordings from that room if (roomId) { - const { roomId: recRoomId } = RecordingHelper.extractInfoFromRecordingId(recordingId); - if (recRoomId !== roomId) { this.logger.warn(`Skipping recording '${recordingId}' as it does not belong to room '${roomId}'`); failedRecordings.add({ @@ -299,6 +303,18 @@ export class RecordingService { }); continue; } + } else { + // Check room member permissions for each recording if no roomId filter is applied + const permissions = await roomService.getAuthenticatedRoomMemberPermissions(recRoomId); + + if (!permissions.canDeleteRecordings) { + this.logger.warn(`Insufficient permissions to delete recording '${recordingId}'`); + failedRecordings.add({ + recordingId, + error: `Insufficient permissions to delete recording '${recordingId}'` + }); + continue; + } } try { diff --git a/meet-ce/backend/src/services/room.service.ts b/meet-ce/backend/src/services/room.service.ts index 93d66d24..2fe9dc35 100644 --- a/meet-ce/backend/src/services/room.service.ts +++ b/meet-ce/backend/src/services/room.service.ts @@ -27,6 +27,7 @@ import { MEET_ENV } from '../environment.js'; import { MeetRoomHelper } from '../helpers/room.helper.js'; import { errorDeletingRoom, + errorInsufficientPermissions, errorRoomActiveMeeting, errorRoomNotFound, internalError, @@ -328,7 +329,7 @@ export class RoomService { /** * Retrieves a list of rooms based on the provided filtering, pagination, and sorting options. - * + * * If the request is made by an authenticated user, access is determined by the user's role: * - ADMIN: Can see all rooms * - USER: Can see rooms they own or are members of @@ -716,6 +717,17 @@ export class RoomService { const roomId = typeof room === 'string' ? room : room.roomId; try { + const user = this.requestSessionService.getAuthenticatedUser(); + + // Check permissions if user is authenticated and not an admin + if (user && user.role !== MeetUserRole.ADMIN) { + const isOwner = await this.isRoomOwner(roomId, user.userId); + + if (!isOwner) { + throw errorInsufficientPermissions(); + } + } + let result; if (typeof room === 'string') {