test: refactor authentication in integration tests to use header-based access tokens; add coverage for cookie-based mode

This commit is contained in:
juancarmore 2025-10-10 18:23:47 +02:00
parent c93306b705
commit 479e94add8
35 changed files with 1380 additions and 605 deletions

View File

@ -598,19 +598,6 @@ export const expectValidParticipantTokenResponse = (
expect(metadata).toHaveProperty('roles'); expect(metadata).toHaveProperty('roles');
expect(metadata.roles).toEqual(expect.arrayContaining(rolesAndPermissions)); expect(metadata.roles).toEqual(expect.arrayContaining(rolesAndPermissions));
expect(metadata).toHaveProperty('selectedRole', participantRole); expect(metadata).toHaveProperty('selectedRole', participantRole);
// Check that the token is included in a cookie
expect(response.headers['set-cookie']).toBeDefined();
const cookies = response.headers['set-cookie'] as unknown as string[];
const participantTokenCookie = cookies.find((cookie) =>
cookie.startsWith(`${INTERNAL_CONFIG.PARTICIPANT_TOKEN_COOKIE_NAME}=`)
) as string;
expect(participantTokenCookie).toBeDefined();
expect(participantTokenCookie).toContain(token);
expect(participantTokenCookie).toContain('HttpOnly');
expect(participantTokenCookie).toContain('SameSite=None');
expect(participantTokenCookie).toContain('Secure');
expect(participantTokenCookie).toContain('Path=/');
}; };
export const expectValidRecordingTokenResponse = ( export const expectValidRecordingTokenResponse = (
@ -636,19 +623,6 @@ export const expectValidRecordingTokenResponse = (
canRetrieveRecordings, canRetrieveRecordings,
canDeleteRecordings canDeleteRecordings
}); });
// Check that the token is included in a cookie
expect(response.headers['set-cookie']).toBeDefined();
const cookies = response.headers['set-cookie'] as unknown as string[];
const recordingTokenCookie = cookies.find((cookie) =>
cookie.startsWith(`${INTERNAL_CONFIG.RECORDING_TOKEN_COOKIE_NAME}=`)
) as string;
expect(recordingTokenCookie).toBeDefined();
expect(recordingTokenCookie).toContain(token);
expect(recordingTokenCookie).toContain('HttpOnly');
expect(recordingTokenCookie).toContain('SameSite=None');
expect(recordingTokenCookie).toContain('Secure');
expect(recordingTokenCookie).toContain('Path=/');
}; };
const decodeJWTToken = (token: string) => { const decodeJWTToken = (token: string) => {

View File

@ -18,7 +18,6 @@ import { RecordingService, RoomService } from '../../src/services/index.js';
import { import {
AuthMode, AuthMode,
AuthTransportMode, AuthTransportMode,
AuthType,
MeetRecordingAccess, MeetRecordingAccess,
MeetRecordingInfo, MeetRecordingInfo,
MeetRecordingStatus, MeetRecordingStatus,
@ -57,10 +56,10 @@ export const startTestServer = (): Express => {
export const generateApiKey = async (): Promise<string> => { export const generateApiKey = async (): Promise<string> => {
checkAppIsRunning(); checkAppIsRunning();
const adminCookie = await loginUser(); const accessToken = await loginUser();
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth/api-keys`) .post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth/api-keys`)
.set('Cookie', adminCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken), accessToken)
.send(); .send();
expect(response.status).toBe(201); expect(response.status).toBe(201);
expect(response.body).toHaveProperty('key'); expect(response.body).toHaveProperty('key');
@ -70,10 +69,10 @@ export const generateApiKey = async (): Promise<string> => {
export const getApiKeys = async () => { export const getApiKeys = async () => {
checkAppIsRunning(); checkAppIsRunning();
const adminCookie = await loginUser(); const accessToken = await loginUser();
const response = await request(app) const response = await request(app)
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth/api-keys`) .get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth/api-keys`)
.set('Cookie', adminCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken), accessToken)
.send(); .send();
return response; return response;
}; };
@ -90,10 +89,10 @@ export const getRoomsAppearanceConfig = async () => {
export const updateRoomsAppearanceConfig = async (config: any) => { export const updateRoomsAppearanceConfig = async (config: any) => {
checkAppIsRunning(); checkAppIsRunning();
const adminCookie = await loginUser(); const accessToken = await loginUser();
const response = await request(app) const response = await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/rooms/appearance`) .put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/rooms/appearance`)
.set('Cookie', adminCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken), accessToken)
.send(config); .send(config);
return response; return response;
}; };
@ -101,10 +100,10 @@ export const updateRoomsAppearanceConfig = async (config: any) => {
export const getWebbhookConfig = async () => { export const getWebbhookConfig = async () => {
checkAppIsRunning(); checkAppIsRunning();
const adminCookie = await loginUser(); const accessToken = await loginUser();
const response = await request(app) const response = await request(app)
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/webhooks`) .get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/webhooks`)
.set('Cookie', adminCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken), accessToken)
.send(); .send();
return response; return response;
}; };
@ -112,10 +111,10 @@ export const getWebbhookConfig = async () => {
export const updateWebbhookConfig = async (config: WebhookConfig) => { export const updateWebbhookConfig = async (config: WebhookConfig) => {
checkAppIsRunning(); checkAppIsRunning();
const adminCookie = await loginUser(); const accessToken = await loginUser();
const response = await request(app) const response = await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/webhooks`) .put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/webhooks`)
.set('Cookie', adminCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken), accessToken)
.send(config); .send(config);
return response; return response;
@ -133,40 +132,51 @@ export const testWebhookUrl = async (url: string) => {
export const getSecurityConfig = async () => { export const getSecurityConfig = async () => {
checkAppIsRunning(); checkAppIsRunning();
const adminCookie = await loginUser(); const response = await request(app).get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/security`).send();
const response = await request(app)
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/security`)
.set('Cookie', adminCookie)
.send();
return response; return response;
}; };
export const updateSecurityConfig = async (config: any) => { export const updateSecurityConfig = async (config: any) => {
checkAppIsRunning(); checkAppIsRunning();
const adminCookie = await loginUser(); const accessToken = await loginUser();
const response = await request(app) const response = await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/security`) .put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/security`)
.set('Cookie', adminCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken), accessToken)
.send(config); .send(config);
return response; return response;
}; };
export const changeSecurityConfig = async (authMode: AuthMode) => { export const changeSecurityConfig = async (authMode: AuthMode) => {
const response = await updateSecurityConfig({ // Get current config to avoid overwriting other properties
authentication: { let response = await getSecurityConfig();
authMethod: { expect(response.status).toBe(200);
type: AuthType.SINGLE_USER const currentConfig = response.body;
},
authTransportMode: AuthTransportMode.COOKIE, currentConfig.authentication.authModeToAccessRoom = authMode;
authModeToAccessRoom: authMode response = await updateSecurityConfig(currentConfig);
}
});
expect(response.status).toBe(200); expect(response.status).toBe(200);
}; };
export const changeAuthTransportMode = async (authTransportMode: AuthTransportMode) => {
// Get current config to avoid overwriting other properties
let response = await getSecurityConfig();
expect(response.status).toBe(200);
const currentConfig = response.body;
currentConfig.authentication.authTransportMode = authTransportMode;
response = await updateSecurityConfig(currentConfig);
expect(response.status).toBe(200);
};
const getAuthTransportMode = async (): Promise<AuthTransportMode> => {
const response = await getSecurityConfig();
return response.body.authentication.authTransportMode;
};
/** /**
* Logs in a user and returns the access token cookie * Logs in a user and returns the access token in the format
* "Bearer <token>" or the cookie string if in cookie mode
*/ */
export const loginUser = async (): Promise<string> => { export const loginUser = async (): Promise<string> => {
checkAppIsRunning(); checkAppIsRunning();
@ -176,28 +186,56 @@ export const loginUser = async (): Promise<string> => {
.send(CREDENTIALS.admin) .send(CREDENTIALS.admin)
.expect(200); .expect(200);
const cookies = response.headers['set-cookie'] as unknown as string[]; const authTransportMode = await getAuthTransportMode();
const accessTokenCookie = cookies.find((cookie) =>
cookie.startsWith(`${INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME}=`) // Return token in header or cookie based on transport mode
) as string; if (authTransportMode === AuthTransportMode.COOKIE) {
return accessTokenCookie; const cookie = extractCookieFromHeaders(response, INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
return cookie!;
}
expect(response.body).toHaveProperty('accessToken');
return `Bearer ${response.body.accessToken}`;
}; };
export const getProfile = async (cookie: string) => { /**
* Extracts cookie from response headers
*
* @param response - The supertest response
* @param cookieName - Name of the cookie to extract
* @returns The cookie string
*/
export const extractCookieFromHeaders = (response: Response, cookieName: string): string | undefined => {
expect(response.headers['set-cookie']).toBeDefined();
const cookies = response.headers['set-cookie'] as unknown as string[];
return cookies?.find((cookie) => cookie.startsWith(`${cookieName}=`));
};
/**
* Selects the appropriate HTTP header name based on the format of the provided access token.
*
* If the access token starts with 'Bearer ', the specified header name is returned (typically 'Authorization').
* Otherwise, 'Cookie' is returned, indicating that the token should be sent as a cookie.
*/
const selectHeaderBasedOnToken = (headerName: string, accessToken: string): string => {
return accessToken.startsWith('Bearer ') ? headerName : 'Cookie';
};
export const getProfile = async (accessToken: string) => {
checkAppIsRunning(); checkAppIsRunning();
return await request(app) return await request(app)
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/profile`) .get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/profile`)
.set('Cookie', cookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken), accessToken)
.send(); .send();
}; };
export const changePassword = async (currentPassword: string, newPassword: string, cookie: string) => { export const changePassword = async (currentPassword: string, newPassword: string, accessToken: string) => {
checkAppIsRunning(); checkAppIsRunning();
return await request(app) return await request(app)
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/change-password`) .post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/change-password`)
.set('Cookie', cookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken), accessToken)
.send({ currentPassword, newPassword }); .send({ currentPassword, newPassword });
}; };
@ -229,13 +267,16 @@ export const getRooms = async (query: Record<string, any> = {}) => {
* @returns A Promise that resolves to the room data * @returns A Promise that resolves to the room data
* @throws Error if the app instance is not defined * @throws Error if the app instance is not defined
*/ */
export const getRoom = async (roomId: string, fields?: string, cookie?: string, role?: ParticipantRole) => { export const getRoom = async (roomId: string, fields?: string, participantToken?: string, role?: ParticipantRole) => {
checkAppIsRunning(); checkAppIsRunning();
const req = request(app).get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}`).query({ fields }); const req = request(app).get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms/${roomId}`).query({ fields });
if (cookie && role) { if (participantToken && role) {
req.set('Cookie', cookie).set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, role); req.set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, participantToken).set(
INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER,
role
);
} else { } else {
req.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY); req.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY);
} }
@ -372,49 +413,51 @@ export const getRoomRoleBySecret = async (roomId: string, secret: string) => {
return response; return response;
}; };
export const generateParticipantToken = async (participantOptions: any, cookie?: string) => { export const generateParticipantTokenRequest = async (participantOptions: any, previousToken?: string) => {
checkAppIsRunning(); checkAppIsRunning();
// Disable authentication to generate the token // Disable authentication to generate the token
await changeSecurityConfig(AuthMode.NONE); await changeSecurityConfig(AuthMode.NONE);
// Generate the participant token // Generate the participant token
const response = await request(app) const req = request(app).post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/participants/token`);
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/participants/token`)
.set('Cookie', cookie || '') if (previousToken) {
.send(participantOptions); req.set(selectHeaderBasedOnToken(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, previousToken), previousToken);
return response; }
req.send(participantOptions);
return await req;
}; };
/** /**
* Generates a participant token for a room and returns the cookie containing the token * Generates a participant token for a room and returns the JWT token in the format "Bearer <token>"
*/ */
export const generateParticipantTokenCookie = async ( export const generateParticipantToken = async (
roomId: string, roomId: string,
secret: string, secret: string,
participantName: string, participantName: string
cookie?: string
): Promise<string> => { ): Promise<string> => {
// Generate the participant token const response = await generateParticipantTokenRequest({
const response = await generateParticipantToken(
{
roomId, roomId,
secret, secret,
participantName participantName
}, });
cookie
);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Return the participant token cookie const authTransportMode = await getAuthTransportMode();
const cookies = response.headers['set-cookie'] as unknown as string[];
const participantTokenCookie = cookies.find((cookie) => // Return token in header or cookie based on transport mode
cookie.startsWith(`${INTERNAL_CONFIG.PARTICIPANT_TOKEN_COOKIE_NAME}=`) if (authTransportMode === AuthTransportMode.COOKIE) {
) as string; const cookie = extractCookieFromHeaders(response, INTERNAL_CONFIG.PARTICIPANT_TOKEN_COOKIE_NAME);
return participantTokenCookie; return cookie!;
}
expect(response.body).toHaveProperty('token');
return `Bearer ${response.body.token}`;
}; };
export const refreshParticipantToken = async (participantOptions: any, cookie: string) => { export const refreshParticipantToken = async (participantOptions: any, previousToken: string) => {
checkAppIsRunning(); checkAppIsRunning();
// Disable authentication to generate the token // Disable authentication to generate the token
@ -422,7 +465,7 @@ export const refreshParticipantToken = async (participantOptions: any, cookie: s
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/participants/token/refresh`) .post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/participants/token/refresh`)
.set('Cookie', cookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, previousToken), previousToken)
.send(participantOptions); .send(participantOptions);
return response; return response;
}; };
@ -541,42 +584,42 @@ export const updateParticipant = async (
roomId: string, roomId: string,
participantIdentity: string, participantIdentity: string,
newRole: ParticipantRole, newRole: ParticipantRole,
moderatorCookie: string moderatorToken: string
) => { ) => {
checkAppIsRunning(); checkAppIsRunning();
const response = await request(app) const response = await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/meetings/${roomId}/participants/${participantIdentity}/role`) .put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/meetings/${roomId}/participants/${participantIdentity}/role`)
.set('Cookie', moderatorCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, moderatorToken), moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR) .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR)
.send({ role: newRole }); .send({ role: newRole });
return response; return response;
}; };
export const kickParticipant = async (roomId: string, participantIdentity: string, moderatorCookie: string) => { export const kickParticipant = async (roomId: string, participantIdentity: string, moderatorToken: string) => {
checkAppIsRunning(); checkAppIsRunning();
const response = await request(app) const response = await request(app)
.delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/meetings/${roomId}/participants/${participantIdentity}`) .delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/meetings/${roomId}/participants/${participantIdentity}`)
.set('Cookie', moderatorCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, moderatorToken), moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR) .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR)
.send(); .send();
return response; return response;
}; };
export const endMeeting = async (roomId: string, moderatorCookie: string) => { export const endMeeting = async (roomId: string, moderatorToken: string) => {
checkAppIsRunning(); checkAppIsRunning();
const response = await request(app) const response = await request(app)
.delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/meetings/${roomId}`) .delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/meetings/${roomId}`)
.set('Cookie', moderatorCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, moderatorToken), moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR) .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR)
.send(); .send();
await sleep('1s'); await sleep('1s');
return response; return response;
}; };
export const generateRecordingToken = async (roomId: string, secret: string) => { export const generateRecordingTokenRequest = async (roomId: string, secret: string) => {
checkAppIsRunning(); checkAppIsRunning();
// Disable authentication to generate the token // Disable authentication to generate the token
@ -591,39 +634,42 @@ export const generateRecordingToken = async (roomId: string, secret: string) =>
}; };
/** /**
* Generates a token for retrieving/deleting recordings from a room and returns the cookie containing the token * Generates a token for retrieving/deleting recordings from a room and returns the JWT token in the format "Bearer <token>"
*/ */
export const generateRecordingTokenCookie = async (roomId: string, secret: string) => { export const generateRecordingToken = async (roomId: string, secret: string) => {
// Generate the recording token const response = await generateRecordingTokenRequest(roomId, secret);
const response = await generateRecordingToken(roomId, secret);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Return the recording token cookie const authTransportMode = await getAuthTransportMode();
const cookies = response.headers['set-cookie'] as unknown as string[];
const recordingTokenCookie = cookies.find((cookie) => // Return token in header or cookie based on transport mode
cookie.startsWith(`${INTERNAL_CONFIG.RECORDING_TOKEN_COOKIE_NAME}=`) if (authTransportMode === AuthTransportMode.COOKIE) {
) as string; const cookie = extractCookieFromHeaders(response, INTERNAL_CONFIG.RECORDING_TOKEN_COOKIE_NAME);
return recordingTokenCookie; return cookie!;
}
expect(response.body).toHaveProperty('token');
return `Bearer ${response.body.token}`;
}; };
export const startRecording = async (roomId: string, moderatorCookie = '') => { export const startRecording = async (roomId: string, moderatorToken: string) => {
checkAppIsRunning(); checkAppIsRunning();
return await request(app) return await request(app)
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/recordings`) .post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/recordings`)
.set('Cookie', moderatorCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, moderatorToken), moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR) .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR)
.send({ .send({
roomId roomId
}); });
}; };
export const stopRecording = async (recordingId: string, moderatorCookie = '') => { export const stopRecording = async (recordingId: string, moderatorToken: string) => {
checkAppIsRunning(); checkAppIsRunning();
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/recordings/${recordingId}/stop`) .post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/recordings/${recordingId}/stop`)
.set('Cookie', moderatorCookie) .set(selectHeaderBasedOnToken(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, moderatorToken), moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR) .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR)
.send(); .send();
await sleep('2.5s'); await sleep('2.5s');
@ -670,15 +716,15 @@ export const deleteRecording = async (recordingId: string) => {
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY); .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY);
}; };
export const bulkDeleteRecordings = async (recordingIds: any[], recordingTokenCookie?: string): Promise<Response> => { export const bulkDeleteRecordings = async (recordingIds: any[], recordingToken?: string): Promise<Response> => {
checkAppIsRunning(); checkAppIsRunning();
const req = request(app) const req = request(app)
.delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`) .delete(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`)
.query({ recordingIds: recordingIds.join(',') }); .query({ recordingIds: recordingIds.join(',') });
if (recordingTokenCookie) { if (recordingToken) {
req.set('Cookie', recordingTokenCookie); req.set(selectHeaderBasedOnToken(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken), recordingToken);
} else { } else {
req.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY); req.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY);
} }
@ -689,7 +735,7 @@ export const bulkDeleteRecordings = async (recordingIds: any[], recordingTokenCo
export const downloadRecordings = async ( export const downloadRecordings = async (
recordingIds: string[], recordingIds: string[],
asBuffer = true, asBuffer = true,
recordingTokenCookie?: string recordingToken?: string
): Promise<Response> => { ): Promise<Response> => {
checkAppIsRunning(); checkAppIsRunning();
@ -697,8 +743,8 @@ export const downloadRecordings = async (
.get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings/download`) .get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings/download`)
.query({ recordingIds: recordingIds.join(',') }); .query({ recordingIds: recordingIds.join(',') });
if (recordingTokenCookie) { if (recordingToken) {
req.set('Cookie', recordingTokenCookie); req.set(selectHeaderBasedOnToken(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken), recordingToken);
} else { } else {
req.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY); req.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY);
} }
@ -714,7 +760,7 @@ export const downloadRecordings = async (
return await req; return await req;
}; };
export const stopAllRecordings = async (moderatorCookie: string) => { export const stopAllRecordings = async (moderatorToken: string) => {
checkAppIsRunning(); checkAppIsRunning();
const response = await getAllRecordings(); const response = await getAllRecordings();
@ -731,8 +777,8 @@ export const stopAllRecordings = async (moderatorCookie: string) => {
const tasks = recordingIds.map((recordingId: string) => const tasks = recordingIds.map((recordingId: string) =>
request(app) request(app)
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/recordings/${recordingId}/stop`) .post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/recordings/${recordingId}/stop`)
.set(selectHeaderBasedOnToken(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, moderatorToken), moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR) .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR)
.set('Cookie', moderatorCookie)
.send() .send()
); );
const results = await Promise.all(tasks); const results = await Promise.all(tasks);
@ -753,10 +799,12 @@ export const getAllRecordings = async (query: Record<string, any> = {}) => {
.query(query); .query(query);
}; };
export const getAllRecordingsFromRoom = async (recordingTokenCookie: string) => { export const getAllRecordingsFromRoom = async (recordingToken: string) => {
checkAppIsRunning(); checkAppIsRunning();
return await request(app).get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`).set('Cookie', recordingTokenCookie); return await request(app)
.get(`${INTERNAL_CONFIG.API_BASE_PATH_V1}/recordings`)
.set(selectHeaderBasedOnToken(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken), recordingToken);
}; };
export const deleteAllRecordings = async () => { export const deleteAllRecordings = async () => {

View File

@ -6,7 +6,7 @@ import { MeetRoom, MeetRoomConfig } from '../../src/typings/ce';
import { expectValidStartRecordingResponse } from './assertion-helpers'; import { expectValidStartRecordingResponse } from './assertion-helpers';
import { import {
createRoom, createRoom,
generateParticipantTokenCookie, generateParticipantToken,
joinFakeParticipant, joinFakeParticipant,
sleep, sleep,
startRecording, startRecording,
@ -18,9 +18,9 @@ let mockWebhookServer: http.Server;
export interface RoomData { export interface RoomData {
room: MeetRoom; room: MeetRoom;
moderatorSecret: string; moderatorSecret: string;
moderatorCookie: string; moderatorToken: string;
speakerSecret: string; speakerSecret: string;
speakerCookie: string; speakerToken: string;
recordingId?: string; recordingId?: string;
} }
@ -36,7 +36,7 @@ export interface TestContext {
* @param withParticipant Whether to join a fake participant in the room. * @param withParticipant Whether to join a fake participant in the room.
* @param roomName Name of the room to create. * @param roomName Name of the room to create.
* @param config Optional room config. * @param config Optional room config.
* @returns Room data including secrets and cookies. * @returns Room data including secrets and tokens.
*/ */
export const setupSingleRoom = async ( export const setupSingleRoom = async (
withParticipant = false, withParticipant = false,
@ -48,11 +48,11 @@ export const setupSingleRoom = async (
config config
}); });
// Extract the room secrets and generate participant tokens, saved as cookies // Extract the room secrets and generate participant tokens
const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room); const { moderatorSecret, speakerSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
const [moderatorCookie, speakerCookie] = await Promise.all([ const [moderatorToken, speakerToken] = await Promise.all([
generateParticipantTokenCookie(room.roomId, moderatorSecret, 'MODERATOR'), generateParticipantToken(room.roomId, moderatorSecret, 'MODERATOR'),
generateParticipantTokenCookie(room.roomId, speakerSecret, 'SPEAKER') generateParticipantToken(room.roomId, speakerSecret, 'SPEAKER')
]); ]);
// Join participant if needed // Join participant if needed
@ -63,9 +63,9 @@ export const setupSingleRoom = async (
return { return {
room, room,
moderatorSecret, moderatorSecret,
moderatorCookie, moderatorToken,
speakerSecret, speakerSecret,
speakerCookie speakerToken
}; };
}; };
@ -109,7 +109,7 @@ export const setupSingleRoomWithRecording = async (
stopDelay?: StringValue stopDelay?: StringValue
): Promise<RoomData> => { ): Promise<RoomData> => {
const roomData = await setupSingleRoom(true, 'TEST_ROOM'); const roomData = await setupSingleRoom(true, 'TEST_ROOM');
const response = await startRecording(roomData.room.roomId, roomData.moderatorCookie); const response = await startRecording(roomData.room.roomId, roomData.moderatorToken);
expectValidStartRecordingResponse(response, roomData.room.roomId, roomData.room.roomName); expectValidStartRecordingResponse(response, roomData.room.roomId, roomData.room.roomName);
roomData.recordingId = response.body.recordingId; roomData.recordingId = response.body.recordingId;
@ -119,7 +119,7 @@ export const setupSingleRoomWithRecording = async (
} }
if (stopRecordingCond) { if (stopRecordingCond) {
await stopRecording(roomData.recordingId!, roomData.moderatorCookie); await stopRecording(roomData.recordingId!, roomData.moderatorToken);
} }
return roomData; return roomData;
@ -154,7 +154,7 @@ export const setupMultiRecordingsTestContext = async (
} }
// Send start recording request // Send start recording request
const response = await startRecording(roomData.room.roomId, roomData.moderatorCookie); const response = await startRecording(roomData.room.roomId, roomData.moderatorToken);
expectValidStartRecordingResponse(response, roomData.room.roomId, roomData.room.roomName); expectValidStartRecordingResponse(response, roomData.room.roomId, roomData.room.roomName);
// Store the recordingId in context // Store the recordingId in context
@ -171,7 +171,7 @@ export const setupMultiRecordingsTestContext = async (
// Stop recordings for the first numStops rooms // Stop recordings for the first numStops rooms
const stopPromises = startedRooms.slice(0, numStops).map(async (roomData) => { const stopPromises = startedRooms.slice(0, numStops).map(async (roomData) => {
if (roomData.recordingId) { if (roomData.recordingId) {
await stopRecording(roomData.recordingId, roomData.moderatorCookie); await stopRecording(roomData.recordingId, roomData.moderatorToken);
console.log(`Recording stopped for room ${roomData.room.roomId}`); console.log(`Recording stopped for room ${roomData.room.roomId}`);
return roomData.recordingId; return roomData.recordingId;
} }

View File

@ -10,7 +10,7 @@ const defaultConfig = {
authMethod: { authMethod: {
type: AuthType.SINGLE_USER type: AuthType.SINGLE_USER
}, },
authTransportMode: AuthTransportMode.COOKIE, authTransportMode: AuthTransportMode.HEADER,
authModeToAccessRoom: AuthMode.NONE authModeToAccessRoom: AuthMode.NONE
} }
}; };
@ -36,7 +36,7 @@ describe('Security Config API Tests', () => {
authMethod: { authMethod: {
type: AuthType.SINGLE_USER type: AuthType.SINGLE_USER
}, },
authTransportMode: AuthTransportMode.COOKIE, authTransportMode: AuthTransportMode.HEADER,
authModeToAccessRoom: AuthMode.ALL_USERS authModeToAccessRoom: AuthMode.ALL_USERS
} }
}; };
@ -108,7 +108,7 @@ describe('Security Config API Tests', () => {
let response = await updateSecurityConfig({ let response = await updateSecurityConfig({
authentication: { authentication: {
authMode: AuthMode.NONE, authMode: AuthMode.NONE,
authTransportMode: AuthTransportMode.COOKIE authTransportMode: AuthTransportMode.HEADER
} }
}); });
expectValidationError(response, 'authentication.authMethod', 'Required'); expectValidationError(response, 'authentication.authMethod', 'Required');
@ -128,7 +128,7 @@ describe('Security Config API Tests', () => {
authMethod: { authMethod: {
type: AuthType.SINGLE_USER type: AuthType.SINGLE_USER
}, },
authTransportMode: AuthTransportMode.COOKIE authTransportMode: AuthTransportMode.HEADER
} }
}); });
expectValidationError(response, 'authentication.authModeToAccessRoom', 'Required'); expectValidationError(response, 'authentication.authModeToAccessRoom', 'Required');

View File

@ -35,7 +35,7 @@ describe('Meetings API Tests', () => {
expect(lkRoom.name).toBe(roomData.room.roomId); expect(lkRoom.name).toBe(roomData.room.roomId);
// End the meeting // End the meeting
let response = await endMeeting(roomData.room.roomId, roomData.moderatorCookie); let response = await endMeeting(roomData.room.roomId, roomData.moderatorToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Check if the LiveKit room has been removed // Check if the LiveKit room has been removed
@ -62,7 +62,7 @@ describe('Meetings API Tests', () => {
} }
// End the meeting // End the meeting
const response = await endMeeting(roomData.room.roomId, roomData.moderatorCookie); const response = await endMeeting(roomData.room.roomId, roomData.moderatorToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Recreate the room with a participant // Recreate the room with a participant
@ -74,7 +74,7 @@ describe('Meetings API Tests', () => {
let response = await deleteRoom(roomData.room.roomId, { withMeeting: 'force' }); let response = await deleteRoom(roomData.room.roomId, { withMeeting: 'force' });
expect(response.status).toBe(200); expect(response.status).toBe(200);
response = await endMeeting(roomData.room.roomId, roomData.moderatorCookie); response = await endMeeting(roomData.room.roomId, roomData.moderatorToken);
expect(response.status).toBe(404); expect(response.status).toBe(404);
}); });
}); });

View File

@ -39,7 +39,7 @@ describe('Meetings API Tests', () => {
expect(participant.identity).toBe(participantIdentity); expect(participant.identity).toBe(participantIdentity);
// Delete the participant // Delete the participant
const response = await kickParticipant(roomData.room.roomId, participantIdentity, roomData.moderatorCookie); const response = await kickParticipant(roomData.room.roomId, participantIdentity, roomData.moderatorToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Check if the participant has been removed from LiveKit // Check if the participant has been removed from LiveKit
@ -54,7 +54,7 @@ describe('Meetings API Tests', () => {
const response = await kickParticipant( const response = await kickParticipant(
roomData.room.roomId, roomData.room.roomId,
'NON_EXISTENT_PARTICIPANT', 'NON_EXISTENT_PARTICIPANT',
roomData.moderatorCookie roomData.moderatorToken
); );
expect(response.status).toBe(404); expect(response.status).toBe(404);
expect(response.body.error).toBe('Participant Error'); expect(response.body.error).toBe('Participant Error');
@ -65,7 +65,7 @@ describe('Meetings API Tests', () => {
let response = await deleteRoom(roomData.room.roomId, { withMeeting: 'force' }); let response = await deleteRoom(roomData.room.roomId, { withMeeting: 'force' });
expect(response.status).toBe(200); expect(response.status).toBe(200);
response = await kickParticipant(roomData.room.roomId, participantIdentity, roomData.moderatorCookie); response = await kickParticipant(roomData.room.roomId, participantIdentity, roomData.moderatorToken);
expect(response.status).toBe(404); expect(response.status).toBe(404);
expect(response.body.error).toBe('Room Error'); expect(response.body.error).toBe('Room Error');
}); });

View File

@ -2,6 +2,7 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it, jest } from '@je
import { container } from '../../../../src/config/index.js'; import { container } from '../../../../src/config/index.js';
import { LIVEKIT_URL } from '../../../../src/environment.js'; import { LIVEKIT_URL } from '../../../../src/environment.js';
import { FrontendEventService, LiveKitService } from '../../../../src/services/index.js'; import { FrontendEventService, LiveKitService } from '../../../../src/services/index.js';
import { MeetSignalType } from '../../../../src/typings/ce/event.model.js';
import { MeetTokenMetadata, ParticipantRole } from '../../../../src/typings/ce/index.js'; import { MeetTokenMetadata, ParticipantRole } from '../../../../src/typings/ce/index.js';
import { getPermissions } from '../../../helpers/assertion-helpers.js'; import { getPermissions } from '../../../helpers/assertion-helpers.js';
import { import {
@ -13,7 +14,6 @@ import {
updateParticipantMetadata updateParticipantMetadata
} from '../../../helpers/request-helpers.js'; } from '../../../helpers/request-helpers.js';
import { RoomData, setupSingleRoom } from '../../../helpers/test-scenarios.js'; import { RoomData, setupSingleRoom } from '../../../helpers/test-scenarios.js';
import { MeetSignalType } from '../../../../src/typings/ce/event.model.js';
const participantIdentity = 'TEST_PARTICIPANT'; const participantIdentity = 'TEST_PARTICIPANT';
@ -60,7 +60,7 @@ describe('Meetings API Tests', () => {
roomData.room.roomId, roomData.room.roomId,
participantIdentity, participantIdentity,
ParticipantRole.MODERATOR, ParticipantRole.MODERATOR,
roomData.moderatorCookie roomData.moderatorToken
); );
expect(response.status).toBe(200); expect(response.status).toBe(200);
@ -76,7 +76,8 @@ describe('Meetings API Tests', () => {
// Verify sendSignal method has been called twice // Verify sendSignal method has been called twice
expect(sendSignalSpy).toHaveBeenCalledTimes(2); expect(sendSignalSpy).toHaveBeenCalledTimes(2);
expect(sendSignalSpy).toHaveBeenNthCalledWith(1, expect(sendSignalSpy).toHaveBeenNthCalledWith(
1,
roomData.room.roomId, roomData.room.roomId,
{ {
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
@ -91,7 +92,8 @@ describe('Meetings API Tests', () => {
} }
); );
expect(sendSignalSpy).toHaveBeenNthCalledWith(2, expect(sendSignalSpy).toHaveBeenNthCalledWith(
2,
roomData.room.roomId, roomData.room.roomId,
{ {
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
@ -114,7 +116,7 @@ describe('Meetings API Tests', () => {
roomData.room.roomId, roomData.room.roomId,
participantIdentity, participantIdentity,
ParticipantRole.SPEAKER, ParticipantRole.SPEAKER,
roomData.moderatorCookie roomData.moderatorToken
); );
expect(response.status).toBe(200); expect(response.status).toBe(200);
@ -133,7 +135,7 @@ describe('Meetings API Tests', () => {
roomData.room.roomId, roomData.room.roomId,
'NON_EXISTENT_PARTICIPANT', 'NON_EXISTENT_PARTICIPANT',
ParticipantRole.MODERATOR, ParticipantRole.MODERATOR,
roomData.moderatorCookie roomData.moderatorToken
); );
expect(response.status).toBe(404); expect(response.status).toBe(404);
expect(response.body.error).toBe('Participant Error'); expect(response.body.error).toBe('Participant Error');
@ -148,7 +150,7 @@ describe('Meetings API Tests', () => {
roomData.room.roomId, roomData.room.roomId,
participantIdentity, participantIdentity,
ParticipantRole.MODERATOR, ParticipantRole.MODERATOR,
roomData.moderatorCookie roomData.moderatorToken
); );
expect(response.status).toBe(404); expect(response.status).toBe(404);
expect(response.body.error).toBe('Room Error'); expect(response.body.error).toBe('Room Error');

View File

@ -1,12 +1,16 @@
import { afterEach, beforeAll, beforeEach, describe, expect, it } from '@jest/globals'; import { afterEach, beforeAll, beforeEach, describe, expect, it } from '@jest/globals';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { AuthTransportMode } from '../../../../src/typings/ce/index.js';
import { ParticipantRole } from '../../../../src/typings/ce/participant.js'; import { ParticipantRole } from '../../../../src/typings/ce/participant.js';
import { expectValidationError, expectValidParticipantTokenResponse } from '../../../helpers/assertion-helpers.js'; import { expectValidationError, expectValidParticipantTokenResponse } from '../../../helpers/assertion-helpers.js';
import { import {
changeAuthTransportMode,
deleteAllRooms, deleteAllRooms,
disconnectFakeParticipants, disconnectFakeParticipants,
endMeeting, endMeeting,
extractCookieFromHeaders,
generateParticipantToken, generateParticipantToken,
generateParticipantTokenCookie, generateParticipantTokenRequest,
startTestServer, startTestServer,
updateRoomStatus updateRoomStatus
} from '../../../helpers/request-helpers.js'; } from '../../../helpers/request-helpers.js';
@ -33,7 +37,7 @@ describe('Participant API Tests', () => {
describe('Generate Participant Token Tests', () => { describe('Generate Participant Token Tests', () => {
it('should generate a participant token without join permissions when not specifying participant name', async () => { it('should generate a participant token without join permissions when not specifying participant name', async () => {
const response = await generateParticipantToken({ const response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret secret: roomData.moderatorSecret
}); });
@ -41,7 +45,7 @@ describe('Participant API Tests', () => {
}); });
it('should generate a participant token with moderator permissions when using the moderator secret', async () => { it('should generate a participant token with moderator permissions when using the moderator secret', async () => {
const response = await generateParticipantToken({ const response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
participantName participantName
@ -55,7 +59,7 @@ describe('Participant API Tests', () => {
}); });
it('should generate a participant token with speaker permissions when using the speaker secret', async () => { it('should generate a participant token with speaker permissions when using the speaker secret', async () => {
const response = await generateParticipantToken({ const response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.speakerSecret, secret: roomData.speakerSecret,
participantName participantName
@ -69,19 +73,22 @@ describe('Participant API Tests', () => {
}); });
it(`should generate a participant token with both speaker and moderator permissions it(`should generate a participant token with both speaker and moderator permissions
when using the speaker secret after having a moderator token`, async () => { when using the speaker secret after having a moderator token in cookie mode`, async () => {
const moderatorCookie = await generateParticipantTokenCookie( // Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
const moderatorToken = await generateParticipantToken(
roomData.room.roomId, roomData.room.roomId,
roomData.moderatorSecret, roomData.moderatorSecret,
`${participantName}_MODERATOR` `${participantName}_MODERATOR`
); );
const speakerResponse = await generateParticipantToken( const speakerResponse = await generateParticipantTokenRequest(
{ {
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.speakerSecret, secret: roomData.speakerSecret,
participantName: `${participantName}_SPEAKER` participantName: `${participantName}_SPEAKER`
}, },
moderatorCookie moderatorToken
); );
expectValidParticipantTokenResponse( expectValidParticipantTokenResponse(
speakerResponse, speakerResponse,
@ -91,11 +98,47 @@ describe('Participant API Tests', () => {
undefined, undefined,
[ParticipantRole.MODERATOR] [ParticipantRole.MODERATOR]
); );
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should generate a participant token and store it in a cookie when in cookie mode', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Generate the participant token
const response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId,
secret: roomData.moderatorSecret,
participantName
});
expectValidParticipantTokenResponse(
response,
roomData.room.roomId,
ParticipantRole.MODERATOR,
participantName
);
// Check that the token is included in a cookie
const participantTokenCookie = extractCookieFromHeaders(
response,
INTERNAL_CONFIG.PARTICIPANT_TOKEN_COOKIE_NAME
);
expect(participantTokenCookie).toBeDefined();
expect(participantTokenCookie).toContain(response.body.token);
expect(participantTokenCookie).toContain('HttpOnly');
expect(participantTokenCookie).toContain('SameSite=None');
expect(participantTokenCookie).toContain('Secure');
expect(participantTokenCookie).toContain('Path=/');
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should success when participant already exists in the room', async () => { it('should success when participant already exists in the room', async () => {
roomData = await setupSingleRoom(true); roomData = await setupSingleRoom(true);
let response = await generateParticipantToken({ let response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
participantName participantName
@ -109,7 +152,7 @@ describe('Participant API Tests', () => {
participantName participantName
); );
response = await generateParticipantToken({ response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
participantName participantName
@ -128,9 +171,9 @@ describe('Participant API Tests', () => {
}); });
it('should fail with 409 when room is closed', async () => { it('should fail with 409 when room is closed', async () => {
await endMeeting(roomData.room.roomId, roomData.moderatorCookie); await endMeeting(roomData.room.roomId, roomData.moderatorToken);
await updateRoomStatus(roomData.room.roomId, 'closed'); await updateRoomStatus(roomData.room.roomId, 'closed');
const response = await generateParticipantToken({ const response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
participantName participantName
@ -139,7 +182,7 @@ describe('Participant API Tests', () => {
}); });
it('should fail with 404 when room does not exist', async () => { it('should fail with 404 when room does not exist', async () => {
const response = await generateParticipantToken({ const response = await generateParticipantTokenRequest({
roomId: 'non_existent_room', roomId: 'non_existent_room',
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
participantName participantName
@ -148,7 +191,7 @@ describe('Participant API Tests', () => {
}); });
it('should fail with 400 when secret is invalid', async () => { it('should fail with 400 when secret is invalid', async () => {
const response = await generateParticipantToken({ const response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: 'invalid_secret', secret: 'invalid_secret',
participantName participantName
@ -159,7 +202,7 @@ describe('Participant API Tests', () => {
describe('Generate Participant Token Validation Tests', () => { describe('Generate Participant Token Validation Tests', () => {
it('should fail when roomId is not provided', async () => { it('should fail when roomId is not provided', async () => {
const response = await generateParticipantToken({ const response = await generateParticipantTokenRequest({
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
participantName participantName
}); });
@ -167,7 +210,7 @@ describe('Participant API Tests', () => {
}); });
it('should fail when secret is not provided', async () => { it('should fail when secret is not provided', async () => {
const response = await generateParticipantToken({ const response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
participantName participantName
}); });
@ -175,7 +218,7 @@ describe('Participant API Tests', () => {
}); });
it('should fail when secret is empty', async () => { it('should fail when secret is empty', async () => {
const response = await generateParticipantToken({ const response = await generateParticipantTokenRequest({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: '', secret: '',
participantName participantName

View File

@ -1,10 +1,13 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals'; import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js'; import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { AuthTransportMode } from '../../../../src/typings/ce/index.js';
import { ParticipantRole } from '../../../../src/typings/ce/participant.js'; import { ParticipantRole } from '../../../../src/typings/ce/participant.js';
import { expectValidationError, expectValidParticipantTokenResponse } from '../../../helpers/assertion-helpers.js'; import { expectValidationError, expectValidParticipantTokenResponse } from '../../../helpers/assertion-helpers.js';
import { import {
changeAuthTransportMode,
deleteAllRooms, deleteAllRooms,
disconnectFakeParticipants, disconnectFakeParticipants,
extractCookieFromHeaders,
refreshParticipantToken, refreshParticipantToken,
sleep, sleep,
startTestServer startTestServer
@ -44,7 +47,7 @@ describe('Participant API Tests', () => {
participantName, participantName,
participantIdentity: participantName participantIdentity: participantName
}, },
roomData.moderatorCookie roomData.moderatorToken
); );
expectValidParticipantTokenResponse( expectValidParticipantTokenResponse(
response, response,
@ -63,7 +66,7 @@ describe('Participant API Tests', () => {
participantName, participantName,
participantIdentity: participantName participantIdentity: participantName
}, },
roomData.speakerCookie roomData.speakerToken
); );
expectValidParticipantTokenResponse( expectValidParticipantTokenResponse(
response, response,
@ -74,6 +77,47 @@ describe('Participant API Tests', () => {
); );
}); });
it('should refresh participant token and store it in a cookie when in cookie mode', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Create a new room to obtain participant token in cookie mode
const newRoomData = await setupSingleRoom(true);
// Refresh the participant token
const response = await refreshParticipantToken(
{
roomId: newRoomData.room.roomId,
secret: newRoomData.moderatorSecret,
participantName,
participantIdentity: participantName
},
newRoomData.moderatorToken
);
expectValidParticipantTokenResponse(
response,
newRoomData.room.roomId,
ParticipantRole.MODERATOR,
participantName,
participantName
);
// Check that the token is included in a cookie
const participantTokenCookie = extractCookieFromHeaders(
response,
INTERNAL_CONFIG.PARTICIPANT_TOKEN_COOKIE_NAME
);
expect(participantTokenCookie).toBeDefined();
expect(participantTokenCookie).toContain(response.body.token);
expect(participantTokenCookie).toContain('HttpOnly');
expect(participantTokenCookie).toContain('SameSite=None');
expect(participantTokenCookie).toContain('Secure');
expect(participantTokenCookie).toContain('Path=/');
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail with 400 when secret is invalid', async () => { it('should fail with 400 when secret is invalid', async () => {
const response = await refreshParticipantToken( const response = await refreshParticipantToken(
{ {
@ -82,7 +126,7 @@ describe('Participant API Tests', () => {
participantName, participantName,
participantIdentity: participantName participantIdentity: participantName
}, },
roomData.moderatorCookie roomData.moderatorToken
); );
expect(response.status).toBe(400); expect(response.status).toBe(400);
}); });
@ -108,7 +152,7 @@ describe('Participant API Tests', () => {
secret: 'invalid_secret', secret: 'invalid_secret',
participantName participantName
}, },
roomData.moderatorCookie roomData.moderatorToken
); );
expect(response.status).toBe(400); expect(response.status).toBe(400);
}); });
@ -122,7 +166,7 @@ describe('Participant API Tests', () => {
participantName, participantName,
participantIdentity: participantName participantIdentity: participantName
}, },
roomData.moderatorCookie roomData.moderatorToken
); );
expect(response.status).toBe(404); expect(response.status).toBe(404);
}); });
@ -135,7 +179,7 @@ describe('Participant API Tests', () => {
participantName, participantName,
participantIdentity: participantName participantIdentity: participantName
}, },
roomData.moderatorCookie roomData.moderatorToken
); );
expect(response.status).toBe(404); expect(response.status).toBe(404);
}); });
@ -149,7 +193,7 @@ describe('Participant API Tests', () => {
participantName, participantName,
participantIdentity: participantName participantIdentity: participantName
}, },
roomData.moderatorCookie roomData.moderatorToken
); );
expectValidationError(response, 'roomId', 'Required'); expectValidationError(response, 'roomId', 'Required');
}); });
@ -161,7 +205,7 @@ describe('Participant API Tests', () => {
participantName, participantName,
participantIdentity: participantName participantIdentity: participantName
}, },
roomData.moderatorCookie roomData.moderatorToken
); );
expectValidationError(response, 'secret', 'Required'); expectValidationError(response, 'secret', 'Required');
}); });
@ -174,7 +218,7 @@ describe('Participant API Tests', () => {
participantName, participantName,
participantIdentity: participantName participantIdentity: participantName
}, },
roomData.moderatorCookie roomData.moderatorToken
); );
expectValidationError(response, 'secret', 'Secret is required'); expectValidationError(response, 'secret', 'Secret is required');
}); });

View File

@ -7,7 +7,7 @@ import {
deleteAllRecordings, deleteAllRecordings,
deleteAllRooms, deleteAllRooms,
disconnectFakeParticipants, disconnectFakeParticipants,
generateRecordingTokenCookie, generateRecordingToken,
getAllRecordings, getAllRecordings,
startRecording, startRecording,
startTestServer, startTestServer,
@ -73,7 +73,7 @@ describe('Recording API Tests', () => {
] ]
}); });
await stopRecording(activeRecordingId!, activeRecordingRoom!.moderatorCookie); await stopRecording(activeRecordingId!, activeRecordingRoom!.moderatorToken);
deleteResponse = await bulkDeleteRecordings([activeRecordingId]); deleteResponse = await bulkDeleteRecordings([activeRecordingId]);
@ -102,7 +102,7 @@ describe('Recording API Tests', () => {
await Promise.all( await Promise.all(
recordingIds.map((id, index) => { recordingIds.map((id, index) => {
return stopRecording(id!, testContext.getRoomByIndex(index)!.moderatorCookie); return stopRecording(id!, testContext.getRoomByIndex(index)!.moderatorToken);
}) })
); );
}); });
@ -122,14 +122,14 @@ describe('Recording API Tests', () => {
const recordingId = roomData.recordingId; const recordingId = roomData.recordingId;
// Generate a recording token for the room // Generate a recording token for the room
const recordingCookie = await generateRecordingTokenCookie(roomId, roomData.moderatorSecret); const recordingToken = await generateRecordingToken(roomId, roomData.moderatorSecret);
// Create another room and start a recording // Create another room and start a recording
const otherRoomData = await setupSingleRoomWithRecording(true); const otherRoomData = await setupSingleRoomWithRecording(true);
const otherRecordingId = otherRoomData.recordingId; const otherRecordingId = otherRoomData.recordingId;
// Intenta eliminar ambas grabaciones usando el token de la primera sala // Intenta eliminar ambas grabaciones usando el token de la primera sala
const deleteResponse = await bulkDeleteRecordings([recordingId, otherRecordingId], recordingCookie); const deleteResponse = await bulkDeleteRecordings([recordingId, otherRecordingId], recordingToken);
expect(deleteResponse.status).toBe(400); expect(deleteResponse.status).toBe(400);
expect(deleteResponse.body).toEqual({ expect(deleteResponse.body).toEqual({
@ -181,7 +181,7 @@ describe('Recording API Tests', () => {
const meetStorageService = container.get<MeetStorageService>(MeetStorageService); const meetStorageService = container.get<MeetStorageService>(MeetStorageService);
// Create two recordings in the same room // Create two recordings in the same room
const testContext = await setupMultiRecordingsTestContext(1, 1, 1); const testContext = await setupMultiRecordingsTestContext(1, 1, 1);
const { room, recordingId: firstRecordingId, moderatorCookie } = testContext.rooms[0]; const { room, recordingId: firstRecordingId, moderatorToken } = testContext.rooms[0];
let roomMetadata = await meetStorageService.getArchivedRoomMetadata(room.roomId); let roomMetadata = await meetStorageService.getArchivedRoomMetadata(room.roomId);
@ -195,11 +195,11 @@ describe('Recording API Tests', () => {
expect(roomMetadata!.moderatorUrl).toContain(room.roomId); expect(roomMetadata!.moderatorUrl).toContain(room.roomId);
expect(roomMetadata!.speakerUrl).toContain(room.roomId); expect(roomMetadata!.speakerUrl).toContain(room.roomId);
const response = await startRecording(room.roomId, moderatorCookie); const response = await startRecording(room.roomId, moderatorToken);
expectValidStartRecordingResponse(response, room.roomId, room.roomName); expectValidStartRecordingResponse(response, room.roomId, room.roomName);
const secondRecordingId = response.body.recordingId; const secondRecordingId = response.body.recordingId;
await stopRecording(secondRecordingId, moderatorCookie); await stopRecording(secondRecordingId, moderatorToken);
// Delete first recording - room metadata should remain // Delete first recording - room metadata should remain
const bulkResponse = await bulkDeleteRecordings([firstRecordingId, secondRecordingId]); const bulkResponse = await bulkDeleteRecordings([firstRecordingId, secondRecordingId]);
expect(bulkResponse.status).toBe(200); expect(bulkResponse.status).toBe(200);

View File

@ -27,17 +27,17 @@ describe('Recording API Tests', () => {
}); });
describe('Delete Recording Tests', () => { describe('Delete Recording Tests', () => {
let room: MeetRoom, recordingId: string, moderatorCookie: string; let room: MeetRoom, recordingId: string, moderatorToken: string;
beforeEach(async () => { beforeEach(async () => {
const testContext = await setupMultiRecordingsTestContext(1, 1, 1); const testContext = await setupMultiRecordingsTestContext(1, 1, 1);
const roomData = testContext.getRoomByIndex(0)!; const roomData = testContext.getRoomByIndex(0)!;
({ room, recordingId = '', moderatorCookie } = roomData); ({ room, recordingId = '', moderatorToken } = roomData);
}); });
afterAll(async () => { afterAll(async () => {
await stopAllRecordings(moderatorCookie); await stopAllRecordings(moderatorToken);
await Promise.all([deleteAllRecordings(), deleteAllRooms()]); await Promise.all([deleteAllRecordings(), deleteAllRooms()]);
}); });
@ -77,11 +77,11 @@ describe('Recording API Tests', () => {
expect(roomMetadata!.speakerUrl).toContain(room.roomId); expect(roomMetadata!.speakerUrl).toContain(room.roomId);
// Generate a new recording // Generate a new recording
const response = await startRecording(room.roomId, moderatorCookie); const response = await startRecording(room.roomId, moderatorToken);
console.log('Start recording response:', response.body); console.log('Start recording response:', response.body);
expectValidStartRecordingResponse(response, room.roomId, room.roomName); expectValidStartRecordingResponse(response, room.roomId, room.roomName);
const secondRecordingId = response.body.recordingId; const secondRecordingId = response.body.recordingId;
await stopRecording(secondRecordingId, moderatorCookie); await stopRecording(secondRecordingId, moderatorToken);
// Check that the room metadata still exists after deleteing the first recording // Check that the room metadata still exists after deleteing the first recording
let deleteResponse = await deleteRecording(recordingId!); let deleteResponse = await deleteRecording(recordingId!);
@ -104,17 +104,17 @@ describe('Recording API Tests', () => {
}); });
describe('Delete Recording Validation', () => { describe('Delete Recording Validation', () => {
let room: MeetRoom, recordingId: string, moderatorCookie: string; let room: MeetRoom, recordingId: string, moderatorToken: string;
beforeAll(async () => { beforeAll(async () => {
await deleteAllRecordings(); await deleteAllRecordings();
const testContext = await setupMultiRecordingsTestContext(1, 1, 1); const testContext = await setupMultiRecordingsTestContext(1, 1, 1);
const roomData = testContext.getRoomByIndex(0)!; const roomData = testContext.getRoomByIndex(0)!;
({ room, recordingId = '', moderatorCookie } = roomData); ({ room, recordingId = '', moderatorToken } = roomData);
}); });
afterAll(async () => { afterAll(async () => {
await stopAllRecordings(moderatorCookie); await stopAllRecordings(moderatorToken);
await Promise.all([deleteAllRecordings(), deleteAllRooms()]); await Promise.all([deleteAllRecordings(), deleteAllRooms()]);
}); });
it('should fail when recordingId has incorrect format', async () => { it('should fail when recordingId has incorrect format', async () => {
@ -154,13 +154,13 @@ describe('Recording API Tests', () => {
it('should return 409 when attempting to delete an active recording', async () => { it('should return 409 when attempting to delete an active recording', async () => {
const testContext = await setupMultiRecordingsTestContext(1, 1, 0); const testContext = await setupMultiRecordingsTestContext(1, 1, 0);
const { recordingId: activeRecordingId = '', moderatorCookie } = testContext.rooms[0]; const { recordingId: activeRecordingId = '', moderatorToken } = testContext.rooms[0];
// Attempt to delete the active recording // Attempt to delete the active recording
let deleteResponse = await deleteRecording(activeRecordingId); let deleteResponse = await deleteRecording(activeRecordingId);
expect(deleteResponse.status).toBe(409); expect(deleteResponse.status).toBe(409);
await stopRecording(activeRecordingId, moderatorCookie); await stopRecording(activeRecordingId, moderatorToken);
// Attempt to delete the recording again // Attempt to delete the recording again
deleteResponse = await deleteRecording(activeRecordingId); deleteResponse = await deleteRecording(activeRecordingId);
expect(deleteResponse.status).toBe(200); expect(deleteResponse.status).toBe(200);

View File

@ -7,7 +7,7 @@ import {
deleteAllRooms, deleteAllRooms,
disconnectFakeParticipants, disconnectFakeParticipants,
downloadRecordings, downloadRecordings,
generateRecordingTokenCookie, generateRecordingToken,
startTestServer startTestServer
} from '../../../helpers/request-helpers'; } from '../../../helpers/request-helpers';
import { setupMultiRecordingsTestContext, setupSingleRoomWithRecording } from '../../../helpers/test-scenarios'; import { setupMultiRecordingsTestContext, setupSingleRoomWithRecording } from '../../../helpers/test-scenarios';
@ -59,12 +59,12 @@ describe('Recording API Tests', () => {
const roomData = await setupSingleRoomWithRecording(true); const roomData = await setupSingleRoomWithRecording(true);
const roomId = roomData.room.roomId; const roomId = roomData.room.roomId;
const recordingId = roomData.recordingId!; const recordingId = roomData.recordingId!;
const recordingCookie = await generateRecordingTokenCookie(roomId, roomData.moderatorSecret); const recordingToken = await generateRecordingToken(roomId, roomData.moderatorSecret);
const otherRoomData = await setupSingleRoomWithRecording(true); const otherRoomData = await setupSingleRoomWithRecording(true);
const otherRecordingId = otherRoomData.recordingId!; const otherRecordingId = otherRoomData.recordingId!;
const res = await downloadRecordings([recordingId, otherRecordingId], true, recordingCookie); const res = await downloadRecordings([recordingId, otherRecordingId], true, recordingToken);
expect(res.status).toBe(200); expect(res.status).toBe(200);
const entries = await getZipEntries(res.body); const entries = await getZipEntries(res.body);
@ -75,12 +75,12 @@ describe('Recording API Tests', () => {
it('should return an error if none of the recordings belong to the room in the token', async () => { it('should return an error if none of the recordings belong to the room in the token', async () => {
const roomData = await setupSingleRoomWithRecording(true); const roomData = await setupSingleRoomWithRecording(true);
const roomId = roomData.room.roomId; const roomId = roomData.room.roomId;
const recordingCookie = await generateRecordingTokenCookie(roomId, roomData.moderatorSecret); const recordingToken = await generateRecordingToken(roomId, roomData.moderatorSecret);
const otherRoomData = await setupSingleRoomWithRecording(true); const otherRoomData = await setupSingleRoomWithRecording(true);
const otherRecordingId = otherRoomData.recordingId!; const otherRecordingId = otherRoomData.recordingId!;
const res = await downloadRecordings([otherRecordingId], false, recordingCookie); const res = await downloadRecordings([otherRecordingId], false, recordingToken);
expect(res.status).toBe(400); expect(res.status).toBe(400);
expect(res.body).toHaveProperty('error'); expect(res.body).toHaveProperty('error');

View File

@ -167,7 +167,7 @@ describe('Recording API Tests', () => {
it('should return a 409 when the recording is in progress', async () => { it('should return a 409 when the recording is in progress', async () => {
const testContext = await setupMultiRecordingsTestContext(1, 1, 0); const testContext = await setupMultiRecordingsTestContext(1, 1, 0);
const { recordingId: activeRecordingId = '', moderatorCookie } = testContext.rooms[0]; const { recordingId: activeRecordingId = '', moderatorToken } = testContext.rooms[0];
// Attempt to get the media of an active recording // Attempt to get the media of an active recording
const response = await getRecordingMedia(activeRecordingId); const response = await getRecordingMedia(activeRecordingId);
@ -176,7 +176,7 @@ describe('Recording API Tests', () => {
expect(response.body).toHaveProperty('message'); expect(response.body).toHaveProperty('message');
expect(response.body.message).toContain(`Recording '${activeRecordingId}' is not stopped yet`); expect(response.body.message).toContain(`Recording '${activeRecordingId}' is not stopped yet`);
await stopRecording(activeRecordingId, moderatorCookie); await stopRecording(activeRecordingId, moderatorToken);
}); });
it('should return 404 when recording not found', async () => { it('should return 404 when recording not found', async () => {

View File

@ -50,7 +50,7 @@ describe('Recording API Tests', () => {
const { const {
room: roomAux, room: roomAux,
recordingId: recordingIdAux = '', recordingId: recordingIdAux = '',
moderatorCookie: moderatorCookieAux moderatorToken: moderatorTokenAux
} = contextAux.getRoomByIndex(0)!; } = contextAux.getRoomByIndex(0)!;
const response = await getRecording(recordingIdAux); const response = await getRecording(recordingIdAux);
@ -62,7 +62,7 @@ describe('Recording API Tests', () => {
MeetRecordingStatus.ACTIVE MeetRecordingStatus.ACTIVE
); );
await stopAllRecordings(moderatorCookieAux); await stopAllRecordings(moderatorTokenAux);
}); });
it('should return 404 when recording does not exist', async () => { it('should return 404 when recording does not exist', async () => {

View File

@ -10,7 +10,7 @@ import {
deleteAllRecordings, deleteAllRecordings,
deleteAllRooms, deleteAllRooms,
disconnectFakeParticipants, disconnectFakeParticipants,
generateRecordingTokenCookie, generateRecordingToken,
getAllRecordings, getAllRecordings,
getAllRecordingsFromRoom, getAllRecordingsFromRoom,
startTestServer startTestServer
@ -63,12 +63,12 @@ describe('Recordings API Tests', () => {
const roomId = roomData.room.roomId; const roomId = roomData.room.roomId;
// Generate a recording token for the room // Generate a recording token for the room
const recordingCookie = await generateRecordingTokenCookie(roomId, roomData.speakerSecret); const recordingToken = await generateRecordingToken(roomId, roomData.speakerSecret);
// Create a new room and start a recording // Create a new room and start a recording
roomData = await setupSingleRoomWithRecording(true); roomData = await setupSingleRoomWithRecording(true);
const response = await getAllRecordingsFromRoom(recordingCookie); const response = await getAllRecordingsFromRoom(recordingToken);
expectSuccessListRecordingResponse(response, 1, false, false); expectSuccessListRecordingResponse(response, 1, false, false);
expect(response.body.recordings[0].roomId).toBe(roomId); expect(response.body.recordings[0].roomId).toBe(roomId);
}); });

View File

@ -41,10 +41,10 @@ describe('Recording API Race Conditions Tests', () => {
}); });
afterEach(async () => { afterEach(async () => {
const moderatorCookie = context?.getRoomByIndex(0)?.moderatorCookie; const moderatorToken = context?.getRoomByIndex(0)?.moderatorToken;
if (moderatorCookie) { if (moderatorToken) {
await stopAllRecordings(moderatorCookie); await stopAllRecordings(moderatorToken);
} }
eventController.reset(); eventController.reset();
@ -67,7 +67,7 @@ describe('Recording API Race Conditions Tests', () => {
try { try {
// Attempt to start recording // Attempt to start recording
const result = await startRecording(roomData.room.roomId, roomData.moderatorCookie); const result = await startRecording(roomData.room.roomId, roomData.moderatorToken);
expect(eventServiceOffSpy).toHaveBeenCalledWith( expect(eventServiceOffSpy).toHaveBeenCalledWith(
DistributedEventType.RECORDING_ACTIVE, DistributedEventType.RECORDING_ACTIVE,
expect.any(Function) expect.any(Function)
@ -122,7 +122,7 @@ describe('Recording API Race Conditions Tests', () => {
try { try {
// Start recording with a short timeout // Start recording with a short timeout
const result = await startRecording(roomData.room.roomId, roomData.moderatorCookie); const result = await startRecording(roomData.room.roomId, roomData.moderatorToken);
expect(eventServiceOffSpy).toHaveBeenCalledWith( expect(eventServiceOffSpy).toHaveBeenCalledWith(
DistributedEventType.RECORDING_ACTIVE, DistributedEventType.RECORDING_ACTIVE,
@ -182,7 +182,7 @@ describe('Recording API Race Conditions Tests', () => {
try { try {
// Start recording in room1 (should timeout) // Start recording in room1 (should timeout)
const rec1 = await startRecording(room1.room.roomId, room1.moderatorCookie); const rec1 = await startRecording(room1.room.roomId, room1.moderatorToken);
expect(rec1.status).toBe(503); expect(rec1.status).toBe(503);
setInternalConfig({ setInternalConfig({
@ -190,18 +190,18 @@ describe('Recording API Race Conditions Tests', () => {
}); });
// ✅ EXPECTED BEHAVIOR: System should remain stable // ✅ EXPECTED BEHAVIOR: System should remain stable
// Recording in different room should work normally // Recording in different room should work normally
const rec2 = await startRecording(room2.room.roomId, room2.moderatorCookie); const rec2 = await startRecording(room2.room.roomId, room2.moderatorToken);
expect(rec2.status).toBe(201); expect(rec2.status).toBe(201);
expectValidStartRecordingResponse(rec2, room2.room.roomId, room2.room.roomName); expectValidStartRecordingResponse(rec2, room2.room.roomId, room2.room.roomName);
let response = await stopRecording(rec2.body.recordingId!, room2.moderatorCookie); let response = await stopRecording(rec2.body.recordingId!, room2.moderatorToken);
expectValidStopRecordingResponse(response, rec2.body.recordingId!, room2.room.roomId, room2.room.roomName); expectValidStopRecordingResponse(response, rec2.body.recordingId!, room2.room.roomId, room2.room.roomName);
// ✅ EXPECTED BEHAVIOR: After timeout cleanup, room1 should be available again // ✅ EXPECTED BEHAVIOR: After timeout cleanup, room1 should be available again
const rec3 = await startRecording(room1.room.roomId, room1.moderatorCookie); const rec3 = await startRecording(room1.room.roomId, room1.moderatorToken);
expect(rec3.status).toBe(201); expect(rec3.status).toBe(201);
expectValidStartRecordingResponse(rec3, room1.room.roomId, room1.room.roomName); expectValidStartRecordingResponse(rec3, room1.room.roomId, room1.room.roomName);
response = await stopRecording(rec3.body.recordingId!, room1.moderatorCookie); response = await stopRecording(rec3.body.recordingId!, room1.moderatorToken);
expectValidStopRecordingResponse(response, rec3.body.recordingId!, room1.room.roomId, room1.room.roomName); expectValidStopRecordingResponse(response, rec3.body.recordingId!, room1.room.roomId, room1.room.roomName);
} finally { } finally {
startRoomCompositeSpy.mockRestore(); startRoomCompositeSpy.mockRestore();
@ -226,7 +226,7 @@ describe('Recording API Race Conditions Tests', () => {
try { try {
// Start recordings in all rooms simultaneously (all should timeout) // Start recordings in all rooms simultaneously (all should timeout)
const results = await Promise.all( const results = await Promise.all(
rooms.map((room) => startRecording(room.room.roomId, room.moderatorCookie)) rooms.map((room) => startRecording(room.room.roomId, room.moderatorToken))
); );
// All should timeout // All should timeout
@ -241,14 +241,14 @@ describe('Recording API Race Conditions Tests', () => {
// ✅ EXPECTED BEHAVIOR: After timeouts, all rooms should be available again // ✅ EXPECTED BEHAVIOR: After timeouts, all rooms should be available again
const retryResults = await Promise.all( const retryResults = await Promise.all(
rooms.map((room) => startRecording(room.room.roomId, room.moderatorCookie)) rooms.map((room) => startRecording(room.room.roomId, room.moderatorToken))
); );
for (const startResult of retryResults) { for (const startResult of retryResults) {
expect(startResult.status).toBe(201); expect(startResult.status).toBe(201);
const room = rooms.find((r) => r.room.roomId === startResult.body.roomId)!; const room = rooms.find((r) => r.room.roomId === startResult.body.roomId)!;
expectValidStartRecordingResponse(startResult, room.room.roomId, room.room.roomName); expectValidStartRecordingResponse(startResult, room.room.roomId, room.room.roomName);
const stopResult = await stopRecording(startResult.body.recordingId!, room.moderatorCookie); const stopResult = await stopRecording(startResult.body.recordingId!, room.moderatorToken);
expectValidStopRecordingResponse( expectValidStopRecordingResponse(
stopResult, stopResult,
startResult.body.recordingId!, startResult.body.recordingId!,
@ -272,18 +272,18 @@ describe('Recording API Race Conditions Tests', () => {
eventController.initialize(); eventController.initialize();
eventController.pauseEventsForRoom(roomDataA!.room.roomId); eventController.pauseEventsForRoom(roomDataA!.room.roomId);
const recordingPromiseA = startRecording(roomDataA!.room.roomId, roomDataA!.moderatorCookie); const recordingPromiseA = startRecording(roomDataA!.room.roomId, roomDataA!.moderatorToken);
// Brief delay to ensure both recordings start in the right order // Brief delay to ensure both recordings start in the right order
await sleep('1s'); await sleep('1s');
// Step 2: Start recording in roomB (this will complete quickly) // Step 2: Start recording in roomB (this will complete quickly)
const recordingResponseB = await startRecording(roomDataB!.room.roomId, roomDataB!.moderatorCookie); const recordingResponseB = await startRecording(roomDataB!.room.roomId, roomDataB!.moderatorToken);
expectValidStartRecordingResponse(recordingResponseB, roomDataB!.room.roomId, roomDataB!.room.roomName); expectValidStartRecordingResponse(recordingResponseB, roomDataB!.room.roomId, roomDataB!.room.roomName);
const recordingIdB = recordingResponseB.body.recordingId; const recordingIdB = recordingResponseB.body.recordingId;
// Step 3: Stop recording in roomB while roomA is still waiting for its event // Step 3: Stop recording in roomB while roomA is still waiting for its event
const stopResponseB = await stopRecording(recordingIdB, roomDataB!.moderatorCookie); const stopResponseB = await stopRecording(recordingIdB, roomDataB!.moderatorToken);
expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId, roomDataB!.room.roomName); expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId, roomDataB!.room.roomName);
eventController.releaseEventsForRoom(roomDataA!.room.roomId); eventController.releaseEventsForRoom(roomDataA!.room.roomId);
@ -303,7 +303,7 @@ describe('Recording API Race Conditions Tests', () => {
const roomDataList = Array.from({ length: 5 }, (_, index) => context!.getRoomByIndex(index)!); const roomDataList = Array.from({ length: 5 }, (_, index) => context!.getRoomByIndex(index)!);
const startResponses = await Promise.all( const startResponses = await Promise.all(
roomDataList.map((roomData) => startRecording(roomData.room.roomId, roomData.moderatorCookie)) roomDataList.map((roomData) => startRecording(roomData.room.roomId, roomData.moderatorToken))
); );
startResponses.forEach((response, index) => { startResponses.forEach((response, index) => {
@ -317,7 +317,7 @@ describe('Recording API Race Conditions Tests', () => {
const recordingIds = startResponses.map((res) => res.body.recordingId); const recordingIds = startResponses.map((res) => res.body.recordingId);
const stopResponses = await Promise.all( const stopResponses = await Promise.all(
recordingIds.map((recordingId, index) => stopRecording(recordingId, roomDataList[index].moderatorCookie)) recordingIds.map((recordingId, index) => stopRecording(recordingId, roomDataList[index].moderatorToken))
); );
stopResponses.forEach((response, index) => { stopResponses.forEach((response, index) => {
@ -334,14 +334,14 @@ describe('Recording API Race Conditions Tests', () => {
context = await setupMultiRoomTestContext(2, true); context = await setupMultiRoomTestContext(2, true);
const roomDataA = context.getRoomByIndex(0); const roomDataA = context.getRoomByIndex(0);
const roomDataB = context.getRoomByIndex(1); const roomDataB = context.getRoomByIndex(1);
const responseA = await startRecording(roomDataA!.room.roomId, roomDataA!.moderatorCookie); const responseA = await startRecording(roomDataA!.room.roomId, roomDataA!.moderatorToken);
const responseB = await startRecording(roomDataB!.room.roomId, roomDataB!.moderatorCookie); const responseB = await startRecording(roomDataB!.room.roomId, roomDataB!.moderatorToken);
const recordingIdA = responseA.body.recordingId; const recordingIdA = responseA.body.recordingId;
const recordingIdB = responseB.body.recordingId; const recordingIdB = responseB.body.recordingId;
const [stopResponseA, stopResponseB] = await Promise.all([ const [stopResponseA, stopResponseB] = await Promise.all([
stopRecording(recordingIdA, roomDataA!.moderatorCookie), stopRecording(recordingIdA, roomDataA!.moderatorToken),
stopRecording(recordingIdB, roomDataB!.moderatorCookie) stopRecording(recordingIdB, roomDataB!.moderatorToken)
]); ]);
expectValidStopRecordingResponse(stopResponseA, recordingIdA, roomDataA!.room.roomId, roomDataA!.room.roomName); expectValidStopRecordingResponse(stopResponseA, recordingIdA, roomDataA!.room.roomId, roomDataA!.room.roomName);
expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId, roomDataB!.room.roomName); expectValidStopRecordingResponse(stopResponseB, recordingIdB, roomDataB!.room.roomId, roomDataB!.room.roomName);
@ -352,8 +352,8 @@ describe('Recording API Race Conditions Tests', () => {
const roomData = context.getRoomByIndex(0)!; const roomData = context.getRoomByIndex(0)!;
const [firstRecordingResponse, secondRecordingResponse] = await Promise.all([ const [firstRecordingResponse, secondRecordingResponse] = await Promise.all([
startRecording(roomData.room.roomId, roomData.moderatorCookie), startRecording(roomData.room.roomId, roomData.moderatorToken),
startRecording(roomData.room.roomId, roomData.moderatorCookie) startRecording(roomData.room.roomId, roomData.moderatorToken)
]); ]);
console.log('First recording response:', firstRecordingResponse.body); console.log('First recording response:', firstRecordingResponse.body);
@ -368,7 +368,7 @@ describe('Recording API Race Conditions Tests', () => {
if (firstRecordingResponse.status === 201) { if (firstRecordingResponse.status === 201) {
expectValidStartRecordingResponse(firstRecordingResponse, roomData.room.roomId, roomData.room.roomName); expectValidStartRecordingResponse(firstRecordingResponse, roomData.room.roomId, roomData.room.roomName);
// stop the first recording // stop the first recording
const stopResponse = await stopRecording(firstRecordingResponse.body.recordingId, roomData.moderatorCookie); const stopResponse = await stopRecording(firstRecordingResponse.body.recordingId, roomData.moderatorToken);
expectValidStopRecordingResponse( expectValidStopRecordingResponse(
stopResponse, stopResponse,
firstRecordingResponse.body.recordingId, firstRecordingResponse.body.recordingId,
@ -378,10 +378,7 @@ describe('Recording API Race Conditions Tests', () => {
} else { } else {
expectValidStartRecordingResponse(secondRecordingResponse, roomData.room.roomId, roomData.room.roomName); expectValidStartRecordingResponse(secondRecordingResponse, roomData.room.roomId, roomData.room.roomName);
// stop the second recording // stop the second recording
const stopResponse = await stopRecording( const stopResponse = await stopRecording(secondRecordingResponse.body.recordingId, roomData.moderatorToken);
secondRecordingResponse.body.recordingId,
roomData.moderatorCookie
);
expectValidStopRecordingResponse( expectValidStopRecordingResponse(
stopResponse, stopResponse,
secondRecordingResponse.body.recordingId, secondRecordingResponse.body.recordingId,
@ -397,12 +394,12 @@ describe('Recording API Race Conditions Tests', () => {
const gcSpy = jest.spyOn(recordingService as any, 'performRecordingLocksGarbageCollection'); const gcSpy = jest.spyOn(recordingService as any, 'performRecordingLocksGarbageCollection');
const startResponse = await startRecording(roomData.room.roomId, roomData.moderatorCookie); const startResponse = await startRecording(roomData.room.roomId, roomData.moderatorToken);
expectValidStartRecordingResponse(startResponse, roomData.room.roomId, roomData.room.roomName); expectValidStartRecordingResponse(startResponse, roomData.room.roomId, roomData.room.roomName);
const recordingId = startResponse.body.recordingId; const recordingId = startResponse.body.recordingId;
// Execute garbage collection while stopping the recording // Execute garbage collection while stopping the recording
const stopPromise = stopRecording(recordingId, roomData.moderatorCookie); const stopPromise = stopRecording(recordingId, roomData.moderatorToken);
const gcPromise = recordingService['performRecordingLocksGarbageCollection'](); const gcPromise = recordingService['performRecordingLocksGarbageCollection']();
// Both operations should complete // Both operations should complete
@ -469,18 +466,18 @@ describe('Recording API Race Conditions Tests', () => {
const room2 = context.getRoomByIndex(1)!; const room2 = context.getRoomByIndex(1)!;
const room3 = context.getRoomByIndex(2)!; const room3 = context.getRoomByIndex(2)!;
const start1 = await startRecording(room1.room.roomId, room1.moderatorCookie); const start1 = await startRecording(room1.room.roomId, room1.moderatorToken);
const start2 = await startRecording(room2.room.roomId, room2.moderatorCookie); const start2 = await startRecording(room2.room.roomId, room2.moderatorToken);
const recordingId1 = start1.body.recordingId; const recordingId1 = start1.body.recordingId;
const recordingId2 = start2.body.recordingId; const recordingId2 = start2.body.recordingId;
await stopRecording(recordingId1, room1.moderatorCookie); await stopRecording(recordingId1, room1.moderatorToken);
await stopRecording(recordingId2, room2.moderatorCookie); await stopRecording(recordingId2, room2.moderatorToken);
// Bulk delete the recordings while starting a new one // Bulk delete the recordings while starting a new one
const bulkDeletePromise = bulkDeleteRecordings([recordingId1, recordingId2]); const bulkDeletePromise = bulkDeleteRecordings([recordingId1, recordingId2]);
const startNewRecordingPromise = startRecording(room3.room.roomId, room3.moderatorCookie); const startNewRecordingPromise = startRecording(room3.room.roomId, room3.moderatorToken);
// Both operations should complete successfully // Both operations should complete successfully
const [bulkDeleteResult, newRecordingResult] = await Promise.all([bulkDeletePromise, startNewRecordingPromise]); const [bulkDeleteResult, newRecordingResult] = await Promise.all([bulkDeletePromise, startNewRecordingPromise]);
@ -490,7 +487,7 @@ describe('Recording API Race Conditions Tests', () => {
// Check that the new recording started successfully // Check that the new recording started successfully
expectValidStartRecordingResponse(newRecordingResult, room3.room.roomId, room3.room.roomName); expectValidStartRecordingResponse(newRecordingResult, room3.room.roomId, room3.room.roomName);
const newStopResponse = await stopRecording(newRecordingResult.body.recordingId, room3.moderatorCookie); const newStopResponse = await stopRecording(newRecordingResult.body.recordingId, room3.moderatorToken);
expectValidStopRecordingResponse( expectValidStopRecordingResponse(
newStopResponse, newStopResponse,
newRecordingResult.body.recordingId, newRecordingResult.body.recordingId,

View File

@ -23,7 +23,7 @@ import { setupMultiRoomTestContext, TestContext } from '../../../helpers/test-sc
describe('Recording API Tests', () => { describe('Recording API Tests', () => {
let context: TestContext | null = null; let context: TestContext | null = null;
let room: MeetRoom, moderatorCookie: string; let room: MeetRoom, moderatorToken: string;
beforeAll(async () => { beforeAll(async () => {
startTestServer(); startTestServer();
@ -39,7 +39,7 @@ describe('Recording API Tests', () => {
beforeAll(async () => { beforeAll(async () => {
// Create a room and join a participant // Create a room and join a participant
context = await setupMultiRoomTestContext(1, true); context = await setupMultiRoomTestContext(1, true);
({ room, moderatorCookie } = context.getRoomByIndex(0)!); ({ room, moderatorToken } = context.getRoomByIndex(0)!);
}); });
afterAll(async () => { afterAll(async () => {
@ -49,16 +49,16 @@ describe('Recording API Tests', () => {
}); });
it('should return 201 with proper response and location header when recording starts successfully', async () => { it('should return 201 with proper response and location header when recording starts successfully', async () => {
const response = await startRecording(room.roomId, moderatorCookie); const response = await startRecording(room.roomId, moderatorToken);
const recordingId = response.body.recordingId; const recordingId = response.body.recordingId;
expectValidStartRecordingResponse(response, room.roomId, room.roomName); expectValidStartRecordingResponse(response, room.roomId, room.roomName);
const stopResponse = await stopRecording(recordingId, moderatorCookie); const stopResponse = await stopRecording(recordingId, moderatorToken);
expectValidStopRecordingResponse(stopResponse, recordingId, room.roomId, room.roomName); expectValidStopRecordingResponse(stopResponse, recordingId, room.roomId, room.roomName);
}); });
it('should secrets and archived room files be created when recording starts', async () => { it('should secrets and archived room files be created when recording starts', async () => {
const response = await startRecording(room.roomId, moderatorCookie); const response = await startRecording(room.roomId, moderatorToken);
const recordingId = response.body.recordingId; const recordingId = response.body.recordingId;
expectValidStartRecordingResponse(response, room.roomId, room.roomName); expectValidStartRecordingResponse(response, room.roomId, room.roomName);
@ -75,24 +75,24 @@ describe('Recording API Tests', () => {
expect(archivedRoom?.speakerUrl).toBeDefined(); expect(archivedRoom?.speakerUrl).toBeDefined();
expect(archivedRoom?.config).toBeDefined(); expect(archivedRoom?.config).toBeDefined();
const secretsResponse = await stopRecording(recordingId, moderatorCookie); const secretsResponse = await stopRecording(recordingId, moderatorToken);
expectValidStopRecordingResponse(secretsResponse, recordingId, room.roomId, room.roomName); expectValidStopRecordingResponse(secretsResponse, recordingId, room.roomId, room.roomName);
}); });
it('should successfully start recording, stop it, and start again (sequential operations)', async () => { it('should successfully start recording, stop it, and start again (sequential operations)', async () => {
const firstStartResponse = await startRecording(room.roomId, moderatorCookie); const firstStartResponse = await startRecording(room.roomId, moderatorToken);
const firstRecordingId = firstStartResponse.body.recordingId; const firstRecordingId = firstStartResponse.body.recordingId;
expectValidStartRecordingResponse(firstStartResponse, room.roomId, room.roomName); expectValidStartRecordingResponse(firstStartResponse, room.roomId, room.roomName);
const firstStopResponse = await stopRecording(firstRecordingId, moderatorCookie); const firstStopResponse = await stopRecording(firstRecordingId, moderatorToken);
expectValidStopRecordingResponse(firstStopResponse, firstRecordingId, room.roomId, room.roomName); expectValidStopRecordingResponse(firstStopResponse, firstRecordingId, room.roomId, room.roomName);
const secondStartResponse = await startRecording(room.roomId, moderatorCookie); const secondStartResponse = await startRecording(room.roomId, moderatorToken);
expectValidStartRecordingResponse(secondStartResponse, room.roomId, room.roomName); expectValidStartRecordingResponse(secondStartResponse, room.roomId, room.roomName);
const secondRecordingId = secondStartResponse.body.recordingId; const secondRecordingId = secondStartResponse.body.recordingId;
const secondStopResponse = await stopRecording(secondRecordingId, moderatorCookie); const secondStopResponse = await stopRecording(secondRecordingId, moderatorToken);
expectValidStopRecordingResponse(secondStopResponse, secondRecordingId, room.roomId, room.roomName); expectValidStopRecordingResponse(secondStopResponse, secondRecordingId, room.roomId, room.roomName);
}); });
@ -102,8 +102,8 @@ describe('Recording API Tests', () => {
const roomDataA = context.getRoomByIndex(0)!; const roomDataA = context.getRoomByIndex(0)!;
const roomDataB = context.getRoomByIndex(1)!; const roomDataB = context.getRoomByIndex(1)!;
const firstResponse = await startRecording(roomDataA.room.roomId, roomDataA.moderatorCookie); const firstResponse = await startRecording(roomDataA.room.roomId, roomDataA.moderatorToken);
const secondResponse = await startRecording(roomDataB.room.roomId, roomDataB.moderatorCookie); const secondResponse = await startRecording(roomDataB.room.roomId, roomDataB.moderatorToken);
expectValidStartRecordingResponse(firstResponse, roomDataA.room.roomId, roomDataA.room.roomName); expectValidStartRecordingResponse(firstResponse, roomDataA.room.roomId, roomDataA.room.roomName);
expectValidStartRecordingResponse(secondResponse, roomDataB.room.roomId, roomDataB.room.roomName); expectValidStartRecordingResponse(secondResponse, roomDataB.room.roomId, roomDataB.room.roomName);
@ -112,8 +112,8 @@ describe('Recording API Tests', () => {
const secondRecordingId = secondResponse.body.recordingId; const secondRecordingId = secondResponse.body.recordingId;
const [firstStopResponse, secondStopResponse] = await Promise.all([ const [firstStopResponse, secondStopResponse] = await Promise.all([
stopRecording(firstRecordingId, roomDataA.moderatorCookie), stopRecording(firstRecordingId, roomDataA.moderatorToken),
stopRecording(secondRecordingId, roomDataB.moderatorCookie) stopRecording(secondRecordingId, roomDataB.moderatorToken)
]); ]);
expectValidStopRecordingResponse( expectValidStopRecordingResponse(
firstStopResponse, firstStopResponse,
@ -134,16 +134,16 @@ describe('Recording API Tests', () => {
beforeAll(async () => { beforeAll(async () => {
// Create a room without participants // Create a room without participants
context = await setupMultiRoomTestContext(1, false); context = await setupMultiRoomTestContext(1, false);
({ room, moderatorCookie } = context.getRoomByIndex(0)!); ({ room, moderatorToken } = context.getRoomByIndex(0)!);
}); });
afterEach(async () => { afterEach(async () => {
await disconnectFakeParticipants(); await disconnectFakeParticipants();
await stopAllRecordings(moderatorCookie); await stopAllRecordings(moderatorToken);
}); });
it('should accept valid roomId but reject with 409', async () => { it('should accept valid roomId but reject with 409', async () => {
const response = await startRecording(room.roomId, moderatorCookie); const response = await startRecording(room.roomId, moderatorToken);
// Room exists but it has no participants // Room exists but it has no participants
expect(response.status).toBe(409); expect(response.status).toBe(409);
expect(response.body.message).toContain(`Room '${room.roomId}' has no participants`); expect(response.body.message).toContain(`Room '${room.roomId}' has no participants`);
@ -151,7 +151,7 @@ describe('Recording API Tests', () => {
it('should sanitize roomId and reject the request with 409 due to no participants', async () => { it('should sanitize roomId and reject the request with 409 due to no participants', async () => {
const malformedRoomId = ' .<!?' + room.roomId + ' '; const malformedRoomId = ' .<!?' + room.roomId + ' ';
const response = await startRecording(malformedRoomId, moderatorCookie); const response = await startRecording(malformedRoomId, moderatorToken);
console.log('Response:', response.body); console.log('Response:', response.body);
expect(response.status).toBe(409); expect(response.status).toBe(409);
@ -159,25 +159,25 @@ describe('Recording API Tests', () => {
}); });
it('should reject request with roomId that becomes empty after sanitization', async () => { it('should reject request with roomId that becomes empty after sanitization', async () => {
const response = await startRecording('!@#$%^&*()', moderatorCookie); const response = await startRecording('!@#$%^&*()', moderatorToken);
expectValidationError(response, 'roomId', 'cannot be empty after sanitization'); expectValidationError(response, 'roomId', 'cannot be empty after sanitization');
}); });
it('should reject request with non-string roomId', async () => { it('should reject request with non-string roomId', async () => {
const response = await startRecording(123 as unknown as string, moderatorCookie); const response = await startRecording(123 as unknown as string, moderatorToken);
expectValidationError(response, 'roomId', 'Expected string'); expectValidationError(response, 'roomId', 'Expected string');
}); });
it('should reject request with very long roomId', async () => { it('should reject request with very long roomId', async () => {
const longRoomId = 'a'.repeat(101); const longRoomId = 'a'.repeat(101);
const response = await startRecording(longRoomId, moderatorCookie); const response = await startRecording(longRoomId, moderatorToken);
expectValidationError(response, 'roomId', 'cannot exceed 100 characters'); expectValidationError(response, 'roomId', 'cannot exceed 100 characters');
}); });
it('should handle room that does not exist', async () => { it('should handle room that does not exist', async () => {
const response = await startRecording('non-existing-room-id', moderatorCookie); const response = await startRecording('non-existing-room-id', moderatorToken);
const error = errorRoomNotFound('non-existing-room-id'); const error = errorRoomNotFound('non-existing-room-id');
expect(response.status).toBe(404); expect(response.status).toBe(404);
expect(response.body).toEqual({ expect(response.body).toEqual({
@ -188,14 +188,14 @@ describe('Recording API Tests', () => {
it('should return 409 when recording is already in progress', async () => { it('should return 409 when recording is already in progress', async () => {
await joinFakeParticipant(room.roomId, 'fakeParticipantId'); await joinFakeParticipant(room.roomId, 'fakeParticipantId');
const firstResponse = await startRecording(room.roomId, moderatorCookie); const firstResponse = await startRecording(room.roomId, moderatorToken);
const recordingId = firstResponse.body.recordingId; const recordingId = firstResponse.body.recordingId;
expectValidStartRecordingResponse(firstResponse, room.roomId, room.roomName); expectValidStartRecordingResponse(firstResponse, room.roomId, room.roomName);
const secondResponse = await startRecording(room!.roomId, moderatorCookie); const secondResponse = await startRecording(room!.roomId, moderatorToken);
expect(secondResponse.status).toBe(409); expect(secondResponse.status).toBe(409);
expect(secondResponse.body.message).toContain('already'); expect(secondResponse.body.message).toContain('already');
const stopResponse = await stopRecording(recordingId, moderatorCookie); const stopResponse = await stopRecording(recordingId, moderatorToken);
expectValidStopRecordingResponse(stopResponse, recordingId, room.roomId, room.roomName); expectValidStopRecordingResponse(stopResponse, recordingId, room.roomId, room.roomName);
}); });
@ -204,7 +204,7 @@ describe('Recording API Tests', () => {
RECORDING_STARTED_TIMEOUT: '1s' RECORDING_STARTED_TIMEOUT: '1s'
}); });
await joinFakeParticipant(room.roomId, 'fakeParticipantId'); await joinFakeParticipant(room.roomId, 'fakeParticipantId');
const response = await startRecording(room.roomId, moderatorCookie); const response = await startRecording(room.roomId, moderatorToken);
expect(response.status).toBe(503); expect(response.status).toBe(503);
expect(response.body.message).toContain('timed out while starting'); expect(response.body.message).toContain('timed out while starting');
setInternalConfig({ setInternalConfig({

View File

@ -14,7 +14,7 @@ import { setupMultiRoomTestContext, TestContext } from '../../../helpers/test-sc
describe('Recording API Tests', () => { describe('Recording API Tests', () => {
let context: TestContext | null = null; let context: TestContext | null = null;
let room: MeetRoom, moderatorCookie: string; let room: MeetRoom, moderatorToken: string;
beforeAll(async () => { beforeAll(async () => {
startTestServer(); startTestServer();
@ -22,7 +22,7 @@ describe('Recording API Tests', () => {
}); });
afterAll(async () => { afterAll(async () => {
await stopAllRecordings(moderatorCookie); await stopAllRecordings(moderatorToken);
await disconnectFakeParticipants(); await disconnectFakeParticipants();
await Promise.all([deleteAllRooms(), deleteAllRecordings()]); await Promise.all([deleteAllRooms(), deleteAllRecordings()]);
}); });
@ -32,13 +32,13 @@ describe('Recording API Tests', () => {
beforeAll(async () => { beforeAll(async () => {
// Create a room and join a participant // Create a room and join a participant
context = await setupMultiRoomTestContext(1, true); context = await setupMultiRoomTestContext(1, true);
({ room, moderatorCookie } = context.getRoomByIndex(0)!); ({ room, moderatorToken } = context.getRoomByIndex(0)!);
const response = await startRecording(room.roomId, moderatorCookie); const response = await startRecording(room.roomId, moderatorToken);
recordingId = response.body.recordingId; recordingId = response.body.recordingId;
}); });
it('should stop an active recording and return 202', async () => { it('should stop an active recording and return 202', async () => {
const response = await stopRecording(recordingId, moderatorCookie); const response = await stopRecording(recordingId, moderatorToken);
expectValidStopRecordingResponse(response, recordingId, room.roomId, room.roomName); expectValidStopRecordingResponse(response, recordingId, room.roomId, room.roomName);
}); });
@ -46,18 +46,18 @@ describe('Recording API Tests', () => {
const context = await setupMultiRoomTestContext(2, true); const context = await setupMultiRoomTestContext(2, true);
const roomDataA = context.getRoomByIndex(0); const roomDataA = context.getRoomByIndex(0);
const roomDataB = context.getRoomByIndex(1); const roomDataB = context.getRoomByIndex(1);
const responseA = await startRecording(roomDataA!.room.roomId, roomDataA?.moderatorCookie); const responseA = await startRecording(roomDataA!.room.roomId, roomDataA!.moderatorToken);
const responseB = await startRecording(roomDataB!.room.roomId, roomDataB?.moderatorCookie); const responseB = await startRecording(roomDataB!.room.roomId, roomDataB!.moderatorToken);
const recordingIdA = responseA.body.recordingId; const recordingIdA = responseA.body.recordingId;
const recordingIdB = responseB.body.recordingId; const recordingIdB = responseB.body.recordingId;
const stopResponseA = await stopRecording(recordingIdA, roomDataA?.moderatorCookie); const stopResponseA = await stopRecording(recordingIdA, roomDataA!.moderatorToken);
expectValidStopRecordingResponse( expectValidStopRecordingResponse(
stopResponseA, stopResponseA,
recordingIdA, recordingIdA,
roomDataA!.room.roomId, roomDataA!.room.roomId,
roomDataA!.room.roomName roomDataA!.room.roomName
); );
const stopResponseB = await stopRecording(recordingIdB, roomDataB?.moderatorCookie); const stopResponseB = await stopRecording(recordingIdB, roomDataB!.moderatorToken);
expectValidStopRecordingResponse( expectValidStopRecordingResponse(
stopResponseB, stopResponseB,
recordingIdB, recordingIdB,
@ -68,7 +68,7 @@ describe('Recording API Tests', () => {
describe('Stop Recording Validation failures', () => { describe('Stop Recording Validation failures', () => {
it('should return 404 when recordingId does not exist', async () => { it('should return 404 when recordingId does not exist', async () => {
const response = await stopRecording(`${room.roomId}--EG_123--444`, moderatorCookie); const response = await stopRecording(`${room.roomId}--EG_123--444`, moderatorToken);
expect(response.status).toBe(404); expect(response.status).toBe(404);
expect(response.body.error).toBe('Recording Error'); expect(response.body.error).toBe('Recording Error');
expect(response.body.message).toContain('not found'); expect(response.body.message).toContain('not found');
@ -76,17 +76,17 @@ describe('Recording API Tests', () => {
it('should return 400 when recording is already stopped', async () => { it('should return 400 when recording is already stopped', async () => {
// First stop the recording // First stop the recording
await stopRecording(recordingId, moderatorCookie); await stopRecording(recordingId, moderatorToken);
// Try to stop it again // Try to stop it again
const response = await stopRecording(recordingId, moderatorCookie); const response = await stopRecording(recordingId, moderatorToken);
console.log('Response:', response.body); console.log('Response:', response.body);
expectErrorResponse(response, 409, '', `Recording '${recordingId}' is already stopped`); expectErrorResponse(response, 409, '', `Recording '${recordingId}' is already stopped`);
}); });
it('should return 404 when recordingId is not in the correct format', async () => { it('should return 404 when recordingId is not in the correct format', async () => {
const response = await stopRecording('invalid-recording-id', moderatorCookie); const response = await stopRecording('invalid-recording-id', moderatorToken);
expect(response.status).toBe(422); expect(response.status).toBe(422);
expect(response.body.error).toBe('Unprocessable Entity'); expect(response.body.error).toBe('Unprocessable Entity');
expect(response.body.message).toContain('Invalid request'); expect(response.body.message).toContain('Invalid request');

View File

@ -154,13 +154,13 @@ describe('Room API Tests', () => {
}); });
it('should handle deletion when specifying withMeeting and withRecordings parameters', async () => { it('should handle deletion when specifying withMeeting and withRecordings parameters', async () => {
const [room1, { room: room2 }, { room: room3 }, { room: room4, moderatorCookie }] = await Promise.all([ const [room1, { room: room2 }, { room: room3 }, { room: room4, moderatorToken }] = await Promise.all([
createRoom(), // Room without active meeting or recordings createRoom(), // Room without active meeting or recordings
setupSingleRoom(true), // Room with active meeting setupSingleRoom(true), // Room with active meeting
setupSingleRoomWithRecording(true), // Room with active meeting and recordings setupSingleRoomWithRecording(true), // Room with active meeting and recordings
setupSingleRoomWithRecording(true) // Room with recordings setupSingleRoomWithRecording(true) // Room with recordings
]); ]);
await endMeeting(room4.roomId, moderatorCookie); await endMeeting(room4.roomId, moderatorToken);
const fakeRoomId = 'fakeRoomId'; // Non-existing room const fakeRoomId = 'fakeRoomId'; // Non-existing room
const response = await bulkDeleteRooms( const response = await bulkDeleteRooms(

View File

@ -3,13 +3,14 @@ import { Express } from 'express';
import ms from 'ms'; import ms from 'ms';
import request from 'supertest'; import request from 'supertest';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js'; import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { MEET_INITIAL_API_KEY } from '../../../../src/environment.js';
import { import {
MeetRecordingAccess, MeetRecordingAccess,
MeetRoomDeletionPolicyWithMeeting, MeetRoomDeletionPolicyWithMeeting,
MeetRoomDeletionPolicyWithRecordings MeetRoomDeletionPolicyWithRecordings
} from '../../../../src/typings/ce/index.js'; } from '../../../../src/typings/ce/index.js';
import { expectValidRoom } from '../../../helpers/assertion-helpers.js'; import { expectValidRoom } from '../../../helpers/assertion-helpers.js';
import { createRoom, deleteAllRooms, loginUser, startTestServer } from '../../../helpers/request-helpers.js'; import { createRoom, deleteAllRooms, startTestServer } from '../../../helpers/request-helpers.js';
const ROOMS_PATH = `${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`; const ROOMS_PATH = `${INTERNAL_CONFIG.API_BASE_PATH_V1}/rooms`;
@ -17,11 +18,9 @@ describe('Room API Tests', () => {
const validAutoDeletionDate = Date.now() + ms('2h'); const validAutoDeletionDate = Date.now() + ms('2h');
let app: Express; let app: Express;
let adminCookie: string;
beforeAll(async () => { beforeAll(async () => {
app = startTestServer(); app = startTestServer();
adminCookie = await loginUser();
}); });
afterAll(async () => { afterAll(async () => {
@ -81,7 +80,11 @@ describe('Room API Tests', () => {
roomName: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
// Check that the error message contains the positive number validation // Check that the error message contains the positive number validation
expect(response.body.error).toContain('Unprocessable Entity'); expect(response.body.error).toContain('Unprocessable Entity');
@ -94,7 +97,11 @@ describe('Room API Tests', () => {
roomName: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(response.body.error).toContain('Unprocessable Entity'); expect(response.body.error).toContain('Unprocessable Entity');
expect(JSON.stringify(response.body.details)).toContain( expect(JSON.stringify(response.body.details)).toContain(
@ -108,7 +115,11 @@ describe('Room API Tests', () => {
roomName: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('Expected number'); expect(JSON.stringify(response.body.details)).toContain('Expected number');
}); });
@ -119,7 +130,11 @@ describe('Room API Tests', () => {
roomName: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('Expected number'); expect(JSON.stringify(response.body.details)).toContain('Expected number');
}); });
@ -130,7 +145,11 @@ describe('Room API Tests', () => {
roomName: 'TestRoom' roomName: 'TestRoom'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('Expected number'); expect(JSON.stringify(response.body.details)).toContain('Expected number');
}); });
@ -142,7 +161,11 @@ describe('Room API Tests', () => {
autoDeletionPolicy: 'invalid-policy' autoDeletionPolicy: 'invalid-policy'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('Expected object'); expect(JSON.stringify(response.body.details)).toContain('Expected object');
}); });
@ -157,7 +180,11 @@ describe('Room API Tests', () => {
} }
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('Invalid enum value'); expect(JSON.stringify(response.body.details)).toContain('Invalid enum value');
}); });
@ -172,7 +199,11 @@ describe('Room API Tests', () => {
} }
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain( expect(JSON.stringify(response.body.details)).toContain(
'FAIL policy is not allowed for withMeeting auto-deletion policy' 'FAIL policy is not allowed for withMeeting auto-deletion policy'
@ -189,7 +220,11 @@ describe('Room API Tests', () => {
} }
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain( expect(JSON.stringify(response.body.details)).toContain(
'FAIL policy is not allowed for withRecordings auto-deletion policy' 'FAIL policy is not allowed for withRecordings auto-deletion policy'
@ -202,7 +237,11 @@ describe('Room API Tests', () => {
autoDeletionDate: validAutoDeletionDate autoDeletionDate: validAutoDeletionDate
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('Expected string'); expect(JSON.stringify(response.body.details)).toContain('Expected string');
}); });
@ -213,7 +252,11 @@ describe('Room API Tests', () => {
autoDeletionDate: validAutoDeletionDate autoDeletionDate: validAutoDeletionDate
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('Expected string'); expect(JSON.stringify(response.body.details)).toContain('Expected string');
}); });
@ -225,7 +268,11 @@ describe('Room API Tests', () => {
config: 'invalid-config' config: 'invalid-config'
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('Expected object'); expect(JSON.stringify(response.body.details)).toContain('Expected object');
}); });
@ -246,7 +293,11 @@ describe('Room API Tests', () => {
} }
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('Expected boolean'); expect(JSON.stringify(response.body.details)).toContain('Expected boolean');
}); });
@ -255,7 +306,7 @@ describe('Room API Tests', () => {
// In this case, instead of sending JSON object, send an invalid JSON string. // In this case, instead of sending JSON object, send an invalid JSON string.
const response = await request(app) const response = await request(app)
.post(ROOMS_PATH) .post(ROOMS_PATH)
.set('Cookie', adminCookie) .set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.set('Content-Type', 'application/json') .set('Content-Type', 'application/json')
.send('{"roomName": "TestRoom",') // invalid JSON syntax .send('{"roomName": "TestRoom",') // invalid JSON syntax
.expect(400); .expect(400);
@ -271,7 +322,11 @@ describe('Room API Tests', () => {
autoDeletionDate: validAutoDeletionDate autoDeletionDate: validAutoDeletionDate
}; };
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send(payload).expect(422); const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_INITIAL_API_KEY)
.send(payload)
.expect(422);
expect(JSON.stringify(response.body.details)).toContain('roomName cannot exceed 50 characters'); expect(JSON.stringify(response.body.details)).toContain('roomName cannot exceed 50 characters');
}); });

View File

@ -51,14 +51,14 @@ describe('Room API Tests', () => {
describe('with active meeting but no recordings', () => { describe('with active meeting but no recordings', () => {
let roomId: string; let roomId: string;
let roomName: string; let roomName: string;
let moderatorCookie: string; let moderatorToken: string;
beforeEach(async () => { beforeEach(async () => {
// Create a room with an active meeting // Create a room with an active meeting
const { room, moderatorCookie: cookie } = await setupSingleRoom(true); const { room, moderatorToken: token } = await setupSingleRoom(true);
roomId = room.roomId; roomId = room.roomId;
roomName = room.roomName; roomName = room.roomName;
moderatorCookie = cookie; moderatorToken = token;
}); });
it('should return 200 with successCode=room_with_active_meeting_deleted when withMeeting=force', async () => { it('should return 200 with successCode=room_with_active_meeting_deleted when withMeeting=force', async () => {
@ -96,7 +96,7 @@ describe('Room API Tests', () => {
); );
// End meeting and check the room is deleted // End meeting and check the room is deleted
await endMeeting(roomId, moderatorCookie); await endMeeting(roomId, moderatorToken);
const getResponse = await getRoom(roomId); const getResponse = await getRoom(roomId);
expect(getResponse.status).toBe(404); expect(getResponse.status).toBe(404);
}); });
@ -114,10 +114,10 @@ describe('Room API Tests', () => {
beforeEach(async () => { beforeEach(async () => {
// Create a room with recordings and end the meeting // Create a room with recordings and end the meeting
const { room, moderatorCookie } = await setupSingleRoomWithRecording(true); const { room, moderatorToken } = await setupSingleRoomWithRecording(true);
roomId = room.roomId; roomId = room.roomId;
roomName = room.roomName; roomName = room.roomName;
await endMeeting(roomId, moderatorCookie); await endMeeting(roomId, moderatorToken);
}); });
it('should return 200 with successCode=room_and_recordings_deleted when withRecording=force', async () => { it('should return 200 with successCode=room_and_recordings_deleted when withRecording=force', async () => {
@ -171,14 +171,14 @@ describe('Room API Tests', () => {
describe('with active meeting and recordings', () => { describe('with active meeting and recordings', () => {
let roomId: string; let roomId: string;
let roomName: string; let roomName: string;
let moderatorCookie: string; let moderatorToken: string;
beforeEach(async () => { beforeEach(async () => {
// Create a room with recordings, keep the meeting active // Create a room with recordings, keep the meeting active
const { room, moderatorCookie: cookie } = await setupSingleRoomWithRecording(true); const { room, moderatorToken: token } = await setupSingleRoomWithRecording(true);
roomId = room.roomId; roomId = room.roomId;
roomName = room.roomName; roomName = room.roomName;
moderatorCookie = cookie; moderatorToken = token;
}); });
it('should return 200 with successCode=room_with_active_meeting_and_recordings_deleted when withMeeting=force and withRecording=force', async () => { it('should return 200 with successCode=room_with_active_meeting_and_recordings_deleted when withMeeting=force and withRecording=force', async () => {
@ -269,7 +269,7 @@ describe('Room API Tests', () => {
); );
// End meeting and check the room and recordings are deleted // End meeting and check the room and recordings are deleted
await endMeeting(roomId, moderatorCookie); await endMeeting(roomId, moderatorToken);
const roomResponse = await getRoom(roomId); const roomResponse = await getRoom(roomId);
expect(roomResponse.status).toBe(404); expect(roomResponse.status).toBe(404);
const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 }); const recordingsResponse = await getAllRecordings({ roomId, maxItems: 1 });
@ -297,7 +297,7 @@ describe('Room API Tests', () => {
); );
// End meeting and check that the room is closed and recordings are not deleted // End meeting and check that the room is closed and recordings are not deleted
await endMeeting(roomId, moderatorCookie); await endMeeting(roomId, moderatorToken);
const roomResponse = await getRoom(roomId); const roomResponse = await getRoom(roomId);
expect(roomResponse.status).toBe(200); expect(roomResponse.status).toBe(200);
expectValidRoom( expectValidRoom(

View File

@ -12,7 +12,7 @@ import {
deleteAllRooms, deleteAllRooms,
disconnectFakeParticipants, disconnectFakeParticipants,
endMeeting, endMeeting,
generateParticipantTokenCookie, generateParticipantToken,
getRoom, getRoom,
joinFakeParticipant, joinFakeParticipant,
runRoomGarbageCollector, runRoomGarbageCollector,
@ -102,8 +102,8 @@ describe('Room Garbage Collector Tests', () => {
// End the meeting // End the meeting
const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room); const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room);
const moderatorCookie = await generateParticipantTokenCookie(room.roomId, moderatorSecret, 'moderator'); const moderatorToken = await generateParticipantToken(room.roomId, moderatorSecret, 'moderator');
await endMeeting(room.roomId, moderatorCookie); await endMeeting(room.roomId, moderatorToken);
// Verify that the room is deleted // Verify that the room is deleted
response = await getRoom(room.roomId); response = await getRoom(room.roomId);
@ -180,8 +180,8 @@ describe('Room Garbage Collector Tests', () => {
// Start recording // Start recording
const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room1); const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(room1);
const moderatorCookie = await generateParticipantTokenCookie(room1.roomId, moderatorSecret, 'moderator'); const moderatorToken = await generateParticipantToken(room1.roomId, moderatorSecret, 'moderator');
await startRecording(room1.roomId, moderatorCookie); await startRecording(room1.roomId, moderatorToken);
await runRoomGarbageCollector(); await runRoomGarbageCollector();

View File

@ -1,13 +1,16 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals'; import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { AuthTransportMode } from '../../../../src/typings/ce/index.js';
import { ParticipantRole } from '../../../../src/typings/ce/participant.js'; import { ParticipantRole } from '../../../../src/typings/ce/participant.js';
import { MeetRecordingAccess } from '../../../../src/typings/ce/room-config.js'; import { MeetRecordingAccess } from '../../../../src/typings/ce/room-config.js';
import { expectValidRecordingTokenResponse } from '../../../helpers/assertion-helpers.js'; import { expectValidRecordingTokenResponse } from '../../../helpers/assertion-helpers.js';
import { import {
changeAuthTransportMode,
deleteAllRecordings, deleteAllRecordings,
deleteAllRooms, deleteAllRooms,
deleteRoom,
disconnectFakeParticipants, disconnectFakeParticipants,
generateRecordingToken, extractCookieFromHeaders,
generateRecordingTokenRequest,
startTestServer, startTestServer,
updateRecordingAccessConfigInRoom updateRecordingAccessConfigInRoom
} from '../../../helpers/request-helpers.js'; } from '../../../helpers/request-helpers.js';
@ -30,46 +33,60 @@ describe('Room API Tests', () => {
it('should generate a recording token with canRetrieve and canDelete permissions when using the moderator secret and recording access is admin_moderator', async () => { it('should generate a recording token with canRetrieve and canDelete permissions when using the moderator secret and recording access is admin_moderator', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret); const response = await generateRecordingTokenRequest(roomData.room.roomId, roomData.moderatorSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true); expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true);
}); });
it('should generate a recording token with canRetrieve and canDelete permissions when using the moderator secret and recording access is admin_moderator_speaker', async () => { it('should generate a recording token with canRetrieve and canDelete permissions when using the moderator secret and recording access is admin_moderator_speaker', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret); const response = await generateRecordingTokenRequest(roomData.room.roomId, roomData.moderatorSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true); expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true);
}); });
it('should generate a recording token without any permissions when using the speaker secret and recording access is admin_moderator', async () => { it('should generate a recording token without any permissions when using the speaker secret and recording access is admin_moderator', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const response = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret); const response = await generateRecordingTokenRequest(roomData.room.roomId, roomData.speakerSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.SPEAKER, false, false); expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.SPEAKER, false, false);
}); });
it('should generate a recording token with canRetrieve permission but not canDelete when using the speaker secret and recording access is admin_moderator_speaker', async () => { it('should generate a recording token with canRetrieve permission but not canDelete when using the speaker secret and recording access is admin_moderator_speaker', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER);
const response = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret); const response = await generateRecordingTokenRequest(roomData.room.roomId, roomData.speakerSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.SPEAKER, true, false); expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.SPEAKER, true, false);
}); });
it('should succeed even if the room is deleted', async () => { it('should generate a recording token and store it in a cookie when in cookie mode', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER); // Set auth transport mode to cookie
await deleteRoom(roomData.room.roomId); await changeAuthTransportMode(AuthTransportMode.COOKIE);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret); // Generate the recording token
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER);
const response = await generateRecordingTokenRequest(roomData.room.roomId, roomData.moderatorSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true); expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true);
// Recreate the room with recording // Check that the token is included in a cookie
roomData = await setupSingleRoomWithRecording(true); const recordingTokenCookie = extractCookieFromHeaders(
response,
INTERNAL_CONFIG.RECORDING_TOKEN_COOKIE_NAME
);
expect(recordingTokenCookie).toBeDefined();
expect(recordingTokenCookie).toContain(response.body.token);
expect(recordingTokenCookie).toContain('HttpOnly');
expect(recordingTokenCookie).toContain('SameSite=None');
expect(recordingTokenCookie).toContain('Secure');
expect(recordingTokenCookie).toContain('Path=/');
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail with a 404 error if there are no recordings in the room', async () => { it('should fail with a 404 error if there are no recordings in the room', async () => {
await deleteAllRecordings(); await deleteAllRecordings();
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret); const response = await generateRecordingTokenRequest(roomData.room.roomId, roomData.moderatorSecret);
expect(response.status).toBe(404); expect(response.status).toBe(404);
// Recreate the room with recording // Recreate the room with recording
@ -77,12 +94,12 @@ describe('Room API Tests', () => {
}); });
it('should fail with a 404 error if the room does not exist', async () => { it('should fail with a 404 error if the room does not exist', async () => {
const response = await generateRecordingToken('non-existent-room-id', roomData.moderatorSecret); const response = await generateRecordingTokenRequest('non-existent-room-id', roomData.moderatorSecret);
expect(response.status).toBe(404); expect(response.status).toBe(404);
}); });
it('should fail with a 400 error if the secret is invalid', async () => { it('should fail with a 400 error if the secret is invalid', async () => {
const response = await generateRecordingToken(roomData.room.roomId, 'invalid-secret'); const response = await generateRecordingTokenRequest(roomData.room.roomId, 'invalid-secret');
expect(response.status).toBe(400); expect(response.status).toBe(400);
}); });
}); });

View File

@ -102,7 +102,7 @@ describe('Room API Tests', () => {
const response = await getRoom( const response = await getRoom(
roomData.room.roomId, roomData.room.roomId,
undefined, undefined,
roomData.speakerCookie, roomData.speakerToken,
ParticipantRole.SPEAKER ParticipantRole.SPEAKER
); );
expect(response.status).toBe(200); expect(response.status).toBe(200);

View File

@ -68,7 +68,7 @@ describe('Room API Tests', () => {
expect(getResponse.body.meetingEndAction).toEqual('close'); expect(getResponse.body.meetingEndAction).toEqual('close');
// End meeting and verify closed status // End meeting and verify closed status
await endMeeting(roomData.room.roomId, roomData.moderatorCookie); await endMeeting(roomData.room.roomId, roomData.moderatorToken);
getResponse = await getRoom(roomData.room.roomId); getResponse = await getRoom(roomData.room.roomId);
expect(getResponse.status).toBe(200); expect(getResponse.status).toBe(200);

View File

@ -5,7 +5,15 @@ import { container } from '../../../../src/config/dependency-injector.config.js'
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js'; import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { MeetStorageService } from '../../../../src/services/index.js'; import { MeetStorageService } from '../../../../src/services/index.js';
import { expectValidationError } from '../../../helpers/assertion-helpers.js'; import { expectValidationError } from '../../../helpers/assertion-helpers.js';
import { generateApiKey, getApiKeys, loginUser, startTestServer } from '../../../helpers/request-helpers.js'; import {
changeAuthTransportMode,
extractCookieFromHeaders,
generateApiKey,
getApiKeys,
loginUser,
startTestServer
} from '../../../helpers/request-helpers.js';
import { AuthTransportMode } from '../../../../src/typings/ce/index.js';
const AUTH_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth`; const AUTH_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/auth`;
@ -28,17 +36,31 @@ describe('Authentication API Tests', () => {
expect(response.body).toHaveProperty('message'); expect(response.body).toHaveProperty('message');
// Check for access token and refresh token cookies // Check for access and refresh tokens
expect(response.headers['set-cookie']).toBeDefined(); expect(response.body).toHaveProperty('accessToken');
const cookies = response.headers['set-cookie'] as unknown as string[]; expect(response.body).toHaveProperty('refreshToken');
const accessTokenCookie = cookies.find((cookie) => });
cookie.startsWith(`${INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME}=`)
); it('should successfully login and set cookies in cookie mode', async () => {
const refreshTokenCookie = cookies.find((cookie) => // Set auth transport mode to cookie
cookie.startsWith(`${INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME}=`) await changeAuthTransportMode(AuthTransportMode.COOKIE);
);
const response = await request(app)
.post(`${AUTH_PATH}/login`)
.send({
username: 'admin',
password: 'admin'
})
.expect(200);
// Check for access and refresh token cookies
const accessTokenCookie = extractCookieFromHeaders(response, INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
const refreshTokenCookie = extractCookieFromHeaders(response, INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME);
expect(accessTokenCookie).toBeDefined(); expect(accessTokenCookie).toBeDefined();
expect(refreshTokenCookie).toBeDefined(); expect(refreshTokenCookie).toBeDefined();
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should return 404 for invalid credentials', async () => { it('should return 404 for invalid credentials', async () => {
@ -107,17 +129,24 @@ describe('Authentication API Tests', () => {
expect(response.body).toHaveProperty('message'); expect(response.body).toHaveProperty('message');
expect(response.body.message).toBe('Logout successful'); expect(response.body.message).toBe('Logout successful');
});
// Check for cleared cookies it('should successfully logout and clear cookies in cookie mode', async () => {
const cookies = response.headers['set-cookie'] as unknown as string[]; // Set auth transport mode to cookie
const accessTokenCookie = cookies.find((cookie) => await changeAuthTransportMode(AuthTransportMode.COOKIE);
cookie.startsWith(`${INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME}=;`)
); const response = await request(app).post(`${AUTH_PATH}/logout`).expect(200);
const refreshTokenCookie = cookies.find((cookie) =>
cookie.startsWith(`${INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME}=;`) // Check that the access and refresh token cookies are cleared
); const accessTokenCookie = extractCookieFromHeaders(response, INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
const refreshTokenCookie = extractCookieFromHeaders(response, INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME);
expect(accessTokenCookie).toBeDefined(); expect(accessTokenCookie).toBeDefined();
expect(accessTokenCookie).toContain('Expires=Thu, 01 Jan 1970 00:00:00 GMT');
expect(refreshTokenCookie).toBeDefined(); expect(refreshTokenCookie).toBeDefined();
expect(refreshTokenCookie).toContain('Expires=Thu, 01 Jan 1970 00:00:00 GMT');
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
}); });
@ -132,24 +161,48 @@ describe('Authentication API Tests', () => {
}) })
.expect(200); .expect(200);
const cookies = loginResponse.headers['set-cookie'] as unknown as string[]; expect(loginResponse.body).toHaveProperty('refreshToken');
const refreshTokenCookie = cookies.find((cookie) => const refreshToken = loginResponse.body.refreshToken;
cookie.startsWith(`${INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME}=`)
) as string;
const response = await request(app) const response = await request(app)
.post(`${AUTH_PATH}/refresh`) .post(`${AUTH_PATH}/refresh`)
.set('Cookie', [refreshTokenCookie]) .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, `Bearer ${refreshToken}`)
.expect(200); .expect(200);
expect(response.body).toHaveProperty('message'); expect(response.body).toHaveProperty('message');
expect(response.body).toHaveProperty('accessToken');
});
// Check for new access token cookie it('should successfully refresh token and set new access token cookie in cookie mode', async () => {
const newCookies = response.headers['set-cookie'] as unknown as string[]; // Set auth transport mode to cookie
const newAccessTokenCookie = newCookies.find((cookie) => await changeAuthTransportMode(AuthTransportMode.COOKIE);
cookie.startsWith(`${INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME}=`)
// First, login to get a valid refresh token cookie
const loginResponse = await request(app)
.post(`${AUTH_PATH}/login`)
.send({
username: 'admin',
password: 'admin'
})
.expect(200);
const refreshTokenCookie = extractCookieFromHeaders(
loginResponse,
INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME
); );
expect(refreshTokenCookie).toBeDefined();
const response = await request(app)
.post(`${AUTH_PATH}/refresh`)
.set('Cookie', refreshTokenCookie!)
.expect(200);
// Check that a new access token cookie is set
const newAccessTokenCookie = extractCookieFromHeaders(response, INTERNAL_CONFIG.ACCESS_TOKEN_COOKIE_NAME);
expect(newAccessTokenCookie).toBeDefined(); expect(newAccessTokenCookie).toBeDefined();
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should return 400 when no refresh token is provided', async () => { it('should return 400 when no refresh token is provided', async () => {
@ -162,7 +215,7 @@ describe('Authentication API Tests', () => {
it('should return 400 when refresh token is invalid', async () => { it('should return 400 when refresh token is invalid', async () => {
const response = await request(app) const response = await request(app)
.post(`${AUTH_PATH}/refresh`) .post(`${AUTH_PATH}/refresh`)
.set('Cookie', `${INTERNAL_CONFIG.REFRESH_TOKEN_COOKIE_NAME}=invalidtoken`) .set(INTERNAL_CONFIG.REFRESH_TOKEN_HEADER, 'Bearer invalidtoken')
.expect(400); .expect(400);
expect(response.body).toHaveProperty('message'); expect(response.body).toHaveProperty('message');
@ -171,10 +224,10 @@ describe('Authentication API Tests', () => {
}); });
describe('API Keys Management', () => { describe('API Keys Management', () => {
let adminCookie: string; let adminAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
afterAll(async () => { afterAll(async () => {
@ -190,7 +243,10 @@ describe('Authentication API Tests', () => {
}; };
it('should create a new API key', async () => { it('should create a new API key', async () => {
const response = await request(app).post(`${AUTH_PATH}/api-keys`).set('Cookie', adminCookie).expect(201); const response = await request(app)
.post(`${AUTH_PATH}/api-keys`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.expect(201);
expect(response.body).toHaveProperty('key'); expect(response.body).toHaveProperty('key');
expect(response.body).toHaveProperty('creationDate'); expect(response.body).toHaveProperty('creationDate');
@ -233,7 +289,10 @@ describe('Authentication API Tests', () => {
it('should delete all API keys', async () => { it('should delete all API keys', async () => {
const apiKey = await generateApiKey(); const apiKey = await generateApiKey();
await request(app).delete(`${AUTH_PATH}/api-keys`).set('Cookie', adminCookie).expect(200); await request(app)
.delete(`${AUTH_PATH}/api-keys`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.expect(200);
// Confirm deletion // Confirm deletion
const getResponse = await getApiKeys(); const getResponse = await getApiKeys();
@ -246,6 +305,21 @@ describe('Authentication API Tests', () => {
expect(apiResponse.status).toBe(401); expect(apiResponse.status).toBe(401);
}); });
it('should succeed API key endpoints for authenticated admin user in cookie mode', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
await request(app).post(`${AUTH_PATH}/api-keys`).set('Cookie', adminCookie).expect(201);
await request(app).get(`${AUTH_PATH}/api-keys`).set('Cookie', adminCookie).expect(200);
await request(app).delete(`${AUTH_PATH}/api-keys`).set('Cookie', adminCookie).expect(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should reject API key endpoints for unauthenticated users', async () => { it('should reject API key endpoints for unauthenticated users', async () => {
await request(app).post(`${AUTH_PATH}/api-keys`).expect(401); await request(app).post(`${AUTH_PATH}/api-keys`).expect(401);
await request(app).get(`${AUTH_PATH}/api-keys`).expect(401); await request(app).get(`${AUTH_PATH}/api-keys`).expect(401);

View File

@ -6,7 +6,7 @@ import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { MEET_INITIAL_API_KEY } from '../../../../src/environment.js'; import { MEET_INITIAL_API_KEY } from '../../../../src/environment.js';
import { MeetStorageService } from '../../../../src/services/index.js'; import { MeetStorageService } from '../../../../src/services/index.js';
import { AuthMode, AuthTransportMode, AuthType, MeetRoomThemeMode } from '../../../../src/typings/ce/index.js'; import { AuthMode, AuthTransportMode, AuthType, MeetRoomThemeMode } from '../../../../src/typings/ce/index.js';
import { loginUser, startTestServer } from '../../../helpers/request-helpers.js'; import { changeAuthTransportMode, loginUser, startTestServer } from '../../../helpers/request-helpers.js';
const CONFIG_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config`; const CONFIG_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config`;
@ -17,11 +17,11 @@ const restoreGlobalConfig = async () => {
describe('Global Config API Security Tests', () => { describe('Global Config API Security Tests', () => {
let app: Express; let app: Express;
let adminCookie: string; let adminAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
app = startTestServer(); app = startTestServer();
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
describe('Update Webhook Config Tests', () => { describe('Update Webhook Config Tests', () => {
@ -39,12 +39,29 @@ describe('Global Config API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.put(`${CONFIG_PATH}/webhooks`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send(webhookConfig);
expect(response.status).toBe(200);
await restoreGlobalConfig();
});
it('should succeed when user is authenticated as admin in cookie mode', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app) const response = await request(app)
.put(`${CONFIG_PATH}/webhooks`) .put(`${CONFIG_PATH}/webhooks`)
.set('Cookie', adminCookie) .set('Cookie', adminCookie)
.send(webhookConfig); .send(webhookConfig);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// This method already restores the config to default (header mode)
await restoreGlobalConfig(); await restoreGlobalConfig();
}); });
@ -63,8 +80,24 @@ describe('Global Config API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.get(`${CONFIG_PATH}/webhooks`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin in cookie mode', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app).get(`${CONFIG_PATH}/webhooks`).set('Cookie', adminCookie); const response = await request(app).get(`${CONFIG_PATH}/webhooks`).set('Cookie', adminCookie);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
@ -79,7 +112,7 @@ describe('Global Config API Security Tests', () => {
authMethod: { authMethod: {
type: AuthType.SINGLE_USER type: AuthType.SINGLE_USER
}, },
authTransportMode: AuthTransportMode.COOKIE, authTransportMode: AuthTransportMode.HEADER,
authModeToAccessRoom: AuthMode.ALL_USERS authModeToAccessRoom: AuthMode.ALL_USERS
} }
}; };
@ -93,12 +126,29 @@ describe('Global Config API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.put(`${CONFIG_PATH}/security`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send(securityConfig);
expect(response.status).toBe(200);
await restoreGlobalConfig();
});
it('should succeed when user is authenticated as admin in cookie mode', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app) const response = await request(app)
.put(`${CONFIG_PATH}/security`) .put(`${CONFIG_PATH}/security`)
.set('Cookie', adminCookie) .set('Cookie', adminCookie)
.send(securityConfig); .send(securityConfig);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// This method already restores the config to default (header mode)
await restoreGlobalConfig(); await restoreGlobalConfig();
}); });
@ -137,6 +187,22 @@ describe('Global Config API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.put(`${CONFIG_PATH}/rooms/appearance`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send(appearanceConfig);
expect(response.status).toBe(200);
await restoreGlobalConfig();
});
it('should succeed when user is authenticated as admin in cookie mode', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app) const response = await request(app)
.put(`${CONFIG_PATH}/rooms/appearance`) .put(`${CONFIG_PATH}/rooms/appearance`)
.set('Cookie', adminCookie) .set('Cookie', adminCookie)

View File

@ -3,9 +3,10 @@ import { Express } from 'express';
import request from 'supertest'; import request from 'supertest';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js'; import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { LIVEKIT_URL, MEET_INITIAL_API_KEY } from '../../../../src/environment.js'; import { LIVEKIT_URL, MEET_INITIAL_API_KEY } from '../../../../src/environment.js';
import { MeetTokenMetadata, ParticipantRole } from '../../../../src/typings/ce'; import { AuthTransportMode, MeetTokenMetadata, ParticipantRole } from '../../../../src/typings/ce';
import { getPermissions } from '../../../helpers/assertion-helpers.js'; import { getPermissions } from '../../../helpers/assertion-helpers.js';
import { import {
changeAuthTransportMode,
deleteAllRooms, deleteAllRooms,
disconnectFakeParticipants, disconnectFakeParticipants,
loginUser, loginUser,
@ -18,12 +19,12 @@ const MEETINGS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/meetings`;
describe('Meeting API Security Tests', () => { describe('Meeting API Security Tests', () => {
let app: Express; let app: Express;
let adminCookie: string; let adminAccessToken: string;
let roomData: RoomData; let roomData: RoomData;
beforeAll(async () => { beforeAll(async () => {
app = startTestServer(); app = startTestServer();
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
beforeEach(async () => { beforeEach(async () => {
@ -46,24 +47,41 @@ describe('Meeting API Security Tests', () => {
it('should fail when user is authenticated as admin', async () => { it('should fail when user is authenticated as admin', async () => {
const response = await request(app) const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`) .delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(401); expect(response.status).toBe(401);
}); });
it('should succeed when participant is moderator', async () => { it('should succeed when participant is moderator', async () => {
const response = await request(app) const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`) .delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should succeed when participant is moderator and token is sent in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Create a new room to obtain participant token in cookie mode
const newRoomData = await setupSingleRoom(true);
const response = await request(app)
.delete(`${MEETINGS_PATH}/${newRoomData.room.roomId}`)
.set('Cookie', newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail when participant is moderator of a different room', async () => { it('should fail when participant is moderator of a different room', async () => {
const newRoomData = await setupSingleRoom(); const newRoomData = await setupSingleRoom();
const response = await request(app) const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`) .delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
.set('Cookie', newRoomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -71,7 +89,7 @@ describe('Meeting API Security Tests', () => {
it('should fail when participant is speaker', async () => { it('should fail when participant is speaker', async () => {
const response = await request(app) const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`) .delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -106,7 +124,7 @@ describe('Meeting API Security Tests', () => {
it('should fail when user is authenticated as admin', async () => { it('should fail when user is authenticated as admin', async () => {
const response = await request(app) const response = await request(app)
.put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`) .put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.set('Cookie', adminCookie) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({ role }); .send({ role });
expect(response.status).toBe(401); expect(response.status).toBe(401);
}); });
@ -114,18 +132,46 @@ describe('Meeting API Security Tests', () => {
it('should succeed when participant is moderator', async () => { it('should succeed when participant is moderator', async () => {
const response = await request(app) const response = await request(app)
.put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`) .put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR) .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR)
.send({ role }); .send({ role });
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should succeed when participant is moderator and token is sent in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Create a new room to obtain participant token in cookie mode
const newRoomData = await setupSingleRoom(true);
await updateParticipantMetadata(newRoomData.room.roomId, PARTICIPANT_NAME, {
livekitUrl: LIVEKIT_URL,
roles: [
{
role: ParticipantRole.SPEAKER,
permissions: getPermissions(newRoomData.room.roomId, ParticipantRole.SPEAKER).openvidu
}
],
selectedRole: ParticipantRole.SPEAKER
});
const response = await request(app)
.put(`${MEETINGS_PATH}/${newRoomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.set('Cookie', newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR)
.send({ role });
expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail when participant is moderator of a different room', async () => { it('should fail when participant is moderator of a different room', async () => {
const newRoomData = await setupSingleRoom(); const newRoomData = await setupSingleRoom();
const response = await request(app) const response = await request(app)
.put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`) .put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.set('Cookie', newRoomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR) .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR)
.send({ role }); .send({ role });
expect(response.status).toBe(403); expect(response.status).toBe(403);
@ -134,7 +180,7 @@ describe('Meeting API Security Tests', () => {
it('should fail when participant is speaker', async () => { it('should fail when participant is speaker', async () => {
const response = await request(app) const response = await request(app)
.put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`) .put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER) .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER)
.send({ role }); .send({ role });
expect(response.status).toBe(403); expect(response.status).toBe(403);
@ -154,24 +200,41 @@ describe('Meeting API Security Tests', () => {
it('should fail when user is authenticated as admin', async () => { it('should fail when user is authenticated as admin', async () => {
const response = await request(app) const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`) .delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(401); expect(response.status).toBe(401);
}); });
it('should succeed when participant is moderator', async () => { it('should succeed when participant is moderator', async () => {
const response = await request(app) const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`) .delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should succeed when participant is moderator and token is sent in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Create a new room to obtain participant token in cookie mode
const newRoomData = await setupSingleRoom(true);
const response = await request(app)
.delete(`${MEETINGS_PATH}/${newRoomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.set('Cookie', newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail when participant is moderator of a different room', async () => { it('should fail when participant is moderator of a different room', async () => {
const newRoomData = await setupSingleRoom(); const newRoomData = await setupSingleRoom();
const response = await request(app) const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`) .delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.set('Cookie', newRoomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -179,7 +242,7 @@ describe('Meeting API Security Tests', () => {
it('should fail when participant is speaker', async () => { it('should fail when participant is speaker', async () => {
const response = await request(app) const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`) .delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });

View File

@ -2,8 +2,9 @@ import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { Express } from 'express'; import { Express } from 'express';
import request from 'supertest'; import request from 'supertest';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js'; import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { AuthMode } from '../../../../src/typings/ce/index.js'; import { AuthMode, AuthTransportMode } from '../../../../src/typings/ce/index.js';
import { import {
changeAuthTransportMode,
changeSecurityConfig, changeSecurityConfig,
deleteAllRooms, deleteAllRooms,
disconnectFakeParticipants, disconnectFakeParticipants,
@ -19,11 +20,11 @@ describe('Participant API Security Tests', () => {
const PARTICIPANT_NAME = 'TEST_PARTICIPANT'; const PARTICIPANT_NAME = 'TEST_PARTICIPANT';
let app: Express; let app: Express;
let adminCookie: string; let adminAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
app = startTestServer(); app = startTestServer();
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
afterAll(async () => { afterAll(async () => {
@ -74,12 +75,33 @@ describe('Participant API Security Tests', () => {
it('should succeed when authentication is required for moderator, participant is moderator and authenticated', async () => { it('should succeed when authentication is required for moderator, participant is moderator and authenticated', async () => {
await changeSecurityConfig(AuthMode.MODERATORS_ONLY); await changeSecurityConfig(AuthMode.MODERATORS_ONLY);
const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({
roomId: roomData.room.roomId,
secret: roomData.moderatorSecret,
participantName: PARTICIPANT_NAME
});
expect(response.status).toBe(200);
});
it('should succeed when authentication is required for moderator, participant is moderator and authenticated via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).set('Cookie', adminCookie).send({ const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).set('Cookie', adminCookie).send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
participantName: PARTICIPANT_NAME participantName: PARTICIPANT_NAME
}); });
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when authentication is required for moderator and participant is moderator but not authenticated', async () => { it('should fail when authentication is required for moderator and participant is moderator but not authenticated', async () => {
@ -96,7 +118,10 @@ describe('Participant API Security Tests', () => {
it('should succeed when authentication is required for all users, participant is speaker and authenticated', async () => { it('should succeed when authentication is required for all users, participant is speaker and authenticated', async () => {
await changeSecurityConfig(AuthMode.ALL_USERS); await changeSecurityConfig(AuthMode.ALL_USERS);
const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).set('Cookie', adminCookie).send({ const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.speakerSecret, secret: roomData.speakerSecret,
participantName: PARTICIPANT_NAME participantName: PARTICIPANT_NAME
@ -118,7 +143,10 @@ describe('Participant API Security Tests', () => {
it('should succeed when authentication is required for all users, participant is moderator and authenticated', async () => { it('should succeed when authentication is required for all users, participant is moderator and authenticated', async () => {
await changeSecurityConfig(AuthMode.ALL_USERS); await changeSecurityConfig(AuthMode.ALL_USERS);
const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).set('Cookie', adminCookie).send({ const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
participantName: PARTICIPANT_NAME participantName: PARTICIPANT_NAME
@ -158,7 +186,7 @@ describe('Participant API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`) .post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.send({ .send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.speakerSecret, secret: roomData.speakerSecret,
@ -173,7 +201,7 @@ describe('Participant API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`) .post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.send({ .send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
@ -188,7 +216,7 @@ describe('Participant API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`) .post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.send({ .send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.speakerSecret, secret: roomData.speakerSecret,
@ -203,7 +231,8 @@ describe('Participant API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`) .post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', [adminCookie, roomData.moderatorCookie]) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.send({ .send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
@ -213,12 +242,38 @@ describe('Participant API Security Tests', () => {
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should succeed when authentication is required for moderator, participant is moderator and authenticated via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
// Create a new room to obtain participant token in cookie mode
const newRoomData = await setupSingleRoom(true);
const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', adminCookie)
.set('Cookie', newRoomData.moderatorToken)
.send({
roomId: newRoomData.room.roomId,
secret: newRoomData.moderatorSecret,
participantName: PARTICIPANT_NAME,
participantIdentity: PARTICIPANT_NAME
});
expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail when authentication is required for moderator and participant is moderator but not authenticated', async () => { it('should fail when authentication is required for moderator and participant is moderator but not authenticated', async () => {
await changeSecurityConfig(AuthMode.MODERATORS_ONLY); await changeSecurityConfig(AuthMode.MODERATORS_ONLY);
const response = await request(app) const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`) .post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.send({ .send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
@ -233,7 +288,8 @@ describe('Participant API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`) .post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', [adminCookie, roomData.speakerCookie]) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.send({ .send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.speakerSecret, secret: roomData.speakerSecret,
@ -248,7 +304,7 @@ describe('Participant API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`) .post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.send({ .send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.speakerSecret, secret: roomData.speakerSecret,
@ -263,7 +319,8 @@ describe('Participant API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`) .post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', [adminCookie, roomData.moderatorCookie]) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.send({ .send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,
@ -278,7 +335,7 @@ describe('Participant API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${PARTICIPANTS_PATH}/token/refresh`) .post(`${PARTICIPANTS_PATH}/token/refresh`)
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.send({ .send({
roomId: roomData.room.roomId, roomId: roomData.room.roomId,
secret: roomData.moderatorSecret, secret: roomData.moderatorSecret,

View File

@ -3,13 +3,14 @@ import { Express } from 'express';
import request from 'supertest'; import request from 'supertest';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js'; import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { MEET_INITIAL_API_KEY } from '../../../../src/environment.js'; import { MEET_INITIAL_API_KEY } from '../../../../src/environment.js';
import { MeetRecordingAccess, ParticipantRole } from '../../../../src/typings/ce/index.js'; import { AuthTransportMode, MeetRecordingAccess, ParticipantRole } from '../../../../src/typings/ce/index.js';
import { expectValidStopRecordingResponse } from '../../../helpers/assertion-helpers.js'; import { expectValidStopRecordingResponse } from '../../../helpers/assertion-helpers.js';
import { import {
changeAuthTransportMode,
deleteAllRecordings, deleteAllRecordings,
deleteAllRooms, deleteAllRooms,
disconnectFakeParticipants, disconnectFakeParticipants,
generateRecordingTokenCookie, generateRecordingToken,
getRecordingUrl, getRecordingUrl,
loginUser, loginUser,
startTestServer, startTestServer,
@ -24,11 +25,11 @@ const INTERNAL_RECORDINGS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/r
describe('Recording API Security Tests', () => { describe('Recording API Security Tests', () => {
let app: Express; let app: Express;
let adminCookie: string; let adminAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
app = startTestServer(); app = startTestServer();
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
afterAll(async () => { afterAll(async () => {
@ -55,7 +56,7 @@ describe('Recording API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(INTERNAL_RECORDINGS_PATH) .post(INTERNAL_RECORDINGS_PATH)
.send({ roomId: roomData.room.roomId }) .send({ roomId: roomData.room.roomId })
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(401); expect(response.status).toBe(401);
}); });
@ -63,23 +64,51 @@ describe('Recording API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(INTERNAL_RECORDINGS_PATH) .post(INTERNAL_RECORDINGS_PATH)
.send({ roomId: roomData.room.roomId }) .send({ roomId: roomData.room.roomId })
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(201); expect(response.status).toBe(201);
// Stop recording to clean up // Stop recording to clean up
const recordingId = response.body.recordingId; const recordingId = response.body.recordingId;
const stopResponse = await stopRecording(recordingId, roomData.moderatorCookie); const stopResponse = await stopRecording(recordingId, roomData.moderatorToken);
expectValidStopRecordingResponse(stopResponse, recordingId, roomData.room.roomId, roomData.room.roomName); expectValidStopRecordingResponse(stopResponse, recordingId, roomData.room.roomId, roomData.room.roomName);
}); });
it('should succeed when participant is moderator and token is sent in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Create a new room to obtain participant token in cookie mode
const newRoomData = await setupSingleRoom(true);
const response = await request(app)
.post(INTERNAL_RECORDINGS_PATH)
.send({ roomId: newRoomData.room.roomId })
.set('Cookie', newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(201);
// Stop recording to clean up
const recordingId = response.body.recordingId;
const stopResponse = await stopRecording(recordingId, newRoomData.moderatorToken);
expectValidStopRecordingResponse(
stopResponse,
recordingId,
newRoomData.room.roomId,
newRoomData.room.roomName
);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail when participant is moderator of a different room', async () => { it('should fail when participant is moderator of a different room', async () => {
const newRoomData = await setupSingleRoom(); const newRoomData = await setupSingleRoom();
const response = await request(app) const response = await request(app)
.post(INTERNAL_RECORDINGS_PATH) .post(INTERNAL_RECORDINGS_PATH)
.send({ roomId: roomData.room.roomId }) .send({ roomId: roomData.room.roomId })
.set('Cookie', newRoomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -88,7 +117,7 @@ describe('Recording API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(INTERNAL_RECORDINGS_PATH) .post(INTERNAL_RECORDINGS_PATH)
.send({ roomId: roomData.room.roomId }) .send({ roomId: roomData.room.roomId })
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -102,7 +131,7 @@ describe('Recording API Security Tests', () => {
}); });
afterAll(async () => { afterAll(async () => {
await stopAllRecordings(roomData.moderatorCookie); await stopAllRecordings(roomData.moderatorToken);
}); });
it('should fail when request includes API key', async () => { it('should fail when request includes API key', async () => {
@ -116,24 +145,41 @@ describe('Recording API Security Tests', () => {
it('should fail when user is authenticated as admin', async () => { it('should fail when user is authenticated as admin', async () => {
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_RECORDINGS_PATH}/${roomData.recordingId}/stop`) .post(`${INTERNAL_RECORDINGS_PATH}/${roomData.recordingId}/stop`)
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(401); expect(response.status).toBe(401);
}); });
it('should succeed when participant is moderator', async () => { it('should succeed when participant is moderator', async () => {
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_RECORDINGS_PATH}/${roomData.recordingId}/stop`) .post(`${INTERNAL_RECORDINGS_PATH}/${roomData.recordingId}/stop`)
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(202); expect(response.status).toBe(202);
}); });
it('should succeed when participant is moderator and token is sent in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Create a new room to obtain participant token in cookie mode
const newRoomData = await setupSingleRoomWithRecording();
const response = await request(app)
.post(`${INTERNAL_RECORDINGS_PATH}/${newRoomData.recordingId}/stop`)
.set('Cookie', newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(202);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail when participant is moderator of a different room', async () => { it('should fail when participant is moderator of a different room', async () => {
const newRoomData = await setupSingleRoom(); const newRoomData = await setupSingleRoom();
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_RECORDINGS_PATH}/${roomData.recordingId}/stop`) .post(`${INTERNAL_RECORDINGS_PATH}/${roomData.recordingId}/stop`)
.set('Cookie', newRoomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -141,7 +187,7 @@ describe('Recording API Security Tests', () => {
it('should fail when participant is speaker', async () => { it('should fail when participant is speaker', async () => {
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_RECORDINGS_PATH}/${roomData.recordingId}/stop`) .post(`${INTERNAL_RECORDINGS_PATH}/${roomData.recordingId}/stop`)
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -165,7 +211,9 @@ describe('Recording API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', adminCookie); const response = await request(app)
.get(RECORDINGS_PATH)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -174,48 +222,61 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie); const response = await request(app)
.get(RECORDINGS_PATH)
.set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should succeed when recording access is admin_moderator_speaker and participant is speaker, token in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingToken);
expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => { it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom( await updateRecordingAccessConfigInRoom(
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie); const response = await request(app)
.get(RECORDINGS_PATH)
.set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should fail when recording access is admin_moderator and participant is speaker', async () => { it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie); const response = await request(app)
.get(RECORDINGS_PATH)
.set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
it('should succeed when recording access is admin_moderator and participant is moderator', async () => { it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie); const response = await request(app)
.get(RECORDINGS_PATH)
.set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
}); });
@ -229,7 +290,9 @@ describe('Recording API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app).get(`${RECORDINGS_PATH}/${recordingId}`).set('Cookie', adminCookie); const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -238,56 +301,61 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}`) .get(`${RECORDINGS_PATH}/${recordingId}`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should succeed when recording access is admin_moderator_speaker and participant is speaker, token in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingToken);
expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => { it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom( await updateRecordingAccessConfigInRoom(
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}`) .get(`${RECORDINGS_PATH}/${recordingId}`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should fail when recording access is admin_moderator and participant is speaker', async () => { it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}`) .get(`${RECORDINGS_PATH}/${recordingId}`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
it('should succeed when recording access is admin_moderator and participant is moderator', async () => { it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}`) .get(`${RECORDINGS_PATH}/${recordingId}`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -331,7 +399,7 @@ describe('Recording API Security Tests', () => {
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}?secret=${secret}`) .get(`${RECORDINGS_PATH}/${recordingId}?secret=${secret}`)
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
}); });
@ -358,7 +426,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app) const response = await request(app)
.delete(`${RECORDINGS_PATH}/${fakeRecordingId}`) .delete(`${RECORDINGS_PATH}/${fakeRecordingId}`)
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(404); expect(response.status).toBe(404);
}); });
@ -367,14 +435,11 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.delete(`${RECORDINGS_PATH}/${fakeRecordingId}`) .delete(`${RECORDINGS_PATH}/${fakeRecordingId}`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -383,40 +448,50 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.delete(`${RECORDINGS_PATH}/${fakeRecordingId}`) .delete(`${RECORDINGS_PATH}/${fakeRecordingId}`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(404); expect(response.status).toBe(404);
}); });
it('should succeed when recording access is admin_moderator_speaker and participant is moderator, token in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app)
.delete(`${RECORDINGS_PATH}/${fakeRecordingId}`)
.set('Cookie', recordingToken);
expect(response.status).toBe(404);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail when recording access is admin_moderator and participant is speaker', async () => { it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.delete(`${RECORDINGS_PATH}/${fakeRecordingId}`) .delete(`${RECORDINGS_PATH}/${fakeRecordingId}`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
it('should succeed when recording access is admin_moderator and participant is moderator', async () => { it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.delete(`${RECORDINGS_PATH}/${fakeRecordingId}`) .delete(`${RECORDINGS_PATH}/${fakeRecordingId}`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(404); expect(response.status).toBe(404);
}); });
}); });
@ -445,7 +520,7 @@ describe('Recording API Security Tests', () => {
const response = await request(app) const response = await request(app)
.delete(RECORDINGS_PATH) .delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId }) .query({ recordingIds: fakeRecordingId })
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(400); expect(response.status).toBe(400);
}); });
@ -454,15 +529,12 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.delete(RECORDINGS_PATH) .delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId }) .query({ recordingIds: fakeRecordingId })
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -471,43 +543,54 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.delete(RECORDINGS_PATH) .delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId }) .query({ recordingIds: fakeRecordingId })
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(400); expect(response.status).toBe(400);
}); });
it('should succeed when recording access is admin_moderator_speaker and participant is moderator, token in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app)
.delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId })
.set('Cookie', recordingToken);
expect(response.status).toBe(400);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail when recording access is admin_moderator and participant is speaker', async () => { it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.delete(RECORDINGS_PATH) .delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId }) .query({ recordingIds: fakeRecordingId })
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
it('should succeed when recording access is admin_moderator and participant is moderator', async () => { it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.delete(RECORDINGS_PATH) .delete(RECORDINGS_PATH)
.query({ recordingIds: fakeRecordingId }) .query({ recordingIds: fakeRecordingId })
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(400); expect(response.status).toBe(400);
}); });
}); });
@ -523,7 +606,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`) .get(`${RECORDINGS_PATH}/${recordingId}/media`)
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -532,14 +615,48 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`) .get(`${RECORDINGS_PATH}/${recordingId}/media`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200);
});
it('should succeed when recording access is admin_moderator_speaker and participant is speaker, token in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`)
.set('Cookie', recordingToken);
expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should succeed when recording access is admin_moderator_speaker and participant is speaker, token in query param', async () => {
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
let recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
// Remove the "Bearer " prefix if present
if (recordingToken.startsWith('Bearer ')) {
recordingToken = recordingToken.slice(7);
}
const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`)
.query({ recordingToken });
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -548,40 +665,31 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`) .get(`${RECORDINGS_PATH}/${recordingId}/media`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should fail when recording access is admin_moderator and participant is speaker', async () => { it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`) .get(`${RECORDINGS_PATH}/${recordingId}/media`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
it('should succeed when recording access is admin_moderator and participant is moderator', async () => { it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`) .get(`${RECORDINGS_PATH}/${recordingId}/media`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -625,7 +733,7 @@ describe('Recording API Security Tests', () => {
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media?secret=${secret}`) .get(`${RECORDINGS_PATH}/${recordingId}/media?secret=${secret}`)
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -647,7 +755,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/url`) .get(`${RECORDINGS_PATH}/${recordingId}/url`)
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -656,56 +764,63 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/url`) .get(`${RECORDINGS_PATH}/${recordingId}/url`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should succeed when recording access is admin_moderator_speaker and participant is speaker, token in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/url`)
.set('Cookie', recordingToken);
expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => { it('should succeed when recording access is admin_moderator_speaker and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom( await updateRecordingAccessConfigInRoom(
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/url`) .get(`${RECORDINGS_PATH}/${recordingId}/url`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should fail when recording access is admin_moderator and participant is speaker', async () => { it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/url`) .get(`${RECORDINGS_PATH}/${recordingId}/url`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
it('should succeed when recording access is admin_moderator and participant is moderator', async () => { it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/url`) .get(`${RECORDINGS_PATH}/${recordingId}/url`)
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
}); });
@ -723,7 +838,7 @@ describe('Recording API Security Tests', () => {
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/download`) .get(`${RECORDINGS_PATH}/download`)
.query({ recordingIds: recordingId }) .query({ recordingIds: recordingId })
.set('Cookie', adminCookie); .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -732,15 +847,50 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/download`) .get(`${RECORDINGS_PATH}/download`)
.query({ recordingIds: recordingId }) .query({ recordingIds: recordingId })
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200);
});
it('should succeed when recording access is admin_moderator_speaker and participant is speaker, token in cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
const response = await request(app)
.get(`${RECORDINGS_PATH}/download`)
.query({ recordingIds: recordingId })
.set('Cookie', recordingToken);
expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should succeed when recording access is admin_moderator_speaker and participant is speaker, token in query param', async () => {
await updateRecordingAccessConfigInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
);
let recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
// Remove the "Bearer " prefix if present
if (recordingToken.startsWith('Bearer ')) {
recordingToken = recordingToken.slice(7);
}
const response = await request(app)
.get(`${RECORDINGS_PATH}/download`)
.query({ recordingIds: recordingId, recordingToken });
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -749,43 +899,34 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId, roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER MeetRecordingAccess.ADMIN_MODERATOR_SPEAKER
); );
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/download`) .get(`${RECORDINGS_PATH}/download`)
.query({ recordingIds: recordingId }) .query({ recordingIds: recordingId })
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should fail when recording access is admin_moderator and participant is speaker', async () => { it('should fail when recording access is admin_moderator and participant is speaker', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.speakerSecret);
roomData.room.roomId,
roomData.speakerSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/download`) .get(`${RECORDINGS_PATH}/download`)
.query({ recordingIds: recordingId }) .query({ recordingIds: recordingId })
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
it('should succeed when recording access is admin_moderator and participant is moderator', async () => { it('should succeed when recording access is admin_moderator and participant is moderator', async () => {
await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR); await updateRecordingAccessConfigInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingTokenCookie( const recordingToken = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
roomData.room.roomId,
roomData.moderatorSecret
);
const response = await request(app) const response = await request(app)
.get(`${RECORDINGS_PATH}/download`) .get(`${RECORDINGS_PATH}/download`)
.query({ recordingIds: recordingId }) .query({ recordingIds: recordingId })
.set('Cookie', recordingCookie); .set(INTERNAL_CONFIG.RECORDING_TOKEN_HEADER, recordingToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
}); });

View File

@ -3,8 +3,9 @@ import { Express } from 'express';
import request from 'supertest'; import request from 'supertest';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js'; import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { MEET_INITIAL_API_KEY } from '../../../../src/environment.js'; import { MEET_INITIAL_API_KEY } from '../../../../src/environment.js';
import { AuthMode, MeetRecordingAccess, ParticipantRole } from '../../../../src/typings/ce/index.js'; import { AuthMode, AuthTransportMode, MeetRecordingAccess, ParticipantRole } from '../../../../src/typings/ce/index.js';
import { import {
changeAuthTransportMode,
changeSecurityConfig, changeSecurityConfig,
createRoom, createRoom,
deleteAllRecordings, deleteAllRecordings,
@ -21,11 +22,11 @@ const INTERNAL_ROOMS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/rooms`
describe('Room API Security Tests', () => { describe('Room API Security Tests', () => {
let app: Express; let app: Express;
let adminCookie: string; let adminAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
app = startTestServer(); app = startTestServer();
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
afterAll(async () => { afterAll(async () => {
@ -43,8 +44,25 @@ describe('Room API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.post(ROOMS_PATH)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({});
expect(response.status).toBe(201);
});
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send({}); const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send({});
expect(response.status).toBe(201); expect(response.status).toBe(201);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
@ -62,10 +80,26 @@ describe('Room API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app).get(ROOMS_PATH).set('Cookie', adminCookie); const response = await request(app)
.get(ROOMS_PATH)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app).post(ROOMS_PATH).set('Cookie', adminCookie).send({});
expect(response.status).toBe(201);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
});
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
const response = await request(app).get(ROOMS_PATH); const response = await request(app).get(ROOMS_PATH);
expect(response.status).toBe(401); expect(response.status).toBe(401);
@ -89,11 +123,28 @@ describe('Room API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.delete(ROOMS_PATH)
.query({ roomIds: roomId })
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app) const response = await request(app)
.delete(ROOMS_PATH) .delete(ROOMS_PATH)
.query({ roomIds: roomId }) .query({ roomIds: roomId })
.set('Cookie', adminCookie); .set('Cookie', adminCookie);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
@ -117,8 +168,24 @@ describe('Room API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app).get(`${ROOMS_PATH}/${roomData.room.roomId}`).set('Cookie', adminCookie); const response = await request(app).get(`${ROOMS_PATH}/${roomData.room.roomId}`).set('Cookie', adminCookie);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
@ -129,7 +196,7 @@ describe('Room API Security Tests', () => {
it('should succeed when participant is moderator', async () => { it('should succeed when participant is moderator', async () => {
const response = await request(app) const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}`) .get(`${ROOMS_PATH}/${roomData.room.roomId}`)
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -139,7 +206,7 @@ describe('Room API Security Tests', () => {
const response = await request(app) const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}`) .get(`${ROOMS_PATH}/${roomData.room.roomId}`)
.set('Cookie', newRoomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -147,7 +214,7 @@ describe('Room API Security Tests', () => {
it('should succeed when participant is speaker', async () => { it('should succeed when participant is speaker', async () => {
const response = await request(app) const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}`) .get(`${ROOMS_PATH}/${roomData.room.roomId}`)
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -169,8 +236,24 @@ describe('Room API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.delete(`${ROOMS_PATH}/${roomId}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app).delete(`${ROOMS_PATH}/${roomId}`).set('Cookie', adminCookie); const response = await request(app).delete(`${ROOMS_PATH}/${roomId}`).set('Cookie', adminCookie);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
@ -194,10 +277,26 @@ describe('Room API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app) const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`) .get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', adminCookie); .set('Cookie', adminCookie);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
@ -208,7 +307,7 @@ describe('Room API Security Tests', () => {
it('should succeed when participant is moderator', async () => { it('should succeed when participant is moderator', async () => {
const response = await request(app) const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`) .get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', roomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -218,7 +317,7 @@ describe('Room API Security Tests', () => {
const response = await request(app) const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`) .get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', newRoomData.moderatorCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, newRoomData.moderatorToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.MODERATOR);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -226,7 +325,7 @@ describe('Room API Security Tests', () => {
it('should succeed when participant is speaker', async () => { it('should succeed when participant is speaker', async () => {
const response = await request(app) const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`) .get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', roomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, roomData.speakerToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -236,7 +335,7 @@ describe('Room API Security Tests', () => {
const response = await request(app) const response = await request(app)
.get(`${ROOMS_PATH}/${roomData.room.roomId}/config`) .get(`${ROOMS_PATH}/${roomData.room.roomId}/config`)
.set('Cookie', newRoomData.speakerCookie) .set(INTERNAL_CONFIG.PARTICIPANT_TOKEN_HEADER, newRoomData.speakerToken)
.set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER); .set(INTERNAL_CONFIG.PARTICIPANT_ROLE_HEADER, ParticipantRole.SPEAKER);
expect(response.status).toBe(403); expect(response.status).toBe(403);
}); });
@ -268,11 +367,28 @@ describe('Room API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/config`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({ config: roomConfig });
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app) const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/config`) .put(`${ROOMS_PATH}/${roomId}/config`)
.set('Cookie', adminCookie) .set('Cookie', adminCookie)
.send({ config: roomConfig }); .send({ config: roomConfig });
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
@ -298,11 +414,28 @@ describe('Room API Security Tests', () => {
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/status`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({ status: 'open' });
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app) const response = await request(app)
.put(`${ROOMS_PATH}/${roomId}/status`) .put(`${ROOMS_PATH}/${roomId}/status`)
.set('Cookie', adminCookie) .set('Cookie', adminCookie)
.send({ status: 'open' }); .send({ status: 'open' });
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
@ -352,11 +485,30 @@ describe('Room API Security Tests', () => {
it('should succeed when authentication is required for moderator, participant is moderator and authenticated', async () => { it('should succeed when authentication is required for moderator, participant is moderator and authenticated', async () => {
await changeSecurityConfig(AuthMode.MODERATORS_ONLY); await changeSecurityConfig(AuthMode.MODERATORS_ONLY);
const response = await request(app)
.post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({ secret: roomData.moderatorSecret });
expect(response.status).toBe(200);
});
it('should succeed when authentication is required for moderator, participant is moderator and authenticated via cookie', async () => {
await changeSecurityConfig(AuthMode.MODERATORS_ONLY);
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`) .post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`)
.set('Cookie', adminCookie) .set('Cookie', adminCookie)
.send({ secret: roomData.moderatorSecret }); .send({ secret: roomData.moderatorSecret });
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when authentication is required for moderator and participant is moderator but not authenticated', async () => { it('should fail when authentication is required for moderator and participant is moderator but not authenticated', async () => {
@ -373,7 +525,7 @@ describe('Room API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`) .post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`)
.set('Cookie', adminCookie) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({ secret: roomData.speakerSecret }); .send({ secret: roomData.speakerSecret });
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });
@ -392,7 +544,7 @@ describe('Room API Security Tests', () => {
const response = await request(app) const response = await request(app)
.post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`) .post(`${INTERNAL_ROOMS_PATH}/${roomData.room.roomId}/recording-token`)
.set('Cookie', adminCookie) .set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send({ secret: roomData.moderatorSecret }); .send({ secret: roomData.moderatorSecret });
expect(response.status).toBe(200); expect(response.status).toBe(200);
}); });

View File

@ -3,7 +3,13 @@ import { Express } from 'express';
import request from 'supertest'; import request from 'supertest';
import INTERNAL_CONFIG from '../../../../src/config/internal-config.js'; import INTERNAL_CONFIG from '../../../../src/config/internal-config.js';
import { MEET_INITIAL_ADMIN_PASSWORD } from '../../../../src/environment.js'; import { MEET_INITIAL_ADMIN_PASSWORD } from '../../../../src/environment.js';
import { changePassword, loginUser, startTestServer } from '../../../helpers/request-helpers.js'; import { AuthTransportMode } from '../../../../src/typings/ce/index.js';
import {
changeAuthTransportMode,
changePassword,
loginUser,
startTestServer
} from '../../../helpers/request-helpers.js';
const USERS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`; const USERS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`;
@ -15,15 +21,31 @@ describe('User API Security Tests', () => {
}); });
describe('Profile Tests', () => { describe('Profile Tests', () => {
let adminCookie: string; let adminAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.get(`${USERS_PATH}/profile`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
expect(response.status).toBe(200);
});
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app).get(`${USERS_PATH}/profile`).set('Cookie', adminCookie); const response = await request(app).get(`${USERS_PATH}/profile`).set('Cookie', adminCookie);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {
@ -38,13 +60,30 @@ describe('User API Security Tests', () => {
newPassword: 'newpassword123' newPassword: 'newpassword123'
}; };
let adminCookie: string; let adminAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
it('should succeed when user is authenticated as admin', async () => { it('should succeed when user is authenticated as admin', async () => {
const response = await request(app)
.post(`${USERS_PATH}/change-password`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.send(changePasswordRequest);
expect(response.status).toBe(200);
// Reset password
await changePassword(changePasswordRequest.newPassword, MEET_INITIAL_ADMIN_PASSWORD, adminAccessToken);
});
it('should succeed when user is authenticated as admin via cookie', async () => {
// Set auth transport mode to cookie
await changeAuthTransportMode(AuthTransportMode.COOKIE);
// Login as admin to get access token cookie
const adminCookie = await loginUser();
const response = await request(app) const response = await request(app)
.post(`${USERS_PATH}/change-password`) .post(`${USERS_PATH}/change-password`)
.set('Cookie', adminCookie) .set('Cookie', adminCookie)
@ -53,6 +92,9 @@ describe('User API Security Tests', () => {
// Reset password // Reset password
await changePassword(changePasswordRequest.newPassword, MEET_INITIAL_ADMIN_PASSWORD, adminCookie); await changePassword(changePasswordRequest.newPassword, MEET_INITIAL_ADMIN_PASSWORD, adminCookie);
// Revert auth transport mode to header
await changeAuthTransportMode(AuthTransportMode.HEADER);
}); });
it('should fail when user is not authenticated', async () => { it('should fail when user is not authenticated', async () => {

View File

@ -4,31 +4,31 @@ import { expectValidationError } from '../../../helpers/assertion-helpers.js';
import { changePassword, loginUser, startTestServer } from '../../../helpers/request-helpers.js'; import { changePassword, loginUser, startTestServer } from '../../../helpers/request-helpers.js';
describe('Users API Tests', () => { describe('Users API Tests', () => {
let adminCookie: string; let adminAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
startTestServer(); startTestServer();
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
describe('Change Password Tests', () => { describe('Change Password Tests', () => {
it('should successfully change password', async () => { it('should successfully change password', async () => {
const newPassword = 'newpassword123'; const newPassword = 'newpassword123';
const response = await changePassword(MEET_INITIAL_ADMIN_PASSWORD, newPassword, adminCookie); const response = await changePassword(MEET_INITIAL_ADMIN_PASSWORD, newPassword, adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
// Reset password // Reset password
await changePassword(newPassword, MEET_INITIAL_ADMIN_PASSWORD, adminCookie); await changePassword(newPassword, MEET_INITIAL_ADMIN_PASSWORD, adminAccessToken);
}); });
it('should fail when current password is incorrect', async () => { it('should fail when current password is incorrect', async () => {
const response = await changePassword('wrongpassword', 'newpassword123', adminCookie); const response = await changePassword('wrongpassword', 'newpassword123', adminAccessToken);
expect(response.status).toBe(400); expect(response.status).toBe(400);
expect(response.body).toHaveProperty('message', 'Invalid current password'); expect(response.body).toHaveProperty('message', 'Invalid current password');
}); });
it('should fail when new password is not 5 characters long', async () => { it('should fail when new password is not 5 characters long', async () => {
const response = await changePassword(MEET_INITIAL_ADMIN_PASSWORD, '1234', adminCookie); const response = await changePassword(MEET_INITIAL_ADMIN_PASSWORD, '1234', adminAccessToken);
expectValidationError(response, 'newPassword', 'New password must be at least 5 characters long'); expectValidationError(response, 'newPassword', 'New password must be at least 5 characters long');
}); });
}); });

View File

@ -2,16 +2,16 @@ import { beforeAll, describe, expect, it } from '@jest/globals';
import { getProfile, loginUser, startTestServer } from '../../../helpers/request-helpers.js'; import { getProfile, loginUser, startTestServer } from '../../../helpers/request-helpers.js';
describe('Users API Tests', () => { describe('Users API Tests', () => {
let adminCookie: string; let adminAccessToken: string;
beforeAll(async () => { beforeAll(async () => {
startTestServer(); startTestServer();
adminCookie = await loginUser(); adminAccessToken = await loginUser();
}); });
describe('Profile Tests', () => { describe('Profile Tests', () => {
it('should return 200 and admin profile', async () => { it('should return 200 and admin profile', async () => {
const response = await getProfile(adminCookie); const response = await getProfile(adminAccessToken);
expect(response.status).toBe(200); expect(response.status).toBe(200);
expect(response.body).toHaveProperty('username'); expect(response.body).toHaveProperty('username');
expect(response.body.username).toBe('admin'); expect(response.body.username).toBe('admin');

View File

@ -91,10 +91,10 @@ describe('Webhook Integration Tests', () => {
it('should send meeting_ended webhook when meeting is closed', async () => { it('should send meeting_ended webhook when meeting is closed', async () => {
const context = await setupSingleRoom(true); const context = await setupSingleRoom(true);
const roomData = context.room; const roomData = context.room;
const moderatorCookie = context.moderatorCookie; const moderatorToken = context.moderatorToken;
// Close the room // Close the room
await endMeeting(roomData.roomId, moderatorCookie); await endMeeting(roomData.roomId, moderatorToken);
// Wait for the room to be closed // Wait for the room to be closed
await sleep('1s'); await sleep('1s');