backend: Add security and webhook preferences controllers and validation middleware
This commit is contained in:
parent
166389b606
commit
2d82d6a96d
@ -0,0 +1,76 @@
|
||||
import { container } from '../../config/dependency-injector.config.js';
|
||||
import { Request, Response } from 'express';
|
||||
import { LoggerService } from '../../services/logger.service.js';
|
||||
import { GlobalPreferencesService } from '../../services/preferences/index.js';
|
||||
import { OpenViduMeetError } from '../../models/error.model.js';
|
||||
import { SecurityPreferencesDTO, UpdateSecurityPreferencesDTO } from '@typings-ce';
|
||||
|
||||
export const updateSecurityPreferences = async (req: Request, res: Response) => {
|
||||
const logger = container.get(LoggerService);
|
||||
const globalPrefService = container.get(GlobalPreferencesService);
|
||||
|
||||
logger.verbose(`Updating security preferences: ${JSON.stringify(req.body)}`);
|
||||
const securityPreferences = req.body as UpdateSecurityPreferencesDTO;
|
||||
|
||||
try {
|
||||
const globalPreferences = await globalPrefService.getGlobalPreferences();
|
||||
|
||||
if (securityPreferences.roomCreationPolicy) {
|
||||
globalPreferences.securityPreferences.roomCreationPolicy = securityPreferences.roomCreationPolicy;
|
||||
}
|
||||
|
||||
if (securityPreferences.authentication) {
|
||||
const currentAuth = globalPreferences.securityPreferences.authentication;
|
||||
const newAuth = securityPreferences.authentication;
|
||||
|
||||
currentAuth.authMode = newAuth.authMode;
|
||||
currentAuth.method.type = newAuth.method.type;
|
||||
}
|
||||
|
||||
await globalPrefService.saveGlobalPreferences(globalPreferences);
|
||||
|
||||
return res.status(200).json({ message: 'Security preferences updated successfully' });
|
||||
} catch (error) {
|
||||
if (error instanceof OpenViduMeetError) {
|
||||
logger.error(`Error updating security preferences: ${error.message}`);
|
||||
return res.status(error.statusCode).json({ name: error.name, message: error.message });
|
||||
}
|
||||
|
||||
logger.error('Error updating security preferences:' + error);
|
||||
return res.status(500).json({ message: 'Error updating security preferences' });
|
||||
}
|
||||
};
|
||||
|
||||
export const getSecurityPreferences = async (_req: Request, res: Response) => {
|
||||
const logger = container.get(LoggerService);
|
||||
const preferenceService = container.get(GlobalPreferencesService);
|
||||
|
||||
try {
|
||||
const preferences = await preferenceService.getGlobalPreferences();
|
||||
|
||||
if (!preferences) {
|
||||
return res.status(404).json({ message: 'Security preferences not found' });
|
||||
}
|
||||
|
||||
// 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);
|
||||
} catch (error) {
|
||||
if (error instanceof OpenViduMeetError) {
|
||||
logger.error(`Error getting security preferences: ${error.message}`);
|
||||
return res.status(error.statusCode).json({ name: error.name, message: error.message });
|
||||
}
|
||||
|
||||
logger.error('Error getting security preferences:' + error);
|
||||
return res.status(500).json({ message: 'Error fetching security preferences from database' });
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,53 @@
|
||||
import { container } from '../../config/dependency-injector.config.js';
|
||||
import { Request, Response } from 'express';
|
||||
import { LoggerService } from '../../services/logger.service.js';
|
||||
import { GlobalPreferencesService } from '../../services/preferences/index.js';
|
||||
import { OpenViduMeetError } from '../../models/error.model.js';
|
||||
import { WebhookPreferences } from '@typings-ce';
|
||||
|
||||
export const updateWebhookPreferences = async (req: Request, res: Response) => {
|
||||
const logger = container.get(LoggerService);
|
||||
const globalPrefService = container.get(GlobalPreferencesService);
|
||||
|
||||
logger.verbose(`Updating webhooks preferences: ${JSON.stringify(req.body)}`);
|
||||
const webhookPreferences = req.body as WebhookPreferences;
|
||||
|
||||
try {
|
||||
const globalPreferences = await globalPrefService.getGlobalPreferences();
|
||||
globalPreferences.webhooksPreferences = webhookPreferences;
|
||||
await globalPrefService.saveGlobalPreferences(globalPreferences);
|
||||
|
||||
return res.status(200).json({ message: 'Webhooks preferences updated successfully' });
|
||||
} catch (error) {
|
||||
if (error instanceof OpenViduMeetError) {
|
||||
logger.error(`Error updating webhooks preferences: ${error.message}`);
|
||||
return res.status(error.statusCode).json({ name: error.name, message: error.message });
|
||||
}
|
||||
|
||||
logger.error('Error updating webhooks preferences:' + error);
|
||||
return res.status(500).json({ message: 'Error updating webhooks preferences' });
|
||||
}
|
||||
};
|
||||
|
||||
export const getWebhookPreferences = async (_req: Request, res: Response) => {
|
||||
const logger = container.get(LoggerService);
|
||||
const preferenceService = container.get(GlobalPreferencesService);
|
||||
|
||||
try {
|
||||
const preferences = await preferenceService.getGlobalPreferences();
|
||||
|
||||
if (!preferences) {
|
||||
return res.status(404).json({ message: 'Webhooks preferences not found' });
|
||||
}
|
||||
|
||||
return res.status(200).json(preferences.webhooksPreferences);
|
||||
} catch (error) {
|
||||
if (error instanceof OpenViduMeetError) {
|
||||
logger.error(`Error getting webhooks preferences: ${error.message}`);
|
||||
return res.status(error.statusCode).json({ name: error.name, message: error.message });
|
||||
}
|
||||
|
||||
logger.error('Error getting webhooks preferences:' + error);
|
||||
return res.status(500).json({ message: 'Error fetching webhooks preferences from database' });
|
||||
}
|
||||
};
|
||||
@ -2,4 +2,8 @@ export * from './auth.controller.js';
|
||||
export * from './recording.controller.js';
|
||||
export * from './room.controller.js';
|
||||
export * from './participant.controller.js';
|
||||
export * from './livekit-webhook.controller.js';
|
||||
export * from './livekit-webhook.controller.js';
|
||||
|
||||
export * from './global-preferences/appearance-preferences.controller.js';
|
||||
export * from './global-preferences/webhook-preferences.controller.js';
|
||||
export * from './global-preferences/security-preferences.controller.js';
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
import {
|
||||
AuthenticationPreferencesDTO,
|
||||
AuthMode,
|
||||
AuthType,
|
||||
RoomCreationPolicy,
|
||||
SingleUserAuthDTO,
|
||||
UpdateSecurityPreferencesDTO,
|
||||
ValidAuthMethodDTO,
|
||||
WebhookPreferences
|
||||
} from '@typings-ce';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { z } from 'zod';
|
||||
|
||||
const WebhookPreferencesSchema: z.ZodType<WebhookPreferences> = z.object({
|
||||
enabled: z.boolean(),
|
||||
url: z.string().url()
|
||||
});
|
||||
|
||||
const AuthModeSchema: z.ZodType<AuthMode> = z.enum([AuthMode.NONE, AuthMode.MODERATORS_ONLY, AuthMode.ALL_USERS]);
|
||||
|
||||
const AuthTypeSchema: z.ZodType<AuthType> = z.enum([AuthType.SINGLE_USER]);
|
||||
|
||||
const SingleUserAuthDTOSchema: z.ZodType<SingleUserAuthDTO> = z.object({
|
||||
type: AuthTypeSchema
|
||||
});
|
||||
|
||||
const ValidAuthMethodDTOSchema: z.ZodType<ValidAuthMethodDTO> = SingleUserAuthDTOSchema;
|
||||
|
||||
const AuthenticationPreferencesDTOSchema: z.ZodType<AuthenticationPreferencesDTO> = z.object({
|
||||
authMode: AuthModeSchema,
|
||||
method: ValidAuthMethodDTOSchema
|
||||
});
|
||||
|
||||
const RoomCreationPolicySchema: z.ZodType<RoomCreationPolicy> = z.object({
|
||||
allowRoomCreation: z.boolean(),
|
||||
requireAuthentication: z.boolean()
|
||||
});
|
||||
|
||||
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'
|
||||
});
|
||||
|
||||
export const validateWebhookPreferences = (req: Request, res: Response, next: NextFunction) => {
|
||||
const { success, error, data } = WebhookPreferencesSchema.safeParse(req.body);
|
||||
|
||||
if (!success) {
|
||||
return rejectRequest(res, error);
|
||||
}
|
||||
|
||||
req.body = data;
|
||||
next();
|
||||
};
|
||||
|
||||
export const validateSecurityPreferences = (req: Request, res: Response, next: NextFunction) => {
|
||||
const { success, error, data } = UpdateSecurityPreferencesDTOSchema.safeParse(req.body);
|
||||
|
||||
if (!success) {
|
||||
return rejectRequest(res, error);
|
||||
}
|
||||
|
||||
req.body = data;
|
||||
next();
|
||||
};
|
||||
|
||||
const rejectRequest = (res: Response, error: z.ZodError) => {
|
||||
const errors = error.errors.map((error) => ({
|
||||
field: error.path.join('.'),
|
||||
message: error.message
|
||||
}));
|
||||
|
||||
return res.status(422).json({
|
||||
error: 'Unprocessable Entity',
|
||||
message: 'Invalid request body',
|
||||
details: errors
|
||||
});
|
||||
};
|
||||
@ -1,24 +1,49 @@
|
||||
import { Router } from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
import {
|
||||
getAppearancePreferences,
|
||||
updateAppearancePreferences
|
||||
} from '../controllers/global-preferences/appearance-preferences.controller.js';
|
||||
import * as appearancePrefCtrl from '../controllers/global-preferences/appearance-preferences.controller.js';
|
||||
import * as webhookPrefCtrl from '../controllers/global-preferences/webhook-preferences.controller.js';
|
||||
import * as securityPrefCtrl from '../controllers/global-preferences/security-preferences.controller.js';
|
||||
import { withAuth, tokenAndRoleValidator, apiKeyValidator } from '../middlewares/auth.middleware.js';
|
||||
import { UserRole } from '@typings-ce';
|
||||
import {
|
||||
validateSecurityPreferences,
|
||||
validateWebhookPreferences
|
||||
} from '../middlewares/request-validators/preferences-validator.middleware.js';
|
||||
|
||||
export const preferencesRouter = Router();
|
||||
|
||||
preferencesRouter.use(bodyParser.urlencoded({ extended: true }));
|
||||
preferencesRouter.use(bodyParser.json());
|
||||
|
||||
// Webhook preferences
|
||||
preferencesRouter.put(
|
||||
'/webhooks',
|
||||
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
|
||||
validateWebhookPreferences,
|
||||
webhookPrefCtrl.updateWebhookPreferences
|
||||
);
|
||||
preferencesRouter.get(
|
||||
'/webhooks',
|
||||
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
|
||||
webhookPrefCtrl.getWebhookPreferences
|
||||
);
|
||||
|
||||
// Security preferences
|
||||
preferencesRouter.put(
|
||||
'/security',
|
||||
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
|
||||
validateSecurityPreferences,
|
||||
securityPrefCtrl.updateSecurityPreferences
|
||||
);
|
||||
preferencesRouter.get('/security', securityPrefCtrl.getSecurityPreferences);
|
||||
|
||||
// Appearance preferences
|
||||
preferencesRouter.put(
|
||||
'/appearance',
|
||||
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
|
||||
updateAppearancePreferences
|
||||
appearancePrefCtrl.updateAppearancePreferences
|
||||
);
|
||||
preferencesRouter.get(
|
||||
'/appearance',
|
||||
withAuth(apiKeyValidator, tokenAndRoleValidator(UserRole.ADMIN)),
|
||||
getAppearancePreferences
|
||||
appearancePrefCtrl.getAppearancePreferences
|
||||
);
|
||||
|
||||
@ -53,6 +53,16 @@ export class GlobalPreferencesService<
|
||||
return await this.ensurePreferencesInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the global preferences.
|
||||
* @param {GlobalPreferences} preferences
|
||||
* @returns {Promise<GlobalPreferences>}
|
||||
*/
|
||||
async saveGlobalPreferences(preferences: G): Promise<G> {
|
||||
this.logger.info('Saving global preferences');
|
||||
return this.storage.saveGlobalPreferences(preferences) as Promise<G>;
|
||||
}
|
||||
|
||||
async saveOpenViduRoom(ovRoom: R): Promise<R> {
|
||||
this.logger.info(`Saving OpenVidu room ${ovRoom.roomName}`);
|
||||
return this.storage.saveOpenViduRoom(ovRoom) as Promise<R>;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user