Files
upstream-mirror 30810eecd7
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 2s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

440 lines
15 KiB
TypeScript

import type { Credentials } from '@rocket.chat/api-client';
import type { IRoom, IUser } from '@rocket.chat/core-typings';
import { Random } from '@rocket.chat/random';
import { expect } from 'chai';
import EJSON from 'ejson';
import { before, describe, it, after } from 'mocha';
import { getCredentials, api, request, credentials, methodCall } from '../../data/api-data';
import { updatePermission } from '../../data/permissions.helper';
import { createRoom, deleteRoom } from '../../data/rooms.helper';
import { password } from '../../data/user';
import { createUser, deleteUser, login } from '../../data/users.helper';
import { IS_EE } from '../../e2e/config/constants';
(IS_EE ? describe : describe.skip)('Audit Panel', () => {
let testChannel: IRoom;
let testPrivateChannel: IRoom;
let dummyUser: IUser;
let auditor: IUser;
let auditorCredentials: Credentials;
before((done) => getCredentials(done));
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `chat.api-test-${Date.now()}` })).body.channel;
testPrivateChannel = (await createRoom({ type: 'p', name: `chat.api-test-${Date.now()}` })).body.group;
dummyUser = await createUser();
auditor = await createUser({ roles: ['user', 'auditor'] });
auditorCredentials = await login(auditor.username, password);
// the auditor role has can-audit but not can-audit-log by default; grant it suite-wide for the audit.auditions cases
await updatePermission('can-audit-log', ['admin', 'auditor']);
});
after(async () => {
await updatePermission('can-audit-log', ['admin']);
await deleteRoom({ type: 'c', roomId: testChannel._id });
await deleteUser({ _id: dummyUser._id });
await deleteUser({ _id: auditor._id });
await deleteRoom({ type: 'p', roomId: testPrivateChannel._id });
});
describe('audit/rooms.members [no permissions]', () => {
before(async () => {
await updatePermission('view-members-list-all-rooms', []);
});
after(async () => {
await updatePermission('view-members-list-all-rooms', ['admin', 'auditor']);
});
it('should fail if user does not have view-members-list-all-rooms permission', async () => {
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: 'GENERAL',
})
.expect(403);
await request
.get(api('audit/rooms.members'))
.set(auditorCredentials)
.query({
roomId: 'GENERAL',
})
.expect(403);
});
});
describe('audit/rooms.members', () => {
it('should fail if user is not logged in', async () => {
await request
.get(api('audit/rooms.members'))
.query({
roomId: 'GENERAL',
})
.expect(401);
});
it('should fail if roomId is invalid', async () => {
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: Random.id(),
})
.expect(404);
});
it('should fail if roomId is not present', async () => {
await request.get(api('audit/rooms.members')).set(credentials).query({}).expect(400);
});
it('should fail if roomId is an empty string', async () => {
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: '',
})
.expect(400);
});
it('should fetch the members of a room', async () => {
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: testChannel._id,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.members).to.be.an('array');
expect(res.body.members).to.have.lengthOf(1);
});
});
it('should persist a log entry', async () => {
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: testChannel._id,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.members).to.be.an('array');
expect(res.body.members).to.have.lengthOf(1);
});
await request
.post(methodCall('auditGetAuditions'))
.set(credentials)
.send({
message: EJSON.stringify({
method: 'auditGetAuditions',
params: [{ startDate: new Date(Date.now() - 86400000), endDate: new Date() }],
id: 'id',
msg: 'method',
}),
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
const message = JSON.parse(res.body.message);
expect(message.result).to.be.an('array').with.lengthOf.greaterThan(1);
const entry = message.result.find((audition: any) => {
return audition.fields?.rids?.includes(testChannel._id);
});
expect(entry).to.have.property('u').that.is.an('object').deep.equal({
_id: 'rocketchat.internal.admin.test',
username: 'rocketchat.internal.admin.test',
name: 'RocketChat Internal Admin Test',
});
expect(entry).to.have.property('fields').that.is.an('object');
const { fields } = entry;
expect(fields).to.have.property('msg', 'Room_members_list');
expect(fields).to.have.property('rids').that.is.an('array').with.lengthOf(1);
});
});
it('should fetch the members of a room with offset and count', async () => {
await request
.post(methodCall('addUsersToRoom'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'addUsersToRoom',
params: [{ rid: testChannel._id, users: [dummyUser.username] }],
id: 'id',
msg: 'method',
}),
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: testChannel._id,
offset: 1,
count: 1,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.members).to.be.an('array');
expect(res.body.members).to.have.lengthOf(1);
expect(res.body.members[0].username).to.be.equal(dummyUser.username);
expect(res.body.total).to.be.equal(2);
expect(res.body.offset).to.be.equal(1);
expect(res.body.count).to.be.equal(1);
});
});
it('should filter by username', async () => {
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: testChannel._id,
filter: dummyUser.username,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.members).to.be.an('array');
expect(res.body.members).to.have.lengthOf(1);
expect(res.body.members[0].username).to.be.equal(dummyUser.username);
});
});
it('should filter by user name', async () => {
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: testChannel._id,
filter: dummyUser.name,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.members).to.be.an('array');
expect(res.body.members).to.have.lengthOf(1);
expect(res.body.members[0].name).to.be.equal(dummyUser.name);
});
});
it('should sort by username', async () => {
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: testChannel._id,
sort: '{ "username": -1 }',
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.members).to.be.an('array');
expect(res.body.members).to.have.lengthOf(2);
expect(res.body.members[1].username).to.be.equal('rocketchat.internal.admin.test');
expect(res.body.members[0].username).to.be.equal(dummyUser.username);
});
});
it('should not allow nosqlinjection on filter param', async () => {
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: testChannel._id,
filter: '{ "$ne": "rocketchat.internal.admin.test" }',
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.members).to.be.an('array');
expect(res.body.members).to.have.lengthOf(0);
});
await request
.get(api('audit/rooms.members'))
.set(credentials)
.query({
roomId: testChannel._id,
filter: { username: 'rocketchat.internal.admin.test' },
})
.expect(400);
});
it('should allow to fetch info even if user is not in the room', async () => {
await request
.get(api('audit/rooms.members'))
.set(auditorCredentials)
.query({
roomId: testChannel._id,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.members).to.be.an('array');
expect(res.body.members[0].username).to.be.equal('rocketchat.internal.admin.test');
expect(res.body.members[1].username).to.be.equal(dummyUser.username);
expect(res.body.total).to.be.equal(2);
});
});
it('should allow to fetch info from private rooms', async () => {
await request
.get(api('audit/rooms.members'))
.set(auditorCredentials)
.query({
roomId: testPrivateChannel._id,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.members).to.be.an('array');
expect(res.body.members[0].username).to.be.equal('rocketchat.internal.admin.test');
expect(res.body.total).to.be.equal(1);
});
});
});
describe('[/audit.auditions]', () => {
const startDate = new Date(0).toISOString();
const endDate = new Date().toISOString();
it('should fail if user is not logged in', () => request.get(api('audit.auditions')).query({ startDate, endDate }).expect(401));
it('should fail with 400 when startDate/endDate are missing', () =>
request.get(api('audit.auditions')).set(auditorCredentials).query({}).expect(400));
it('should fail with 400 when a date is invalid', () =>
request.get(api('audit.auditions')).set(auditorCredentials).query({ startDate: 'not-a-date', endDate }).expect(400));
it('should fail with 403 when the user lacks the can-audit-log permission', async () => {
await updatePermission('can-audit-log', []);
await request.get(api('audit.auditions')).set(auditorCredentials).query({ startDate, endDate }).expect(403);
await updatePermission('can-audit-log', ['admin', 'auditor']);
});
it('should return the list of auditions', () =>
request
.get(api('audit.auditions'))
.set(auditorCredentials)
.query({ startDate, endDate })
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('auditions').that.is.an('array');
}));
});
describe('[/audit.messages]', () => {
const startDate = new Date(0).toISOString();
const endDate = new Date().toISOString();
it('should fail if user is not logged in', () =>
request.post(api('audit.messages')).send({ startDate, endDate, users: [], msg: '', type: 'u' }).expect(401));
it('should fail with 400 when required params are missing', () =>
request.post(api('audit.messages')).set(auditorCredentials).send({ startDate, endDate }).expect(400));
it('should fail with 400 when a date is invalid', () =>
request
.post(api('audit.messages'))
.set(auditorCredentials)
.send({ startDate: 'not-a-date', endDate, users: [], msg: '', type: 'u' })
.expect(400));
it('should fail with 403 when the user lacks the can-audit permission', async () => {
await updatePermission('can-audit', []);
await request
.post(api('audit.messages'))
.set(auditorCredentials)
.send({ startDate, endDate, users: [], msg: '', type: 'u' })
.expect(403);
await updatePermission('can-audit', ['admin', 'auditor']);
});
it('should return matching messages', () =>
request
.post(api('audit.messages'))
.set(auditorCredentials)
.send({ startDate, endDate, users: [], msg: '', type: 'u' })
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').that.is.an('array');
}));
it('should persist an audit log entry', async () => {
// query auditions with a future endDate so the entry created by the request below (ts = now) is in range
const auditionsRange = { startDate, endDate: new Date(Date.now() + 60000).toISOString() };
const before = await request.get(api('audit.auditions')).set(auditorCredentials).query(auditionsRange);
await request
.post(api('audit.messages'))
.set(auditorCredentials)
.send({ startDate, endDate, users: [], msg: '', type: 'u' })
.expect(200);
const after = await request.get(api('audit.auditions')).set(auditorCredentials).query(auditionsRange);
expect(after.body.auditions.length).to.be.greaterThan(before.body.auditions.length);
});
});
describe('[/audit.omnichannelMessages]', () => {
const startDate = new Date(0).toISOString();
const endDate = new Date().toISOString();
it('should fail if user is not logged in', () =>
request.post(api('audit.omnichannelMessages')).send({ startDate, endDate, users: [], msg: '', type: 'l' }).expect(401));
it('should fail with 400 when required params are missing', () =>
request.post(api('audit.omnichannelMessages')).set(auditorCredentials).send({ startDate, endDate }).expect(400));
it('should fail with 400 when a date is invalid', () =>
request
.post(api('audit.omnichannelMessages'))
.set(auditorCredentials)
.send({ startDate: 'not-a-date', endDate, users: [], msg: '', type: 'l' })
.expect(400));
it('should fail with 403 when the user lacks the can-audit permission', async () => {
await updatePermission('can-audit', []);
await request
.post(api('audit.omnichannelMessages'))
.set(auditorCredentials)
.send({ startDate, endDate, users: [], msg: '', type: 'l' })
.expect(403);
await updatePermission('can-audit', ['admin', 'auditor']);
});
it('should return matching omnichannel messages', () =>
request
.post(api('audit.omnichannelMessages'))
.set(auditorCredentials)
.send({ startDate, endDate, users: [], msg: '', type: 'l' })
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').that.is.an('array');
}));
it('should persist an audit log entry', async () => {
// query auditions with a future endDate so the entry created by the request below (ts = now) is in range
const auditionsRange = { startDate, endDate: new Date(Date.now() + 60000).toISOString() };
const before = await request.get(api('audit.auditions')).set(auditorCredentials).query(auditionsRange);
await request
.post(api('audit.omnichannelMessages'))
.set(auditorCredentials)
.send({ startDate, endDate, users: [], msg: '', type: 'l' })
.expect(200);
const after = await request.get(api('audit.auditions')).set(auditorCredentials).query(auditionsRange);
expect(after.body.auditions.length).to.be.greaterThan(before.body.auditions.length);
});
});
});