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
Code scanning - action / CodeQL-Build (push) Failing after 2s
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
862 lines
31 KiB
TypeScript
862 lines
31 KiB
TypeScript
import type { IServerEvents, LoginServiceConfiguration } from '@rocket.chat/core-typings';
|
|
import { expect } from 'chai';
|
|
import { before, describe, it, after, afterEach } from 'mocha';
|
|
|
|
import { getCredentials, api, request, credentials } from '../../data/api-data';
|
|
import { updatePermission, updateSetting, getSettingValueById } from '../../data/permissions.helper';
|
|
import { IS_EE } from '../../e2e/config/constants';
|
|
|
|
describe('[Settings]', () => {
|
|
before((done) => getCredentials(done));
|
|
|
|
describe('[/settings.public]', () => {
|
|
it('should return public settings', async () => {
|
|
const res = await request.get(api('settings.public')).expect('Content-Type', 'application/json').expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('settings');
|
|
expect(res.body).to.have.property('count');
|
|
});
|
|
it('should return public settings even requested with count and offset params', async () => {
|
|
const res = await request
|
|
.get(api('settings.public'))
|
|
.query({
|
|
count: 5,
|
|
offset: 0,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success').and.to.be.true;
|
|
expect(res.body).to.have.property('settings').and.to.be.an('array').and.to.have.lengthOf(5);
|
|
expect(res.body).to.have.property('count').and.to.be.a('number').and.to.equal(5);
|
|
});
|
|
it('should return public settings even requested with _id param', async () => {
|
|
const res = await request
|
|
.get(api('settings.public'))
|
|
.query({
|
|
_id: 'Site_Url',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success').and.to.be.true;
|
|
expect(res.body).to.have.property('settings').and.to.be.an('array').and.to.have.lengthOf(1);
|
|
expect(res.body).to.have.property('count').and.to.be.a('number').and.to.equal(1);
|
|
});
|
|
it('should return public settings even requested with _id param as an array', async () => {
|
|
const res = await request
|
|
.get(api('settings.public'))
|
|
.query({
|
|
_id: 'Site_Url,LDAP_Enable',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success').and.to.be.true;
|
|
expect(res.body).to.have.property('settings').and.to.be.an('array').and.to.have.lengthOf(2);
|
|
expect(res.body).to.have.property('count').and.to.be.a('number').and.to.equal(2);
|
|
});
|
|
it('should return an empty response when requesting public settings with a broken _id param', async () => {
|
|
const res = await request
|
|
.get(api('settings.public'))
|
|
.query({
|
|
_id: 10,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success').and.to.be.true;
|
|
expect(res.body).to.have.property('settings').and.to.be.an('array').and.to.be.empty;
|
|
expect(res.body).to.have.property('count').and.to.be.a('number').and.to.equal(0);
|
|
expect(res.body).to.have.property('offset').and.to.be.a('number').and.to.equal(0);
|
|
expect(res.body).to.have.property('total').and.to.be.a('number').and.to.equal(0);
|
|
});
|
|
});
|
|
|
|
describe('[/settings]', () => {
|
|
it('should return private settings', async () => {
|
|
const res = await request.get(api('settings')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('settings');
|
|
expect(res.body).to.have.property('count');
|
|
});
|
|
it('should return the default values of the settings when includeDefaults is true', async () => {
|
|
return request
|
|
.get(api('settings'))
|
|
.query({ includeDefaults: true })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('settings');
|
|
expect(res.body).to.have.property('count');
|
|
expect(res.body.settings[0]).to.have.property('packageValue');
|
|
});
|
|
});
|
|
|
|
describe('POST (bulk)', () => {
|
|
before(async () => {
|
|
await updatePermission('edit-privileged-setting', ['admin']);
|
|
});
|
|
|
|
after(async () => {
|
|
await updatePermission('edit-privileged-setting', ['admin']);
|
|
});
|
|
|
|
it('should return unauthorized when not authenticated', async () => {
|
|
await request
|
|
.post(api('settings'))
|
|
.send({ settings: [{ _id: 'LDAP_Enable', value: false }] })
|
|
.expect(401);
|
|
});
|
|
|
|
it('should return bad request when settings is missing', async () => {
|
|
await request.post(api('settings')).set(credentials).send({}).expect(400);
|
|
});
|
|
|
|
it('should return bad request when settings is empty', async () => {
|
|
await request.post(api('settings')).set(credentials).send({ settings: [] }).expect(400);
|
|
});
|
|
|
|
it('should return bad request when an item is missing value', async () => {
|
|
await request
|
|
.post(api('settings'))
|
|
.set(credentials)
|
|
.send({ settings: [{ _id: 'LDAP_Enable' }] })
|
|
.expect(400);
|
|
});
|
|
|
|
it('should reject an empty integer value without changing the setting', async () => {
|
|
const settingId = 'UserData_ProcessingFrequency';
|
|
const originalValue = await getSettingValueById(settingId);
|
|
|
|
await request
|
|
.post(api('settings'))
|
|
.set(credentials)
|
|
.send({ settings: [{ _id: settingId, value: null }] })
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
|
|
expect(await getSettingValueById(settingId)).to.equal(originalValue);
|
|
});
|
|
|
|
it('should successfully update multiple settings in a single request', async () => {
|
|
await request
|
|
.post(api('settings'))
|
|
.set(credentials)
|
|
.send({
|
|
settings: [
|
|
{ _id: 'LDAP_Enable', value: false },
|
|
{ _id: 'Accounts_Default_User_Preferences_masterVolume', value: 50 },
|
|
],
|
|
})
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
|
|
await request
|
|
.get(api('settings/LDAP_Enable'))
|
|
.set(credentials)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('value', false);
|
|
});
|
|
|
|
await request
|
|
.get(api('settings/Accounts_Default_User_Preferences_masterVolume'))
|
|
.set(credentials)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('value', 50);
|
|
});
|
|
});
|
|
|
|
it('should accept an optional editor for a setting', async () => {
|
|
await request
|
|
.post(api('settings'))
|
|
.set(credentials)
|
|
.send({ settings: [{ _id: 'Livechat_title_color', value: '#000000', editor: 'color' }] })
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
|
|
await request
|
|
.get(api('settings/Livechat_title_color'))
|
|
.set(credentials)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('value', '#000000');
|
|
});
|
|
});
|
|
|
|
it('should return bad request when editor is an empty string', async () => {
|
|
await request
|
|
.post(api('settings'))
|
|
.set(credentials)
|
|
.send({ settings: [{ _id: 'Livechat_title_color', value: '#C1272D', editor: '' }] })
|
|
.expect(400);
|
|
});
|
|
|
|
it('should fail when the user does not have edit-privileged-setting permission', async () => {
|
|
await updatePermission('edit-privileged-setting', []);
|
|
await request
|
|
.post(api('settings'))
|
|
.set(credentials)
|
|
.send({ settings: [{ _id: 'LDAP_Enable', value: false }] })
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.error).to.match(/error-action-not-allowed/);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('[/settings/:_id]', () => {
|
|
before(async () => {
|
|
await updatePermission('view-privileged-setting', ['admin']);
|
|
await updatePermission('edit-privileged-setting', ['admin']);
|
|
});
|
|
|
|
after(async () => {
|
|
await updatePermission('view-privileged-setting', ['admin']);
|
|
await updatePermission('edit-privileged-setting', ['admin']);
|
|
});
|
|
|
|
it('should successfully return one setting (GET)', async () => {
|
|
return request
|
|
.get(api('settings/Site_Url'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('_id', 'Site_Url');
|
|
expect(res.body).to.have.property('value');
|
|
});
|
|
});
|
|
|
|
it('should fail returning a setting if user does NOT have the view-privileged-setting permission (GET)', async () => {
|
|
await updatePermission('view-privileged-setting', []);
|
|
return request
|
|
.get(api('settings/Site_Url'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(403)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]');
|
|
});
|
|
});
|
|
|
|
it('should successfully set the value of a setting (POST)', async () => {
|
|
return request
|
|
.post(api('settings/LDAP_Enable'))
|
|
.set(credentials)
|
|
.send({
|
|
value: false,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
it('should fail updating the value of a setting less than its minValue (POST)', async () => {
|
|
return request
|
|
.post(api('settings/Accounts_Default_User_Preferences_masterVolume'))
|
|
.set(credentials)
|
|
.send({
|
|
value: '-1',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property(
|
|
'error',
|
|
'Value for setting Accounts_Default_User_Preferences_masterVolume must be greater than or equal to 0 [error-invalid-setting-value]',
|
|
);
|
|
});
|
|
});
|
|
|
|
it('should fail updating the value of a setting greater than its maxValue (POST)', async () => {
|
|
return request
|
|
.post(api('settings/Accounts_Default_User_Preferences_masterVolume'))
|
|
.set(credentials)
|
|
.send({
|
|
value: '101',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property(
|
|
'error',
|
|
'Value for setting Accounts_Default_User_Preferences_masterVolume must be less than or equal to 100 [error-invalid-setting-value]',
|
|
);
|
|
});
|
|
});
|
|
|
|
it('should fail updating the value of a setting if user does NOT have the edit-privileged-setting permission (POST)', async () => {
|
|
await updatePermission('edit-privileged-setting', []);
|
|
return request
|
|
.post(api('settings/LDAP_Enable'))
|
|
.set(credentials)
|
|
.send({
|
|
value: false,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(403)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('[Room_Toolbox_Layout]', () => {
|
|
const validLayout = [
|
|
{ roomType: ['c', 'p'], maxVisibleNormal: 3, items: [{ id: 'members-list', featured: true, order: 1 }] },
|
|
{ roomType: ['d'], maxVisibleNormal: 1 },
|
|
];
|
|
|
|
const saveLayout = (value: string) => request.post(api('settings/Room_Toolbox_Layout')).set(credentials).send({ value });
|
|
|
|
after(() => updateSetting('Room_Toolbox_Layout', ''));
|
|
|
|
it('should reject a value that is not valid JSON', async () => {
|
|
await saveLayout('not json {')
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'Room_Toolbox_Layout_Invalid');
|
|
expect(res.body).to.have.property('errorType', 'error-setting-validation-failed');
|
|
});
|
|
});
|
|
|
|
it('should reject an entry declaring an unsupported room type', async () => {
|
|
await saveLayout(JSON.stringify([{ roomType: ['l'] }]))
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'Room_Toolbox_Layout_Invalid');
|
|
expect(res.body).to.have.property('errorType', 'error-setting-validation-failed');
|
|
});
|
|
});
|
|
|
|
it('should reject entries claiming the same room type', async () => {
|
|
await saveLayout(JSON.stringify([{ roomType: ['c', 'p'] }, { roomType: ['c'] }]))
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'Room_Toolbox_Layout_Invalid');
|
|
expect(res.body).to.have.property('errorType', 'error-setting-validation-failed');
|
|
});
|
|
});
|
|
|
|
it('should reject an entry repeating a room type', async () => {
|
|
await saveLayout(JSON.stringify([{ roomType: ['c', 'c'] }]))
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'Room_Toolbox_Layout_Invalid');
|
|
expect(res.body).to.have.property('errorType', 'error-setting-validation-failed');
|
|
});
|
|
});
|
|
|
|
it('should reject an entry carrying an unknown key', async () => {
|
|
await saveLayout(JSON.stringify([{ roomType: ['c'], somethingElse: true }]))
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'Room_Toolbox_Layout_Invalid');
|
|
expect(res.body).to.have.property('errorType', 'error-setting-validation-failed');
|
|
});
|
|
});
|
|
|
|
it('should accept a document matching the schema', async () => {
|
|
await saveLayout(JSON.stringify(validLayout))
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
it('should accept an empty value, leaving the setting unconfigured', async () => {
|
|
await saveLayout('')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('[/service.configurations]', () => {
|
|
it('should return service configurations', async () => {
|
|
const res = await request.get(api('service.configurations')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('configurations');
|
|
});
|
|
|
|
describe('With OAuth enabled', () => {
|
|
before(() => updateSetting('Accounts_OAuth_Google', true));
|
|
|
|
after(() => updateSetting('Accounts_OAuth_Google', false));
|
|
|
|
it('should include the OAuth service in the response', (done) => {
|
|
// wait 3 seconds before getting the service list so the server has had time to update it
|
|
setTimeout(() => {
|
|
void request
|
|
.get(api('service.configurations'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('configurations');
|
|
|
|
expect((res.body.configurations as LoginServiceConfiguration[]).find(({ service }) => service === 'google')).to.exist;
|
|
})
|
|
.end(done);
|
|
}, 3000);
|
|
});
|
|
});
|
|
|
|
describe('With OAuth disabled', () => {
|
|
before(() => updateSetting('Accounts_OAuth_Google', false));
|
|
|
|
after(() => updateSetting('Accounts_OAuth_Google', false));
|
|
|
|
it('should not include the OAuth service in the response', (done) => {
|
|
// wait 3 seconds before getting the service list so the server has had time to update it
|
|
setTimeout(() => {
|
|
void request
|
|
.get(api('service.configurations'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('configurations');
|
|
|
|
expect((res.body.configurations as LoginServiceConfiguration[]).find(({ service }) => service === 'google')).to.not.exist;
|
|
})
|
|
.end(done);
|
|
}, 3000);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('/settings.oauth', () => {
|
|
it('should have return list of available oauth services when user is not logged', async () => {
|
|
const res = await request.get(api('settings.oauth')).expect('Content-Type', 'application/json').expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('services').and.to.be.an('array');
|
|
});
|
|
|
|
it('should have return list of available oauth services when user is logged', async () => {
|
|
const res = await request.get(api('settings.oauth')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('services').and.to.be.an('array');
|
|
});
|
|
});
|
|
|
|
(IS_EE ? describe : describe.skip)('/audit.settings', () => {
|
|
const formatDate = (date: Date) => date.toISOString();
|
|
let startDate: Date;
|
|
let endDate: Date;
|
|
|
|
before(async () => {
|
|
// Changing these settings to true and then false ensures that we have audited events
|
|
// So we can test filtering reliably
|
|
startDate = new Date();
|
|
await Promise.all([updateSetting('Accounts_AllowAnonymousRead', true), updateSetting('Accounts_AllowDeleteOwnAccount', true)]);
|
|
|
|
await Promise.all([updateSetting('Accounts_AllowAnonymousRead', false), updateSetting('Accounts_AllowDeleteOwnAccount', false)]);
|
|
endDate = new Date();
|
|
});
|
|
|
|
it('should return list of settings changed (no filters)', async () => {
|
|
await request
|
|
.get(api('audit.settings'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('events').and.to.be.an('array');
|
|
expect(res.body.events.length).to.be.greaterThanOrEqual(4);
|
|
});
|
|
});
|
|
|
|
it('should return list of settings between date ranges', async () => {
|
|
await request
|
|
.get(api('audit.settings'))
|
|
.query({ start: formatDate(startDate), end: formatDate(endDate) })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('events').and.to.be.an('array');
|
|
expect(res.body.events.length).to.be.greaterThanOrEqual(4);
|
|
(res.body.events as IServerEvents['settings.changed'][]).forEach((event) => {
|
|
expect(new Date(event.ts).getTime()).to.be.greaterThanOrEqual(startDate.getTime());
|
|
expect(new Date(event.ts).getTime()).to.be.lessThanOrEqual(endDate.getTime());
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should throw error when sending invalid dates', async () => {
|
|
const startDate = new Date();
|
|
const endDate = 'abcdef';
|
|
|
|
await request
|
|
.get(api('audit.settings'))
|
|
.query({ start: formatDate(startDate), end: endDate })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'The "end" query parameter must be a valid date.');
|
|
});
|
|
});
|
|
|
|
it('should return list of settings changed filtered by an actor', async () => {
|
|
await request
|
|
.get(api('audit.settings'))
|
|
.query({ actor: { type: 'user' }, count: 10 })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('events').and.to.be.an('array');
|
|
expect(res.body.events.length).to.be.greaterThanOrEqual(1);
|
|
(res.body.events as IServerEvents['settings.changed'][]).forEach((sEvent) => expect(sEvent.actor.type).to.be.equal('user'));
|
|
});
|
|
});
|
|
|
|
it('should return list of changes of an specific setting', async () => {
|
|
await request
|
|
.get(api('audit.settings'))
|
|
.query({ settingId: 'Site_Url' })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('events').and.to.be.an('array');
|
|
(res.body.events as IServerEvents['settings.changed'][]).forEach((event) => {
|
|
expect(event.data[0].key).to.be.equal('id');
|
|
expect(event.data[0].value).to.be.equal('Site_Url');
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should return list of changes filtered by an actor between date ranges', async () => {
|
|
await request
|
|
.get(api('audit.settings'))
|
|
.query({ actor: { type: 'user' }, start: formatDate(startDate), end: formatDate(endDate) })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('events').and.to.be.an('array');
|
|
expect(res.body.events.length).to.be.greaterThanOrEqual(4);
|
|
(res.body.events as IServerEvents['settings.changed'][]).forEach((event) => {
|
|
expect(event.actor.type).to.be.equal('user');
|
|
expect(new Date(event.ts).getTime()).to.be.greaterThanOrEqual(startDate.getTime());
|
|
expect(new Date(event.ts).getTime()).to.be.lessThanOrEqual(endDate.getTime());
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Masking sensitive settings', () => {
|
|
let originalSmtpPassword: string | undefined;
|
|
let originalSmtpUsername: string | undefined;
|
|
let maskingStart: Date;
|
|
let maskingEnd: Date;
|
|
const testPassword = 'testpassword123';
|
|
const testUsername = 'testuser@example.com';
|
|
|
|
before(async () => {
|
|
maskingStart = new Date();
|
|
originalSmtpPassword = (await getSettingValueById('SMTP_Password')) as string | undefined;
|
|
originalSmtpUsername = (await getSettingValueById('SMTP_Username')) as string | undefined;
|
|
|
|
await updateSetting('SMTP_Password', testPassword);
|
|
await updateSetting('SMTP_Username', testUsername);
|
|
maskingEnd = new Date();
|
|
});
|
|
|
|
after(async () => {
|
|
await updateSetting('SMTP_Password', originalSmtpPassword || '');
|
|
await updateSetting('SMTP_Username', originalSmtpUsername || '');
|
|
});
|
|
|
|
it('should mask sensitive settings in audit logs', async () => {
|
|
await request
|
|
.get(api('audit.settings'))
|
|
.query({ settingId: 'SMTP_Password', start: formatDate(maskingStart), end: formatDate(maskingEnd) })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('events').and.to.be.an('array');
|
|
expect(res.body.events.length).to.be.greaterThanOrEqual(1);
|
|
|
|
const passwordEvents = res.body.events as IServerEvents['settings.changed'][];
|
|
const passwordEvent = passwordEvents.find((event) => {
|
|
const settingId = event.data.find((item) => item.key === 'id')?.value;
|
|
return settingId === 'SMTP_Password';
|
|
});
|
|
|
|
expect(passwordEvent).to.exist;
|
|
|
|
if (passwordEvent) {
|
|
const previous = passwordEvent.data.find((item) => item.key === 'previous')?.value;
|
|
const current = passwordEvent.data.find((item) => item.key === 'current')?.value;
|
|
|
|
if (previous && typeof previous === 'string' && previous.length > 0) {
|
|
expect(previous).to.include('*');
|
|
expect(previous).to.not.equal(testPassword);
|
|
if (previous.length > 8) {
|
|
expect(previous.substring(0, 3)).to.equal(originalSmtpPassword?.substring(0, 3));
|
|
expect(previous.substring(3)).to.match(/^\*+$/);
|
|
}
|
|
}
|
|
|
|
if (current && typeof current === 'string' && current.length > 0) {
|
|
expect(current).to.include('*');
|
|
expect(current).to.not.equal(testPassword);
|
|
if (testPassword.length > 8) {
|
|
expect(current.substring(0, 3)).to.equal(testPassword.substring(0, 3));
|
|
expect(current.substring(3)).to.match(/^\*+$/);
|
|
} else {
|
|
expect(current).to.match(/^\*+$/);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
await request
|
|
.get(api('audit.settings'))
|
|
.query({ settingId: 'SMTP_Username', start: formatDate(maskingStart), end: formatDate(maskingEnd) })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('events').and.to.be.an('array');
|
|
expect(res.body.events.length).to.be.greaterThanOrEqual(1);
|
|
|
|
const usernameEvents = res.body.events as IServerEvents['settings.changed'][];
|
|
const usernameEvent = usernameEvents.find((event) => {
|
|
const settingId = event.data.find((item) => item.key === 'id')?.value;
|
|
return settingId === 'SMTP_Username';
|
|
});
|
|
|
|
expect(usernameEvent).to.exist;
|
|
|
|
if (usernameEvent) {
|
|
const previous = usernameEvent.data.find((item) => item.key === 'previous')?.value;
|
|
const current = usernameEvent.data.find((item) => item.key === 'current')?.value;
|
|
|
|
if (previous && typeof previous === 'string' && previous.length > 0) {
|
|
expect(previous).to.include('*');
|
|
expect(previous).to.not.equal(originalSmtpUsername);
|
|
}
|
|
|
|
if (current && typeof current === 'string' && current.length > 0) {
|
|
expect(current).to.include('*');
|
|
expect(current).to.not.equal(testUsername);
|
|
if (testUsername.length > 8) {
|
|
expect(current.substring(0, 3)).to.equal(testUsername.substring(0, 3));
|
|
expect(current.substring(3)).to.match(/^\*+$/);
|
|
} else {
|
|
expect(current).to.match(/^\*+$/);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Sorting', () => {
|
|
const isSortedAscending = (events: IServerEvents['settings.changed'][]) =>
|
|
events.every((event, i) => i === 0 || new Date(event.ts).getTime() >= new Date(events[i - 1].ts).getTime());
|
|
|
|
const isSortedDescending = (events: IServerEvents['settings.changed'][]) =>
|
|
events.every((event, i) => i === 0 || new Date(event.ts).getTime() <= new Date(events[i - 1].ts).getTime());
|
|
|
|
it('should sort by timestamp ascending', async () => {
|
|
const response = await request
|
|
.get(api('audit.settings'))
|
|
.query({ sort: JSON.stringify({ ts: 1 }), start: formatDate(startDate), end: formatDate(endDate) })
|
|
.set(credentials)
|
|
.expect(200);
|
|
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.events).to.have.length.greaterThanOrEqual(2);
|
|
expect(isSortedAscending(response.body.events)).to.be.true;
|
|
});
|
|
|
|
it('should sort by timestamp descending', async () => {
|
|
const response = await request
|
|
.get(api('audit.settings'))
|
|
.query({ sort: JSON.stringify({ ts: -1 }), start: formatDate(startDate), end: formatDate(endDate) })
|
|
.set(credentials)
|
|
.expect(200);
|
|
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.events).to.have.length.greaterThanOrEqual(2);
|
|
expect(isSortedDescending(response.body.events)).to.be.true;
|
|
});
|
|
|
|
it('should sort by timestamp descending by default', async () => {
|
|
const response = await request.get(api('audit.settings')).set(credentials).expect(200);
|
|
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.events).to.have.length.greaterThanOrEqual(2);
|
|
expect(isSortedDescending(response.body.events)).to.be.true;
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Custom OAuth admin endpoints', () => {
|
|
const oauthName = 'apitest';
|
|
const enableSettingId = 'Accounts_OAuth_Custom-Apitest';
|
|
|
|
before(() => updatePermission('add-oauth-service', ['admin']));
|
|
|
|
// guarantee the permission is restored even if a permission-denied assertion fails mid-test,
|
|
// so a failure cannot cascade into the following cases
|
|
afterEach(() => updatePermission('add-oauth-service', ['admin']));
|
|
|
|
after(async () => {
|
|
await updatePermission('add-oauth-service', ['admin']);
|
|
// best-effort cleanup in case a test left the custom service behind
|
|
await request.post(api('settings.removeCustomOAuth')).set(credentials).send({ name: oauthName });
|
|
});
|
|
|
|
describe('[/settings.addCustomOAuth]', () => {
|
|
it('should fail when unauthenticated', () => request.post(api('settings.addCustomOAuth')).send({ name: oauthName }).expect(401));
|
|
|
|
it('should fail when the "name" param is not provided', () =>
|
|
request.post(api('settings.addCustomOAuth')).set(credentials).send({}).expect(400));
|
|
|
|
it('should fail when the user does not have the add-oauth-service permission', async () => {
|
|
await updatePermission('add-oauth-service', []);
|
|
await request
|
|
.post(api('settings.addCustomOAuth'))
|
|
.set(credentials)
|
|
.send({ name: oauthName })
|
|
.expect((res) => {
|
|
expect(res.status).to.equal(403);
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
});
|
|
|
|
it('should add a custom oauth service', async () => {
|
|
await request
|
|
.post(api('settings.addCustomOAuth'))
|
|
.set(credentials)
|
|
.send({ name: oauthName })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
|
|
const value = await getSettingValueById(enableSettingId);
|
|
expect(value).to.be.a('boolean');
|
|
});
|
|
});
|
|
|
|
describe('[/settings.refreshOAuthServices]', () => {
|
|
it('should fail when unauthenticated', () => request.post(api('settings.refreshOAuthServices')).expect(401));
|
|
|
|
it('should fail when the user does not have the add-oauth-service permission', async () => {
|
|
await updatePermission('add-oauth-service', []);
|
|
await request
|
|
.post(api('settings.refreshOAuthServices'))
|
|
.set(credentials)
|
|
.expect((res) => {
|
|
expect(res.status).to.equal(403);
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
});
|
|
|
|
it('should refresh the oauth services', () =>
|
|
request
|
|
.post(api('settings.refreshOAuthServices'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
}));
|
|
});
|
|
|
|
describe('[/settings.removeCustomOAuth]', () => {
|
|
it('should fail when unauthenticated', () => request.post(api('settings.removeCustomOAuth')).send({ name: oauthName }).expect(401));
|
|
|
|
it('should fail when the "name" param is not provided', () =>
|
|
request.post(api('settings.removeCustomOAuth')).set(credentials).send({}).expect(400));
|
|
|
|
it('should fail when the "name" normalizes to an empty string', () =>
|
|
request
|
|
.post(api('settings.removeCustomOAuth'))
|
|
.set(credentials)
|
|
.send({ name: '!!!' })
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
}));
|
|
|
|
it('should fail when the user does not have the add-oauth-service permission', async () => {
|
|
await updatePermission('add-oauth-service', []);
|
|
await request
|
|
.post(api('settings.removeCustomOAuth'))
|
|
.set(credentials)
|
|
.send({ name: oauthName })
|
|
.expect((res) => {
|
|
expect(res.status).to.equal(403);
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
});
|
|
|
|
it('should remove a custom oauth service', async () => {
|
|
await request
|
|
.post(api('settings.removeCustomOAuth'))
|
|
.set(credentials)
|
|
.send({ name: oauthName })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
|
|
// the provider settings should be gone: settings/:_id answers 400 for an unknown id
|
|
await request.get(`/api/v1/settings/${enableSettingId}`).set(credentials).expect(400);
|
|
});
|
|
});
|
|
});
|
|
});
|