test: add integration tests for generating recording tokens and update related assertions

This commit is contained in:
juancarmore 2025-05-13 12:52:31 +02:00
parent d71111e415
commit 886fd5109b
4 changed files with 182 additions and 27 deletions

View File

@ -512,3 +512,39 @@ const getPermissions = (roomId: string, role: ParticipantRole) => {
throw new Error(`Unknown role ${role}`);
}
};
export const expectValidRecordingTokenResponse = (
response: any,
roomId: string,
participantRole: ParticipantRole,
canRetrieveRecordings: boolean,
canDeleteRecordings: boolean
) => {
expect(response.status).toBe(200);
expect(response.body).toHaveProperty('token');
const decodedToken = decodeJWTToken(response.body.token);
expect(decodedToken).toHaveProperty('video', {
room: roomId
});
expect(decodedToken).toHaveProperty('metadata');
const metadata = JSON.parse(decodedToken.metadata);
expect(metadata).toHaveProperty('role', participantRole);
expect(metadata).toHaveProperty('recordingPermissions', {
canRetrieveRecordings,
canDeleteRecordings
});
};
const decodeJWTToken = (token: string) => {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
atob(base64)
.split('')
.map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
.join('')
);
return JSON.parse(jsonPayload);
};

View File

@ -398,8 +398,19 @@ export const generateRecordingToken = async (roomId: string, secret: string) =>
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/rooms/${roomId}/recording-token`)
.send({
secret
})
.expect(200);
});
return response;
};
/**
* Generates a token for retrieving/deleting recordings from a room and returns the cookie containing the token
*/
export const generateRecordingTokenCookie = async (roomId: string, secret: string) => {
checkAppIsRunning();
// Generate the recording token
const response = await generateRecordingToken(roomId, secret);
expect(response.status).toBe(200);
// Return the recording token cookie
const cookies = response.headers['set-cookie'] as unknown as string[];

View File

@ -0,0 +1,108 @@
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { ParticipantRole } from '../../../../src/typings/ce/participant.js';
import { MeetRecordingAccess } from '../../../../src/typings/ce/room-preferences.js';
import { expectValidRecordingTokenResponse } from '../../../helpers/assertion-helpers.js';
import {
deleteAllRecordings,
deleteAllRooms,
deleteRoom,
generateRecordingToken,
startTestServer,
updateRecordingAccessPreferencesInRoom
} from '../../../helpers/request-helpers.js';
import { RoomData, setupSingleRoomWithRecording } from '../../../helpers/test-scenarios.js';
describe('Room API Tests', () => {
let roomData: RoomData;
beforeAll(async () => {
startTestServer();
roomData = await setupSingleRoomWithRecording(true);
});
afterAll(async () => {
await deleteAllRecordings();
await deleteAllRooms();
});
describe('Generate Recording Token Tests', () => {
it('should generate a recording token with canRetrieve and canDelete permissions when using the moderator secret and recording access is admin-moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
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-publisher', async () => {
await updateRecordingAccessPreferencesInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
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 public', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true);
});
it('should generate a recording token without any permissions when using the publisher secret and recording access is admin-moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const response = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.PUBLISHER, false, false);
});
it('should generate a recording token with canRetrieve permission but not canDelete when using the publisher secret and recording access is admin-moderator-publisher', async () => {
await updateRecordingAccessPreferencesInRoom(
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const response = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.PUBLISHER, true, false);
});
it('should generate a recording token with canRetrieve permission but not canDelete when using the publisher secret and recording access is public', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const response = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.PUBLISHER, true, false);
});
it('should succeed even if the room is deleted', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
await deleteRoom(roomData.room.roomId);
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
expectValidRecordingTokenResponse(response, roomData.room.roomId, ParticipantRole.MODERATOR, true, true);
// Recreate the room with recording
roomData = await setupSingleRoomWithRecording(true);
});
it('should fail with a 404 error if there are no recordings in the room', async () => {
await deleteAllRecordings();
const response = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
expect(response.status).toBe(404);
// Recreate the room with recording
roomData = await setupSingleRoomWithRecording(true);
});
it('should fail with a 404 error if the room does not exist', async () => {
const response = await generateRecordingToken('non-existent-room-id', roomData.moderatorSecret);
expect(response.status).toBe(404);
});
it('should fail with a 400 error if the secret is invalid', async () => {
const response = await generateRecordingToken(roomData.room.roomId, 'invalid-secret');
expect(response.status).toBe(400);
});
});
});

View File

@ -9,7 +9,7 @@ import {
deleteAllRecordings,
deleteAllRooms,
disconnectFakeParticipants,
generateRecordingToken,
generateRecordingTokenCookie,
loginUserAsRole,
startTestServer,
stopAllRecordings,
@ -184,7 +184,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is public and participant is publisher', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -192,7 +192,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is public and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -210,7 +210,7 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -221,7 +221,7 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -229,7 +229,7 @@ describe('Recording API Security Tests', () => {
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie);
expect(response.status).toBe(403);
@ -237,7 +237,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is admin-moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app).get(RECORDINGS_PATH).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -272,7 +272,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is public and participant is publisher', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app).get(`${RECORDINGS_PATH}/${recordingId}`).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -280,7 +280,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is public and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app).get(`${RECORDINGS_PATH}/${recordingId}`).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -298,7 +298,7 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app).get(`${RECORDINGS_PATH}/${recordingId}`).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -309,7 +309,7 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app).get(`${RECORDINGS_PATH}/${recordingId}`).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -317,7 +317,7 @@ describe('Recording API Security Tests', () => {
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app).get(`${RECORDINGS_PATH}/${recordingId}`).set('Cookie', recordingCookie);
expect(response.status).toBe(403);
@ -325,7 +325,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is admin-moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app).get(`${RECORDINGS_PATH}/${recordingId}`).set('Cookie', recordingCookie);
expect(response.status).toBe(200);
@ -360,7 +360,7 @@ describe('Recording API Security Tests', () => {
it('should fail when recording access is public and participant is publisher', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app)
.delete(`${RECORDINGS_PATH}/${recordingId}`)
@ -370,7 +370,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is public and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app)
.delete(`${RECORDINGS_PATH}/${recordingId}`)
@ -390,7 +390,7 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app)
.delete(`${RECORDINGS_PATH}/${recordingId}`)
@ -403,7 +403,7 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app)
.delete(`${RECORDINGS_PATH}/${recordingId}`)
@ -413,7 +413,7 @@ describe('Recording API Security Tests', () => {
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app)
.delete(`${RECORDINGS_PATH}/${recordingId}`)
@ -423,7 +423,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is admin-moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app)
.delete(`${RECORDINGS_PATH}/${recordingId}`)
@ -497,7 +497,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is public and participant is publisher', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`)
@ -507,7 +507,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is public and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.PUBLIC);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`)
@ -527,7 +527,7 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`)
@ -540,7 +540,7 @@ describe('Recording API Security Tests', () => {
roomData.room.roomId,
MeetRecordingAccess.ADMIN_MODERATOR_PUBLISHER
);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`)
@ -550,7 +550,7 @@ describe('Recording API Security Tests', () => {
it('should fail when recording access is admin-moderator and participant is publisher', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.publisherSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.publisherSecret);
const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`)
@ -560,7 +560,7 @@ describe('Recording API Security Tests', () => {
it('should succeed when recording access is admin-moderator and participant is moderator', async () => {
await updateRecordingAccessPreferencesInRoom(roomData.room.roomId, MeetRecordingAccess.ADMIN_MODERATOR);
const recordingCookie = await generateRecordingToken(roomData.room.roomId, roomData.moderatorSecret);
const recordingCookie = await generateRecordingTokenCookie(roomData.room.roomId, roomData.moderatorSecret);
const response = await request(app)
.get(`${RECORDINGS_PATH}/${recordingId}/media`)