backend: update security preferences structure to remove unused attributes and refactor associated code

This commit is contained in:
juancarmore 2025-05-31 00:03:18 +02:00
parent dd3a2939e4
commit 55bc8726d0
7 changed files with 54 additions and 156 deletions

View File

@ -1,4 +1,4 @@
import { SecurityPreferencesDTO, UpdateSecurityPreferencesDTO } from '@typings-ce';
import { SecurityPreferences } from '@typings-ce';
import { Request, Response } from 'express';
import { container } from '../../config/index.js';
import { handleError } from '../../models/error.model.js';
@ -9,29 +9,15 @@ export const updateSecurityPreferences = async (req: Request, res: Response) =>
const globalPrefService = container.get(MeetStorageService);
logger.verbose(`Updating security preferences: ${JSON.stringify(req.body)}`);
const securityPreferences = req.body as UpdateSecurityPreferencesDTO;
const securityPreferences = req.body as SecurityPreferences;
try {
const globalPreferences = await globalPrefService.getGlobalPreferences();
const currentAuth = globalPreferences.securityPreferences.authentication;
const newAuth = securityPreferences.authentication;
if (securityPreferences.roomCreationPolicy) {
globalPreferences.securityPreferences.roomCreationPolicy = {
allowRoomCreation: securityPreferences.roomCreationPolicy.allowRoomCreation,
requireAuthentication:
securityPreferences.roomCreationPolicy.requireAuthentication === undefined
? globalPreferences.securityPreferences.roomCreationPolicy.requireAuthentication
: securityPreferences.roomCreationPolicy.requireAuthentication
};
}
if (securityPreferences.authentication) {
const currentAuth = globalPreferences.securityPreferences.authentication;
const newAuth = securityPreferences.authentication;
currentAuth.authMode = newAuth.authMode;
currentAuth.method.type = newAuth.method.type;
}
currentAuth.authMethod = newAuth.authMethod;
currentAuth.authModeToAccessRoom = newAuth.authModeToAccessRoom;
await globalPrefService.saveGlobalPreferences(globalPreferences);
return res.status(200).json({ message: 'Security preferences updated successfully' });
@ -48,19 +34,8 @@ export const getSecurityPreferences = async (_req: Request, res: Response) => {
try {
const preferences = await preferenceService.getGlobalPreferences();
// Convert the preferences to the DTO format by removing credentials
const securityPreferences = preferences.securityPreferences;
const securityPreferencesDTO: SecurityPreferencesDTO = {
roomCreationPolicy: securityPreferences.roomCreationPolicy,
authentication: {
authMode: securityPreferences.authentication.authMode,
method: {
type: securityPreferences.authentication.method.type
}
}
};
return res.status(200).json(securityPreferencesDTO);
return res.status(200).json(securityPreferences);
} catch (error) {
handleError(res, error, 'getting security preferences');
}

View File

@ -25,22 +25,22 @@ export const configureParticipantTokenAuth = async (req: Request, res: Response,
return handleError(res, error, 'getting room role by secret');
}
let authMode: AuthMode;
let authModeToAccessRoom: AuthMode;
try {
const { securityPreferences } = await globalPrefService.getGlobalPreferences();
authMode = securityPreferences.authentication.authMode;
authModeToAccessRoom = securityPreferences.authentication.authModeToAccessRoom;
} catch (error) {
return handleError(res, error, 'checking authentication preferences');
}
const authValidators = [];
if (authMode === AuthMode.NONE) {
if (authModeToAccessRoom === AuthMode.NONE) {
authValidators.push(allowAnonymous);
} else {
const isModeratorsOnlyMode = authMode === AuthMode.MODERATORS_ONLY && role === ParticipantRole.MODERATOR;
const isAllUsersMode = authMode === AuthMode.ALL_USERS;
const isModeratorsOnlyMode = authModeToAccessRoom === AuthMode.MODERATORS_ONLY && role === ParticipantRole.MODERATOR;
const isAllUsersMode = authModeToAccessRoom === AuthMode.ALL_USERS;
if (isModeratorsOnlyMode || isAllUsersMode) {
authValidators.push(tokenAndRoleValidator(UserRole.USER));

View File

@ -1,11 +1,10 @@
import {
AuthenticationPreferencesDTO,
AuthenticationPreferences,
AuthMode,
AuthType,
RoomCreationPolicy,
SingleUserAuthDTO,
UpdateSecurityPreferencesDTO,
ValidAuthMethodDTO,
SecurityPreferences,
SingleUserAuth,
ValidAuthMethod,
WebhookPreferences
} from '@typings-ce';
import { NextFunction, Request, Response } from 'express';
@ -36,41 +35,20 @@ const AuthModeSchema: z.ZodType<AuthMode> = z.enum([AuthMode.NONE, AuthMode.MODE
const AuthTypeSchema: z.ZodType<AuthType> = z.enum([AuthType.SINGLE_USER]);
const SingleUserAuthDTOSchema: z.ZodType<SingleUserAuthDTO> = z.object({
const SingleUserAuthSchema: z.ZodType<SingleUserAuth> = z.object({
type: AuthTypeSchema
});
const ValidAuthMethodDTOSchema: z.ZodType<ValidAuthMethodDTO> = SingleUserAuthDTOSchema;
const ValidAuthMethodSchema: z.ZodType<ValidAuthMethod> = SingleUserAuthSchema;
const AuthenticationPreferencesDTOSchema: z.ZodType<AuthenticationPreferencesDTO> = z.object({
authMode: AuthModeSchema,
method: ValidAuthMethodDTOSchema
const AuthenticationPreferencesSchema: z.ZodType<AuthenticationPreferences> = z.object({
authMethod: ValidAuthMethodSchema,
authModeToAccessRoom: AuthModeSchema
});
const RoomCreationPolicySchema: z.ZodType<RoomCreationPolicy> = z
.object({
allowRoomCreation: z.boolean(),
requireAuthentication: z.boolean().optional()
})
.refine(
(data) => {
// If allowRoomCreation is true, requireAuthentication must be provided
return !data.allowRoomCreation || data.requireAuthentication !== undefined;
},
{
message: 'requireAuthentication is required when allowRoomCreation is true',
path: ['requireAuthentication']
}
);
const UpdateSecurityPreferencesDTOSchema: z.ZodType<UpdateSecurityPreferencesDTO> = z
.object({
authentication: AuthenticationPreferencesDTOSchema.optional(),
roomCreationPolicy: RoomCreationPolicySchema.optional()
})
.refine((data) => Object.keys(data).length > 0, {
message: 'At least one field must be provided for the update'
});
const SecurityPreferencesSchema: z.ZodType<SecurityPreferences> = z.object({
authentication: AuthenticationPreferencesSchema
});
export const validateWebhookPreferences = (req: Request, res: Response, next: NextFunction) => {
const { success, error, data } = WebhookPreferencesSchema.safeParse(req.body);
@ -84,7 +62,7 @@ export const validateWebhookPreferences = (req: Request, res: Response, next: Ne
};
export const validateSecurityPreferences = (req: Request, res: Response, next: NextFunction) => {
const { success, error, data } = UpdateSecurityPreferencesDTOSchema.safeParse(req.body);
const { success, error, data } = SecurityPreferencesSchema.safeParse(req.body);
if (!success) {
return rejectUnprocessableRequest(res, error);

View File

@ -8,39 +8,7 @@ import {
rejectRequestFromMeetError
} from '../models/error.model.js';
import { MeetStorageService, RoomService } from '../services/index.js';
import { allowAnonymous, apiKeyValidator, tokenAndRoleValidator, withAuth } from './auth.middleware.js';
/**
* Middleware that configures authentication for creating a room based on global settings.
*
* - Admin role and API key authentication methods are always allowed.
* - If room creation is allowed and requires authentication, the user must have a valid token.
* - If room creation is allowed and does not require authentication, anonymous users are allowed.
*/
export const configureCreateRoomAuth = async (req: Request, res: Response, next: NextFunction) => {
const globalPrefService = container.get(MeetStorageService);
let allowRoomCreation: boolean;
let requireAuthentication: boolean | undefined;
try {
const { securityPreferences } = await globalPrefService.getGlobalPreferences();
({ allowRoomCreation, requireAuthentication } = securityPreferences.roomCreationPolicy);
} catch (error) {
return handleError(res, error, 'checking room creation policy');
}
const authValidators = [apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)];
if (allowRoomCreation) {
if (requireAuthentication) {
authValidators.push(tokenAndRoleValidator(UserRole.USER));
} else {
authValidators.push(allowAnonymous);
}
}
return withAuth(...authValidators)(req, res, next);
};
import { allowAnonymous, tokenAndRoleValidator, withAuth } from './auth.middleware.js';
/**
* Middleware that configures authorization for accessing a specific room.
@ -79,7 +47,7 @@ export const configureRoomAuthorization = async (req: Request, res: Response, ne
};
/**
* Middleware to configure authentication based on participant role and authentication mode
* Middleware to configure authentication based on participant role and authentication mode to access a room
* for generating a token for retrieving/deleting recordings.
*
* - If the authentication mode is MODERATORS_ONLY and the participant role is MODERATOR, configure user authentication.
@ -114,22 +82,22 @@ export const configureRecordingTokenAuth = async (req: Request, res: Response, n
return handleError(res, error, 'getting room role by secret');
}
let authMode: AuthMode;
let authModeToAccessRoom: AuthMode;
try {
const { securityPreferences } = await storageService.getGlobalPreferences();
authMode = securityPreferences.authentication.authMode;
authModeToAccessRoom = securityPreferences.authentication.authModeToAccessRoom;
} catch (error) {
return handleError(res, error, 'checking authentication preferences');
}
const authValidators = [];
if (authMode === AuthMode.NONE) {
if (authModeToAccessRoom === AuthMode.NONE) {
authValidators.push(allowAnonymous);
} else {
const isModeratorsOnlyMode = authMode === AuthMode.MODERATORS_ONLY && role === ParticipantRole.MODERATOR;
const isAllUsersMode = authMode === AuthMode.ALL_USERS;
const isModeratorsOnlyMode = authModeToAccessRoom === AuthMode.MODERATORS_ONLY && role === ParticipantRole.MODERATOR;
const isAllUsersMode = authModeToAccessRoom === AuthMode.ALL_USERS;
if (isModeratorsOnlyMode || isAllUsersMode) {
authValidators.push(tokenAndRoleValidator(UserRole.USER));

View File

@ -3,8 +3,8 @@ import bodyParser from 'body-parser';
import { Router } from 'express';
import * as roomCtrl from '../controllers/room.controller.js';
import {
allowAnonymous,
apiKeyValidator,
configureCreateRoomAuth,
configureRecordingTokenAuth,
configureRoomAuthorization,
participantTokenValidator,
@ -24,7 +24,12 @@ roomRouter.use(bodyParser.urlencoded({ extended: true }));
roomRouter.use(bodyParser.json());
// Room Routes
roomRouter.post('/', configureCreateRoomAuth, withValidRoomOptions, roomCtrl.createRoom);
roomRouter.post(
'/',
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
withValidRoomOptions,
roomCtrl.createRoom
);
roomRouter.get(
'/',
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
@ -56,7 +61,6 @@ export const internalRoomRouter = Router();
internalRoomRouter.use(bodyParser.urlencoded({ extended: true }));
internalRoomRouter.use(bodyParser.json());
// Room preferences
internalRoomRouter.put(
'/:roomId',
withAuth(tokenAndRoleValidator(UserRole.ADMIN)),
@ -64,7 +68,6 @@ internalRoomRouter.put(
withValidRoomPreferences,
roomCtrl.updateRoomPreferences
);
internalRoomRouter.post(
'/:roomId/recording-token',
configureRecordingTokenAuth,
@ -72,7 +75,15 @@ internalRoomRouter.post(
withValidRoomSecret,
roomCtrl.generateRecordingToken
);
// Roles and permissions
internalRoomRouter.get('/:roomId/roles', withValidRoomId, roomCtrl.getRoomRolesAndPermissions);
internalRoomRouter.get('/:roomId/roles/:secret', withValidRoomId, roomCtrl.getRoomRoleAndPermissions);
internalRoomRouter.get(
'/:roomId/roles',
withAuth(allowAnonymous),
withValidRoomId,
roomCtrl.getRoomRolesAndPermissions
);
internalRoomRouter.get(
'/:roomId/roles/:secret',
withAuth(allowAnonymous),
withValidRoomId,
roomCtrl.getRoomRoleAndPermissions
);

View File

@ -1,6 +1,6 @@
export interface AuthenticationPreferences {
authMode: AuthMode;
method: ValidAuthMethod;
authMethod: ValidAuthMethod;
authModeToAccessRoom: AuthMode;
}
/**
@ -33,7 +33,6 @@ export const enum AuthType {
*/
export interface SingleUserAuth extends AuthMethod {
type: AuthType.SINGLE_USER;
credentials: SingleUserCredentials;
}
/**
@ -57,14 +56,6 @@ export interface SingleUserAuth extends AuthMethod {
*/
export type ValidAuthMethod = SingleUserAuth /* | MultiUserAuth | OAuthOnlyAuth */;
/**
* Configuration for a single user login method.
*/
export interface SingleUserCredentials {
username: string;
passwordHash: string;
}
/**
* Configuration for OAuth authentication.
*/
@ -82,13 +73,3 @@ export interface SingleUserCredentials {
// GOOGLE = 'google',
// GITHUB = 'github'
// }
// DTOs
export interface AuthenticationPreferencesDTO {
authMode: AuthMode;
method: ValidAuthMethodDTO;
}
export type ValidAuthMethodDTO = SingleUserAuthDTO;
export type SingleUserAuthDTO = Omit<SingleUserAuth, 'credentials'>;

View File

@ -1,4 +1,4 @@
import { AuthenticationPreferences, AuthenticationPreferencesDTO } from './auth-preferences.js';
import { AuthenticationPreferences } from './auth-preferences.js';
/**
* Represents global preferences for OpenVidu Meet.
@ -18,19 +18,4 @@ export interface WebhookPreferences {
export interface SecurityPreferences {
authentication: AuthenticationPreferences;
roomCreationPolicy: RoomCreationPolicy;
// e2eEncryption: {};
}
export interface RoomCreationPolicy {
allowRoomCreation: boolean;
requireAuthentication?: boolean;
}
// DTOs
export interface SecurityPreferencesDTO {
authentication: AuthenticationPreferencesDTO;
roomCreationPolicy: RoomCreationPolicy;
}
export type UpdateSecurityPreferencesDTO = Partial<SecurityPreferencesDTO>;