openvidu/backend/src/middlewares/recording.middleware.ts

159 lines
5.1 KiB
TypeScript

import { MeetRecordingAccess, MeetRoom, OpenViduMeetPermissions, RecordingPermissions, UserRole } from '@typings-ce';
import { NextFunction, Request, Response } from 'express';
import { container } from '../config/index.js';
import { RecordingHelper } from '../helpers/index.js';
import {
errorInsufficientPermissions,
errorRecordingDisabled,
errorRoomMetadataNotFound,
handleError,
rejectRequestFromMeetError
} from '../models/error.model.js';
import { LoggerService, MeetStorageService, RoomService } from '../services/index.js';
import {
allowAnonymous,
apiKeyValidator,
recordingTokenValidator,
tokenAndRoleValidator,
withAuth
} from './auth.middleware.js';
export const withRecordingEnabled = async (req: Request, res: Response, next: NextFunction) => {
const logger = container.get(LoggerService);
const roomService = container.get(RoomService);
try {
const roomId = extractRoomIdFromRequest(req);
const room: MeetRoom = await roomService.getMeetRoom(roomId!);
if (!room.preferences?.recordingPreferences?.enabled) {
logger.debug(`Recording is disabled for room '${roomId}'`);
const error = errorRecordingDisabled(roomId!);
return rejectRequestFromMeetError(res, error);
}
return next();
} catch (error) {
handleError(res, error, 'checking recording preferences');
}
};
export const withCanRecordPermission = async (req: Request, res: Response, next: NextFunction) => {
const roomId = extractRoomIdFromRequest(req);
const payload = req.session?.tokenClaims;
if (!payload) {
const error = errorInsufficientPermissions();
return rejectRequestFromMeetError(res, error);
}
const sameRoom = payload.video?.room === roomId;
const metadata = JSON.parse(payload.metadata || '{}');
const permissions = metadata.permissions as OpenViduMeetPermissions | undefined;
const canRecord = permissions?.canRecord;
if (!sameRoom || !canRecord) {
const error = errorInsufficientPermissions();
return rejectRequestFromMeetError(res, error);
}
return next();
};
export const withCanRetrieveRecordingsPermission = async (req: Request, res: Response, next: NextFunction) => {
const roomId = extractRoomIdFromRequest(req);
const payload = req.session?.tokenClaims;
// If there is no token, it is invoked using the API key, the user is admin or
// the user is anonymous and recording access is public.
// In this case, the user is allowed to access the resource
if (!payload) {
return next();
}
const sameRoom = roomId ? payload.video?.room === roomId : true;
const metadata = JSON.parse(payload.metadata || '{}');
const permissions = metadata.recordingPermissions as RecordingPermissions | undefined;
const canRetrieveRecordings = permissions?.canRetrieveRecordings;
if (!sameRoom || !canRetrieveRecordings) {
const error = errorInsufficientPermissions();
return rejectRequestFromMeetError(res, error);
}
return next();
};
export const withCanDeleteRecordingsPermission = async (req: Request, res: Response, next: NextFunction) => {
const roomId = extractRoomIdFromRequest(req);
const payload = req.session?.tokenClaims;
// If there is no token, the user is admin or it is invoked using the API key
// In this case, the user is allowed to access the resource
if (!payload) {
return next();
}
const sameRoom = payload.video?.room === roomId;
const metadata = JSON.parse(payload.metadata || '{}');
const permissions = metadata.recordingPermissions as RecordingPermissions | undefined;
const canDeleteRecordings = permissions?.canDeleteRecordings;
if (!sameRoom || !canDeleteRecordings) {
const error = errorInsufficientPermissions();
return rejectRequestFromMeetError(res, error);
}
return next();
};
/**
* Middleware to configure authentication for retrieving recording media based on recording access.
*
* - Admin and recording token are always allowed
* - If recording access is public, anonymous users are allowed
*/
export const configureRecordingMediaAuth = async (req: Request, res: Response, next: NextFunction) => {
const storageService = container.get(MeetStorageService);
let recordingAccess: MeetRecordingAccess;
try {
const roomId = extractRoomIdFromRequest(req);
const room = await storageService.getArchivedRoomMetadata(roomId!);
if (!room) {
const error = errorRoomMetadataNotFound(roomId!);
return rejectRequestFromMeetError(res, error);
}
recordingAccess = room.preferences!.recordingPreferences.allowAccessTo;
} catch (error) {
return handleError(res, error, 'checking recording permissions');
}
const authValidators = [apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN), recordingTokenValidator];
if (recordingAccess === MeetRecordingAccess.PUBLIC) {
authValidators.push(allowAnonymous);
}
return withAuth(...authValidators)(req, res, next);
};
const extractRoomIdFromRequest = (req: Request): string | undefined => {
if (req.body.roomId) {
return req.body.roomId as string;
}
// If roomId is not in the body, check if it's in the params
const recordingId = req.params.recordingId as string;
if (!recordingId) {
return undefined;
}
const { roomId } = RecordingHelper.extractInfoFromRecordingId(recordingId);
return roomId;
};