diff --git a/backend/tests/integration/api/security/participant-security.test.ts b/backend/tests/integration/api/security/participant-security.test.ts index f201b9e..3c20061 100644 --- a/backend/tests/integration/api/security/participant-security.test.ts +++ b/backend/tests/integration/api/security/participant-security.test.ts @@ -1,99 +1,51 @@ import request from 'supertest'; import { describe, it, expect, beforeAll, afterAll } from '@jest/globals'; import { Express } from 'express'; -import { startTestServer, stopTestServer } from '../../../utils/helpers.js'; -import { AuthMode, AuthType } from '../../../../src/typings/ce/index.js'; +import { createRoom, generateParticipantToken, startTestServer, stopTestServer } from '../../../utils/helpers.js'; +import { AuthMode, UserRole } from '../../../../src/typings/ce/index.js'; +import { MEET_INTERNAL_API_BASE_PATH_V1 } from '../../../../src/environment.js'; +import { MeetRoomHelper } from '../../../../src/helpers/room.helper.js'; +import { changeSecurityPreferences, deleteAllRooms, loginUserAsRole } from '../../../utils/helpers.js'; -const BASE_URL = '/meet/api/v1'; -const INTERNAL_BASE_URL = '/meet/internal-api/v1'; -const PARTICIPANTS_URL = `${INTERNAL_BASE_URL}/participants`; - -const API_KEY_HEADER = 'X-API-Key'; -const API_KEY = 'meet-api-key'; - -const EXPIRATION_DATE = 1772129829000; -const PARTICIPANT_NAME = 'testParticipant'; +const PARTICIPANTS_PATH = `${MEET_INTERNAL_API_BASE_PATH_V1}/participants`; describe('Participant API Security Tests', () => { + const PARTICIPANT_NAME = 'testParticipant'; + let app: Express; let userCookie: string; + let adminCookie: string; let roomId: string; let moderatorSecret: string; let publisherSecret: string; - const changeSecurityPreferences = async (authMode: AuthMode) => { - await request(app) - .put(`${BASE_URL}/preferences/security`) - .set(API_KEY_HEADER, API_KEY) - .send({ - authentication: { - authMode: authMode, - method: { - type: AuthType.SINGLE_USER - } - } - }); - }; - - const loginUser = async (username: string, password: string): Promise => { - const response = await request(app) - .post(`${INTERNAL_BASE_URL}/auth/login`) - .send({ - username, - password - }) - .expect(200); - - const cookies = response.headers['set-cookie'] as unknown as string[]; - const accessTokenCookie = cookies.find((cookie) => cookie.startsWith('OvMeetAccessToken=')) as string; - return accessTokenCookie; - }; - - const extractSecretByRoomUrl = (urlString: string, type: string): string => { - const url = new URL(urlString); - const secret = url.searchParams.get('secret'); - - if (!secret) throw new Error(`${type} secret not found`); - - return secret; - }; - beforeAll(async () => { app = await startTestServer(); - // Get access token cookie for user - userCookie = await loginUser('user', 'user'); + // Get cookies for admin and user + userCookie = await loginUserAsRole(UserRole.USER); + adminCookie = await loginUserAsRole(UserRole.ADMIN); // Create a room and extract the roomId - const response = await request(app).post(`${BASE_URL}/rooms`).set(API_KEY_HEADER, API_KEY).send({ - autoDeletionDate: EXPIRATION_DATE - }); - roomId = response.body.roomId; + const room = await createRoom(); + roomId = room.roomId; - // Extract the moderator and publisher secrets from the room URL - const { moderatorRoomUrl, publisherRoomUrl } = response.body; - moderatorSecret = extractSecretByRoomUrl(moderatorRoomUrl, 'Moderator'); - publisherSecret = extractSecretByRoomUrl(publisherRoomUrl, 'Publisher'); + // Extract the moderator and publisher secrets from the room + ({ moderatorSecret, publisherSecret } = MeetRoomHelper.extractSecretsFromRoom(room)); }); afterAll(async () => { - // Clean up created rooms - const roomsResponse = await request(app).get(`${BASE_URL}/rooms`).set(API_KEY_HEADER, API_KEY); - - for (const room of roomsResponse.body) { - await request(app).delete(`${BASE_URL}/rooms/${room.roomId}`).set(API_KEY_HEADER, API_KEY); - } - + await deleteAllRooms(); await stopTestServer(); }, 20000); describe('Generate Participant Token Tests', () => { it('should succeed when no authentication is required and participant is publisher', async () => { - await changeSecurityPreferences(AuthMode.NONE); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.NONE }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).send({ roomId, participantName: PARTICIPANT_NAME, secret: publisherSecret @@ -102,9 +54,9 @@ describe('Participant API Security Tests', () => { }); it('should succeed when no authentication is required and participant is moderator', async () => { - await changeSecurityPreferences(AuthMode.NONE); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.NONE }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).send({ roomId, participantName: PARTICIPANT_NAME, secret: moderatorSecret @@ -113,9 +65,9 @@ describe('Participant API Security Tests', () => { }); it('should succeed when authentication is required for moderator and participant is publisher', async () => { - await changeSecurityPreferences(AuthMode.MODERATORS_ONLY); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.MODERATORS_ONLY }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).send({ roomId, participantName: PARTICIPANT_NAME, secret: publisherSecret @@ -124,23 +76,20 @@ describe('Participant API Security Tests', () => { }); it('should succeed when authentication is required for moderator, participant is moderator and authenticated', async () => { - await changeSecurityPreferences(AuthMode.MODERATORS_ONLY); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.MODERATORS_ONLY }); - const response = await request(app) - .post(`${PARTICIPANTS_URL}/token`) - .set('Cookie', userCookie) - .send({ - roomId, - participantName: PARTICIPANT_NAME, - secret: moderatorSecret - }); + const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).set('Cookie', userCookie).send({ + roomId, + participantName: PARTICIPANT_NAME, + secret: moderatorSecret + }); expect(response.status).toBe(200); }); it('should fail when authentication is required for moderator and participant is moderator but not authenticated', async () => { - await changeSecurityPreferences(AuthMode.MODERATORS_ONLY); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.MODERATORS_ONLY }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).send({ roomId, participantName: PARTICIPANT_NAME, secret: moderatorSecret @@ -149,23 +98,20 @@ describe('Participant API Security Tests', () => { }); it('should succeed when authentication is required for all users, participant is publisher and authenticated', async () => { - await changeSecurityPreferences(AuthMode.ALL_USERS); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.ALL_USERS }); - const response = await request(app) - .post(`${PARTICIPANTS_URL}/token`) - .set('Cookie', userCookie) - .send({ - roomId, - participantName: PARTICIPANT_NAME, - secret: publisherSecret - }); + const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).set('Cookie', userCookie).send({ + roomId, + participantName: PARTICIPANT_NAME, + secret: publisherSecret + }); expect(response.status).toBe(200); }); it('should fail when authentication is required for all users and participant is publisher but not authenticated', async () => { - await changeSecurityPreferences(AuthMode.ALL_USERS); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.ALL_USERS }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).send({ roomId, participantName: PARTICIPANT_NAME, secret: publisherSecret @@ -174,23 +120,20 @@ describe('Participant API Security Tests', () => { }); it('should succeed when authentication is required for all users, participant is moderator and authenticated', async () => { - await changeSecurityPreferences(AuthMode.ALL_USERS); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.ALL_USERS }); - const response = await request(app) - .post(`${PARTICIPANTS_URL}/token`) - .set('Cookie', userCookie) - .send({ - roomId, - participantName: PARTICIPANT_NAME, - secret: moderatorSecret - }); + const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).set('Cookie', userCookie).send({ + roomId, + participantName: PARTICIPANT_NAME, + secret: moderatorSecret + }); expect(response.status).toBe(200); }); it('should fail when authentication is required for all users and participant is moderator but not authenticated', async () => { - await changeSecurityPreferences(AuthMode.ALL_USERS); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.ALL_USERS }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token`).send({ roomId, participantName: PARTICIPANT_NAME, secret: moderatorSecret @@ -201,9 +144,9 @@ describe('Participant API Security Tests', () => { describe('Refresh Participant Token Tests', () => { it('should succeed when no authentication is required and participant is publisher', async () => { - await changeSecurityPreferences(AuthMode.NONE); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.NONE }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token/refresh`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token/refresh`).send({ roomId, participantName: PARTICIPANT_NAME, secret: publisherSecret @@ -214,9 +157,9 @@ describe('Participant API Security Tests', () => { }); it('should succeed when no authentication is required and participant is moderator', async () => { - await changeSecurityPreferences(AuthMode.NONE); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.NONE }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token/refresh`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token/refresh`).send({ roomId, participantName: PARTICIPANT_NAME, secret: moderatorSecret @@ -227,9 +170,9 @@ describe('Participant API Security Tests', () => { }); it('should succeed when authentication is required for moderator and participant is publisher', async () => { - await changeSecurityPreferences(AuthMode.MODERATORS_ONLY); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.MODERATORS_ONLY }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token/refresh`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token/refresh`).send({ roomId, participantName: PARTICIPANT_NAME, secret: publisherSecret @@ -240,10 +183,10 @@ describe('Participant API Security Tests', () => { }); it('should succeed when authentication is required for moderator, participant is moderator and authenticated', async () => { - await changeSecurityPreferences(AuthMode.MODERATORS_ONLY); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.MODERATORS_ONLY }); const response = await request(app) - .post(`${PARTICIPANTS_URL}/token/refresh`) + .post(`${PARTICIPANTS_PATH}/token/refresh`) .set('Cookie', userCookie) .send({ roomId, @@ -256,9 +199,9 @@ describe('Participant API Security Tests', () => { }); it('should fail when authentication is required for moderator and participant is moderator but not authenticated', async () => { - await changeSecurityPreferences(AuthMode.MODERATORS_ONLY); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.MODERATORS_ONLY }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token/refresh`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token/refresh`).send({ roomId, participantName: PARTICIPANT_NAME, secret: moderatorSecret @@ -267,10 +210,10 @@ describe('Participant API Security Tests', () => { }); it('should succeed when authentication is required for all users, participant is publisher and authenticated', async () => { - await changeSecurityPreferences(AuthMode.ALL_USERS); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.ALL_USERS }); const response = await request(app) - .post(`${PARTICIPANTS_URL}/token/refresh`) + .post(`${PARTICIPANTS_PATH}/token/refresh`) .set('Cookie', userCookie) .send({ roomId, @@ -283,9 +226,9 @@ describe('Participant API Security Tests', () => { }); it('should fail when authentication is required for all users and participant is publisher but not authenticated', async () => { - await changeSecurityPreferences(AuthMode.ALL_USERS); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.ALL_USERS }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token/refresh`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token/refresh`).send({ roomId, participantName: PARTICIPANT_NAME, secret: publisherSecret @@ -294,10 +237,10 @@ describe('Participant API Security Tests', () => { }); it('should succeed when authentication is required for all users, participant is moderator and authenticated', async () => { - await changeSecurityPreferences(AuthMode.ALL_USERS); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.ALL_USERS }); const response = await request(app) - .post(`${PARTICIPANTS_URL}/token/refresh`) + .post(`${PARTICIPANTS_PATH}/token/refresh`) .set('Cookie', userCookie) .send({ roomId, @@ -310,9 +253,9 @@ describe('Participant API Security Tests', () => { }); it('should fail when authentication is required for all users and participant is moderator but not authenticated', async () => { - await changeSecurityPreferences(AuthMode.ALL_USERS); + await changeSecurityPreferences(adminCookie, { authMode: AuthMode.ALL_USERS }); - const response = await request(app).post(`${PARTICIPANTS_URL}/token/refresh`).send({ + const response = await request(app).post(`${PARTICIPANTS_PATH}/token/refresh`).send({ roomId, participantName: PARTICIPANT_NAME, secret: moderatorSecret @@ -325,41 +268,15 @@ describe('Participant API Security Tests', () => { let moderatorCookie: string; let publisherCookie: string; - const generateParticipantToken = async ( - roomId: string, - participantName: string, - secret: string - ): Promise => { - // Disable authentication to generate the token - await changeSecurityPreferences(AuthMode.NONE); - - // Generate the participant token - const response = await request(app) - .post(`${PARTICIPANTS_URL}/token`) - .send({ - roomId, - participantName, - secret - }) - .expect(200); - - // Return the participant token cookie - const cookies = response.headers['set-cookie'] as unknown as string[]; - const participantTokenCookie = cookies.find((cookie) => - cookie.startsWith('OvMeetParticipantToken=') - ) as string; - return participantTokenCookie; - }; - beforeAll(async () => { // Generate participant tokens for the room and extract the cookies - moderatorCookie = await generateParticipantToken(roomId, 'Moderator', moderatorSecret); - publisherCookie = await generateParticipantToken(roomId, 'Publisher', publisherSecret); + moderatorCookie = await generateParticipantToken(adminCookie, roomId, 'Moderator', moderatorSecret); + publisherCookie = await generateParticipantToken(adminCookie, roomId, 'Publisher', publisherSecret); }); it('should succeed when participant is moderator', async () => { const response = await request(app) - .delete(`${PARTICIPANTS_URL}/${PARTICIPANT_NAME}`) + .delete(`${PARTICIPANTS_PATH}/${PARTICIPANT_NAME}`) .query({ roomId }) .set('Cookie', moderatorCookie); @@ -369,17 +286,20 @@ describe('Participant API Security Tests', () => { it('should fail when participant is moderator of a different room', async () => { // Create a new room to get a different roomId - const roomResponse = await request(app).post(`${BASE_URL}/rooms`).set(API_KEY_HEADER, API_KEY).send({ - autoDeletionDate: EXPIRATION_DATE - }); - const newRoomId = roomResponse.body.roomId; + const newRoom = await createRoom(); + const newRoomId = newRoom.roomId; // Extract the moderator secret and generate a participant token for the new room - const newModeratorSecret = extractSecretByRoomUrl(roomResponse.body.moderatorRoomUrl, 'Moderator'); - const newModeratorCookie = await generateParticipantToken(newRoomId, 'Moderator', newModeratorSecret); + const { moderatorSecret } = MeetRoomHelper.extractSecretsFromRoom(newRoom); + const newModeratorCookie = await generateParticipantToken( + adminCookie, + newRoomId, + 'Moderator', + moderatorSecret + ); const response = await request(app) - .delete(`${PARTICIPANTS_URL}/${PARTICIPANT_NAME}`) + .delete(`${PARTICIPANTS_PATH}/${PARTICIPANT_NAME}`) .query({ roomId }) .set('Cookie', newModeratorCookie); expect(response.status).toBe(403); @@ -387,7 +307,7 @@ describe('Participant API Security Tests', () => { it('should fail when participant is publisher', async () => { const response = await request(app) - .delete(`${PARTICIPANTS_URL}/${PARTICIPANT_NAME}`) + .delete(`${PARTICIPANTS_PATH}/${PARTICIPANT_NAME}`) .query({ roomId }) .set('Cookie', publisherCookie); expect(response.status).toBe(403);