test: enhance analytics, API key, global config, meeting, and user API security tests with user role and permissions validations

This commit is contained in:
juancarmore 2026-01-28 18:04:11 +01:00
parent 0a5852d89a
commit 6a350b07a5
6 changed files with 769 additions and 129 deletions

View File

@ -55,7 +55,7 @@ export const startTestServer = async (): Promise<Express> => {
export const generateApiKey = async (): Promise<string> => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
const response = await request(app)
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -68,7 +68,7 @@ export const generateApiKey = async (): Promise<string> => {
export const getApiKeys = async () => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
const response = await request(app)
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -79,7 +79,7 @@ export const getApiKeys = async () => {
export const deleteApiKeys = async () => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
const response = await request(app)
.delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -114,7 +114,7 @@ export const getRoomsAppearanceConfig = async () => {
export const updateRoomsAppearanceConfig = async (config: { appearance: MeetAppearanceConfig }) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
const response = await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/rooms/appearance`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -125,7 +125,7 @@ export const updateRoomsAppearanceConfig = async (config: { appearance: MeetAppe
export const getWebbhookConfig = async () => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
const response = await request(app)
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/webhooks`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -136,7 +136,7 @@ export const getWebbhookConfig = async () => {
export const updateWebbhookConfig = async (config: WebhookConfig) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
const response = await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/webhooks`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -164,7 +164,7 @@ export const getSecurityConfig = async () => {
export const updateSecurityConfig = async (config: SecurityConfig) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
const response = await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config/security`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -199,9 +199,9 @@ export const loginUser = async (userId: string, password: string): Promise<strin
};
/**
* Logs in admin user and returns the access token in the format "Bearer <token>"
* Logs in the root admin user and returns the access token in the format "Bearer <token>"
*/
export const loginAdminUser = async (): Promise<string> => {
export const loginRootAdmin = async (): Promise<string> => {
return loginUser(MEET_ENV.INITIAL_ADMIN_USER, MEET_ENV.INITIAL_ADMIN_PASSWORD);
};
@ -210,7 +210,7 @@ export const loginAdminUser = async (): Promise<string> => {
export const createUser = async (options: MeetUserOptions) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
return await request(app)
.post(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -220,7 +220,7 @@ export const createUser = async (options: MeetUserOptions) => {
export const getUsers = async (query: Record<string, unknown> = {}) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
return await request(app)
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -230,7 +230,7 @@ export const getUsers = async (query: Record<string, unknown> = {}) => {
export const getUser = async (userId: string) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
return await request(app)
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -274,7 +274,7 @@ export const changePasswordAfterFirstLogin = async (
export const resetUserPassword = async (userId: string, newPassword: string) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
return await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}/password`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -284,7 +284,7 @@ export const resetUserPassword = async (userId: string, newPassword: string) =>
export const updateUserRole = async (userId: string, role: string) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
return await request(app)
.put(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}/role`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -294,7 +294,7 @@ export const updateUserRole = async (userId: string, role: string) => {
export const deleteUser = async (userId: string) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
return await request(app)
.delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users/${userId}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -304,7 +304,7 @@ export const deleteUser = async (userId: string) => {
export const bulkDeleteUsers = async (userIds: string[]) => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
return await request(app)
.delete(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/users`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)
@ -905,7 +905,7 @@ export const runReleaseActiveRecordingLock = async (roomId: string) => {
export const getAnalytics = async () => {
checkAppIsRunning();
const accessToken = await loginAdminUser();
const accessToken = await loginRootAdmin();
const response = await request(app)
.get(`${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/analytics`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, accessToken)

View File

@ -3,17 +3,23 @@ import { Express } from 'express';
import request from 'supertest';
import { INTERNAL_CONFIG } from '../../../../src/config/internal-config.js';
import { MEET_ENV } from '../../../../src/environment.js';
import { loginAdminUser, startTestServer } from '../../../helpers/request-helpers.js';
import { deleteAllUsers, startTestServer } from '../../../helpers/request-helpers.js';
import { setupTestUsers } from '../../../helpers/test-scenarios.js';
import { TestUsers } from '../../../interfaces/scenarios.js';
const ANALYTICS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/analytics`;
describe('Analytics API Security Tests', () => {
let app: Express;
let adminAccessToken: string;
let testUsers: TestUsers;
beforeAll(async () => {
app = await startTestServer();
adminAccessToken = await loginAdminUser();
testUsers = await setupTestUsers();
});
afterAll(async () => {
await deleteAllUsers();
});
describe('Get Analytics Tests', () => {
@ -24,13 +30,27 @@ describe('Analytics API Security Tests', () => {
expect(response.status).toBe(401);
});
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(ANALYTICS_PATH)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken);
expect(response.status).toBe(200);
});
it('should fail when user is authenticated as USER', async () => {
const response = await request(app)
.get(ANALYTICS_PATH)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is authenticated as ROOM_MEMBER', async () => {
const response = await request(app)
.get(ANALYTICS_PATH)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app).get(ANALYTICS_PATH);
expect(response.status).toBe(401);

View File

@ -2,36 +2,48 @@ import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { Express } from 'express';
import request from 'supertest';
import { INTERNAL_CONFIG } from '../../../../src/config/internal-config.js';
import {
generateApiKey,
loginAdminUser,
restoreDefaultApiKeys,
startTestServer
} from '../../../helpers/request-helpers.js';
import { deleteAllUsers, generateApiKey, restoreDefaultApiKeys, startTestServer } from '../../../helpers/request-helpers.js';
import { setupTestUsers } from '../../../helpers/test-scenarios.js';
import { TestUsers } from '../../../interfaces/scenarios.js';
const API_KEYS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/api-keys`;
describe('API Keys API Security Tests', () => {
let app: Express;
let adminAccessToken: string;
let testUsers: TestUsers;
beforeAll(async () => {
app = await startTestServer();
adminAccessToken = await loginAdminUser();
testUsers = await setupTestUsers();
});
afterAll(async () => {
await restoreDefaultApiKeys();
await deleteAllUsers();
});
describe('Create API Key', () => {
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(`${API_KEYS_PATH}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken);
expect(response.status).toBe(201);
});
it('should fail when user is authenticated as USER', async () => {
const response = await request(app)
.post(`${API_KEYS_PATH}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is authenticated as ROOM_MEMBER', async () => {
const response = await request(app)
.post(`${API_KEYS_PATH}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app).post(`${API_KEYS_PATH}`);
expect(response.status).toBe(401);
@ -39,13 +51,27 @@ describe('API Keys API Security Tests', () => {
});
describe('Get API Keys', () => {
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(`${API_KEYS_PATH}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken);
expect(response.status).toBe(200);
});
it('should fail when user is authenticated as USER', async () => {
const response = await request(app)
.get(`${API_KEYS_PATH}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is authenticated as ROOM_MEMBER', async () => {
const response = await request(app)
.get(`${API_KEYS_PATH}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app).get(`${API_KEYS_PATH}`);
expect(response.status).toBe(401);
@ -58,13 +84,27 @@ describe('API Keys API Security Tests', () => {
await generateApiKey();
});
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(`${API_KEYS_PATH}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken);
expect(response.status).toBe(200);
});
it('should fail when user is authenticated as USER', async () => {
const response = await request(app)
.delete(`${API_KEYS_PATH}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is authenticated as ROOM_MEMBER', async () => {
const response = await request(app)
.delete(`${API_KEYS_PATH}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app).delete(`${API_KEYS_PATH}`);
expect(response.status).toBe(401);

View File

@ -4,17 +4,23 @@ import { Express } from 'express';
import request from 'supertest';
import { INTERNAL_CONFIG } from '../../../../src/config/internal-config.js';
import { MEET_ENV } from '../../../../src/environment.js';
import { loginAdminUser, restoreDefaultGlobalConfig, startTestServer } from '../../../helpers/request-helpers.js';
import { deleteAllUsers, restoreDefaultGlobalConfig, startTestServer } from '../../../helpers/request-helpers.js';
import { setupTestUsers } from '../../../helpers/test-scenarios.js';
import { TestUsers } from '../../../interfaces/scenarios.js';
const CONFIG_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/config`;
describe('Global Config API Security Tests', () => {
let app: Express;
let adminAccessToken: string;
let testUsers: TestUsers;
beforeAll(async () => {
app = await startTestServer();
adminAccessToken = await loginAdminUser();
testUsers = await setupTestUsers();
});
afterAll(async () => {
await deleteAllUsers();
});
describe('Update Webhook Config Tests', () => {
@ -31,16 +37,32 @@ describe('Global Config API Security Tests', () => {
expect(response.status).toBe(401);
});
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)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken)
.send(webhookConfig);
expect(response.status).toBe(200);
await restoreDefaultGlobalConfig();
});
it('should fail when user is authenticated as USER', async () => {
const response = await request(app)
.put(`${CONFIG_PATH}/webhooks`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken)
.send(webhookConfig);
expect(response.status).toBe(403);
});
it('should fail when user is authenticated as ROOM_MEMBER', async () => {
const response = await request(app)
.put(`${CONFIG_PATH}/webhooks`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken)
.send(webhookConfig);
expect(response.status).toBe(403);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app).put(`${CONFIG_PATH}/webhooks`).send(webhookConfig);
expect(response.status).toBe(401);
@ -55,13 +77,27 @@ describe('Global Config API Security Tests', () => {
expect(response.status).toBe(401);
});
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);
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken);
expect(response.status).toBe(200);
});
it('should fail when user is authenticated as USER', async () => {
const response = await request(app)
.get(`${CONFIG_PATH}/webhooks`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is authenticated as ROOM_MEMBER', async () => {
const response = await request(app)
.get(`${CONFIG_PATH}/webhooks`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken);
expect(response.status).toBe(403);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app).get(`${CONFIG_PATH}/webhooks`);
expect(response.status).toBe(401);
@ -84,16 +120,32 @@ describe('Global Config API Security Tests', () => {
expect(response.status).toBe(401);
});
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)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken)
.send(securityConfig);
expect(response.status).toBe(200);
await restoreDefaultGlobalConfig();
});
it('should fail when user is authenticated as USER', async () => {
const response = await request(app)
.put(`${CONFIG_PATH}/security`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken)
.send(securityConfig);
expect(response.status).toBe(403);
});
it('should fail when user is authenticated as ROOM_MEMBER', async () => {
const response = await request(app)
.put(`${CONFIG_PATH}/security`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken)
.send(securityConfig);
expect(response.status).toBe(403);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app).put(`${CONFIG_PATH}/security`).send(securityConfig);
expect(response.status).toBe(401);
@ -128,16 +180,32 @@ describe('Global Config API Security Tests', () => {
expect(response.status).toBe(401);
});
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)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.admin.accessToken)
.send(appearanceConfig);
expect(response.status).toBe(200);
await restoreDefaultGlobalConfig();
});
it('should fail when user is authenticated as USER', async () => {
const response = await request(app)
.put(`${CONFIG_PATH}/rooms/appearance`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.user.accessToken)
.send(appearanceConfig);
expect(response.status).toBe(403);
});
it('should fail when user is authenticated as ROOM_MEMBER', async () => {
const response = await request(app)
.put(`${CONFIG_PATH}/rooms/appearance`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, testUsers.roomMember.accessToken)
.send(appearanceConfig);
expect(response.status).toBe(403);
});
it('should fail when user is not authenticated', async () => {
const response = await request(app).put(`${CONFIG_PATH}/rooms/appearance`).send(appearanceConfig);
expect(response.status).toBe(401);

View File

@ -1,4 +1,4 @@
import { afterAll, beforeAll, beforeEach, describe, expect, it } from '@jest/globals';
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import { MeetRoomMemberRole, MeetRoomMemberTokenMetadata } from '@openvidu-meet/typings';
import { Express } from 'express';
import request from 'supertest';
@ -8,27 +8,36 @@ import { getPermissions } from '../../../helpers/assertion-helpers.js';
import {
deleteAllRooms,
disconnectFakeParticipants,
loginAdminUser,
joinFakeParticipant,
loginRootAdmin,
startTestServer,
updateParticipantMetadata
} from '../../../helpers/request-helpers.js';
import { setupSingleRoom } from '../../../helpers/test-scenarios.js';
import { RoomData } from '../../../interfaces/scenarios.js';
import { setupRoomMember, setupSingleRoom, updateRoomMemberPermissions } from '../../../helpers/test-scenarios.js';
import { RoomData, RoomMemberData } from '../../../interfaces/scenarios.js';
const MEETINGS_PATH = `${INTERNAL_CONFIG.INTERNAL_API_BASE_PATH_V1}/meetings`;
describe('Meeting API Security Tests', () => {
const participantIdentity = 'TEST_PARTICIPANT';
let app: Express;
let adminAccessToken: string;
let rootAdminAccessToken: string;
let roomData: RoomData;
let roomId: string;
let roomMember: RoomMemberData;
beforeAll(async () => {
app = await startTestServer();
adminAccessToken = await loginAdminUser();
});
rootAdminAccessToken = await loginRootAdmin();
beforeEach(async () => {
roomData = await setupSingleRoom(true);
roomData = await setupSingleRoom();
roomId = roomData.room.roomId;
roomMember = await setupRoomMember(roomId, {
name: 'External Member',
baseRole: MeetRoomMemberRole.MODERATOR
});
});
afterAll(async () => {
@ -39,136 +48,176 @@ describe('Meeting API Security Tests', () => {
describe('End Meeting Tests', () => {
it('should fail when request includes API key', async () => {
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
.delete(`${MEETINGS_PATH}/${roomId}`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY);
expect(response.status).toBe(401);
});
it('should fail when user is authenticated as admin', async () => {
it('should fail when using access token', async () => {
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
.delete(`${MEETINGS_PATH}/${roomId}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, rootAdminAccessToken);
expect(response.status).toBe(401);
});
it('should succeed when participant is moderator', async () => {
it('should succeed when using room member token with canEndMeeting permission', async () => {
// Update room member to have canEndMeeting permission
roomMember = await updateRoomMemberPermissions(roomId, roomMember.member.memberId, { canEndMeeting: true });
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.moderatorToken);
.delete(`${MEETINGS_PATH}/${roomId}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMember.memberToken);
expect(response.status).toBe(200);
// Re-join participant for further tests
await joinFakeParticipant(roomId, participantIdentity);
});
it('should fail when participant is moderator of a different room', async () => {
const newRoomData = await setupSingleRoom();
it('should fail when using room member token without canEndMeeting permission', async () => {
// Update room member to not have canEndMeeting permission
roomMember = await updateRoomMemberPermissions(roomId, roomMember.member.memberId, {
canEndMeeting: false
});
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, newRoomData.moderatorToken);
.delete(`${MEETINGS_PATH}/${roomId}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMember.memberToken);
expect(response.status).toBe(403);
});
it('should fail when participant is speaker', async () => {
it('should fail when using room member token from a different room', async () => {
const newRoomData = await setupSingleRoom();
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.speakerToken);
.delete(`${MEETINGS_PATH}/${roomId}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, newRoomData.moderatorToken);
expect(response.status).toBe(403);
});
});
describe('Update Participant in Meeting Tests', () => {
const PARTICIPANT_NAME = 'TEST_PARTICIPANT';
const role = MeetRoomMemberRole.MODERATOR;
beforeEach(async () => {
const setParticipantMetadata = async () => {
const metadata: MeetRoomMemberTokenMetadata = {
livekitUrl: MEET_ENV.LIVEKIT_URL,
roomId: roomData.room.roomId,
roomId,
baseRole: MeetRoomMemberRole.SPEAKER,
effectivePermissions: getPermissions(MeetRoomMemberRole.SPEAKER)
};
await updateParticipantMetadata(roomData.room.roomId, PARTICIPANT_NAME, metadata);
await updateParticipantMetadata(roomId, participantIdentity, metadata);
};
beforeAll(async () => {
// Ensure participant has the correct metadata before tests
await setParticipantMetadata();
});
it('should fail when request includes API key', async () => {
const response = await request(app)
.put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.put(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}/role`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY)
.send({ role });
expect(response.status).toBe(401);
});
it('should fail when user is authenticated as admin', async () => {
it('should fail when using access token', async () => {
const response = await request(app)
.put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken)
.put(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}/role`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, rootAdminAccessToken)
.send({ role });
expect(response.status).toBe(401);
});
it('should succeed when participant is moderator', async () => {
it('should succeed when using room member token with canMakeModerator permission', async () => {
// Update room member to have canMakeModerator permission
roomMember = await updateRoomMemberPermissions(roomId, roomMember.member.memberId, {
canMakeModerator: true
});
const response = await request(app)
.put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.moderatorToken)
.put(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}/role`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMember.memberToken)
.send({ role });
expect(response.status).toBe(200);
// Re-join participant for further tests
await joinFakeParticipant(roomId, participantIdentity);
await setParticipantMetadata();
});
it('should fail when participant is moderator of a different room', async () => {
const newRoomData = await setupSingleRoom();
it('should fail when using room member token without canMakeModerator permission', async () => {
// Update room member to not have canMakeModerator permission
roomMember = await updateRoomMemberPermissions(roomId, roomMember.member.memberId, {
canMakeModerator: false
});
const response = await request(app)
.put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, newRoomData.moderatorToken)
.put(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}/role`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMember.memberToken)
.send({ role });
expect(response.status).toBe(403);
});
it('should fail when participant is speaker', async () => {
it('should fail when using room member token from a different room', async () => {
const newRoomData = await setupSingleRoom();
const response = await request(app)
.put(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_NAME}/role`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.speakerToken)
.put(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}/role`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, newRoomData.moderatorToken)
.send({ role });
expect(response.status).toBe(403);
});
});
describe('Kick Participant from Meeting Tests', () => {
const PARTICIPANT_IDENTITY = 'TEST_PARTICIPANT';
it('should fail when request includes API key', async () => {
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.delete(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}`)
.set(INTERNAL_CONFIG.API_KEY_HEADER, MEET_ENV.INITIAL_API_KEY);
expect(response.status).toBe(401);
});
it('should fail when user is authenticated as admin', async () => {
it('should fail when using access token', async () => {
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, adminAccessToken);
.delete(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}`)
.set(INTERNAL_CONFIG.ACCESS_TOKEN_HEADER, rootAdminAccessToken);
expect(response.status).toBe(401);
});
it('should succeed when participant is moderator', async () => {
it('should succeed when using room member token with canKickParticipants permission', async () => {
// Update room member to have canKickParticipants permission
roomMember = await updateRoomMemberPermissions(roomId, roomMember.member.memberId, {
canKickParticipants: true
});
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.moderatorToken);
.delete(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMember.memberToken);
expect(response.status).toBe(200);
// Re-join participant for further tests
await joinFakeParticipant(roomId, participantIdentity);
});
it('should fail when participant is moderator of a different room', async () => {
const newRoomData = await setupSingleRoom();
it('should fail when using room member token without canKickParticipants permission', async () => {
// Update room member to not have canKickParticipants permission
roomMember = await updateRoomMemberPermissions(roomId, roomMember.member.memberId, {
canKickParticipants: false
});
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, newRoomData.moderatorToken);
.delete(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomMember.memberToken);
expect(response.status).toBe(403);
});
it('should fail when participant is speaker', async () => {
it('should fail when using room member token from a different room', async () => {
const newRoomData = await setupSingleRoom();
const response = await request(app)
.delete(`${MEETINGS_PATH}/${roomData.room.roomId}/participants/${PARTICIPANT_IDENTITY}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, roomData.speakerToken);
.delete(`${MEETINGS_PATH}/${roomId}/participants/${participantIdentity}`)
.set(INTERNAL_CONFIG.ROOM_MEMBER_TOKEN_HEADER, newRoomData.moderatorToken);
expect(response.status).toBe(403);
});
});