backend: Implement configureRecordingTokenAuth middleware and update room route

This commit is contained in:
juancarmore 2025-04-28 12:20:38 +02:00
parent 8bbbee731b
commit 66f2a10406
3 changed files with 84 additions and 4 deletions

View File

@ -1,7 +1,12 @@
import { AuthMode, ParticipantRole, UserRole } from '@typings-ce';
import { AuthMode, MeetRecordingAccess, MeetRoom, ParticipantRole, UserRole } from '@typings-ce';
import { NextFunction, Request, Response } from 'express';
import { container } from '../config/index.js';
import { LoggerService, MeetStorageService } from '../services/index.js';
import {
errorInsufficientPermissions,
errorRoomNotFoundOrEmptyRecordings,
OpenViduMeetError
} from '../models/error.model.js';
import { LoggerService, MeetStorageService, RoomService } from '../services/index.js';
import { allowAnonymous, apiKeyValidator, tokenAndRoleValidator, withAuth } from './auth.middleware.js';
/**
@ -93,3 +98,77 @@ export const configureRoomAuthorization = async (req: Request, res: Response, ne
// If the user is not a moderator, it is not allowed to access the resource
return res.status(403).json({ message: 'Insufficient permissions to access this resource' });
};
/**
* Middleware to configure authentication based on participant role and authentication mode
* for generating a token for retrieving/deleting recordings.
*
* - If the authentication mode is MODERATORS_ONLY and the participant role is MODERATOR, configure user authentication.
* - If the authentication mode is ALL_USERS, configure user authentication.
* - Otherwise, allow anonymous access.
*/
export const configureRecordingTokenAuth = async (req: Request, res: Response, next: NextFunction) => {
const logger = container.get(LoggerService);
const storageService = container.get(MeetStorageService);
const roomService = container.get(RoomService);
let role: ParticipantRole;
try {
const roomId = req.params.roomId;
const { secret } = req.body;
const room = await storageService.getArchivedRoomMetadata(roomId);
if (!room) {
// If the room is not found, it means that there are no recordings for that room or the room doesn't exist
throw errorRoomNotFoundOrEmptyRecordings(roomId);
}
const recordingAccess = room.preferences!.recordingPreferences.allowAccessTo;
if (recordingAccess === MeetRecordingAccess.ADMIN) {
// Deny request if the room is configured to allow access to recordings only for admins
throw errorInsufficientPermissions();
}
role = roomService.getRoomRoleBySecretFromRoom(room as MeetRoom, secret);
} catch (error) {
logger.error('Error getting room role by secret', error);
if (error instanceof OpenViduMeetError) {
return res.status(error.statusCode).json({ name: error.name, message: error.message });
} else {
return res.status(500).json({
name: 'Room Error',
message: 'Internal server error. Room operation failed'
});
}
}
let authMode: AuthMode;
try {
const { securityPreferences } = await storageService.getGlobalPreferences();
authMode = securityPreferences.authentication.authMode;
} catch (error) {
logger.error('Error checking authentication preferences', error);
return res.status(500).json({ message: 'Internal server error' });
}
const authValidators = [];
if (authMode === AuthMode.NONE) {
authValidators.push(allowAnonymous);
} else {
const isModeratorsOnlyMode = authMode === AuthMode.MODERATORS_ONLY && role === ParticipantRole.MODERATOR;
const isAllUsersMode = authMode === AuthMode.ALL_USERS;
if (isModeratorsOnlyMode || isAllUsersMode) {
authValidators.push(tokenAndRoleValidator(UserRole.USER));
} else {
authValidators.push(allowAnonymous);
}
}
return withAuth(...authValidators)(req, res, next);
};

View File

@ -5,6 +5,7 @@ import * as roomCtrl from '../controllers/room.controller.js';
import {
apiKeyValidator,
configureCreateRoomAuth,
configureRecordingTokenAuth,
configureRoomAuthorization,
participantTokenValidator,
tokenAndRoleValidator,
@ -66,7 +67,7 @@ internalRoomRouter.put(
internalRoomRouter.post(
'/:roomId/recording-token',
configureCreateRoomAuth,
configureRecordingTokenAuth,
withValidRoomSecret,
roomCtrl.generateRecordingToken
);

View File

@ -249,7 +249,7 @@ export class RoomService {
return this.getRoomRoleBySecretFromRoom(room, secret);
}
protected getRoomRoleBySecretFromRoom(room: MeetRoom, secret: string): ParticipantRole {
getRoomRoleBySecretFromRoom(room: MeetRoom, secret: string): ParticipantRole {
const { moderatorSecret, publisherSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
switch (secret) {