Compare commits

...

19 Commits

Author SHA1 Message Date
Barış Soner Uşaklı
129474f268 Merge branch 'develop' into axios 2023-12-17 19:08:58 -05:00
Barış Soner Uşaklı
40170f8133 cleaunup 2023-12-17 14:42:09 -05:00
Barış Soner Uşaklı
d4452ba25c socket.io 2023-12-17 14:37:53 -05:00
Barış Soner Uşaklı
5c6a631fc6 uploads.js 2023-12-17 14:20:45 -05:00
Barış Soner Uşaklı
dc1cd3feaa user/emails 2023-12-17 14:02:41 -05:00
Barış Soner Uşaklı
841b856e27 topics/thumbs 2023-12-17 13:32:59 -05:00
Barış Soner Uşaklı
abf81ec57d search 2023-12-17 13:09:53 -05:00
Barış Soner Uşaklı
b89502ce0b posts 2023-12-17 13:07:21 -05:00
Barış Soner Uşaklı
381e64e657 plugins 2023-12-17 12:54:22 -05:00
Barış Soner Uşaklı
110b867ed5 meta 2023-12-17 12:52:23 -05:00
Barış Soner Uşaklı
4b006b37cc remove log 2023-12-17 12:44:30 -05:00
Barış Soner Uşaklı
e51c8de9cb messaging/middleware 2023-12-17 12:42:07 -05:00
Barış Soner Uşaklı
918008fffd locale-detect 2023-12-17 12:08:48 -05:00
Barış Soner Uşaklı
2e2b1e9d6c flags 2023-12-17 12:03:49 -05:00
Barış Soner Uşaklı
ca42094f3f remove unused async 2023-12-17 11:40:31 -05:00
Barış Soner Uşaklı
3d07f14859 feeds 2023-12-17 11:40:13 -05:00
Barış Soner Uşaklı
32175666d5 add missing deps 2023-12-16 23:30:06 -05:00
Barış Soner Uşaklı
1501a9303c controller tests 2023-12-16 23:28:14 -05:00
Barış Soner Uşaklı
db75571923 axios migration 2023-12-16 19:40:36 -05:00
31 changed files with 2408 additions and 4138 deletions

View File

@@ -38,6 +38,8 @@
"archiver": "6.0.1",
"async": "3.2.5",
"autoprefixer": "10.4.16",
"axios": "1.6.2",
"axios-cookiejar-support": "4.0.7",
"bcryptjs": "2.4.3",
"benchpressjs": "2.5.1",
"body-parser": "1.20.2",
@@ -68,6 +70,7 @@
"express-session": "1.17.3",
"express-useragent": "1.0.15",
"file-loader": "6.2.0",
"form-data": "4.0.0",
"fs-extra": "11.2.0",
"graceful-fs": "4.2.11",
"helmet": "7.1.0",
@@ -119,8 +122,6 @@
"progress-webpack-plugin": "1.0.16",
"prompt": "1.3.0",
"ioredis": "5.3.2",
"request": "2.88.2",
"request-promise-native": "1.0.9",
"rimraf": "5.0.5",
"rss": "1.2.2",
"rtlcss": "4.1.1",
@@ -142,6 +143,7 @@
"timeago": "1.6.7",
"tinycon": "0.6.8",
"toobusy-js": "0.5.1",
"tough-cookie": "4.1.3",
"validator": "13.11.0",
"webpack": "5.89.0",
"webpack-merge": "5.10.0",

View File

@@ -62,9 +62,6 @@ define('forum/flags/detail', [
Detail.reloadHistory(payload.history);
}).catch(alerts.error);
},
onShown: (e) => {
console.log(e);
},
});
break;
}

View File

@@ -1,15 +1,16 @@
'use strict';
const request = require('request');
const request = require('../request');
const meta = require('../meta');
let versionCache = '';
let versionCacheLastModified = '';
const isPrerelease = /^v?\d+\.\d+\.\d+-.+$/;
const latestReleaseUrl = 'https://api.github.com/repos/NodeBB/NodeBB/releases/latest';
function getLatestVersion(callback) {
async function getLatestVersion() {
return '';
const headers = {
Accept: 'application/vnd.github.v3+json',
'User-Agent': encodeURIComponent(`NodeBB Admin Control Panel/${meta.config.title}`),
@@ -18,32 +19,25 @@ function getLatestVersion(callback) {
if (versionCacheLastModified) {
headers['If-Modified-Since'] = versionCacheLastModified;
}
request('https://api.github.com/repos/NodeBB/NodeBB/releases/latest', {
json: true,
headers: headers,
timeout: 2000,
}, (err, res, latestRelease) => {
if (err) {
return callback(err);
}
if (res.statusCode === 304) {
return callback(null, versionCache);
}
if (res.statusCode !== 200) {
return callback(new Error(res.statusMessage));
}
try {
const { body: latestRelease, response } = await request.get(latestReleaseUrl, {
headers: headers,
timeout: 2000,
});
if (!latestRelease || !latestRelease.tag_name) {
return callback(new Error('[[error:cant-get-latest-release]]'));
throw new Error('[[error:cant-get-latest-release]]');
}
const tagName = latestRelease.tag_name.replace(/^v/, '');
versionCache = tagName;
versionCacheLastModified = res.headers['last-modified'];
callback(null, versionCache);
});
versionCacheLastModified = response.headers['last-modified'];
return versionCache;
} catch (err) {
if (err.response && err.response.status === 304) {
return versionCache;
}
throw err;
}
}
exports.getLatestVersion = getLatestVersion;

View File

@@ -1,13 +1,13 @@
'use strict';
const prompt = require('prompt');
const request = require('request-promise-native');
const cproc = require('child_process');
const semver = require('semver');
const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const request = require('../request');
const { paths, pluginNamePattern } = require('../constants');
const pkgInstall = require('./package-install');
@@ -74,11 +74,7 @@ async function getCurrentVersion() {
}
async function getSuggestedModules(nbbVersion, toCheck) {
let body = await request({
method: 'GET',
url: `https://packages.nodebb.org/api/v1/suggest?version=${nbbVersion}&package[]=${toCheck.join('&package[]=')}`,
json: true,
});
let { body } = await request.get(`https://packages.nodebb.org/api/v1/suggest?version=${nbbVersion}&package[]=${toCheck.join('&package[]=')}`);
if (!Array.isArray(body) && toCheck.length === 1) {
body = [body];
}

View File

@@ -39,7 +39,7 @@ module.exports = function (middleware) {
const data = {
site_title: meta.config.title || 'NodeBB',
message: meta.config.maintenanceModeMessage,
message: meta.config.maintenanceModeMessage || '',
};
if (res.locals.isAPI) {

View File

@@ -6,8 +6,8 @@ const winston = require('winston');
const semver = require('semver');
const nconf = require('nconf');
const chalk = require('chalk');
const request = require('request-promise-native');
const request = require('../request');
const user = require('../user');
const posts = require('../posts');
@@ -153,9 +153,7 @@ Plugins.reloadRoutes = async function (params) {
Plugins.get = async function (id) {
const url = `${nconf.get('registry') || 'https://packages.nodebb.org'}/api/v1/plugins/${id}`;
const body = await request(url, {
json: true,
});
const { body } = await request.get(url);
let normalised = await Plugins.normalise([body ? body.payload : {}]);
normalised = normalised.filter(plugin => plugin.id === id);
@@ -169,9 +167,7 @@ Plugins.list = async function (matching) {
const { version } = require(paths.currentPackage);
const url = `${nconf.get('registry') || 'https://packages.nodebb.org'}/api/v1/plugins${matching !== false ? `?version=${version}` : ''}`;
try {
const body = await request(url, {
json: true,
});
const { body } = await request.get(url);
return await Plugins.normalise(body);
} catch (err) {
winston.error(`Error loading ${url}`, err);
@@ -181,9 +177,8 @@ Plugins.list = async function (matching) {
Plugins.listTrending = async () => {
const url = `${nconf.get('registry') || 'https://packages.nodebb.org'}/api/v1/analytics/top/week`;
return await request(url, {
json: true,
});
const { body } = await request.get(url);
return body;
};
Plugins.normalise = async function (apiReturn) {

View File

@@ -7,8 +7,8 @@ const nconf = require('nconf');
const os = require('os');
const cproc = require('child_process');
const util = require('util');
const request = require('request-promise-native');
const request = require('../request');
const db = require('../database');
const meta = require('../meta');
const pubsub = require('../pubsub');
@@ -74,11 +74,7 @@ module.exports = function (Plugins) {
};
Plugins.checkWhitelist = async function (id, version) {
const body = await request({
method: 'GET',
url: `https://packages.nodebb.org/api/v1/plugins/${encodeURIComponent(id)}`,
json: true,
});
const { body } = await request.get(`https://packages.nodebb.org/api/v1/plugins/${encodeURIComponent(id)}`);
if (body && body.code === 'ok' && (version === 'latest' || body.payload.valid.includes(version))) {
return;
@@ -88,11 +84,7 @@ module.exports = function (Plugins) {
};
Plugins.suggest = async function (pluginId, nbbVersion) {
const body = await request({
method: 'GET',
url: `https://packages.nodebb.org/api/v1/suggest?package=${encodeURIComponent(pluginId)}&version=${encodeURIComponent(nbbVersion)}`,
json: true,
});
const { body } = await request.get(`https://packages.nodebb.org/api/v1/suggest?package=${encodeURIComponent(pluginId)}&version=${encodeURIComponent(nbbVersion)}`);
return body;
};

View File

@@ -1,48 +1,45 @@
'use strict';
const nconf = require('nconf');
const request = require('request');
const winston = require('winston');
const crypto = require('crypto');
const cronJob = require('cron').CronJob;
const request = require('../request');
const pkg = require('../../package.json');
const meta = require('../meta');
module.exports = function (Plugins) {
Plugins.startJobs = function () {
new cronJob('0 0 0 * * *', (() => {
Plugins.submitUsageData();
new cronJob('0 0 0 * * *', (async () => {
await Plugins.submitUsageData();
}), null, true);
};
Plugins.submitUsageData = function (callback) {
callback = callback || function () {};
Plugins.submitUsageData = async function () {
if (!meta.config.submitPluginUsage || !Plugins.loadedPlugins.length || global.env !== 'production') {
return callback();
return;
}
const hash = crypto.createHash('sha256');
hash.update(nconf.get('url'));
request.post(`${nconf.get('registry') || 'https://packages.nodebb.org'}/api/v1/plugin/usage`, {
form: {
id: hash.digest('hex'),
version: pkg.version,
plugins: Plugins.loadedPlugins,
},
timeout: 5000,
}, (err, res, body) => {
if (err) {
winston.error(err.stack);
return callback(err);
const url = `${nconf.get('registry') || 'https://packages.nodebb.org'}/api/v1/plugin/usage`;
try {
const { response, body } = await request.post(url, {
data: {
id: hash.digest('hex'),
version: pkg.version,
plugins: Plugins.loadedPlugins,
},
timeout: 5000,
});
if (response.status !== 200) {
winston.error(`[plugins.submitUsageData] received ${response.status} ${body}`);
}
if (res.statusCode !== 200) {
winston.error(`[plugins.submitUsageData] received ${res.statusCode} ${body}`);
callback(new Error(`[[error:nbbpm-${res.statusCode}]]`));
} else {
callback();
}
});
} catch (err) {
winston.error(err.stack);
}
};
};

50
src/request.js Normal file
View File

@@ -0,0 +1,50 @@
'use strict';
const axios = require('axios').default;
const { CookieJar } = require('tough-cookie');
const { wrapper } = require('axios-cookiejar-support');
wrapper(axios);
exports.jar = function () {
return new CookieJar();
};
async function call(url, method, config = {}) {
const result = await axios({
...config,
method,
url: url,
});
return {
body: result.data,
response: {
status: result.status,
statusCode: result.status,
statusText: result.statusText,
headers: result.headers,
},
};
}
/*
const { body, response } = await request.get('someurl?foo=1&baz=2')
or
const { body, response } = await request.get('someurl', { params: { foo:1, baz: 2 } })
*/
exports.get = async (url, config) => call(url, 'get', config);
exports.head = async (url, config) => call(url, 'head', config);
exports.del = async (url, config) => call(url, 'delete', config);
exports.delete = exports.del;
exports.options = async (url, config) => call(url, 'delete', config);
/*
const { body, response } = await request.post('someurl', { data: { foo: 1, baz: 2}})
*/
exports.post = async (url, config) => call(url, 'post', config);
exports.put = async (url, config) => call(url, 'put', config);
exports.patch = async (url, config) => call(url, 'patch', config);

View File

@@ -100,8 +100,8 @@ SocketAdmin.getSearchDict = async function (socket) {
return await getAdminSearchDict(lang);
};
SocketAdmin.deleteAllSessions = function (socket, data, callback) {
user.auth.deleteAllSessions(callback);
SocketAdmin.deleteAllSessions = async function () {
await user.auth.deleteAllSessions();
};
SocketAdmin.reloadAllSessions = function (socket, data, callback) {

View File

@@ -5,13 +5,13 @@ const assert = require('assert');
const path = require('path');
const fs = require('fs');
const SwaggerParser = require('@apidevtools/swagger-parser');
const request = require('request-promise-native');
const nconf = require('nconf');
const jwt = require('jsonwebtoken');
const util = require('util');
const wait = util.promisify(setTimeout);
const request = require('../src/request');
const db = require('./mocks/databasemock');
const helpers = require('./helpers');
const meta = require('../src/meta');
@@ -314,12 +314,7 @@ describe('API', async () => {
({ jar } = await helpers.loginUser('admin', '123456'));
// Retrieve CSRF token using cookie, to test Write API
const config = await request({
url: `${nconf.get('url')}/api/config`,
json: true,
jar: jar,
});
csrfToken = config.csrf_token;
csrfToken = await helpers.getCsrfToken(jar);
setup = true;
}
@@ -409,7 +404,7 @@ describe('API', async () => {
paths.forEach((path) => {
const context = api.paths[path];
let schema;
let response;
let result;
let url;
let method;
const headers = {};
@@ -498,26 +493,16 @@ describe('API', async () => {
try {
if (type === 'json') {
response = await request(url, {
method: method,
result = await request[method](url, {
jar: !unauthenticatedRoutes.includes(path) ? jar : undefined,
json: true,
followRedirect: false, // all responses are significant (e.g. 302)
simple: false, // don't throw on non-200 (e.g. 302)
resolveWithFullResponse: true, // send full request back (to check statusCode)
maxRedirects: 0,
validateStatus: null, // don't throw on non-200 (e.g. 302)
headers: headers,
qs: qs,
body: body,
params: qs,
data: body,
});
} else if (type === 'form') {
response = await new Promise((resolve, reject) => {
helpers.uploadFile(url, pathLib.join(__dirname, './files/test.png'), {}, jar, csrfToken, (err, res) => {
if (err) {
return reject(err);
}
resolve(res);
});
});
result = await helpers.uploadFile(url, pathLib.join(__dirname, './files/test.png'), {}, jar, csrfToken);
}
} catch (e) {
assert(!e, `${method.toUpperCase()} ${path} errored with: ${e.message}`);
@@ -526,13 +511,18 @@ describe('API', async () => {
it('response status code should match one of the schema defined responses', () => {
// HACK: allow HTTP 418 I am a teapot, for now 👇
assert(context[method].responses.hasOwnProperty('418') || Object.keys(context[method].responses).includes(String(response.statusCode)), `${method.toUpperCase()} ${path} sent back unexpected HTTP status code: ${response.statusCode}`);
const { responses } = context[method];
assert(
responses.hasOwnProperty('418') ||
Object.keys(responses).includes(String(result.response.statusCode)),
`${method.toUpperCase()} ${path} sent back unexpected HTTP status code: ${result.response.statusCode}`
);
});
// Recursively iterate through schema properties, comparing type
it('response body should match schema definition', () => {
const http302 = context[method].responses['302'];
if (http302 && response.statusCode === 302) {
if (http302 && result.response.statusCode === 302) {
// Compare headers instead
const expectedHeaders = Object.keys(http302.headers).reduce((memo, name) => {
const value = http302.headers[name].schema.example;
@@ -541,13 +531,13 @@ describe('API', async () => {
}, {});
for (const header of Object.keys(expectedHeaders)) {
assert(response.headers[header.toLowerCase()]);
assert.strictEqual(response.headers[header.toLowerCase()], expectedHeaders[header]);
assert(result.response.headers[header.toLowerCase()]);
assert.strictEqual(result.response.headers[header.toLowerCase()], expectedHeaders[header]);
}
return;
}
if (response.statusCode === 400 && context[method].responses['400']) {
if (result.response.statusCode === 400 && context[method].responses['400']) {
// TODO: check 400 schema to response.body?
return;
}
@@ -557,12 +547,12 @@ describe('API', async () => {
return;
}
assert.strictEqual(response.statusCode, 200, `HTTP 200 expected (path: ${method} ${path}`);
assert.strictEqual(result.response.statusCode, 200, `HTTP 200 expected (path: ${method} ${path}`);
const hasJSON = http200.content && http200.content['application/json'];
if (hasJSON) {
schema = context[method].responses['200'].content['application/json'].schema;
compare(schema, response.body, method.toUpperCase(), path, 'root');
compare(schema, result.body, method.toUpperCase(), path, 'root');
}
// TODO someday: text/csv, binary file type checking?
@@ -576,12 +566,7 @@ describe('API', async () => {
mocks.delete['/users/{uid}/sessions/{uuid}'][1].example = Object.keys(sessionUUIDs).pop();
// Retrieve CSRF token using cookie, to test Write API
const config = await request({
url: `${nconf.get('url')}/api/config`,
json: true,
jar: jar,
});
csrfToken = config.csrf_token;
csrfToken = await helpers.getCsrfToken(jar);
}
});
});

View File

@@ -3,12 +3,9 @@
const assert = require('assert');
const url = require('url');
const async = require('async');
const nconf = require('nconf');
const request = require('request');
const requestAsync = require('request-promise-native');
const util = require('util');
const request = require('../src/request');
const db = require('./mocks/databasemock');
const user = require('../src/user');
const utils = require('../src/utils');
@@ -45,8 +42,8 @@ describe('authentication', () => {
it('should allow login with email for uid 1', async () => {
const oldValue = meta.config.allowLoginWith;
meta.config.allowLoginWith = 'username-email';
const { res } = await helpers.loginUser('regular@nodebb.org', 'regularpwd');
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.loginUser('regular@nodebb.org', 'regularpwd');
assert.strictEqual(response.statusCode, 200);
meta.config.allowLoginWith = oldValue;
});
@@ -54,150 +51,113 @@ describe('authentication', () => {
const oldValue = meta.config.allowLoginWith;
meta.config.allowLoginWith = 'username-email';
const uid = await user.create({ username: '2nduser', password: '2ndpassword', email: '2nduser@nodebb.org' });
const { res, body } = await helpers.loginUser('2nduser@nodebb.org', '2ndpassword');
assert.strictEqual(res.statusCode, 403);
const { response, body } = await helpers.loginUser('2nduser@nodebb.org', '2ndpassword');
assert.strictEqual(response.statusCode, 403);
assert.strictEqual(body, '[[error:invalid-login-credentials]]');
meta.config.allowLoginWith = oldValue;
});
it('should fail to create user if username is too short', (done) => {
helpers.registerUser({
it('should fail to create user if username is too short', async () => {
const { response, body } = await helpers.registerUser({
username: 'a',
password: '123456',
}, (err, jar, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
done();
});
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
});
it('should fail to create user if userslug is too short', (done) => {
helpers.registerUser({
it('should fail to create user if userslug is too short', async () => {
const { response, body } = await helpers.registerUser({
username: '----a-----',
password: '123456',
}, (err, jar, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
done();
});
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
});
it('should fail to create user if userslug is too short', (done) => {
helpers.registerUser({
it('should fail to create user if userslug is too short', async () => {
const { response, body } = await helpers.registerUser({
username: ' a',
password: '123456',
}, (err, jar, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
done();
});
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
});
it('should fail to create user if userslug is too short', (done) => {
helpers.registerUser({
it('should fail to create user if userslug is too short', async () => {
const { response, body } = await helpers.registerUser({
username: 'a ',
password: '123456',
}, (err, jar, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
done();
});
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
});
it('should register and login a user', (done) => {
request({
url: `${nconf.get('url')}/api/config`,
json: true,
it('should register and login a user', async () => {
const jar = request.jar();
const csrf_token = await helpers.getCsrfToken(jar);
const { body } = await request.post(`${nconf.get('url')}/register`, {
jar,
data: {
email: 'admin@nodebb.org',
username: 'admin',
password: 'adminpwd',
'password-confirm': 'adminpwd',
userLang: 'it',
gdpr_consent: true,
},
headers: {
'x-csrf-token': csrf_token,
},
});
const validationPending = await user.email.isValidationPending(body.uid, 'admin@nodebb.org');
assert.strictEqual(validationPending, true);
assert(body);
assert(body.hasOwnProperty('uid') && body.uid > 0);
const newUid = body.uid;
const { body: self } = await request.get(`${nconf.get('url')}/api/self`, {
jar,
});
assert(self);
assert.equal(self.username, 'admin');
assert.equal(self.uid, newUid);
const settings = await user.getSettings(body.uid);
assert.equal(settings.userLang, 'it');
});
it('should logout a user', async () => {
await helpers.logoutUser(jar);
const { response, body } = await request.get(`${nconf.get('url')}/api/me`, {
jar: jar,
}, (err, response, body) => {
assert.ifError(err);
request.post(`${nconf.get('url')}/register`, {
form: {
email: 'admin@nodebb.org',
username: 'admin',
password: 'adminpwd',
'password-confirm': 'adminpwd',
userLang: 'it',
gdpr_consent: true,
},
json: true,
jar: jar,
headers: {
'x-csrf-token': body.csrf_token,
},
}, async (err, response, body) => {
const validationPending = await user.email.isValidationPending(body.uid, 'admin@nodebb.org');
assert.strictEqual(validationPending, true);
assert.ifError(err);
assert(body);
assert(body.hasOwnProperty('uid') && body.uid > 0);
const newUid = body.uid;
request({
url: `${nconf.get('url')}/api/self`,
json: true,
jar: jar,
}, (err, response, body) => {
assert.ifError(err);
assert(body);
assert.equal(body.username, 'admin');
assert.equal(body.uid, newUid);
user.getSettings(body.uid, (err, settings) => {
assert.ifError(err);
assert.equal(settings.userLang, 'it');
done();
});
});
});
});
});
it('should logout a user', (done) => {
helpers.logoutUser(jar, (err) => {
assert.ifError(err);
request({
url: `${nconf.get('url')}/api/me`,
json: true,
jar: jar,
}, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 401);
assert.strictEqual(body.status.code, 'not-authorised');
done();
});
validateStatus: null,
});
assert.equal(response.statusCode, 401);
assert.strictEqual(body.status.code, 'not-authorised');
});
it('should regenerate the session identifier on successful login', async () => {
const matchRegexp = /express\.sid=s%3A(.+?);/;
const { hostname, path } = url.parse(nconf.get('url'));
const sid = String(jar._jar.store.idx[hostname][path]['express.sid']).match(matchRegexp)[1];
const sid = String(jar.store.idx[hostname][path]['express.sid']).match(matchRegexp)[1];
await helpers.logoutUser(jar);
const newJar = (await helpers.loginUser('regular', 'regularpwd')).jar;
const newSid = String(newJar._jar.store.idx[hostname][path]['express.sid']).match(matchRegexp)[1];
const newSid = String(newJar.store.idx[hostname][path]['express.sid']).match(matchRegexp)[1];
assert.notStrictEqual(newSid, sid);
});
it('should revoke all sessions', (done) => {
it('should revoke all sessions', async () => {
const socketAdmin = require('../src/socket.io/admin');
db.sortedSetCard(`uid:${regularUid}:sessions`, (err, count) => {
assert.ifError(err);
assert(count);
socketAdmin.deleteAllSessions({ uid: 1 }, {}, (err) => {
assert.ifError(err);
db.sortedSetCard(`uid:${regularUid}:sessions`, (err, count) => {
assert.ifError(err);
assert(!count);
done();
});
});
});
let sessionCount = await db.sortedSetCard(`uid:${regularUid}:sessions`);
assert(sessionCount);
await socketAdmin.deleteAllSessions({ uid: 1 }, {});
sessionCount = await db.sortedSetCard(`uid:${regularUid}:sessions`);
assert(!sessionCount);
});
describe('login', () => {
@@ -205,11 +165,12 @@ describe('authentication', () => {
let password;
let uid;
function getCookieExpiry(res) {
assert(res.headers['set-cookie']);
assert.strictEqual(res.headers['set-cookie'][0].includes('Expires'), true);
function getCookieExpiry(response) {
const { headers } = response;
assert(headers['set-cookie']);
assert.strictEqual(headers['set-cookie'][0].includes('Expires'), true);
const values = res.headers['set-cookie'][0].split(';');
const values = headers['set-cookie'][0].split(';');
return values.reduce((memo, cur) => {
if (!memo) {
const [name, value] = cur.split('=');
@@ -230,9 +191,7 @@ describe('authentication', () => {
it('should login a user', async () => {
const { jar, body: loginBody } = await helpers.loginUser(username, password);
assert(loginBody);
const body = await requestAsync({
url: `${nconf.get('url')}/api/self`,
json: true,
const { body } = await request.get(`${nconf.get('url')}/api/self`, {
jar,
});
assert(body);
@@ -243,11 +202,11 @@ describe('authentication', () => {
});
it('should set a cookie that only lasts for the life of the browser session', async () => {
const { res } = await helpers.loginUser(username, password);
const { response } = await helpers.loginUser(username, password);
assert(res.headers);
assert(res.headers['set-cookie']);
assert.strictEqual(res.headers['set-cookie'][0].includes('Expires'), false);
assert(response.headers);
assert(response.headers['set-cookie']);
assert.strictEqual(response.headers['set-cookie'][0].includes('Expires'), false);
});
it('should set a different expiry if sessionDuration is set', async () => {
@@ -255,9 +214,9 @@ describe('authentication', () => {
const days = 1;
meta.config.sessionDuration = days * 24 * 60 * 60;
const { res } = await helpers.loginUser(username, password);
const { response } = await helpers.loginUser(username, password);
const expiry = getCookieExpiry(res);
const expiry = getCookieExpiry(response);
const expected = new Date();
expected.setUTCDate(expected.getUTCDate() + days);
@@ -267,9 +226,9 @@ describe('authentication', () => {
});
it('should set a cookie that lasts for x days where x is loginDays setting, if asked to remember', async () => {
const { res } = await helpers.loginUser(username, password, { remember: 'on' });
const { response } = await helpers.loginUser(username, password, { remember: 'on' });
const expiry = getCookieExpiry(res);
const expiry = getCookieExpiry(response);
const expected = new Date();
expected.setUTCDate(expected.getUTCDate() + meta.config.loginDays);
@@ -280,9 +239,9 @@ describe('authentication', () => {
const _loginDays = meta.config.loginDays;
meta.config.loginDays = 5;
const { res } = await helpers.loginUser(username, password, { remember: 'on' });
const { response } = await helpers.loginUser(username, password, { remember: 'on' });
const expiry = getCookieExpiry(res);
const expiry = getCookieExpiry(response);
const expected = new Date();
expected.setUTCDate(expected.getUTCDate() + meta.config.loginDays);
@@ -295,9 +254,9 @@ describe('authentication', () => {
const _loginSeconds = meta.config.loginSeconds;
meta.config.loginSeconds = 60;
const { res } = await helpers.loginUser(username, password, { remember: 'on' });
const { response } = await helpers.loginUser(username, password, { remember: 'on' });
const expiry = getCookieExpiry(res);
const expiry = getCookieExpiry(response);
const expected = new Date();
expected.setUTCSeconds(expected.getUTCSeconds() + meta.config.loginSeconds);
@@ -308,64 +267,53 @@ describe('authentication', () => {
});
});
it('should fail to login if ip address is invalid', (done) => {
it('should fail to login if ip address is invalid', async () => {
const jar = request.jar();
request({
url: `${nconf.get('url')}/api/config`,
json: true,
jar: jar,
}, (err, response, body) => {
if (err) {
return done(err);
}
const csrf_token = await helpers.getCsrfToken(jar);
request.post(`${nconf.get('url')}/login`, {
form: {
username: 'regular',
password: 'regularpwd',
},
json: true,
jar: jar,
headers: {
'x-csrf-token': body.csrf_token,
'x-forwarded-for': '<script>alert("xss")</script>',
},
}, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 500);
done();
});
const { response } = await request.post(`${nconf.get('url')}/login`, {
data: {
username: 'regular',
password: 'regularpwd',
},
jar: jar,
validateStatus: () => true,
headers: {
'x-csrf-token': csrf_token,
'x-forwarded-for': '<script>alert("xss")</script>',
},
});
assert.equal(response.status, 500);
});
it('should fail to login if user does not exist', async () => {
const { res, body } = await helpers.loginUser('doesnotexist', 'nopassword');
assert.equal(res.statusCode, 403);
const { response, body } = await helpers.loginUser('doesnotexist', 'nopassword');
assert.equal(response.statusCode, 403);
assert.equal(body, '[[error:invalid-login-credentials]]');
});
it('should fail to login if username is empty', async () => {
const { res, body } = await helpers.loginUser('', 'some password');
assert.equal(res.statusCode, 403);
const { response, body } = await helpers.loginUser('', 'some password');
assert.equal(response.statusCode, 403);
assert.equal(body, '[[error:invalid-username-or-password]]');
});
it('should fail to login if password is empty', async () => {
const { res, body } = await helpers.loginUser('someuser', '');
assert.equal(res.statusCode, 403);
const { response, body } = await helpers.loginUser('someuser', '');
assert.equal(response.statusCode, 403);
assert.equal(body, '[[error:invalid-username-or-password]]');
});
it('should fail to login if username and password are empty', async () => {
const { res, body } = await helpers.loginUser('', '');
assert.equal(res.statusCode, 403);
const { response, body } = await helpers.loginUser('', '');
assert.equal(response.statusCode, 403);
assert.equal(body, '[[error:invalid-username-or-password]]');
});
it('should fail to login if user does not have password field in db', async () => {
await user.create({ username: 'hasnopassword', email: 'no@pass.org' });
const { res, body } = await helpers.loginUser('hasnopassword', 'doesntmatter');
assert.equal(res.statusCode, 403);
const { response, body } = await helpers.loginUser('hasnopassword', 'doesntmatter');
assert.equal(response.statusCode, 403);
assert.equal(body, '[[error:invalid-login-credentials]]');
});
@@ -374,92 +322,74 @@ describe('authentication', () => {
for (let i = 0; i < 5000; i++) {
longPassword += 'a';
}
const { res, body } = await helpers.loginUser('someuser', longPassword);
assert.equal(res.statusCode, 403);
const { response, body } = await helpers.loginUser('someuser', longPassword);
assert.equal(response.statusCode, 403);
assert.equal(body, '[[error:password-too-long]]');
});
it('should fail to login if local login is disabled', async () => {
await privileges.global.rescind(['groups:local:login'], 'registered-users');
const { res, body } = await helpers.loginUser('regular', 'regularpwd');
assert.equal(res.statusCode, 403);
const { response, body } = await helpers.loginUser('regular', 'regularpwd');
assert.equal(response.statusCode, 403);
assert.equal(body, '[[error:local-login-disabled]]');
await privileges.global.give(['groups:local:login'], 'registered-users');
});
it('should fail to register if registraton is disabled', (done) => {
it('should fail to register if registraton is disabled', async () => {
meta.config.registrationType = 'disabled';
helpers.registerUser({
const { response, body } = await helpers.registerUser({
username: 'someuser',
password: 'somepassword',
}, (err, jar, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 403);
assert.equal(body, 'Forbidden');
done();
});
assert.equal(response.statusCode, 403);
assert.equal(body, 'Forbidden');
});
it('should return error if invitation is not valid', (done) => {
it('should return error if invitation is not valid', async () => {
meta.config.registrationType = 'invite-only';
helpers.registerUser({
const { response, body } = await helpers.registerUser({
username: 'someuser',
password: 'somepassword',
}, (err, jar, response, body) => {
meta.config.registrationType = 'normal';
assert.ifError(err);
assert.equal(response.statusCode, 400);
assert.equal(body, '[[register:invite.error-invite-only]]');
done();
});
meta.config.registrationType = 'normal';
assert.equal(response.statusCode, 400);
assert.equal(body, '[[register:invite.error-invite-only]]');
});
it('should fail to register if username is falsy or too short', (done) => {
helpers.registerUser({
username: '',
password: 'somepassword',
}, (err, jar, response, body) => {
assert.ifError(err);
it('should fail to register if username is falsy or too short', async () => {
const userData = [
{ username: '', password: 'somepassword' },
{ username: 'a', password: 'somepassword' },
];
for (const user of userData) {
// eslint-disable-next-line no-await-in-loop
const { response, body } = await helpers.registerUser(user);
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
helpers.registerUser({
username: 'a',
password: 'somepassword',
}, (err, jar, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-short]]');
done();
});
});
}
});
it('should fail to register if username is too long', (done) => {
helpers.registerUser({
it('should fail to register if username is too long', async () => {
const { response, body } = await helpers.registerUser({
username: 'thisisareallylongusername',
password: '123456',
}, (err, jar, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-long]]');
done();
});
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:username-too-long]]');
});
it('should queue user if ip is used before', (done) => {
it('should queue user if ip is used before', async () => {
meta.config.registrationApprovalType = 'admin-approval-ip';
helpers.registerUser({
const { response, body } = await helpers.registerUser({
email: 'another@user.com',
username: 'anotheruser',
password: 'anotherpwd',
gdpr_consent: 1,
}, (err, jar, response, body) => {
meta.config.registrationApprovalType = 'normal';
assert.ifError(err);
assert.equal(response.statusCode, 200);
assert.equal(body.message, '[[register:registration-added-to-queue]]');
done();
});
meta.config.registrationApprovalType = 'normal';
assert.equal(response.statusCode, 200);
assert.equal(body.message, '[[register:registration-added-to-queue]]');
});
@@ -468,41 +398,32 @@ describe('authentication', () => {
const uid = await user.create({ username: 'ginger', password: '123456', email });
await user.setUserField(uid, 'email', email);
await user.email.confirmByUid(uid);
const { res } = await helpers.loginUser('ginger@nodebb.org', '123456');
assert.equal(res.statusCode, 200);
const { response } = await helpers.loginUser('ginger@nodebb.org', '123456');
assert.equal(response.statusCode, 200);
});
it('should fail to login if login type is username and an email is sent', async () => {
meta.config.allowLoginWith = 'username';
const { res, body } = await helpers.loginUser('ginger@nodebb.org', '123456');
const { response, body } = await helpers.loginUser('ginger@nodebb.org', '123456');
meta.config.allowLoginWith = 'username-email';
assert.equal(res.statusCode, 400);
assert.equal(response.statusCode, 400);
assert.equal(body, '[[error:wrong-login-type-username]]');
});
it('should send 200 if not logged in', (done) => {
it('should send 200 if not logged in', async () => {
const jar = request.jar();
request({
url: `${nconf.get('url')}/api/config`,
json: true,
jar: jar,
}, (err, response, body) => {
assert.ifError(err);
const csrf_token = await helpers.getCsrfToken(jar);
request.post(`${nconf.get('url')}/logout`, {
form: {},
json: true,
jar: jar,
headers: {
'x-csrf-token': body.csrf_token,
},
}, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert.equal(body, 'not-logged-in');
done();
});
const { response, body } = await request.post(`${nconf.get('url')}/logout`, {
data: {},
jar: jar,
headers: {
'x-csrf-token': csrf_token,
},
});
assert.equal(response.statusCode, 200);
assert.equal(body, 'not-logged-in');
});
describe('banned user authentication', () => {
@@ -518,7 +439,7 @@ describe('authentication', () => {
it('should prevent banned user from logging in', async () => {
await user.bans.ban(bannedUser.uid, 0, 'spammer');
const { res: res1, body: body1 } = await helpers.loginUser(bannedUser.username, bannedUser.pw);
const { response: res1, body: body1 } = await helpers.loginUser(bannedUser.username, bannedUser.pw);
assert.equal(res1.statusCode, 403);
delete body1.timestamp;
assert.deepStrictEqual(body1, {
@@ -532,7 +453,7 @@ describe('authentication', () => {
await user.bans.unban(bannedUser.uid);
const expiry = Date.now() + 10000;
await user.bans.ban(bannedUser.uid, expiry, '');
const { res: res2, body: body2 } = await helpers.loginUser(bannedUser.username, bannedUser.pw);
const { response: res2, body: body2 } = await helpers.loginUser(bannedUser.username, bannedUser.pw);
assert.equal(res2.statusCode, 403);
assert(body2.banned_until);
assert(body2.reason, '[[user:info.banned-no-reason]]');
@@ -540,15 +461,15 @@ describe('authentication', () => {
it('should allow banned user to log in if the "banned-users" group has "local-login" privilege', async () => {
await privileges.global.give(['groups:local:login'], 'banned-users');
const { res } = await helpers.loginUser(bannedUser.username, bannedUser.pw);
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.loginUser(bannedUser.username, bannedUser.pw);
assert.strictEqual(response.statusCode, 200);
});
it('should allow banned user to log in if the user herself has "local-login" privilege', async () => {
await privileges.global.rescind(['groups:local:login'], 'banned-users');
await privileges.categories.give(['local:login'], 0, bannedUser.uid);
const { res } = await helpers.loginUser(bannedUser.username, bannedUser.pw);
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.loginUser(bannedUser.username, bannedUser.pw);
assert.strictEqual(response.statusCode, 200);
});
});
@@ -561,10 +482,10 @@ describe('authentication', () => {
let data = await helpers.loginUser('lockme', 'abcdef');
meta.config.loginAttempts = 5;
assert.equal(data.res.statusCode, 403);
assert.equal(data.response.statusCode, 403);
assert.equal(data.body, '[[error:account-locked]]');
data = await helpers.loginUser('lockme', 'abcdef');
assert.equal(data.res.statusCode, 403);
assert.equal(data.response.statusCode, 403);
assert.equal(data.body, '[[error:account-locked]]');
const locked = await db.exists(`lockout:${uid}`);
assert(locked);
@@ -594,48 +515,47 @@ describe('authentication', () => {
});
it('should fail with invalid token', async () => {
const { res, body } = await helpers.request('get', `/api/self`, {
form: {
const { response, body } = await helpers.request('get', `/api/self`, {
data: {
_uid: newUid,
},
json: true,
jar: jar,
headers: {
Authorization: `Bearer sdfhaskfdja-jahfdaksdf`,
},
});
assert.strictEqual(res.statusCode, 401);
assert.strictEqual(response.statusCode, 401);
assert.strictEqual(body, 'not-authorized');
});
it('should use a token tied to an uid', async () => {
const { res, body } = await helpers.request('get', `/api/self`, {
const { response, body } = await helpers.request('get', `/api/self`, {
json: true,
headers: {
Authorization: `Bearer ${userToken}`,
},
});
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(response.statusCode, 200);
assert.strictEqual(body.username, 'apiUserTarget');
});
it('should fail if _uid is not passed in with master token', async () => {
const { res, body } = await helpers.request('get', `/api/self`, {
form: {},
const { response, body } = await helpers.request('get', `/api/self`, {
data: {},
json: true,
headers: {
Authorization: `Bearer ${masterToken}`,
},
});
assert.strictEqual(res.statusCode, 500);
assert.strictEqual(response.statusCode, 500);
assert.strictEqual(body.error, '[[error:api.master-token-no-uid]]');
});
it('should use master api token and _uid', async () => {
const { res, body } = await helpers.request('get', `/api/self`, {
form: {
const { response, body } = await helpers.request('get', `/api/self`, {
data: {
_uid: newUid,
},
json: true,
@@ -644,7 +564,7 @@ describe('authentication', () => {
},
});
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(response.statusCode, 200);
assert.strictEqual(body.username, 'apiUserTarget');
});
});

View File

@@ -2,8 +2,8 @@
const assert = require('assert');
const nconf = require('nconf');
const request = require('request');
const request = require('../src/request');
const db = require('./mocks/databasemock');
const Categories = require('../src/categories');
const Topics = require('../src/topics');
@@ -76,14 +76,11 @@ describe('Categories', () => {
});
});
it('should load a category route', (done) => {
request(`${nconf.get('url')}/api/category/${categoryObj.cid}/test-category`, { json: true }, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 200);
assert.equal(body.name, 'Test Category &amp; NodeBB');
assert(body);
done();
});
it('should load a category route', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/category/${categoryObj.cid}/test-category`, { json: true });
assert.equal(response.statusCode, 200);
assert.equal(body.name, 'Test Category &amp; NodeBB');
assert(body);
});
describe('Categories.getRecentTopicReplies', () => {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,12 @@
'use strict';
const assert = require('assert');
const async = require('async');
const request = require('request');
const nconf = require('nconf');
const db = require('./mocks/databasemock');
const request = require('../src/request');
const topics = require('../src/topics');
const categories = require('../src/categories');
const groups = require('../src/groups');
const user = require('../src/user');
const meta = require('../src/meta');
const privileges = require('../src/privileges');
@@ -16,38 +14,27 @@ const helpers = require('./helpers');
describe('feeds', () => {
let tid;
let pid;
let fooUid;
let cid;
before((done) => {
before(async () => {
meta.config['feeds:disableRSS'] = 1;
async.series({
category: function (next) {
categories.create({
name: 'Test Category',
description: 'Test category created by testing script',
}, next);
},
user: function (next) {
user.create({ username: 'foo', password: 'barbar', email: 'foo@test.com' }, next);
},
}, (err, results) => {
if (err) {
return done(err);
}
cid = results.category.cid;
fooUid = results.user;
topics.post({ uid: results.user, title: 'test topic title', content: 'test topic content', cid: results.category.cid }, (err, result) => {
tid = result.topicData.tid;
pid = result.postData.pid;
done(err);
});
const category = await categories.create({
name: 'Test Category',
description: 'Test category created by testing script',
});
cid = category.cid;
fooUid = await user.create({ username: 'foo', password: 'barbar', email: 'foo@test.com' });
const result = await topics.post({
cid: cid,
uid: fooUid,
title: 'test topic title',
content: 'test topic content',
});
tid = result.topicData.tid;
});
it('should 404', (done) => {
it('should 404', async () => {
const feedUrls = [
`${nconf.get('url')}/topic/${tid}.rss`,
`${nconf.get('url')}/category/${cid}.rss`,
@@ -61,67 +48,45 @@ describe('feeds', () => {
`${nconf.get('url')}/user/foo/topics.rss`,
`${nconf.get('url')}/tags/nodebb.rss`,
];
async.eachSeries(feedUrls, (url, next) => {
request(url, (err, res) => {
assert.ifError(err);
assert.equal(res.statusCode, 404);
next();
});
}, (err) => {
assert.ifError(err);
meta.config['feeds:disableRSS'] = 0;
done();
});
for (const url of feedUrls) {
// eslint-disable-next-line no-await-in-loop
const { response } = await request.get(url, { validateStatus: null });
assert.equal(response.statusCode, 404);
}
meta.config['feeds:disableRSS'] = 0;
});
it('should 404 if topic does not exist', (done) => {
request(`${nconf.get('url')}/topic/${1000}.rss`, (err, res) => {
assert.ifError(err);
assert.equal(res.statusCode, 404);
done();
});
it('should 404 if topic does not exist', async () => {
const { response } = await request.get(`${nconf.get('url')}/topic/${1000}.rss`, { validateStatus: null });
assert.equal(response.statusCode, 404);
});
it('should 404 if category id is not a number', (done) => {
request(`${nconf.get('url')}/category/invalid.rss`, (err, res) => {
assert.ifError(err);
assert.equal(res.statusCode, 404);
done();
});
it('should 404 if category id is not a number', async () => {
const { response } = await request.get(`${nconf.get('url')}/category/invalid.rss`, { validateStatus: null });
assert.equal(response.statusCode, 404);
});
it('should redirect if we do not have read privilege', (done) => {
privileges.categories.rescind(['groups:topics:read'], cid, 'guests', (err) => {
assert.ifError(err);
request(`${nconf.get('url')}/topic/${tid}.rss`, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body);
assert(body.includes('Login to your account'));
privileges.categories.give(['groups:topics:read'], cid, 'guests', done);
});
});
it('should redirect if we do not have read privilege', async () => {
await privileges.categories.rescind(['groups:topics:read'], cid, 'guests');
const { response, body } = await request.get(`${nconf.get('url')}/topic/${tid}.rss`);
assert.equal(response.statusCode, 200);
assert(body);
assert(body.includes('Login to your account'));
await privileges.categories.give(['groups:topics:read'], cid, 'guests');
});
it('should 404 if user is not found', (done) => {
request(`${nconf.get('url')}/user/doesnotexist/topics.rss`, (err, res) => {
assert.ifError(err);
assert.equal(res.statusCode, 404);
done();
});
it('should 404 if user is not found', async () => {
const { response } = await request.get(`${nconf.get('url')}/user/doesnotexist/topics.rss`, { validateStatus: null });
assert.equal(response.statusCode, 404);
});
it('should redirect if we do not have read privilege', (done) => {
privileges.categories.rescind(['groups:read'], cid, 'guests', (err) => {
assert.ifError(err);
request(`${nconf.get('url')}/category/${cid}.rss`, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body);
assert(body.includes('Login to your account'));
privileges.categories.give(['groups:read'], cid, 'guests', done);
});
});
it('should redirect if we do not have read privilege', async () => {
await privileges.categories.rescind(['groups:read'], cid, 'guests');
const { response, body } = await request.get(`${nconf.get('url')}/category/${cid}.rss`);
assert.equal(response.statusCode, 200);
assert(body);
assert(body.includes('Login to your account'));
await privileges.categories.give(['groups:read'], cid, 'guests');
});
describe('private feeds and tokens', () => {
@@ -131,69 +96,45 @@ describe('feeds', () => {
({ jar } = await helpers.loginUser('foo', 'barbar'));
});
it('should load feed if its not private', (done) => {
request(`${nconf.get('url')}/category/${cid}.rss`, { }, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body);
done();
});
it('should load feed if its not private', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/category/${cid}.rss`);
assert.equal(response.statusCode, 200);
assert(body);
});
it('should not allow access if uid or token is missing', (done) => {
privileges.categories.rescind(['groups:read'], cid, 'guests', (err) => {
assert.ifError(err);
async.parallel({
test1: function (next) {
request(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}`, { }, next);
},
test2: function (next) {
request(`${nconf.get('url')}/category/${cid}.rss?token=sometoken`, { }, next);
},
}, (err, results) => {
assert.ifError(err);
assert.equal(results.test1[0].statusCode, 200);
assert.equal(results.test2[0].statusCode, 200);
assert(results.test1[0].body.includes('Login to your account'));
assert(results.test2[0].body.includes('Login to your account'));
done();
});
});
it('should not allow access if uid or token is missing', async () => {
await privileges.categories.rescind(['groups:read'], cid, 'guests');
const [test1, test2] = await Promise.all([
request.get(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}`, { }),
request.get(`${nconf.get('url')}/category/${cid}.rss?token=sometoken`, { }),
]);
assert.equal(test1.response.statusCode, 200);
assert.equal(test2.response.statusCode, 200);
assert(test1.body.includes('Login to your account'));
assert(test2.body.includes('Login to your account'));
});
it('should not allow access if token is wrong', (done) => {
request(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=sometoken`, { }, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body.includes('Login to your account'));
done();
});
it('should not allow access if token is wrong', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=sometoken`);
assert.equal(response.statusCode, 200);
assert(body.includes('Login to your account'));
});
it('should allow access if token is correct', (done) => {
request(`${nconf.get('url')}/api/category/${cid}`, { jar: jar, json: true }, (err, res, body) => {
assert.ifError(err);
rssToken = body.rssFeedUrl.split('token')[1].slice(1);
request(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=${rssToken}`, { }, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body.startsWith('<?xml version="1.0"'));
done();
});
});
it('should allow access if token is correct', async () => {
const { body: body1 } = await request.get(`${nconf.get('url')}/api/category/${cid}`, { jar });
rssToken = body1.rssFeedUrl.split('token')[1].slice(1);
const { response, body: body2 } = await request.get(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=${rssToken}`);
assert.equal(response.statusCode, 200);
assert(body2.startsWith('<?xml version="1.0"'));
});
it('should not allow access if token is correct but has no privilege', (done) => {
privileges.categories.rescind(['groups:read'], cid, 'registered-users', (err) => {
assert.ifError(err);
request(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=${rssToken}`, { }, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body.includes('Login to your account'));
done();
});
});
it('should not allow access if token is correct but has no privilege', async () => {
await privileges.categories.rescind(['groups:read'], cid, 'registered-users');
const { response, body } = await request.get(`${nconf.get('url')}/category/${cid}.rss?uid=${fooUid}&token=${rssToken}`);
assert.equal(response.statusCode, 200);
assert(body.includes('Login to your account'));
});
});
});

View File

@@ -2,15 +2,13 @@
const assert = require('assert');
const nconf = require('nconf');
const async = require('async');
const request = require('request-promise-native');
const util = require('util');
const sleep = util.promisify(setTimeout);
const db = require('./mocks/databasemock');
const helpers = require('./helpers');
const request = require('../src/request');
const Flags = require('../src/flags');
const Categories = require('../src/categories');
const Topics = require('../src/topics');
@@ -243,13 +241,11 @@ describe('Flags', () => {
it('should show user history for admins', async () => {
await Groups.join('administrators', moderatorUid);
const flagData = await request({
uri: `${nconf.get('url')}/api/flags/1`,
const { body: flagData } = await request.get(`${nconf.get('url')}/api/flags/1`, {
jar,
headers: {
'x-csrf-token': csrfToken,
},
json: true,
});
assert(flagData.history);
@@ -260,13 +256,11 @@ describe('Flags', () => {
it('should show user history for global moderators', async () => {
await Groups.join('Global Moderators', moderatorUid);
const flagData = await request({
uri: `${nconf.get('url')}/api/flags/1`,
const { body: flagData } = await request.get(`${nconf.get('url')}/api/flags/1`, {
jar,
headers: {
'x-csrf-token': csrfToken,
},
json: true,
});
assert(flagData.history);
@@ -895,19 +889,16 @@ describe('Flags', () => {
describe('.create()', () => {
it('should create a flag with no errors', async () => {
await request({
method: 'post',
uri: `${nconf.get('url')}/api/v3/flags`,
await request.post(`${nconf.get('url')}/api/v3/flags`, {
jar,
headers: {
'x-csrf-token': csrfToken,
},
body: {
data: {
type: 'post',
id: pid,
reason: 'foobar',
},
json: true,
});
const exists = await Flags.exists('post', pid, 2);
@@ -921,22 +912,19 @@ describe('Flags', () => {
content: 'This is flaggable content',
});
const { response } = await request({
method: 'post',
uri: `${nconf.get('url')}/api/v3/flags`,
const { body } = await request.post(`${nconf.get('url')}/api/v3/flags`, {
jar,
headers: {
'x-csrf-token': csrfToken,
},
body: {
data: {
type: 'post',
id: postData.pid,
reason: '"<script>alert(\'ok\');</script>',
},
json: true,
});
const flagData = await Flags.get(response.flagId);
const flagData = await Flags.get(body.response.flagId);
assert.strictEqual(flagData.reports[0].value, '&quot;&lt;script&gt;alert(&#x27;ok&#x27;);&lt;&#x2F;script&gt;');
});
@@ -953,29 +941,21 @@ describe('Flags', () => {
});
const login = await helpers.loginUser('unprivileged', 'abcdef');
const jar3 = login.jar;
const config = await request({
url: `${nconf.get('url')}/api/config`,
json: true,
jar: jar3,
});
const csrfToken = config.csrf_token;
const { statusCode, body } = await request({
method: 'post',
uri: `${nconf.get('url')}/api/v3/flags`,
const csrfToken = await helpers.getCsrfToken(jar3);
const { response, body } = await request.post(`${nconf.get('url')}/api/v3/flags`, {
jar: jar3,
headers: {
'x-csrf-token': csrfToken,
},
body: {
data: {
type: 'post',
id: result.postData.pid,
reason: 'foobar',
},
json: true,
simple: false,
resolveWithFullResponse: true,
validateStatus: null,
});
assert.strictEqual(statusCode, 403);
assert.strictEqual(response.statusCode, 403);
// Handle dev mode test
delete body.stack;
@@ -992,20 +972,17 @@ describe('Flags', () => {
describe('.update()', () => {
it('should update a flag\'s properties', async () => {
const { response } = await request({
method: 'put',
uri: `${nconf.get('url')}/api/v3/flags/4`,
const { body } = await request.put(`${nconf.get('url')}/api/v3/flags/4`, {
jar,
headers: {
'x-csrf-token': csrfToken,
},
body: {
data: {
state: 'wip',
},
json: true,
});
const { history } = response;
const { history } = body.response;
assert(Array.isArray(history));
assert(history[0].fields.hasOwnProperty('state'));
assert.strictEqual('[[flags:state-wip]]', history[0].fields.state);
@@ -1014,14 +991,11 @@ describe('Flags', () => {
describe('.rescind()', () => {
it('should remove a flag\'s report', async () => {
const response = await request({
method: 'delete',
uri: `${nconf.get('url')}/api/v3/flags/4/report`,
const { response } = await request.del(`${nconf.get('url')}/api/v3/flags/4/report`, {
jar,
headers: {
'x-csrf-token': csrfToken,
},
resolveWithFullResponse: true,
});
assert.strictEqual(response.statusCode, 200);
@@ -1030,20 +1004,17 @@ describe('Flags', () => {
describe('.appendNote()', () => {
it('should append a note to the flag', async () => {
const { response } = await request({
method: 'post',
uri: `${nconf.get('url')}/api/v3/flags/4/notes`,
const { body } = await request.post(`${nconf.get('url')}/api/v3/flags/4/notes`, {
jar,
headers: {
'x-csrf-token': csrfToken,
},
body: {
data: {
note: 'lorem ipsum dolor sit amet',
datetime: 1626446956652,
},
json: true,
});
const { response } = body;
assert(response.hasOwnProperty('notes'));
assert(Array.isArray(response.notes));
assert.strictEqual('lorem ipsum dolor sit amet', response.notes[0].content);
@@ -1058,16 +1029,13 @@ describe('Flags', () => {
describe('.deleteNote()', () => {
it('should delete a note from a flag', async () => {
const { response } = await request({
method: 'delete',
uri: `${nconf.get('url')}/api/v3/flags/4/notes/1626446956652`,
const { body } = await request.del(`${nconf.get('url')}/api/v3/flags/4/notes/1626446956652`, {
jar,
headers: {
'x-csrf-token': csrfToken,
},
json: true,
});
const { response } = body;
assert(Array.isArray(response.history));
assert(Array.isArray(response.notes));
assert.strictEqual(response.notes.length, 0);
@@ -1106,68 +1074,45 @@ describe('Flags', () => {
});
({ flagId } = await Flags.create('post', postData.pid, flaggerUid, 'spam'));
const commonOpts = {
jar,
headers: {
'x-csrf-token': csrf_token,
},
validateStatus: null,
};
requests = new Set([
{
...commonOpts,
method: 'get',
uri: `${nconf.get('url')}/api/v3/flags/${flagId}`,
jar,
headers: {
'x-csrf-token': csrf_token,
},
json: true,
simple: false,
resolveWithFullResponse: true,
},
{
...commonOpts,
method: 'put',
uri: `${nconf.get('url')}/api/v3/flags/${flagId}`,
jar,
headers: {
'x-csrf-token': csrf_token,
},
body: {
data: {
state: 'wip',
},
json: true,
simple: false,
resolveWithFullResponse: true,
},
{
...commonOpts,
method: 'post',
uri: `${nconf.get('url')}/api/v3/flags/${flagId}/notes`,
jar,
headers: {
'x-csrf-token': csrf_token,
},
body: {
data: {
note: 'test note',
datetime: noteTime,
},
json: true,
simple: false,
resolveWithFullResponse: true,
},
{
...commonOpts,
method: 'delete',
uri: `${nconf.get('url')}/api/v3/flags/${flagId}/notes/${noteTime}`,
jar,
headers: {
'x-csrf-token': csrf_token,
},
json: true,
simple: false,
resolveWithFullResponse: true,
},
{
...commonOpts,
method: 'delete',
uri: `${nconf.get('url')}/api/v3/flags/${flagId}`,
jar,
headers: {
'x-csrf-token': csrf_token,
},
json: true,
simple: false,
resolveWithFullResponse: true,
},
]);
});
@@ -1179,7 +1124,8 @@ describe('Flags', () => {
delete opts.headers;
// eslint-disable-next-line no-await-in-loop
const { statusCode } = await request(opts);
const { response } = await request[opts.method](opts.uri, opts);
const { statusCode } = response;
assert(statusCode.toString().startsWith(4), `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`);
}
});
@@ -1187,7 +1133,8 @@ describe('Flags', () => {
it('should not allow access to privileged flag endpoints to regular users', async () => {
for (const opts of requests) {
// eslint-disable-next-line no-await-in-loop
const { statusCode } = await request(opts);
const { response } = await request[opts.method](opts.uri, opts);
const { statusCode } = response;
assert(statusCode.toString().startsWith(4), `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`);
}
});
@@ -1197,7 +1144,8 @@ describe('Flags', () => {
for (const opts of requests) {
// eslint-disable-next-line no-await-in-loop
const { statusCode } = await request(opts);
const { response } = await request[opts.method](opts.uri, opts);
const { statusCode } = response;
assert.strictEqual(statusCode, 200, `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`);
}
});
@@ -1207,7 +1155,8 @@ describe('Flags', () => {
for (const opts of requests) {
// eslint-disable-next-line no-await-in-loop
const { statusCode } = await request(opts);
const { response } = await request[opts.method](opts.uri, opts);
const { statusCode } = response;
assert.strictEqual(statusCode, 200, `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`);
}
});
@@ -1217,7 +1166,8 @@ describe('Flags', () => {
for (const opts of requests) {
// eslint-disable-next-line no-await-in-loop
const { statusCode } = await request(opts);
const { response } = await request[opts.method](opts.uri, opts);
const { statusCode } = response;
assert.strictEqual(statusCode, 200, `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`);
}
});
@@ -1231,7 +1181,8 @@ describe('Flags', () => {
for (const opts of requests) {
// eslint-disable-next-line no-await-in-loop
const { statusCode } = await request(opts);
const { response } = await request[opts.method](opts.uri, opts);
const { statusCode } = response;
assert(statusCode.toString().startsWith(4), `${opts.method.toUpperCase()} ${opts.uri} => ${statusCode}`);
}
});

View File

@@ -1,26 +1,22 @@
'use strict';
const request = require('request');
const requestAsync = require('request-promise-native');
const nconf = require('nconf');
const fs = require('fs');
const path = require('path');
const winston = require('winston');
const utils = require('../../src/utils');
const request = require('../../src/request');
const helpers = module.exports;
helpers.getCsrfToken = async (jar) => {
const { csrf_token: token } = await requestAsync({
url: `${nconf.get('url')}/api/config`,
json: true,
const { body } = await request.get(`${nconf.get('url')}/api/config`, {
jar,
});
return token;
return body.csrf_token;
};
helpers.request = async function (method, uri, options) {
helpers.request = async function (method, uri, options = {}) {
const ignoreMethods = ['GET', 'HEAD', 'OPTIONS'];
const lowercaseMethod = String(method).toLowerCase();
let csrf_token;
@@ -28,75 +24,46 @@ helpers.request = async function (method, uri, options) {
csrf_token = await helpers.getCsrfToken(options.jar);
}
return new Promise((resolve, reject) => {
options.headers = options.headers || {};
if (csrf_token) {
options.headers['x-csrf-token'] = csrf_token;
}
request[lowercaseMethod](`${nconf.get('url')}${uri}`, options, (err, res, body) => {
if (err) reject(err);
else resolve({ res, body });
});
});
options.headers = options.headers || {};
if (csrf_token) {
options.headers['x-csrf-token'] = csrf_token;
}
options.validateStatus = null;
const { response, body } = await request[lowercaseMethod](`${nconf.get('url')}${uri}`, options);
return { response, body };
};
helpers.loginUser = async (username, password, payload = {}) => {
const jar = request.jar();
const form = { username, password, ...payload };
const data = { username, password, ...payload };
const { statusCode, body: configBody } = await requestAsync({
url: `${nconf.get('url')}/api/config`,
json: true,
const csrf_token = await helpers.getCsrfToken(jar);
const { response, body } = await request.post(`${nconf.get('url')}/login`, {
data,
jar: jar,
followRedirect: false,
simple: false,
resolveWithFullResponse: true,
});
if (statusCode !== 200) {
throw new Error('[[error:invalid-response]]');
}
const { csrf_token } = configBody;
const res = await requestAsync.post(`${nconf.get('url')}/login`, {
form,
json: true,
jar: jar,
followRedirect: false,
simple: false,
resolveWithFullResponse: true,
validateStatus: () => true,
headers: {
'x-csrf-token': csrf_token,
},
});
return { jar, res, body: res.body, csrf_token: csrf_token };
return { jar, response, body, csrf_token };
};
helpers.logoutUser = function (jar, callback) {
request({
url: `${nconf.get('url')}/api/config`,
json: true,
jar: jar,
}, (err, response, body) => {
if (err) {
return callback(err, response, body);
}
request.post(`${nconf.get('url')}/logout`, {
form: {},
json: true,
jar: jar,
headers: {
'x-csrf-token': body.csrf_token,
},
}, (err, response, body) => {
callback(err, response, body);
});
helpers.logoutUser = async function (jar) {
const csrf_token = await helpers.getCsrfToken(jar);
const { response, body } = await request.post(`${nconf.get('url')}/logout`, {
data: {},
jar,
validateStatus: () => true,
headers: {
'x-csrf-token': csrf_token,
},
});
return { response, body };
};
helpers.connectSocketIO = function (res, csrf_token, callback) {
helpers.connectSocketIO = function (res, csrf_token) {
const io = require('socket.io-client');
let cookies = res.headers['set-cookie'];
cookies = cookies.filter(c => /express.sid=[^;]+;/.test(c));
@@ -111,73 +78,63 @@ helpers.connectSocketIO = function (res, csrf_token, callback) {
_csrf: csrf_token,
},
});
let error;
socket.on('connect', () => {
if (error) {
return;
}
callback(null, socket);
});
return new Promise((resolve, reject) => {
let error;
socket.on('connect', () => {
if (error) {
return;
}
resolve(socket);
});
socket.on('error', (err) => {
error = err;
console.log('socket.io error', err.stack);
callback(err);
socket.on('error', (err) => {
error = err;
console.log('socket.io error', err.stack);
reject(err);
});
});
};
helpers.uploadFile = function (uploadEndPoint, filePath, body, jar, csrf_token, callback) {
let formData = {
files: [
fs.createReadStream(filePath),
],
};
formData = utils.merge(formData, body);
request.post({
url: uploadEndPoint,
formData: formData,
json: true,
helpers.uploadFile = async function (uploadEndPoint, filePath, data, jar, csrf_token) {
const FormData = require('form-data');
const form = new FormData();
form.append('files', fs.createReadStream(filePath), path.basename(filePath));
if (data && data.params) {
form.append('params', data.params);
}
const { response, body } = await request.post(uploadEndPoint, {
data: form,
jar: jar,
validateStatus: null,
headers: {
'x-csrf-token': csrf_token,
...form.getHeaders(),
},
});
if (response.status !== 200) {
winston.error(JSON.stringify(data));
}
return { response, body };
};
helpers.registerUser = async function (data) {
const jar = request.jar();
const csrf_token = await helpers.getCsrfToken(jar);
if (!data.hasOwnProperty('password-confirm')) {
data['password-confirm'] = data.password;
}
const { response, body } = await request.post(`${nconf.get('url')}/register`, {
data,
jar,
validateStatus: () => true,
headers: {
'x-csrf-token': csrf_token,
},
}, (err, res, body) => {
if (err) {
return callback(err);
}
if (res.statusCode !== 200) {
winston.error(JSON.stringify(body));
}
callback(null, res, body);
});
};
helpers.registerUser = function (data, callback) {
const jar = request.jar();
request({
url: `${nconf.get('url')}/api/config`,
json: true,
jar: jar,
}, (err, response, body) => {
if (err) {
return callback(err);
}
if (!data.hasOwnProperty('password-confirm')) {
data['password-confirm'] = data.password;
}
request.post(`${nconf.get('url')}/register`, {
form: data,
json: true,
jar: jar,
headers: {
'x-csrf-token': body.csrf_token,
},
}, (err, response, body) => {
callback(err, jar, response, body);
});
});
return { jar, response, body };
};
// http://stackoverflow.com/a/14387791/583363
@@ -205,37 +162,28 @@ helpers.copyFile = function (source, target, callback) {
}
};
helpers.invite = async function (body, uid, jar, csrf_token) {
console.log('making call');
const res = await requestAsync.post(`${nconf.get('url')}/api/v3/users/${uid}/invites`, {
helpers.invite = async function (data, uid, jar, csrf_token) {
return await request.post(`${nconf.get('url')}/api/v3/users/${uid}/invites`, {
jar: jar,
// using "form" since client "api" module make requests with "application/x-www-form-urlencoded" content-type
form: body,
data: data,
headers: {
'x-csrf-token': csrf_token,
},
simple: false,
resolveWithFullResponse: true,
validateStatus: null,
});
console.log(res.statusCode, res.body);
res.body = JSON.parse(res.body);
return { res, body };
};
helpers.createFolder = function (path, folderName, jar, csrf_token) {
return requestAsync.put(`${nconf.get('url')}/api/v3/files/folder`, {
helpers.createFolder = async function (path, folderName, jar, csrf_token) {
return await request.put(`${nconf.get('url')}/api/v3/files/folder`, {
jar,
body: {
data: {
path,
folderName,
},
json: true,
headers: {
'x-csrf-token': csrf_token,
},
simple: false,
resolveWithFullResponse: true,
validateStatus: null,
});
};

View File

@@ -2,45 +2,34 @@
const assert = require('assert');
const nconf = require('nconf');
const request = require('request');
const db = require('./mocks/databasemock');
const meta = require('../src/meta');
const request = require('../src/request');
describe('Language detection', () => {
it('should detect the language for a guest', (done) => {
meta.configs.set('autoDetectLang', 1, (err) => {
assert.ifError(err);
request(`${nconf.get('url')}/api/config`, {
headers: {
'Accept-Language': 'de-DE,de;q=0.5',
},
json: true,
}, (err, res, body) => {
assert.ifError(err);
assert.ok(body);
it('should detect the language for a guest', async () => {
await meta.configs.set('autoDetectLang', 1);
assert.strictEqual(body.userLang, 'de');
done();
});
const { body } = await request.get(`${nconf.get('url')}/api/config`, {
headers: {
'Accept-Language': 'de-DE,de;q=0.5',
},
});
assert.ok(body);
assert.strictEqual(body.userLang, 'de');
});
it('should do nothing when disabled', (done) => {
meta.configs.set('autoDetectLang', 0, (err) => {
assert.ifError(err);
request(`${nconf.get('url')}/api/config`, {
headers: {
'Accept-Language': 'de-DE,de;q=0.5',
},
json: true,
}, (err, res, body) => {
assert.ifError(err);
assert.ok(body);
it('should do nothing when disabled', async () => {
await meta.configs.set('autoDetectLang', 0);
assert.strictEqual(body.userLang, 'en-GB');
done();
});
const { body } = await request.get(`${nconf.get('url')}/api/config`, {
headers: {
'Accept-Language': 'de-DE,de;q=0.5',
},
});
assert.ok(body);
assert.strictEqual(body.userLang, 'en-GB');
});
});

View File

@@ -1,7 +1,7 @@
'use strict';
const assert = require('assert');
const request = require('request-promise-native');
const nconf = require('nconf');
const util = require('util');
@@ -14,7 +14,7 @@ const Groups = require('../src/groups');
const Messaging = require('../src/messaging');
const api = require('../src/api');
const helpers = require('./helpers');
const socketModules = require('../src/socket.io/modules');
const request = require('../src/request');
const utils = require('../src/utils');
const translator = require('../src/translator');
@@ -33,12 +33,9 @@ describe('Messaging Library', () => {
const callv3API = async (method, path, body, user) => {
const options = {
method,
body,
json: true,
data: body,
jar: mocks.users[user].jar,
resolveWithFullResponse: true,
simple: false,
validateStatus: null,
};
if (method !== 'get') {
@@ -47,7 +44,7 @@ describe('Messaging Library', () => {
};
}
return request(`${nconf.get('url')}/api/v3${path}`, options);
return request[method](`${nconf.get('url')}/api/v3${path}`, options);
};
before(async () => {
@@ -162,11 +159,11 @@ describe('Messaging Library', () => {
uids: [mocks.users.baz.uid],
}, 'foo');
const { statusCode, body } = await callv3API('post', `/chats`, {
const { response, body } = await callv3API('post', `/chats`, {
uids: [mocks.users.baz.uid],
}, 'foo');
assert.equal(statusCode, 400);
assert.equal(response.statusCode, 400);
assert.equal(body.status.code, 'bad-request');
assert.equal(body.status.message, await translator.translate('[[error:too-many-messages]]'));
meta.config.chatMessageDelay = oldValue;
@@ -190,20 +187,20 @@ describe('Messaging Library', () => {
assert.strictEqual(messages[0].system, 1);
assert.strictEqual(messages[0].content, 'user-join');
const { statusCode, body: body2 } = await callv3API('put', `/chats/${roomId}/messages/${messages[0].messageId}`, {
const { response, body: body2 } = await callv3API('put', `/chats/${roomId}/messages/${messages[0].messageId}`, {
message: 'test',
}, 'foo');
assert.strictEqual(statusCode, 400);
assert.strictEqual(response.statusCode, 400);
assert.equal(body2.status.message, await translator.translate('[[error:cant-edit-chat-message]]'));
});
it('should fail to add user to room with invalid data', async () => {
let { statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, {}, 'foo');
assert.strictEqual(statusCode, 400);
let { response, body } = await callv3API('post', `/chats/${roomId}/users`, {}, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:required-parameters-missing, uids]]'));
({ statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [null] }, 'foo'));
assert.strictEqual(statusCode, 400);
({ response, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [null] }, 'foo'));
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]'));
});
@@ -220,38 +217,38 @@ describe('Messaging Library', () => {
});
it('should throw error if user is not in room', async () => {
const { statusCode, body } = await callv3API('get', `/chats/${roomId}/users`, {}, 'bar');
assert.strictEqual(statusCode, 403);
const { response, body } = await callv3API('get', `/chats/${roomId}/users`, {}, 'bar');
assert.strictEqual(response.statusCode, 403);
assert.equal(body.status.message, await translator.translate('[[error:no-privileges]]'));
});
it('should fail to add users to room if max is reached', async () => {
meta.config.maximumUsersInChatRoom = 2;
const { statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [mocks.users.bar.uid] }, 'foo');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [mocks.users.bar.uid] }, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.equal(body.status.message, await translator.translate('[[error:cant-add-more-users-to-chat-room]]'));
meta.config.maximumUsersInChatRoom = 0;
});
it('should fail to add users to room if user does not exist', async () => {
const { statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [98237498234] }, 'foo');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [98237498234] }, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]'));
});
it('should fail to add self to room', async () => {
const { statusCode, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [mocks.users.foo.uid] }, 'foo');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('post', `/chats/${roomId}/users`, { uids: [mocks.users.foo.uid] }, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:cant-chat-with-yourself]]'));
});
it('should fail to leave room with invalid data', async () => {
let { statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, {}, 'foo');
assert.strictEqual(statusCode, 400);
let { response, body } = await callv3API('delete', `/chats/${roomId}/users`, {}, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:required-parameters-missing, uids]]'));
({ statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [98237423] }, 'foo'));
assert.strictEqual(statusCode, 400);
({ response, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [98237423] }, 'foo'));
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]'));
});
@@ -303,11 +300,9 @@ describe('Messaging Library', () => {
const { jar: senderJar, csrf_token: senderCsrf } = await helpers.loginUser('deleted_chat_user', 'barbar');
const receiver = await User.create({ username: 'receiver' });
const { response } = await request(`${nconf.get('url')}/api/v3/chats`, {
method: 'post',
json: true,
const { body } = await request.post(`${nconf.get('url')}/api/v3/chats`, {
jar: senderJar,
body: {
data: {
uids: [receiver],
},
headers: {
@@ -315,31 +310,31 @@ describe('Messaging Library', () => {
},
});
await User.deleteAccount(sender);
assert(await Messaging.isRoomOwner(receiver, response.roomId));
assert(await Messaging.isRoomOwner(receiver, body.response.roomId));
});
it('should fail to remove user from room', async () => {
let { statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, {}, 'foo');
assert.strictEqual(statusCode, 400);
let { response, body } = await callv3API('delete', `/chats/${roomId}/users`, {}, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:required-parameters-missing, uids]]'));
({ statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [null] }, 'foo'));
assert.strictEqual(statusCode, 400);
({ response, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [null] }, 'foo'));
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]'));
});
it('should fail to remove user from room if user does not exist', async () => {
const { statusCode, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [99] }, 'foo');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('delete', `/chats/${roomId}/users`, { uids: [99] }, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:no-user]]'));
});
it('should remove user from room', async () => {
const { statusCode, body } = await callv3API('post', `/chats`, {
const { response, body } = await callv3API('post', `/chats`, {
uids: [mocks.users.herp.uid],
}, 'foo');
const { roomId } = body.response;
assert.strictEqual(statusCode, 200);
assert.strictEqual(response.statusCode, 200);
let isInRoom = await Messaging.isUserInRoom(mocks.users.herp.uid, roomId);
assert(isInRoom);
@@ -488,8 +483,8 @@ describe('Messaging Library', () => {
});
it('should rename room', async () => {
const { statusCode } = await callv3API('put', `/chats/${roomId}`, { name: 'new room name' }, 'foo');
assert.strictEqual(statusCode, 200);
const { response } = await callv3API('put', `/chats/${roomId}`, { name: 'new room name' }, 'foo');
assert.strictEqual(response.statusCode, 200);
});
it('should send a room-rename system message when a room is renamed', async () => {
@@ -638,46 +633,46 @@ describe('Messaging Library', () => {
});
it('should fail to edit message with invalid data', async () => {
let { statusCode, body } = await callv3API('put', `/chats/1/messages/10000`, { message: 'foo' }, 'foo');
assert.strictEqual(statusCode, 400);
let { response, body } = await callv3API('put', `/chats/1/messages/10000`, { message: 'foo' }, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:invalid-mid]]'));
({ statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, {}, 'foo'));
assert.strictEqual(statusCode, 400);
({ response, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, {}, 'foo'));
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:invalid-chat-message]]'));
});
it('should fail to edit message if new content is empty string', async () => {
const { statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: ' ' }, 'foo');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: ' ' }, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:invalid-chat-message]]'));
});
it('should fail to edit message if not own message', async () => {
const { statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: 'message edited' }, 'herp');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: 'message edited' }, 'herp');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:cant-edit-chat-message]]'));
});
it('should fail to edit message if message not in room', async () => {
const { statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/1014`, { message: 'message edited' }, 'herp');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('put', `/chats/${roomId}/messages/1014`, { message: 'message edited' }, 'herp');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, await translator.translate('[[error:invalid-mid]]'));
});
it('should edit message', async () => {
let { statusCode, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: 'message edited' }, 'foo');
assert.strictEqual(statusCode, 200);
let { response, body } = await callv3API('put', `/chats/${roomId}/messages/${mid}`, { message: 'message edited' }, 'foo');
assert.strictEqual(response.statusCode, 200);
assert.strictEqual(body.response.content, 'message edited');
({ statusCode, body } = await callv3API('get', `/chats/${roomId}/messages/${mid}`, {}, 'foo'));
assert.strictEqual(statusCode, 200);
({ response, body } = await callv3API('get', `/chats/${roomId}/messages/${mid}`, {}, 'foo'));
assert.strictEqual(response.statusCode, 200);
assert.strictEqual(body.response.content, 'message edited');
});
it('should fail to delete message if not owner', async () => {
const { statusCode, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid}`, {}, 'herp');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid}`, {}, 'herp');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, 'You are not allowed to delete this message');
});
@@ -716,8 +711,8 @@ describe('Messaging Library', () => {
});
it('should error out if a message is deleted again', async () => {
const { statusCode, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid}`, {}, 'foo');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid}`, {}, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, 'This chat message has already been deleted.');
});
@@ -728,8 +723,8 @@ describe('Messaging Library', () => {
});
it('should error out if a message is restored again', async () => {
const { statusCode, body } = await callv3API('post', `/chats/${roomId}/messages/${mid}`, {}, 'foo');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('post', `/chats/${roomId}/messages/${mid}`, {}, 'foo');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, 'This chat message has already been restored.');
});
@@ -743,8 +738,8 @@ describe('Messaging Library', () => {
});
it('should error out for regular users', async () => {
const { statusCode, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid2}`, {}, 'baz');
assert.strictEqual(statusCode, 400);
const { response, body } = await callv3API('delete', `/chats/${roomId}/messages/${mid2}`, {}, 'baz');
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, 'chat-message-editing-disabled');
});
@@ -767,9 +762,8 @@ describe('Messaging Library', () => {
describe('controller', () => {
it('should 404 if chat is disabled', async () => {
meta.config.disableChat = 1;
const response = await request(`${nconf.get('url')}/user/baz/chats`, {
resolveWithFullResponse: true,
simple: false,
const { response } = await request.get(`${nconf.get('url')}/user/baz/chats`, {
validateStatus: null,
});
assert.equal(response.statusCode, 404);
@@ -777,21 +771,18 @@ describe('Messaging Library', () => {
it('should 401 for guest with not-authorised status code', async () => {
meta.config.disableChat = 0;
const response = await request(`${nconf.get('url')}/api/user/baz/chats`, {
const { response, body } = await request.get(`${nconf.get('url')}/api/user/baz/chats`, {
resolveWithFullResponse: true,
simple: false,
json: true,
validateStatus: null,
});
const { body } = response;
assert.equal(response.statusCode, 401);
assert.equal(body.status.code, 'not-authorised');
});
it('should 404 for non-existent user', async () => {
const response = await request(`${nconf.get('url')}/user/doesntexist/chats`, {
resolveWithFullResponse: true,
simple: false,
const { response } = await request.get(`${nconf.get('url')}/user/doesntexist/chats`, {
validateStatus: null,
});
assert.equal(response.statusCode, 404);
@@ -805,13 +796,10 @@ describe('Messaging Library', () => {
});
it('should return chats page data', async () => {
const response = await request(`${nconf.get('url')}/api/user/herp/chats`, {
resolveWithFullResponse: true,
simple: false,
json: true,
const { response, body } = await request.get(`${nconf.get('url')}/api/user/herp/chats`, {
validateStatus: null,
jar,
});
const { body } = response;
assert.equal(response.statusCode, 200);
assert(Array.isArray(body.rooms));
@@ -820,13 +808,10 @@ describe('Messaging Library', () => {
});
it('should return room data', async () => {
const response = await request(`${nconf.get('url')}/api/user/herp/chats/${roomId}`, {
resolveWithFullResponse: true,
simple: false,
json: true,
const { response, body } = await request.get(`${nconf.get('url')}/api/user/herp/chats/${roomId}`, {
validateStatus: null,
jar,
});
const { body } = response;
assert.equal(response.statusCode, 200);
assert.equal(body.roomId, roomId);
@@ -834,25 +819,20 @@ describe('Messaging Library', () => {
});
it('should redirect to chats page', async () => {
const res = await request(`${nconf.get('url')}/api/chats`, {
resolveWithFullResponse: true,
simple: false,
const { response, body } = await request.get(`${nconf.get('url')}/api/chats`, {
validateStatus: null,
jar,
json: true,
});
const { body } = res;
assert.equal(res.statusCode, 200);
assert.equal(res.headers['x-redirect'], '/user/herp/chats');
assert.equal(response.statusCode, 200);
assert.equal(response.headers['x-redirect'], '/user/herp/chats');
assert.equal(body, '/user/herp/chats');
});
it('should return 404 if user is not in room', async () => {
const data = await helpers.loginUser('baz', 'quuxquux');
const response = await request(`${nconf.get('url')}/api/user/baz/chats/${roomId}`, {
resolveWithFullResponse: true,
simple: false,
json: true,
const { response } = await request.get(`${nconf.get('url')}/api/user/baz/chats/${roomId}`, {
validateStatus: null,
jar: data.jar,
});

View File

@@ -2,13 +2,14 @@
const assert = require('assert');
const async = require('async');
const request = require('request');
const nconf = require('nconf');
const db = require('./mocks/databasemock');
const meta = require('../src/meta');
const User = require('../src/user');
const Groups = require('../src/groups');
const request = require('../src/request');
describe('meta', () => {
let fooUid;
@@ -489,117 +490,97 @@ describe('meta', () => {
});
describe('Access-Control-Allow-Origin', () => {
it('Access-Control-Allow-Origin header should be empty', (done) => {
it('Access-Control-Allow-Origin header should be empty', async () => {
const jar = request.jar();
request.get(`${nconf.get('url')}/api/search?term=bug`, {
form: {},
json: true,
const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, {
data: {},
validateStatus: null,
jar: jar,
}, (err, response, body) => {
assert.ifError(err);
assert.equal(response.headers['access-control-allow-origin'], undefined);
done();
});
assert.equal(response.headers['access-control-allow-origin'], undefined);
});
it('should set proper Access-Control-Allow-Origin header', (done) => {
it('should set proper Access-Control-Allow-Origin header', async () => {
const jar = request.jar();
const oldValue = meta.config['access-control-allow-origin'];
meta.config['access-control-allow-origin'] = 'test.com, mydomain.com';
request.get(`${nconf.get('url')}/api/search?term=bug`, {
form: {
},
json: true,
const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, {
data: { },
jar: jar,
headers: {
origin: 'mydomain.com',
},
}, (err, response, body) => {
assert.ifError(err);
assert.equal(response.headers['access-control-allow-origin'], 'mydomain.com');
meta.config['access-control-allow-origin'] = oldValue;
done(err);
validateStatus: null,
});
assert.equal(response.headers['access-control-allow-origin'], 'mydomain.com');
meta.config['access-control-allow-origin'] = oldValue;
});
it('Access-Control-Allow-Origin header should be empty if origin does not match', (done) => {
it('Access-Control-Allow-Origin header should be empty if origin does not match', async () => {
const jar = request.jar();
const oldValue = meta.config['access-control-allow-origin'];
meta.config['access-control-allow-origin'] = 'test.com, mydomain.com';
request.get(`${nconf.get('url')}/api/search?term=bug`, {
form: {
},
json: true,
const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, {
data: {},
jar: jar,
headers: {
origin: 'notallowed.com',
},
}, (err, response, body) => {
assert.ifError(err);
assert.equal(response.headers['access-control-allow-origin'], undefined);
meta.config['access-control-allow-origin'] = oldValue;
done(err);
validateStatus: null,
});
assert.equal(response.headers['access-control-allow-origin'], undefined);
meta.config['access-control-allow-origin'] = oldValue;
});
it('should set proper Access-Control-Allow-Origin header', (done) => {
it('should set proper Access-Control-Allow-Origin header', async () => {
const jar = request.jar();
const oldValue = meta.config['access-control-allow-origin-regex'];
meta.config['access-control-allow-origin-regex'] = 'match\\.this\\..+\\.domain.com, mydomain\\.com';
request.get(`${nconf.get('url')}/api/search?term=bug`, {
form: {
},
json: true,
const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, {
data: {},
jar: jar,
headers: {
origin: 'match.this.anything123.domain.com',
},
}, (err, response, body) => {
assert.ifError(err);
assert.equal(response.headers['access-control-allow-origin'], 'match.this.anything123.domain.com');
meta.config['access-control-allow-origin-regex'] = oldValue;
done(err);
validateStatus: null,
});
assert.equal(response.headers['access-control-allow-origin'], 'match.this.anything123.domain.com');
meta.config['access-control-allow-origin-regex'] = oldValue;
});
it('Access-Control-Allow-Origin header should be empty if origin does not match', (done) => {
it('Access-Control-Allow-Origin header should be empty if origin does not match', async () => {
const jar = request.jar();
const oldValue = meta.config['access-control-allow-origin-regex'];
meta.config['access-control-allow-origin-regex'] = 'match\\.this\\..+\\.domain.com, mydomain\\.com';
request.get(`${nconf.get('url')}/api/search?term=bug`, {
form: {
},
json: true,
const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, {
data: {},
jar: jar,
headers: {
origin: 'notallowed.com',
},
}, (err, response, body) => {
assert.ifError(err);
assert.equal(response.headers['access-control-allow-origin'], undefined);
meta.config['access-control-allow-origin-regex'] = oldValue;
done(err);
validateStatus: null,
});
assert.equal(response.headers['access-control-allow-origin'], undefined);
meta.config['access-control-allow-origin-regex'] = oldValue;
});
it('should not error with invalid regexp', (done) => {
it('should not error with invalid regexp', async () => {
const jar = request.jar();
const oldValue = meta.config['access-control-allow-origin-regex'];
meta.config['access-control-allow-origin-regex'] = '[match\\.this\\..+\\.domain.com, mydomain\\.com';
request.get(`${nconf.get('url')}/api/search?term=bug`, {
form: {
},
json: true,
const { response } = await request.get(`${nconf.get('url')}/api/search?term=bug`, {
data: { },
jar: jar,
headers: {
origin: 'mydomain.com',
},
}, (err, response, body) => {
assert.ifError(err);
assert.equal(response.headers['access-control-allow-origin'], 'mydomain.com');
meta.config['access-control-allow-origin-regex'] = oldValue;
done(err);
validateStatus: null,
});
assert.equal(response.headers['access-control-allow-origin'], 'mydomain.com');
meta.config['access-control-allow-origin-regex'] = oldValue;
});
});

View File

@@ -2,13 +2,13 @@
const assert = require('assert');
const nconf = require('nconf');
const request = require('request-promise-native');
const db = require('./mocks/databasemock');
const user = require('../src/user');
const groups = require('../src/groups');
const utils = require('../src/utils');
const request = require('../src/request');
const helpers = require('./helpers');
describe('Middlewares', () => {
@@ -116,81 +116,77 @@ describe('Middlewares', () => {
});
it('should be absent on non-existent routes, for guests', async () => {
const res = await request(`${nconf.get('url')}/${utils.generateUUID()}`, {
simple: false,
resolveWithFullResponse: true,
const { response } = await request.get(`${nconf.get('url')}/${utils.generateUUID()}`, {
validateStatus: null,
});
assert.strictEqual(res.statusCode, 404);
assert(!Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(response.statusCode, 404);
assert(!Object.keys(response.headers).includes('cache-control'));
});
it('should be set to "private" on non-existent routes, for logged in users', async () => {
const res = await request(`${nconf.get('url')}/${utils.generateUUID()}`, {
simple: false,
resolveWithFullResponse: true,
const { response } = await request.get(`${nconf.get('url')}/${utils.generateUUID()}`, {
validateStatus: null,
jar,
headers: {
accept: 'text/html',
},
});
assert.strictEqual(res.statusCode, 404);
assert(Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(res.headers['cache-control'], 'private');
assert.strictEqual(response.statusCode, 404);
assert(Object.keys(response.headers).includes('cache-control'));
assert.strictEqual(response.headers['cache-control'], 'private');
});
it('should be absent on regular routes, for guests', async () => {
const res = await request(nconf.get('url'), {
simple: false,
resolveWithFullResponse: true,
const { response } = await request.get(nconf.get('url'), {
validateStatus: null,
});
assert.strictEqual(res.statusCode, 200);
assert(!Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(response.statusCode, 200);
assert(!Object.keys(response.headers).includes('cache-control'));
});
it('should be absent on api routes, for guests', async () => {
const res = await request(`${nconf.get('url')}/api`, {
simple: false,
resolveWithFullResponse: true,
const { response } = await request.get(`${nconf.get('url')}/api`, {
validateStatus: null,
});
assert.strictEqual(res.statusCode, 200);
assert(!Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(response.statusCode, 200);
assert(!Object.keys(response.headers).includes('cache-control'));
});
it('should be set to "private" on regular routes, for logged-in users', async () => {
const res = await request(nconf.get('url'), {
simple: false,
resolveWithFullResponse: true,
const { response } = await request.get(nconf.get('url'), {
validateStatus: null,
jar,
});
assert.strictEqual(res.statusCode, 200);
assert(Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(res.headers['cache-control'], 'private');
assert.strictEqual(response.statusCode, 200);
assert(Object.keys(response.headers).includes('cache-control'));
assert.strictEqual(response.headers['cache-control'], 'private');
});
it('should be set to "private" on api routes, for logged-in users', async () => {
const res = await request(`${nconf.get('url')}/api`, {
simple: false,
resolveWithFullResponse: true,
const { response } = await request.get(`${nconf.get('url')}/api`, {
validateStatus: null,
jar,
});
assert.strictEqual(res.statusCode, 200);
assert(Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(res.headers['cache-control'], 'private');
assert.strictEqual(response.statusCode, 200);
assert(Object.keys(response.headers).includes('cache-control'));
assert.strictEqual(response.headers['cache-control'], 'private');
});
it('should be set to "private" on apiv3 routes, for logged-in users', async () => {
const res = await request(`${nconf.get('url')}/api/v3/users/${uid}`, {
simple: false,
resolveWithFullResponse: true,
const { response } = await request.get(`${nconf.get('url')}/api/v3/users/${uid}`, {
validateStatus: null,
jar,
});
assert.strictEqual(res.statusCode, 200);
assert(Object.keys(res.headers).includes('cache-control'));
assert.strictEqual(res.headers['cache-control'], 'private');
assert.strictEqual(response.statusCode, 200);
assert(Object.keys(response.headers).includes('cache-control'));
assert.strictEqual(response.headers['cache-control'], 'private');
});
});
});

View File

@@ -3,11 +3,12 @@
const assert = require('assert');
const path = require('path');
const nconf = require('nconf');
const request = require('request');
const fs = require('fs');
const db = require('./mocks/databasemock');
const plugins = require('../src/plugins');
const request = require('../src/request');
describe('Plugins', () => {
it('should load plugin data', (done) => {
@@ -290,33 +291,24 @@ describe('Plugins', () => {
});
describe('static assets', () => {
it('should 404 if resource does not exist', (done) => {
request.get(`${nconf.get('url')}/plugins/doesnotexist/should404.tpl`, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 404);
assert(body);
done();
});
it('should 404 if resource does not exist', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/plugins/doesnotexist/should404.tpl`, { validateStatus: null });
assert.equal(response.statusCode, 404);
assert(body);
});
it('should 404 if resource does not exist', (done) => {
it('should 404 if resource does not exist', async () => {
const url = `${nconf.get('url')}/plugins/nodebb-plugin-dbsearch/dbsearch/templates/admin/plugins/should404.tpl`;
request.get(url, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 404);
assert(body);
done();
});
const { response, body } = await request.get(url, { validateStatus: null });
assert.equal(response.statusCode, 404);
assert(body);
});
it('should get resource', (done) => {
it('should get resource', async () => {
const url = `${nconf.get('url')}/assets/templates/admin/plugins/dbsearch.tpl`;
request.get(url, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body);
done();
});
const { response, body } = await request.get(url);
assert.equal(response.statusCode, 200);
assert(body);
});
});
@@ -371,7 +363,6 @@ describe('Plugins', () => {
assert.ifError(err);
assert(Array.isArray(data));
data.forEach((pluginData) => {
console.log(pluginData);
assert(activePlugins.includes(pluginData));
});
done();

View File

@@ -3,7 +3,7 @@
const assert = require('assert');
const async = require('async');
const request = require('request-promise-native');
const nconf = require('nconf');
const path = require('path');
const util = require('util');
@@ -24,6 +24,7 @@ const meta = require('../src/meta');
const file = require('../src/file');
const helpers = require('./helpers');
const utils = require('../src/utils');
const request = require('../src/request');
describe('Post\'s', () => {
let voterUid;
@@ -78,7 +79,7 @@ describe('Post\'s', () => {
});
it('should update category teaser properly', async () => {
const getCategoriesAsync = async () => await request(`${nconf.get('url')}/api/categories`, { json: true });
const getCategoriesAsync = async () => (await request.get(`${nconf.get('url')}/api/categories`, { })).body;
const postResult = await topics.post({ uid: globalModUid, cid: cid, title: 'topic title', content: '123456789' });
let data = await getCategoriesAsync();
@@ -372,24 +373,14 @@ describe('Post\'s', () => {
assert.strictEqual(isDeleted, 1);
});
it('should not see post content if global mod does not have posts:view_deleted privilege', (done) => {
async.waterfall([
function (next) {
user.create({ username: 'global mod', password: '123456' }, next);
},
function (uid, next) {
groups.join('Global Moderators', uid, next);
},
function (next) {
privileges.categories.rescind(['groups:posts:view_deleted'], cid, 'Global Moderators', next);
},
async () => {
const { jar } = await helpers.loginUser('global mod', '123456');
const { posts } = await request(`${nconf.get('url')}/api/topic/${tid}`, { jar, json: true });
assert.equal(posts[1].content, '[[topic:post-is-deleted]]');
await privileges.categories.give(['groups:posts:view_deleted'], cid, 'Global Moderators');
},
], done);
it('should not see post content if global mod does not have posts:view_deleted privilege', async () => {
const uid = await user.create({ username: 'global mod', password: '123456' });
await groups.join('Global Moderators', uid);
await privileges.categories.rescind(['groups:posts:view_deleted'], cid, 'Global Moderators');
const { jar } = await helpers.loginUser('global mod', '123456');
const { body } = await request.get(`${nconf.get('url')}/api/topic/${tid}`, { jar });
assert.equal(body.posts[1].content, '[[topic:post-is-deleted]]');
await privileges.categories.give(['groups:posts:view_deleted'], cid, 'Global Moderators');
});
it('should restore a post', async () => {
@@ -1013,7 +1004,8 @@ describe('Post\'s', () => {
it('should load queued posts', async () => {
({ jar } = await helpers.loginUser('globalmod', 'globalmodpwd'));
const { posts } = await request(`${nconf.get('url')}/api/post-queue`, { jar: jar, json: true });
const { body } = await request.get(`${nconf.get('url')}/api/post-queue`, { jar });
const { posts } = body;
assert.equal(posts[0].type, 'topic');
assert.equal(posts[0].data.content, 'queued topic content');
assert.equal(posts[1].type, 'reply');
@@ -1029,21 +1021,24 @@ describe('Post\'s', () => {
it('should edit post in queue', async () => {
await socketPosts.editQueuedContent({ uid: globalModUid }, { id: queueId, content: 'newContent' });
const { posts } = await request(`${nconf.get('url')}/api/post-queue`, { jar: jar, json: true });
const { body } = await request.get(`${nconf.get('url')}/api/post-queue`, { jar });
const { posts } = body;
assert.equal(posts[1].type, 'reply');
assert.equal(posts[1].data.content, 'newContent');
});
it('should edit topic title in queue', async () => {
await socketPosts.editQueuedContent({ uid: globalModUid }, { id: topicQueueId, title: 'new topic title' });
const { posts } = await request(`${nconf.get('url')}/api/post-queue`, { jar: jar, json: true });
const { body } = await request.get(`${nconf.get('url')}/api/post-queue`, { jar });
const { posts } = body;
assert.equal(posts[0].type, 'topic');
assert.equal(posts[0].data.title, 'new topic title');
});
it('should edit topic category in queue', async () => {
await socketPosts.editQueuedContent({ uid: globalModUid }, { id: topicQueueId, cid: 2 });
const { posts } = await request(`${nconf.get('url')}/api/post-queue`, { jar: jar, json: true });
const { body } = await request.get(`${nconf.get('url')}/api/post-queue`, { jar });
const { posts } = body;
assert.equal(posts[0].type, 'topic');
assert.equal(posts[0].data.cid, 2);
await socketPosts.editQueuedContent({ uid: globalModUid }, { id: topicQueueId, cid: cid });

View File

@@ -3,7 +3,7 @@
const assert = require('assert');
const async = require('async');
const request = require('request');
const nconf = require('nconf');
const db = require('./mocks/databasemock');
@@ -12,6 +12,7 @@ const categories = require('../src/categories');
const user = require('../src/user');
const search = require('../src/search');
const privileges = require('../src/privileges');
const request = require('../src/request');
describe('Search', () => {
let phoebeUid;
@@ -104,25 +105,19 @@ describe('Search', () => {
], done);
});
it('should search term in titles and posts', (done) => {
it('should search term in titles and posts', async () => {
const meta = require('../src/meta');
const qs = `/api/search?term=cucumber&in=titlesposts&categories[]=${cid1}&by=phoebe&replies=1&repliesFilter=atleast&sortBy=timestamp&sortDirection=desc&showAs=posts`;
privileges.global.give(['groups:search:content'], 'guests', (err) => {
assert.ifError(err);
request({
url: nconf.get('url') + qs,
json: true,
}, (err, response, body) => {
assert.ifError(err);
assert(body);
assert.equal(body.matchCount, 1);
assert.equal(body.posts.length, 1);
assert.equal(body.posts[0].pid, post1Data.pid);
assert.equal(body.posts[0].uid, phoebeUid);
await privileges.global.give(['groups:search:content'], 'guests');
privileges.global.rescind(['groups:search:content'], 'guests', done);
});
});
const { body } = await request.get(nconf.get('url') + qs);
assert(body);
assert.equal(body.matchCount, 1);
assert.equal(body.posts.length, 1);
assert.equal(body.posts[0].pid, post1Data.pid);
assert.equal(body.posts[0].uid, phoebeUid);
await privileges.global.rescind(['groups:search:content'], 'guests');
});
it('should search for a user', (done) => {
@@ -254,40 +249,27 @@ describe('Search', () => {
], done);
});
it('should return json search data with no categories', (done) => {
it('should return json search data with no categories', async () => {
const qs = '/api/search?term=cucumber&in=titlesposts&searchOnly=1';
privileges.global.give(['groups:search:content'], 'guests', (err) => {
assert.ifError(err);
request({
url: nconf.get('url') + qs,
json: true,
}, (err, response, body) => {
assert.ifError(err);
assert(body);
assert(body.hasOwnProperty('matchCount'));
assert(body.hasOwnProperty('pagination'));
assert(body.hasOwnProperty('pageCount'));
assert(body.hasOwnProperty('posts'));
assert(!body.hasOwnProperty('categories'));
await privileges.global.give(['groups:search:content'], 'guests');
privileges.global.rescind(['groups:search:content'], 'guests', done);
});
});
const { body } = await request.get(nconf.get('url') + qs);
assert(body);
assert(body.hasOwnProperty('matchCount'));
assert(body.hasOwnProperty('pagination'));
assert(body.hasOwnProperty('pageCount'));
assert(body.hasOwnProperty('posts'));
assert(!body.hasOwnProperty('categories'));
await privileges.global.rescind(['groups:search:content'], 'guests');
});
it('should not crash without a search term', (done) => {
it('should not crash without a search term', async () => {
const qs = '/api/search';
privileges.global.give(['groups:search:content'], 'guests', (err) => {
assert.ifError(err);
request({
url: nconf.get('url') + qs,
json: true,
}, (err, response, body) => {
assert.ifError(err);
assert(body);
assert.strictEqual(response.statusCode, 200);
privileges.global.rescind(['groups:search:content'], 'guests', done);
});
});
await privileges.global.give(['groups:search:content'], 'guests');
const { response, body } = await request.get(nconf.get('url') + qs);
assert(body);
assert.strictEqual(response.statusCode, 200);
await privileges.global.rescind(['groups:search:content'], 'guests');
});
});

View File

@@ -11,9 +11,7 @@ const sleep = util.promisify(setTimeout);
const assert = require('assert');
const async = require('async');
const nconf = require('nconf');
const request = require('request');
const cookies = request.jar();
const db = require('./mocks/databasemock');
const user = require('../src/user');
@@ -52,35 +50,11 @@ describe('socket.io', () => {
});
it('should connect and auth properly', (done) => {
request.get({
url: `${nconf.get('url')}/api/config`,
jar: cookies,
json: true,
}, (err, res, body) => {
assert.ifError(err);
request.post(`${nconf.get('url')}/login`, {
jar: cookies,
form: {
username: 'admin',
password: 'adminpwd',
},
headers: {
'x-csrf-token': body.csrf_token,
},
json: true,
}, (err, res) => {
assert.ifError(err);
helpers.connectSocketIO(res, body.csrf_token, (err, _io) => {
io = _io;
assert.ifError(err);
done();
});
});
});
it('should connect and auth properly', async () => {
const { response, csrf_token } = await helpers.loginUser('admin', 'adminpwd');
io = await helpers.connectSocketIO(response, csrf_token);
assert(io);
assert(io.emit);
});
it('should return error for unknown event', (done) => {

View File

@@ -6,7 +6,6 @@ const assert = require('assert');
const validator = require('validator');
const mockdate = require('mockdate');
const nconf = require('nconf');
const request = require('request');
const util = require('util');
const sleep = util.promisify(setTimeout);
@@ -22,14 +21,10 @@ const User = require('../src/user');
const groups = require('../src/groups');
const utils = require('../src/utils');
const helpers = require('./helpers');
const socketPosts = require('../src/socket.io/posts');
const socketTopics = require('../src/socket.io/topics');
const apiTopics = require('../src/api/topics');
const apiPosts = require('../src/api/posts');
const requestType = util.promisify((type, url, opts, cb) => {
request[type](url, opts, (err, res, body) => cb(err, { res: res, body: body }));
});
const request = require('../src/request');
describe('Topic\'s', () => {
let topic;
@@ -142,8 +137,8 @@ describe('Topic\'s', () => {
});
await privileges.categories.give(['groups:topics:create'], categoryObj.cid, 'guests');
await privileges.categories.give(['groups:topics:reply'], categoryObj.cid, 'guests');
const result = await requestType('post', `${nconf.get('url')}/api/v3/topics`, {
form: {
const result = await request.post(`${nconf.get('url')}/api/v3/topics`, {
data: {
title: 'just a title',
cid: categoryObj.cid,
content: 'content for the main post',
@@ -151,9 +146,9 @@ describe('Topic\'s', () => {
headers: {
'x-csrf-token': 'invalid',
},
json: true,
validateStatus: null,
});
assert.strictEqual(result.res.statusCode, 403);
assert.strictEqual(result.response.statusCode, 403);
assert.strictEqual(result.body, 'Forbidden');
});
@@ -164,13 +159,12 @@ describe('Topic\'s', () => {
});
const jar = request.jar();
const result = await helpers.request('post', `/api/v3/topics`, {
form: {
data: {
title: 'just a title',
cid: categoryObj.cid,
content: 'content for the main post',
},
jar: jar,
json: true,
});
assert.strictEqual(result.body.status.message, 'You do not have enough privileges for this action.');
});
@@ -185,7 +179,7 @@ describe('Topic\'s', () => {
const jar = request.jar();
const result = await helpers.request('post', `/api/v3/topics`, {
form: {
data: {
title: 'just a title',
cid: categoryObj.cid,
content: 'content for the main post',
@@ -199,7 +193,7 @@ describe('Topic\'s', () => {
assert.strictEqual(result.body.response.user.username, '[[global:guest]]');
const replyResult = await helpers.request('post', `/api/v3/topics/${result.body.response.tid}`, {
form: {
data: {
content: 'a reply by guest',
},
jar: jar,
@@ -219,14 +213,13 @@ describe('Topic\'s', () => {
const oldValue = meta.config.allowGuestHandles;
meta.config.allowGuestHandles = 1;
const result = await helpers.request('post', `/api/v3/topics`, {
form: {
data: {
title: 'just a title',
cid: categoryObj.cid,
content: 'content for the main post',
handle: 'guest123',
},
jar: request.jar(),
json: true,
});
assert.strictEqual(result.body.status.code, 'ok');
@@ -235,12 +228,11 @@ describe('Topic\'s', () => {
assert.strictEqual(result.body.response.user.displayname, 'guest123');
const replyResult = await helpers.request('post', `/api/v3/topics/${result.body.response.tid}`, {
form: {
data: {
content: 'a reply by guest',
handle: 'guest124',
},
jar: request.jar(),
json: true,
});
assert.strictEqual(replyResult.body.response.content, 'a reply by guest');
assert.strictEqual(replyResult.body.response.user.username, 'guest124');
@@ -1267,162 +1259,114 @@ describe('Topic\'s', () => {
});
});
it('should load topic', (done) => {
request(`${nconf.get('url')}/topic/${topicData.slug}`, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 200);
assert(body);
done();
});
it('should load topic', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`);
assert.equal(response.statusCode, 200);
assert(body);
});
it('should load topic api data', (done) => {
request(`${nconf.get('url')}/api/topic/${topicData.slug}`, { json: true }, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 200);
assert.strictEqual(body._header.tags.meta.find(t => t.name === 'description').content, 'topic content');
assert.strictEqual(body._header.tags.meta.find(t => t.property === 'og:description').content, 'topic content');
done();
});
it('should load topic api data', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/topic/${topicData.slug}`, { json: true });
assert.equal(response.statusCode, 200);
assert.strictEqual(body._header.tags.meta.find(t => t.name === 'description').content, 'topic content');
assert.strictEqual(body._header.tags.meta.find(t => t.property === 'og:description').content, 'topic content');
});
it('should 404 if post index is invalid', (done) => {
request(`${nconf.get('url')}/topic/${topicData.slug}/derp`, (err, response) => {
assert.ifError(err);
assert.equal(response.statusCode, 404);
done();
});
it('should 404 if post index is invalid', async () => {
const { response } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}/derp`, { validateStatus: null });
assert.equal(response.statusCode, 404);
});
it('should 404 if topic does not exist', (done) => {
request(`${nconf.get('url')}/topic/123123/does-not-exist`, (err, response) => {
assert.ifError(err);
assert.equal(response.statusCode, 404);
done();
});
it('should 404 if topic does not exist', async () => {
const { response } = await request.get(`${nconf.get('url')}/topic/123123/does-not-exist`, { validateStatus: null });
assert.equal(response.statusCode, 404);
});
it('should 401 if not allowed to read as guest', (done) => {
it('should 401 if not allowed to read as guest', async () => {
const privileges = require('../src/privileges');
privileges.categories.rescind(['groups:topics:read'], topicData.cid, 'guests', (err) => {
assert.ifError(err);
request(`${nconf.get('url')}/api/topic/${topicData.slug}`, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 401);
assert(body);
privileges.categories.give(['groups:topics:read'], topicData.cid, 'guests', done);
});
});
await privileges.categories.rescind(['groups:topics:read'], topicData.cid, 'guests');
const { response, body } = await request.get(`${nconf.get('url')}/api/topic/${topicData.slug}`, { validateStatus: null });
assert.equal(response.statusCode, 401);
assert(body);
await privileges.categories.give(['groups:topics:read'], topicData.cid, 'guests');
});
it('should redirect to correct topic if slug is missing', (done) => {
request(`${nconf.get('url')}/topic/${topicData.tid}/herpderp/1?page=2`, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 200);
assert(body);
done();
});
it('should redirect to correct topic if slug is missing', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.tid}/herpderp/1?page=2`);
assert.equal(response.statusCode, 200);
assert(body);
});
it('should redirect if post index is out of range', (done) => {
request(`${nconf.get('url')}/api/topic/${topicData.slug}/-1`, { json: true }, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert.equal(res.headers['x-redirect'], `/topic/${topicData.tid}/topic-for-controller-test`);
assert.equal(body, `/topic/${topicData.tid}/topic-for-controller-test`);
done();
});
it('should redirect if post index is out of range', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/topic/${topicData.slug}/-1`);
assert.equal(response.statusCode, 200);
assert.equal(response.headers['x-redirect'], `/topic/${topicData.tid}/topic-for-controller-test`);
assert.equal(body, `/topic/${topicData.tid}/topic-for-controller-test`);
});
it('should 404 if page is out of bounds', (done) => {
it('should 404 if page is out of bounds', async () => {
const meta = require('../src/meta');
meta.config.usePagination = 1;
request(`${nconf.get('url')}/topic/${topicData.slug}?page=100`, (err, response) => {
assert.ifError(err);
assert.equal(response.statusCode, 404);
done();
});
const { response } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}?page=100`, { validateStatus: null });
assert.equal(response.statusCode, 404);
});
it('should mark topic read', (done) => {
request(`${nconf.get('url')}/topic/${topicData.slug}`, {
it('should mark topic read', async () => {
const { response } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`, {
jar: adminJar,
}, (err, res) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
topics.hasReadTopics([topicData.tid], adminUid, (err, hasRead) => {
assert.ifError(err);
assert.equal(hasRead[0], true);
done();
});
});
assert.equal(response.statusCode, 200);
const hasRead = await topics.hasReadTopics([topicData.tid], adminUid);
assert.equal(hasRead[0], true);
});
it('should 404 if tid is not a number', (done) => {
request(`${nconf.get('url')}/api/topic/teaser/nan`, { json: true }, (err, response) => {
assert.ifError(err);
assert.equal(response.statusCode, 404);
done();
});
it('should 404 if tid is not a number', async () => {
const { response } = await request.get(`${nconf.get('url')}/api/topic/teaser/nan`, { validateStatus: null });
assert.equal(response.statusCode, 404);
});
it('should 403 if cant read', (done) => {
request(`${nconf.get('url')}/api/topic/teaser/${123123}`, { json: true }, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 403);
assert.equal(body, '[[error:no-privileges]]');
done();
});
it('should 403 if cant read', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/topic/teaser/${123123}`, { validateStatus: null });
assert.equal(response.statusCode, 403);
assert.equal(body, '[[error:no-privileges]]');
});
it('should load topic teaser', (done) => {
request(`${nconf.get('url')}/api/topic/teaser/${topicData.tid}`, { json: true }, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 200);
assert(body);
assert.equal(body.tid, topicData.tid);
assert.equal(body.content, 'topic content');
assert(body.user);
assert(body.topic);
assert(body.category);
done();
});
it('should load topic teaser', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/topic/teaser/${topicData.tid}`);
assert.equal(response.statusCode, 200);
assert(body);
assert.equal(body.tid, topicData.tid);
assert.equal(body.content, 'topic content');
assert(body.user);
assert(body.topic);
assert(body.category);
});
it('should 404 if tid is not a number', (done) => {
request(`${nconf.get('url')}/api/topic/pagination/nan`, { json: true }, (err, response) => {
assert.ifError(err);
assert.equal(response.statusCode, 404);
done();
});
it('should 404 if tid is not a number', async () => {
const { response } = await request.get(`${nconf.get('url')}/api/topic/pagination/nan`, { validateStatus: null });
assert.equal(response.statusCode, 404);
});
it('should 404 if tid does not exist', (done) => {
request(`${nconf.get('url')}/api/topic/pagination/1231231`, { json: true }, (err, response) => {
assert.ifError(err);
assert.equal(response.statusCode, 404);
done();
});
it('should 404 if tid does not exist', async () => {
const { response } = await request.get(`${nconf.get('url')}/api/topic/pagination/1231231`, { validateStatus: null });
assert.equal(response.statusCode, 404);
});
it('should load pagination', (done) => {
request(`${nconf.get('url')}/api/topic/pagination/${topicData.tid}`, { json: true }, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 200);
assert(body);
assert.deepEqual(body.pagination, {
prev: { page: 1, active: false },
next: { page: 1, active: false },
first: { page: 1, active: true },
last: { page: 1, active: true },
rel: [],
pages: [],
currentPage: 1,
pageCount: 1,
});
done();
it('should load pagination', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/topic/pagination/${topicData.tid}`);
assert.equal(response.statusCode, 200);
assert(body);
assert.deepEqual(body.pagination, {
prev: { page: 1, active: false },
next: { page: 1, active: false },
first: { page: 1, active: true },
last: { page: 1, active: true },
rel: [],
pages: [],
currentPage: 1,
pageCount: 1,
});
});
});
@@ -2565,14 +2509,11 @@ describe('Topic\'s', () => {
assert.equal(topic1.title, 'topic 1');
});
it('should return properly for merged topic', (done) => {
request(`${nconf.get('url')}/api/topic/${topic2Data.slug}`, { jar: adminJar, json: true }, (err, response, body) => {
assert.ifError(err);
assert.equal(response.statusCode, 200);
assert(body);
assert.deepStrictEqual(body.posts, []);
done();
});
it('should return properly for merged topic', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/topic/${topic2Data.slug}`, { jar: adminJar });
assert.equal(response.statusCode, 200);
assert(body);
assert.deepStrictEqual(body.posts, []);
});
it('should merge 2 topics with options mainTid', async () => {
@@ -2697,19 +2638,19 @@ describe('Topic\'s', () => {
let adminApiOpts;
let postData;
const replyData = {
form: {
data: {
content: 'a reply by guest',
},
json: true,
validateStatus: null,
};
before(async () => {
adminApiOpts = {
json: true,
jar: adminJar,
headers: {
'x-csrf-token': csrf_token,
},
validateStatus: null,
};
categoryObj = await categories.create({
name: 'Another Test Category',
@@ -2750,85 +2691,85 @@ describe('Topic\'s', () => {
});
it('should not load topic for an unprivileged user', async () => {
const response = await requestType('get', `${nconf.get('url')}/topic/${topicData.slug}`);
const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`, { validateStatus: null });
assert.strictEqual(response.statusCode, 404);
assert(response.body);
assert(body);
});
it('should load topic for a privileged user', async () => {
const response = (await requestType('get', `${nconf.get('url')}/topic/${topicData.slug}`, { jar: adminJar })).res;
const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`, { jar: adminJar });
assert.strictEqual(response.statusCode, 200);
assert(response.body);
assert(body);
});
it('should not be amongst topics of the category for an unprivileged user', async () => {
const response = await requestType('get', `${nconf.get('url')}/api/category/${categoryObj.slug}`, { json: true });
assert.strictEqual(response.body.topics.filter(topic => topic.tid === topicData.tid).length, 0);
const { body } = await request.get(`${nconf.get('url')}/api/category/${categoryObj.slug}`);
assert.strictEqual(body.topics.filter(topic => topic.tid === topicData.tid).length, 0);
});
it('should be amongst topics of the category for a privileged user', async () => {
const response = await requestType('get', `${nconf.get('url')}/api/category/${categoryObj.slug}`, { json: true, jar: adminJar });
const topic = response.body.topics.filter(topic => topic.tid === topicData.tid)[0];
const { body } = await request.get(`${nconf.get('url')}/api/category/${categoryObj.slug}`, { jar: adminJar });
const topic = body.topics.filter(topic => topic.tid === topicData.tid)[0];
assert.strictEqual(topic && topic.tid, topicData.tid);
});
it('should load topic for guests if privilege is given', async () => {
await privileges.categories.give(['groups:topics:schedule'], categoryObj.cid, 'guests');
const response = await requestType('get', `${nconf.get('url')}/topic/${topicData.slug}`);
const { response, body } = await request.get(`${nconf.get('url')}/topic/${topicData.slug}`);
assert.strictEqual(response.statusCode, 200);
assert(response.body);
assert(body);
});
it('should be amongst topics of the category for guests if privilege is given', async () => {
const response = await requestType('get', `${nconf.get('url')}/api/category/${categoryObj.slug}`, { json: true });
const topic = response.body.topics.filter(topic => topic.tid === topicData.tid)[0];
const { body } = await request.get(`${nconf.get('url')}/api/category/${categoryObj.slug}`);
const topic = body.topics.filter(topic => topic.tid === topicData.tid)[0];
assert.strictEqual(topic && topic.tid, topicData.tid);
});
it('should not allow deletion of a scheduled topic', async () => {
const response = await requestType('delete', `${nconf.get('url')}/api/v3/topics/${topicData.tid}/state`, adminApiOpts);
assert.strictEqual(response.res.statusCode, 400);
const { response } = await request.delete(`${nconf.get('url')}/api/v3/topics/${topicData.tid}/state`, adminApiOpts);
assert.strictEqual(response.statusCode, 400);
});
it('should not allow to unpin a scheduled topic', async () => {
const response = await requestType('delete', `${nconf.get('url')}/api/v3/topics/${topicData.tid}/pin`, adminApiOpts);
assert.strictEqual(response.res.statusCode, 400);
const { response } = await request.delete(`${nconf.get('url')}/api/v3/topics/${topicData.tid}/pin`, adminApiOpts);
assert.strictEqual(response.statusCode, 400);
});
it('should not allow to restore a scheduled topic', async () => {
const response = await requestType('put', `${nconf.get('url')}/api/v3/topics/${topicData.tid}/state`, adminApiOpts);
assert.strictEqual(response.res.statusCode, 400);
const { response } = await request.put(`${nconf.get('url')}/api/v3/topics/${topicData.tid}/state`, adminApiOpts);
assert.strictEqual(response.statusCode, 400);
});
it('should not allow unprivileged to reply', async () => {
await privileges.categories.rescind(['groups:topics:schedule'], categoryObj.cid, 'guests');
await privileges.categories.give(['groups:topics:reply'], categoryObj.cid, 'guests');
const response = await requestType('post', `${nconf.get('url')}/api/v3/topics/${topicData.tid}`, replyData);
assert.strictEqual(response.res.statusCode, 403);
const { response } = await request.post(`${nconf.get('url')}/api/v3/topics/${topicData.tid}`, replyData);
assert.strictEqual(response.statusCode, 403);
});
it('should allow guests to reply if privilege is given', async () => {
await privileges.categories.give(['groups:topics:schedule'], categoryObj.cid, 'guests');
const response = await helpers.request('post', `/api/v3/topics/${topicData.tid}`, {
const { body } = await helpers.request('post', `/api/v3/topics/${topicData.tid}`, {
...replyData,
jar: request.jar(),
});
assert.strictEqual(response.body.response.content, 'a reply by guest');
assert.strictEqual(response.body.response.user.username, '[[global:guest]]');
assert.strictEqual(body.response.content, 'a reply by guest');
assert.strictEqual(body.response.user.username, '[[global:guest]]');
});
it('should have replies with greater timestamp than the scheduled topics itself', async () => {
const response = await requestType('get', `${nconf.get('url')}/api/topic/${topicData.slug}`, { json: true });
postData = response.body.posts[1];
assert(postData.timestamp > response.body.posts[0].timestamp);
const { body } = await request.get(`${nconf.get('url')}/api/topic/${topicData.slug}`);
postData = body.posts[1];
assert(postData.timestamp > body.posts[0].timestamp);
});
it('should have post edits with greater timestamp than the original', async () => {
const editData = { ...adminApiOpts, form: { content: 'an edit by the admin' } };
const result = await requestType('put', `${nconf.get('url')}/api/v3/posts/${postData.pid}`, editData);
const editData = { ...adminApiOpts, data: { content: 'an edit by the admin' } };
const result = await request.put(`${nconf.get('url')}/api/v3/posts/${postData.pid}`, editData);
assert(result.body.response.edited > postData.timestamp);
const diffsResult = await requestType('get', `${nconf.get('url')}/api/v3/posts/${postData.pid}/diffs`, adminApiOpts);
const diffsResult = await request.get(`${nconf.get('url')}/api/v3/posts/${postData.pid}/diffs`, adminApiOpts);
const { revisions } = diffsResult.body.response;
// diffs are LIFO
assert(revisions[0].timestamp > revisions[1].timestamp);
@@ -2836,8 +2777,8 @@ describe('Topic\'s', () => {
it('should able to reschedule', async () => {
const newDate = new Date(Date.now() + (5 * 86400000)).getTime();
const editData = { ...adminApiOpts, form: { ...topic, pid: topicData.mainPid, timestamp: newDate } };
const response = await requestType('put', `${nconf.get('url')}/api/v3/posts/${topicData.mainPid}`, editData);
const editData = { ...adminApiOpts, data: { ...topic, pid: topicData.mainPid, timestamp: newDate } };
await request.put(`${nconf.get('url')}/api/v3/posts/${topicData.mainPid}`, editData);
const editedTopic = await topics.getTopicFields(topicData.tid, ['lastposttime', 'timestamp']);
const editedPost = await posts.getPostFields(postData.pid, ['timestamp']);
@@ -2875,17 +2816,16 @@ describe('Topic\'s', () => {
it('should not be able to schedule a "published" topic', async () => {
const newDate = new Date(Date.now() + 86400000).getTime();
const editData = { ...adminApiOpts, form: { ...topic, pid: topicData.mainPid, timestamp: newDate } };
const response = await requestType('put', `${nconf.get('url')}/api/v3/posts/${topicData.mainPid}`, editData);
assert.strictEqual(response.body.response.timestamp, Date.now());
const editData = { ...adminApiOpts, data: { ...topic, pid: topicData.mainPid, timestamp: newDate } };
const { body } = await request.put(`${nconf.get('url')}/api/v3/posts/${topicData.mainPid}`, editData);
assert.strictEqual(body.response.timestamp, Date.now());
mockdate.reset();
});
it('should allow to purge a scheduled topic', async () => {
topicData = (await topics.post(topic)).topicData;
const response = await requestType('delete', `${nconf.get('url')}/api/v3/topics/${topicData.tid}`, adminApiOpts);
assert.strictEqual(response.res.statusCode, 200);
const { response } = await request.delete(`${nconf.get('url')}/api/v3/topics/${topicData.tid}`, adminApiOpts);
assert.strictEqual(response.statusCode, 200);
});
it('should remove from topics:scheduled on purge', async () => {

View File

@@ -324,20 +324,14 @@ describe('Topic thumbs', () => {
createFiles();
});
it('should succeed with a valid tid', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/1/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 200);
done();
});
it('should succeed with a valid tid', async () => {
const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/1/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF);
assert.strictEqual(response.statusCode, 200);
});
it('should succeed with a uuid', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 200);
done();
});
it('should succeed with a uuid', async () => {
const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF);
assert.strictEqual(response.statusCode, 200);
});
it('should succeed with uploader plugins', async () => {
@@ -350,63 +344,49 @@ describe('Topic thumbs', () => {
method: hookMethod,
});
await new Promise((resolve) => {
helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 200);
resolve();
});
});
const { response } = await helpers.uploadFile(
`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`,
path.join(__dirname, '../files/test.png'),
{},
adminJar,
adminCSRF
);
assert.strictEqual(response.statusCode, 200);
await plugins.hooks.unregister('test', 'filter:uploadFile', hookMethod);
});
it('should fail with a non-existant tid', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/4/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 404);
done();
});
it('should fail with a non-existant tid', async () => {
const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/4/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF);
assert.strictEqual(response.statusCode, 404);
});
it('should fail when garbage is passed in', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/abracadabra/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 404);
done();
});
it('should fail when garbage is passed in', async () => {
const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/abracadabra/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF);
assert.strictEqual(response.statusCode, 404);
});
it('should fail when calling user cannot edit the tid', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/2/thumbs`, path.join(__dirname, '../files/test.png'), {}, fooJar, fooCSRF, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 403);
done();
});
it('should fail when calling user cannot edit the tid', async () => {
const { response } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/2/thumbs`, path.join(__dirname, '../files/test.png'), {}, fooJar, fooCSRF);
assert.strictEqual(response.statusCode, 403);
});
it('should fail if thumbnails are not enabled', (done) => {
it('should fail if thumbnails are not enabled', async () => {
meta.config.allowTopicsThumbnail = 0;
helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 503);
assert(body && body.status);
assert.strictEqual(body.status.message, 'Topic thumbnails are disabled.');
done();
});
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/test.png'), {}, adminJar, adminCSRF);
assert.strictEqual(response.statusCode, 503);
assert(body && body.status);
assert.strictEqual(body.status.message, 'Topic thumbnails are disabled.');
});
it('should fail if file is not image', (done) => {
it('should fail if file is not image', async () => {
meta.config.allowTopicsThumbnail = 1;
helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/503.html'), {}, adminJar, adminCSRF, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 500);
assert(body && body.status);
assert.strictEqual(body.status.message, 'Invalid File');
done();
});
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/v3/topics/${uuid}/thumbs`, path.join(__dirname, '../files/503.html'), {}, adminJar, adminCSRF);
assert.strictEqual(response.statusCode, 500);
assert(body && body.status);
assert.strictEqual(body.status.message, 'Invalid File');
});
});

View File

@@ -5,9 +5,6 @@ const assert = require('assert');
const nconf = require('nconf');
const path = require('path');
const fs = require('fs').promises;
const request = require('request');
const requestAsync = require('request-promise-native');
const util = require('util');
const db = require('./mocks/databasemock');
const categories = require('../src/categories');
@@ -21,6 +18,7 @@ const socketUser = require('../src/socket.io/user');
const helpers = require('./helpers');
const file = require('../src/file');
const image = require('../src/image');
const request = require('../src/request');
const emptyUploadsFolder = async () => {
const files = await fs.readdir(`${nconf.get('upload_path')}/files`);
@@ -83,31 +81,25 @@ describe('Upload Controllers', () => {
await privileges.global.give(['groups:upload:post:file'], 'registered-users');
});
it('should fail if the user exceeds the upload rate limit threshold', (done) => {
it('should fail if the user exceeds the upload rate limit threshold', async () => {
const oldValue = meta.config.allowedFileExtensions;
meta.config.allowedFileExtensions = 'png,jpg,bmp,html';
require('../src/middleware/uploads').clearCache();
const times = meta.config.uploadRateLimitThreshold + 1;
async.timesSeries(times, (i, next) => {
helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/503.html'), {}, jar, csrf_token, (err, res, body) => {
if (i + 1 >= times) {
assert.strictEqual(res.statusCode, 500);
assert.strictEqual(body.error, '[[error:upload-ratelimit-reached]]');
} else {
assert.ifError(err);
assert.strictEqual(res.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
}
next(err);
});
}, (err) => {
meta.config.allowedFileExtensions = oldValue;
assert.ifError(err);
done();
});
for (let i = 0; i < times; i++) {
// eslint-disable-next-line no-await-in-loop
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/503.html'), {}, jar, csrf_token);
if (i + 1 >= times) {
assert.strictEqual(response.statusCode, 500);
assert.strictEqual(body.error, '[[error:upload-ratelimit-reached]]');
} else {
assert.strictEqual(response.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
}
}
meta.config.allowedFileExtensions = oldValue;
});
});
@@ -121,34 +113,26 @@ describe('Upload Controllers', () => {
await privileges.global.give(['groups:upload:post:file'], 'registered-users');
});
it('should upload an image to a post', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
done();
});
it('should upload an image to a post', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token);
assert.equal(response.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
});
it('should upload an image to a post and then delete the upload', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
const name = body.response.images[0].url.replace(`${nconf.get('relative_path') + nconf.get('upload_url')}/`, '');
socketUser.deleteUpload({ uid: regularUid }, { uid: regularUid, name: name }, (err) => {
assert.ifError(err);
db.getSortedSetRange(`uid:${regularUid}:uploads`, 0, -1, (err, uploads) => {
assert.ifError(err);
assert.equal(uploads.includes(name), false);
done();
});
});
});
it('should upload an image to a post and then delete the upload', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
const name = body.response.images[0].url.replace(`${nconf.get('relative_path') + nconf.get('upload_url')}/`, '');
await socketUser.deleteUpload({ uid: regularUid }, { uid: regularUid, name: name });
const uploads = await db.getSortedSetRange(`uid:${regularUid}:uploads`, 0, -1);
assert.equal(uploads.includes(name), false);
});
it('should not allow deleting if path is not correct', (done) => {
@@ -165,55 +149,45 @@ describe('Upload Controllers', () => {
});
});
it('should resize and upload an image to a post', (done) => {
it('should resize and upload an image to a post', async () => {
const oldValue = meta.config.resizeImageWidth;
meta.config.resizeImageWidth = 10;
meta.config.resizeImageWidthThreshold = 10;
helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
assert(body.response.images[0].url.match(/\/assets\/uploads\/files\/\d+-test-resized\.png/));
meta.config.resizeImageWidth = oldValue;
meta.config.resizeImageWidthThreshold = 1520;
done();
});
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token);
assert.equal(response.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
assert(body.response.images[0].url.match(/\/assets\/uploads\/files\/\d+-test-resized\.png/));
meta.config.resizeImageWidth = oldValue;
meta.config.resizeImageWidthThreshold = 1520;
});
it('should upload a file to a post', (done) => {
it('should upload a file to a post', async () => {
const oldValue = meta.config.allowedFileExtensions;
meta.config.allowedFileExtensions = 'png,jpg,bmp,html';
helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/503.html'), {}, jar, csrf_token, (err, res, body) => {
meta.config.allowedFileExtensions = oldValue;
assert.ifError(err);
assert.strictEqual(res.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
done();
});
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/503.html'), {}, jar, csrf_token);
meta.config.allowedFileExtensions = oldValue;
assert.strictEqual(response.statusCode, 200);
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
});
it('should fail to upload image to post if image dimensions are too big', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/toobig.png'), {}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 500);
assert(body && body.status && body.status.message);
assert.strictEqual(body.status.message, 'Image dimensions are too big');
done();
});
it('should fail to upload image to post if image dimensions are too big', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/toobig.png'), {}, jar, csrf_token);
assert.strictEqual(response.statusCode, 500);
assert(body && body.status && body.status.message);
assert.strictEqual(body.status.message, 'Image dimensions are too big');
});
it('should fail to upload image to post if image is broken', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/brokenimage.png'), {}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 500);
assert(body && body.status && body.status.message);
assert.strictEqual(body.status.message, 'Input file contains unsupported image format');
done();
});
it('should fail to upload image to post if image is broken', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/brokenimage.png'), {}, jar, csrf_token);
assert.strictEqual(response.statusCode, 500);
assert(body && body.status && body.status.message);
assert.strictEqual(body.status.message, 'Input file contains unsupported image format');
});
it('should fail if file is not an image', (done) => {
@@ -286,39 +260,22 @@ describe('Upload Controllers', () => {
});
});
it('should delete users uploads if account is deleted', (done) => {
let uid;
let url;
it('should delete users uploads if account is deleted', async () => {
const uid = await user.create({ username: 'uploader', password: 'barbar' });
const file = require('../src/file');
const data = await helpers.loginUser('uploader', 'barbar');
const { body } = await helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, data.jar, data.csrf_token);
async.waterfall([
function (next) {
user.create({ username: 'uploader', password: 'barbar' }, next);
},
function (_uid, next) {
uid = _uid;
helpers.loginUser('uploader', 'barbar', next);
},
function (data, next) {
helpers.uploadFile(`${nconf.get('url')}/api/post/upload`, path.join(__dirname, '../test/files/test.png'), {}, data.jar, data.csrf_token, next);
},
function (res, body, next) {
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
url = body.response.images[0].url;
assert(body && body.status && body.response && body.response.images);
assert(Array.isArray(body.response.images));
assert(body.response.images[0].url);
const { url } = body.response.images[0];
user.delete(1, uid, next);
},
function (userData, next) {
const filePath = path.join(nconf.get('upload_path'), url.replace('/assets/uploads', ''));
file.exists(filePath, next);
},
function (exists, next) {
assert(!exists);
done();
},
], done);
await user.delete(1, uid);
const filePath = path.join(nconf.get('upload_path'), url.replace('/assets/uploads', ''));
const exists = await file.exists(filePath);
assert(!exists);
});
after(emptyUploadsFolder);
@@ -337,173 +294,148 @@ describe('Upload Controllers', () => {
regular_csrf_token = regularLogin.csrf_token;
});
it('should upload site logo', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadlogo`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/site-logo.png`);
done();
});
it('should upload site logo', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadlogo`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/site-logo.png`);
});
it('should fail to upload invalid file type', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/503.html'), { params: JSON.stringify({ cid: cid }) }, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 500);
assert.equal(body.error, '[[error:invalid-image-type, image&#x2F;png&amp;#44; image&#x2F;jpeg&amp;#44; image&#x2F;pjpeg&amp;#44; image&#x2F;jpg&amp;#44; image&#x2F;gif&amp;#44; image&#x2F;svg+xml]]');
done();
});
it('should fail to upload invalid file type', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/503.html'), { params: JSON.stringify({ cid: cid }) }, jar, csrf_token);
assert.strictEqual(response.statusCode, 500);
assert.equal(body.error, '[[error:invalid-image-type, image&#x2F;png&amp;#44; image&#x2F;jpeg&amp;#44; image&#x2F;pjpeg&amp;#44; image&#x2F;jpg&amp;#44; image&#x2F;gif&amp;#44; image&#x2F;svg+xml]]');
});
it('should fail to upload category image with invalid json params', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/test.png'), { params: 'invalid json' }, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 500);
assert.equal(body.error, '[[error:invalid-json]]');
done();
});
it('should fail to upload category image with invalid json params', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/test.png'), { params: 'invalid json' }, jar, csrf_token);
assert.strictEqual(response.statusCode, 500);
assert.equal(body.error, '[[error:invalid-json]]');
});
it('should upload category image', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/test.png'), { params: JSON.stringify({ cid: cid }) }, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/category/category-1.png`);
done();
});
it('should upload category image', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/category/uploadpicture`, path.join(__dirname, '../test/files/test.png'), { params: JSON.stringify({ cid: cid }) }, jar, csrf_token);
assert.equal(response.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/category/category-1.png`);
});
it('should upload default avatar', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadDefaultAvatar`, path.join(__dirname, '../test/files/test.png'), { }, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/avatar-default.png`);
done();
});
it('should upload default avatar', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadDefaultAvatar`, path.join(__dirname, '../test/files/test.png'), { }, jar, csrf_token);
assert.equal(response.statusCode, 200);
assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/avatar-default.png`);
});
it('should upload og image', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadOgImage`, path.join(__dirname, '../test/files/test.png'), { }, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/og-image.png`);
done();
});
it('should upload og image', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadOgImage`, path.join(__dirname, '../test/files/test.png'), { }, jar, csrf_token);
assert.equal(response.statusCode, 200);
assert.equal(body[0].url, `${nconf.get('relative_path')}/assets/uploads/system/og-image.png`);
});
it('should upload favicon', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadfavicon`, path.join(__dirname, '../test/files/favicon.ico'), {}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, '/assets/uploads/system/favicon.ico');
done();
});
it('should upload favicon', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadfavicon`, path.join(__dirname, '../test/files/favicon.ico'), {}, jar, csrf_token);
assert.equal(response.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, '/assets/uploads/system/favicon.ico');
});
it('should upload touch icon', (done) => {
it('should upload touch icon', async () => {
const touchiconAssetPath = '/assets/uploads/system/touchicon-orig.png';
helpers.uploadFile(`${nconf.get('url')}/api/admin/uploadTouchIcon`, path.join(__dirname, '../test/files/test.png'), {}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, touchiconAssetPath);
meta.config['brand:touchIcon'] = touchiconAssetPath;
request(`${nconf.get('url')}/apple-touch-icon`, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body);
done();
});
});
const { response, body } = await helpers.uploadFile(
`${nconf.get('url')}/api/admin/uploadTouchIcon`,
path.join(__dirname, '../test/files/test.png'),
{},
jar,
csrf_token
);
assert.equal(response.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, touchiconAssetPath);
meta.config['brand:touchIcon'] = touchiconAssetPath;
const { response: res1, body: body1 } = await request.get(`${nconf.get('url')}/apple-touch-icon`);
assert.equal(res1.statusCode, 200);
assert(body1);
});
it('should upload regular file', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/admin/upload/file`, path.join(__dirname, '../test/files/test.png'), {
it('should upload regular file', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/upload/file`, path.join(__dirname, '../test/files/test.png'), {
params: JSON.stringify({
folder: 'system',
}),
}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, '/assets/uploads/system/test.png');
assert(file.existsSync(path.join(nconf.get('upload_path'), 'system', 'test.png')));
done();
});
}, jar, csrf_token);
assert.equal(response.statusCode, 200);
assert(Array.isArray(body));
assert.equal(body[0].url, '/assets/uploads/system/test.png');
assert(file.existsSync(path.join(nconf.get('upload_path'), 'system', 'test.png')));
});
it('should fail to upload regular file in wrong directory', (done) => {
helpers.uploadFile(`${nconf.get('url')}/api/admin/upload/file`, path.join(__dirname, '../test/files/test.png'), {
it('should fail to upload regular file in wrong directory', async () => {
const { response, body } = await helpers.uploadFile(`${nconf.get('url')}/api/admin/upload/file`, path.join(__dirname, '../test/files/test.png'), {
params: JSON.stringify({
folder: '../../system',
}),
}, jar, csrf_token, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 500);
assert.strictEqual(body.error, '[[error:invalid-path]]');
done();
});
}, jar, csrf_token);
assert.equal(response.statusCode, 500);
assert.strictEqual(body.error, '[[error:invalid-path]]');
});
describe('ACP uploads screen', () => {
it('should create a folder', async () => {
const res = await helpers.createFolder('', 'myfolder', jar, csrf_token);
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.createFolder('', 'myfolder', jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
assert(file.existsSync(path.join(nconf.get('upload_path'), 'myfolder')));
});
it('should fail to create a folder if it already exists', async () => {
const res = await helpers.createFolder('', 'myfolder', jar, csrf_token);
assert.strictEqual(res.statusCode, 403);
assert.deepStrictEqual(res.body.status, {
const { response, body } = await helpers.createFolder('', 'myfolder', jar, csrf_token);
assert.strictEqual(response.statusCode, 403);
assert.deepStrictEqual(body.status, {
code: 'forbidden',
message: 'Folder exists',
});
});
it('should fail to create a folder as a non-admin', async () => {
const res = await helpers.createFolder('', 'hisfolder', regularJar, regular_csrf_token);
assert.strictEqual(res.statusCode, 403);
assert.deepStrictEqual(res.body.status, {
const { response, body } = await helpers.createFolder('', 'hisfolder', regularJar, regular_csrf_token);
assert.strictEqual(response.statusCode, 403);
assert.deepStrictEqual(body.status, {
code: 'forbidden',
message: 'You are not authorised to make this call',
});
});
it('should fail to create a folder in wrong directory', async () => {
const res = await helpers.createFolder('../traversing', 'unexpectedfolder', jar, csrf_token);
assert.strictEqual(res.statusCode, 403);
assert.deepStrictEqual(res.body.status, {
const { response, body } = await helpers.createFolder('../traversing', 'unexpectedfolder', jar, csrf_token);
assert.strictEqual(response.statusCode, 403);
assert.deepStrictEqual(body.status, {
code: 'forbidden',
message: 'Invalid path',
});
});
it('should use basename of given folderName to create new folder', async () => {
const res = await helpers.createFolder('/myfolder', '../another folder', jar, csrf_token);
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.createFolder('/myfolder', '../another folder', jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
const slugifiedName = 'another-folder';
assert(file.existsSync(path.join(nconf.get('upload_path'), 'myfolder', slugifiedName)));
});
it('should fail to delete a file as a non-admin', async () => {
const res = await requestAsync.delete(`${nconf.get('url')}/api/v3/files`, {
body: {
const { response, body } = await request.delete(`${nconf.get('url')}/api/v3/files`, {
data: {
path: '/system/test.png',
},
jar: regularJar,
json: true,
headers: {
'x-csrf-token': regular_csrf_token,
},
simple: false,
resolveWithFullResponse: true,
validateStatus: null,
});
assert.strictEqual(res.statusCode, 403);
assert.deepStrictEqual(res.body.status, {
assert.strictEqual(response.statusCode, 403);
assert.deepStrictEqual(body.status, {
code: 'forbidden',
message: 'You are not authorised to make this call',
});

View File

@@ -6,8 +6,6 @@ const fs = require('fs');
const path = require('path');
const nconf = require('nconf');
const validator = require('validator');
const request = require('request');
const requestAsync = require('request-promise-native');
const jwt = require('jsonwebtoken');
const db = require('./mocks/databasemock');
@@ -24,6 +22,7 @@ const socketUser = require('../src/socket.io/user');
const apiUser = require('../src/api/users');
const utils = require('../src/utils');
const privileges = require('../src/privileges');
const request = require('../src/request');
describe('User', () => {
let userData;
@@ -355,13 +354,12 @@ describe('User', () => {
const titles = new Array(10).fill('topic title');
const res = await Promise.allSettled(titles.map(async (title) => {
const { body } = await helpers.request('post', '/api/v3/topics', {
form: {
data: {
cid: testCid,
title: title,
content: 'the content',
},
jar: jar,
json: true,
});
return body.status;
}));
@@ -991,14 +989,12 @@ describe('User', () => {
it('should let you set an external image', async () => {
const token = await helpers.getCsrfToken(jar);
const body = await requestAsync(`${nconf.get('url')}/api/v3/users/${uid}/picture`, {
const { body } = await request.put(`${nconf.get('url')}/api/v3/users/${uid}/picture`, {
jar,
method: 'put',
json: true,
headers: {
'x-csrf-token': token,
},
body: {
data: {
type: 'external',
url: 'https://example.org/picture.jpg',
},
@@ -1193,46 +1189,35 @@ describe('User', () => {
});
});
it('should load profile page', (done) => {
request(`${nconf.get('url')}/api/user/updatedagain`, { jar: jar, json: true }, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body);
done();
});
it('should load profile page', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain`, { jar });
assert.equal(response.statusCode, 200);
assert(body);
});
it('should load settings page', (done) => {
request(`${nconf.get('url')}/api/user/updatedagain/settings`, { jar: jar, json: true }, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body.settings);
assert(body.languages);
assert(body.homePageRoutes);
done();
});
it('should load settings page', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/settings`, { jar });
assert.equal(response.statusCode, 200);
assert(body.settings);
assert(body.languages);
assert(body.homePageRoutes);
});
it('should load edit page', (done) => {
request(`${nconf.get('url')}/api/user/updatedagain/edit`, { jar: jar, json: true }, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert(body);
done();
});
it('should load edit page', async () => {
const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/edit`, { jar });
assert.equal(response.statusCode, 200);
assert(body);
});
it('should load edit/email page', async () => {
const res = await requestAsync(`${nconf.get('url')}/api/user/updatedagain/edit/email`, { jar: jar, json: true, resolveWithFullResponse: true });
assert.strictEqual(res.statusCode, 200);
assert(res.body);
const { response, body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/edit/email`, { jar });
assert.strictEqual(response.statusCode, 200);
assert(body);
// Accessing this page will mark the user's account as needing an updated email, below code undo's.
await requestAsync({
uri: `${nconf.get('url')}/register/abort`,
await request.post(`${nconf.get('url')}/register/abort`, {
jar,
method: 'POST',
simple: false,
validateStatus: null,
headers: {
'x-csrf-token': csrf_token,
},
@@ -1246,7 +1231,7 @@ describe('User', () => {
});
await groups.join('Test', uid);
const body = await requestAsync(`${nconf.get('url')}/api/user/updatedagain/groups`, { jar: jar, json: true });
const { body } = await request.get(`${nconf.get('url')}/api/user/updatedagain/groups`, { jar });
assert(Array.isArray(body.groups));
assert.equal(body.groups[0].name, 'Test');
@@ -1549,106 +1534,65 @@ describe('User', () => {
});
describe('unsubscribe via POST', () => {
it('should unsubscribe from digest if one-click unsubscribe is POSTed', (done) => {
it('should unsubscribe from digest if one-click unsubscribe is POSTed', async () => {
const token = jwt.sign({
template: 'digest',
uid: uid,
}, nconf.get('secret'));
request({
method: 'post',
url: `${nconf.get('url')}/email/unsubscribe/${token}`,
}, (err, res) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 200);
db.getObjectField(`user:${uid}:settings`, 'dailyDigestFreq', (err, value) => {
assert.ifError(err);
assert.strictEqual(value, 'off');
done();
});
});
const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`);
assert.strictEqual(response.statusCode, 200);
const value = await db.getObjectField(`user:${uid}:settings`, 'dailyDigestFreq');
assert.strictEqual(value, 'off');
});
it('should unsubscribe from notifications if one-click unsubscribe is POSTed', (done) => {
it('should unsubscribe from notifications if one-click unsubscribe is POSTed', async () => {
const token = jwt.sign({
template: 'notification',
type: 'test',
uid: uid,
}, nconf.get('secret'));
request({
method: 'post',
url: `${nconf.get('url')}/email/unsubscribe/${token}`,
}, (err, res) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 200);
const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`);
assert.strictEqual(response.statusCode, 200);
db.getObjectField(`user:${uid}:settings`, 'notificationType_test', (err, value) => {
assert.ifError(err);
assert.strictEqual(value, 'notification');
done();
});
});
const value = await db.getObjectField(`user:${uid}:settings`, 'notificationType_test');
assert.strictEqual(value, 'notification');
});
it('should return errors on missing template in token', (done) => {
it('should return errors on missing template in token', async () => {
const token = jwt.sign({
uid: uid,
}, nconf.get('secret'));
request({
method: 'post',
url: `${nconf.get('url')}/email/unsubscribe/${token}`,
}, (err, res) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 404);
done();
});
const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`, { validateStatus: null });
assert.strictEqual(response.statusCode, 404);
});
it('should return errors on wrong template in token', (done) => {
it('should return errors on wrong template in token', async () => {
const token = jwt.sign({
template: 'user',
uid: uid,
}, nconf.get('secret'));
request({
method: 'post',
url: `${nconf.get('url')}/email/unsubscribe/${token}`,
}, (err, res) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 404);
done();
});
const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`, { validateStatus: null });
assert.strictEqual(response.statusCode, 404);
});
it('should return errors on missing token', (done) => {
request({
method: 'post',
url: `${nconf.get('url')}/email/unsubscribe/`,
}, (err, res) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 404);
done();
});
it('should return errors on missing token', async () => {
const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/`, { validateStatus: null });
assert.strictEqual(response.statusCode, 404);
});
it('should return errors on token signed with wrong secret (verify-failure)', (done) => {
it('should return errors on token signed with wrong secret (verify-failure)', async () => {
const token = jwt.sign({
template: 'notification',
type: 'test',
uid: uid,
}, `${nconf.get('secret')}aababacaba`);
request({
method: 'post',
url: `${nconf.get('url')}/email/unsubscribe/${token}`,
}, (err, res) => {
assert.ifError(err);
assert.strictEqual(res.statusCode, 403);
done();
});
const { response } = await request.post(`${nconf.get('url')}/email/unsubscribe/${token}`, { validateStatus: null });
assert.strictEqual(response.statusCode, 403);
});
});
});
@@ -1974,89 +1918,66 @@ describe('User', () => {
gdpr_consent: true,
});
const { jar } = await helpers.loginUser('admin', '123456');
const { users } = await requestAsync(`${nconf.get('url')}/api/admin/manage/registration`, { jar, json: true });
const { body: { users } } = await request.get(`${nconf.get('url')}/api/admin/manage/registration`, { jar });
assert.equal(users[0].username, 'rejectme');
assert.equal(users[0].email, '&lt;script&gt;alert(&quot;ok&quot;)&lt;script&gt;reject@me.com');
});
it('should fail to add user to queue if username is taken', (done) => {
helpers.registerUser({
it('should fail to add user to queue if username is taken', async () => {
const { body } = await helpers.registerUser({
username: 'rejectme',
password: '123456',
'password-confirm': '123456',
email: '<script>alert("ok")<script>reject@me.com',
gdpr_consent: true,
}, (err, jar, res, body) => {
assert.ifError(err);
assert.equal(body, '[[error:username-taken]]');
done();
});
assert.equal(body, '[[error:username-taken]]');
});
it('should fail to add user to queue if email is taken', (done) => {
helpers.registerUser({
it('should fail to add user to queue if email is taken', async () => {
const { body } = await helpers.registerUser({
username: 'rejectmenew',
password: '123456',
'password-confirm': '123456',
email: '<script>alert("ok")<script>reject@me.com',
gdpr_consent: true,
}, (err, jar, res, body) => {
assert.ifError(err);
assert.equal(body, '[[error:email-taken]]');
done();
});
assert.equal(body, '[[error:email-taken]]');
});
it('should reject user registration', (done) => {
socketUser.rejectRegistration({ uid: adminUid }, { username: 'rejectme' }, (err) => {
assert.ifError(err);
User.getRegistrationQueue(0, -1, (err, users) => {
assert.ifError(err);
assert.equal(users.length, 0);
done();
});
});
it('should reject user registration', async () => {
await socketUser.rejectRegistration({ uid: adminUid }, { username: 'rejectme' });
const users = await User.getRegistrationQueue(0, -1);
assert.equal(users.length, 0);
});
it('should accept user registration', (done) => {
helpers.registerUser({
it('should accept user registration', async () => {
await helpers.registerUser({
username: 'acceptme',
password: '123456',
'password-confirm': '123456',
email: 'accept@me.com',
gdpr_consent: true,
}, (err) => {
assert.ifError(err);
socketUser.acceptRegistration({ uid: adminUid }, { username: 'acceptme' }, (err, uid) => {
assert.ifError(err);
User.exists(uid, (err, exists) => {
assert.ifError(err);
assert(exists);
User.getRegistrationQueue(0, -1, (err, users) => {
assert.ifError(err);
assert.equal(users.length, 0);
done();
});
});
});
});
const uid = await socketUser.acceptRegistration({ uid: adminUid }, { username: 'acceptme' });
const exists = await User.exists(uid);
assert(exists);
const users = await User.getRegistrationQueue(0, -1);
assert.equal(users.length, 0);
});
it('should trim username and add user to registration queue', (done) => {
helpers.registerUser({
it('should trim username and add user to registration queue', async () => {
await helpers.registerUser({
username: 'invalidname\r\n',
password: '123456',
'password-confirm': '123456',
email: 'invalidtest@test.com',
gdpr_consent: true,
}, (err) => {
assert.ifError(err);
db.getSortedSetRange('registration:queue', 0, -1, (err, data) => {
assert.ifError(err);
assert.equal(data[0], 'invalidname');
done();
});
});
const users = await db.getSortedSetRange('registration:queue', 0, -1);
assert.equal(users[0], 'invalidname');
});
});
@@ -2104,16 +2025,16 @@ describe('User', () => {
});
it('should error if user does not have invite privilege', async () => {
const { res } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, notAnInviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 403);
assert.strictEqual(res.body.status.message, 'You do not have enough privileges for this action.');
const { response, body } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, notAnInviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 403);
assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.');
});
it('should error out if user tries to use an inviter\'s uid via the API', async () => {
const { res } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
const { response, body } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
const numInvites = await User.getInvitesNumber(inviterUid);
assert.strictEqual(res.statusCode, 403);
assert.strictEqual(res.body.status.message, 'You do not have enough privileges for this action.');
assert.strictEqual(response.statusCode, 403);
assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.');
assert.strictEqual(numInvites, 0);
});
});
@@ -2127,82 +2048,82 @@ describe('User', () => {
});
it('should error with invalid data', async () => {
const { res } = await helpers.invite({}, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 400);
assert.strictEqual(res.body.status.message, 'Invalid Data');
const { response, body } = await helpers.invite({}, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 400);
assert.strictEqual(body.status.message, 'Invalid Data');
});
it('should error if user is not admin and type is admin-invite-only', async () => {
meta.config.registrationType = 'admin-invite-only';
const { res } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 403);
assert.strictEqual(res.body.status.message, 'You do not have enough privileges for this action.');
const { response, body } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 403);
assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.');
});
it('should send invitation email (without groups to be joined)', async () => {
meta.config.registrationType = 'normal';
const { res } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.invite({ emails: 'invite1@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
});
it('should send multiple invitation emails (with a public group to be joined)', async () => {
const { res } = await helpers.invite({ emails: 'invite2@test.com,invite3@test.com', groupsToJoin: [PUBLIC_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 200);
const { response, body } = await helpers.invite({ emails: 'invite2@test.com,invite3@test.com', groupsToJoin: [PUBLIC_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
});
it('should error if the user has not permission to invite to the group', async () => {
const { res } = await helpers.invite({ emails: 'invite4@test.com', groupsToJoin: [PRIVATE_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 403);
assert.strictEqual(res.body.status.message, 'You do not have enough privileges for this action.');
const { response, body } = await helpers.invite({ emails: 'invite4@test.com', groupsToJoin: [PRIVATE_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 403);
assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.');
});
it('should error if a non-admin tries to invite to the administrators group', async () => {
const { res } = await helpers.invite({ emails: 'invite4@test.com', groupsToJoin: ['administrators'] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 403);
assert.strictEqual(res.body.status.message, 'You do not have enough privileges for this action.');
const { response, body } = await helpers.invite({ emails: 'invite4@test.com', groupsToJoin: ['administrators'] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 403);
assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.');
});
it('should to invite to own private group', async () => {
const { res } = await helpers.invite({ emails: 'invite4@test.com', groupsToJoin: [OWN_PRIVATE_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.invite({ emails: 'invite4@test.com', groupsToJoin: [OWN_PRIVATE_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
});
it('should to invite to multiple groups', async () => {
const { res } = await helpers.invite({ emails: 'invite5@test.com', groupsToJoin: [PUBLIC_GROUP, OWN_PRIVATE_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.invite({ emails: 'invite5@test.com', groupsToJoin: [PUBLIC_GROUP, OWN_PRIVATE_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
});
it('should error if tries to invite to hidden group', async () => {
const { res } = await helpers.invite({ emails: 'invite6@test.com', groupsToJoin: [HIDDEN_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 403);
const { response } = await helpers.invite({ emails: 'invite6@test.com', groupsToJoin: [HIDDEN_GROUP] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 403);
});
it('should error if out of invitations', async () => {
meta.config.maximumInvites = 1;
const { res } = await helpers.invite({ emails: 'invite6@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 403);
assert.strictEqual(res.body.status.message, `You have invited the maximum amount of people (${5} out of ${1}).`);
const { response, body } = await helpers.invite({ emails: 'invite6@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 403);
assert.strictEqual(body.status.message, `You have invited the maximum amount of people (${5} out of ${1}).`);
meta.config.maximumInvites = 10;
});
it('should send invitation email after maximumInvites increased', async () => {
const { res } = await helpers.invite({ emails: 'invite6@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.invite({ emails: 'invite6@test.com', groupsToJoin: [] }, inviterUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
});
it('should error if invite is sent via API with a different UID', async () => {
const { res } = await helpers.invite({ emails: 'inviter@nodebb.org', groupsToJoin: [] }, adminUid, jar, csrf_token);
const { response, body } = await helpers.invite({ emails: 'inviter@nodebb.org', groupsToJoin: [] }, adminUid, jar, csrf_token);
const numInvites = await User.getInvitesNumber(adminUid);
assert.strictEqual(res.statusCode, 403);
assert.strictEqual(res.body.status.message, 'You do not have enough privileges for this action.');
assert.strictEqual(response.statusCode, 403);
assert.strictEqual(body.status.message, 'You do not have enough privileges for this action.');
assert.strictEqual(numInvites, 0);
});
it('should succeed if email exists but not actually send an invite', async () => {
const { res } = await helpers.invite({ emails: 'inviter@nodebb.org', groupsToJoin: [] }, inviterUid, jar, csrf_token);
const { response } = await helpers.invite({ emails: 'inviter@nodebb.org', groupsToJoin: [] }, inviterUid, jar, csrf_token);
const numInvites = await User.getInvitesNumber(adminUid);
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(response.statusCode, 200);
assert.strictEqual(numInvites, 0);
});
});
@@ -2223,8 +2144,8 @@ describe('User', () => {
});
it('should invite to the administrators group if inviter is an admin', async () => {
const { res } = await helpers.invite({ emails: 'invite99@test.com', groupsToJoin: ['administrators'] }, adminUid, jar, csrf_token);
assert.strictEqual(res.statusCode, 200);
const { response } = await helpers.invite({ emails: 'invite99@test.com', groupsToJoin: ['administrators'] }, adminUid, jar, csrf_token);
assert.strictEqual(response.statusCode, 200);
});
});
@@ -2319,29 +2240,18 @@ describe('User', () => {
const groupsToJoin = [PUBLIC_GROUP, OWN_PRIVATE_GROUP];
const token = await db.get(`invitation:uid:${inviterUid}:invited:${email}`);
await new Promise((resolve, reject) => {
helpers.registerUser({
username: 'invite5',
password: '123456',
'password-confirm': '123456',
email: email,
gdpr_consent: true,
token: token,
}, async (err, jar, response, body) => {
if (err) {
reject(err);
}
const memberships = await groups.isMemberOfGroups(body.uid, groupsToJoin);
const joinedToAll = memberships.filter(Boolean);
if (joinedToAll.length !== groupsToJoin.length) {
reject(new Error('Not joined to the groups'));
}
resolve();
});
const { body } = await helpers.registerUser({
username: 'invite5',
password: '123456',
'password-confirm': '123456',
email: email,
gdpr_consent: true,
token: token,
});
const memberships = await groups.isMemberOfGroups(body.uid, groupsToJoin);
const joinedToAll = memberships.filter(Boolean);
assert.strictEqual(joinedToAll.length, groupsToJoin.length, 'Not joined to the groups');
});
});
@@ -2354,9 +2264,7 @@ describe('User', () => {
});
it('should show a list of groups for adding to an invite', async () => {
const body = await requestAsync({
url: `${nconf.get('url')}/api/v3/users/${inviterUid}/invites/groups`,
json: true,
const { body } = await helpers.request('get', `/api/v3/users/${inviterUid}/invites/groups`, {
jar,
});
@@ -2366,15 +2274,11 @@ describe('User', () => {
});
it('should error out if you request invite groups for another uid', async () => {
const res = await requestAsync({
url: `${nconf.get('url')}/api/v3/users/${adminUid}/invites/groups`,
json: true,
const { response } = await helpers.request('get', `/api/v3/users/${adminUid}/invites/groups`, {
jar,
simple: false,
resolveWithFullResponse: true,
});
assert.strictEqual(res.statusCode, 403);
assert.strictEqual(response.statusCode, 403);
});
});
});
@@ -2515,8 +2419,8 @@ describe('User', () => {
async function assertPrivacy({ expectVisible, jar, v3Api, emailOnly }) {
const path = v3Api ? `v3/users/${hidingUser.uid}` : `user/${hidingUser.username}`;
const response = await requestAsync(`${nconf.get('url')}/api/${path}`, { json: true, jar });
const { response: userData } = v3Api ? response : { response };
const { body } = await request.get(`${nconf.get('url')}/api/${path}`, { jar });
const userData = v3Api ? body.response : body;
assert.strictEqual(userData.email, expectVisible ? hidingUser.email : '');
if (!emailOnly) {
@@ -2657,24 +2561,19 @@ describe('User', () => {
assert.strictEqual(userData[1].email, '');
});
it('should hide fullname in topic list and topic', (done) => {
Topics.post({
it('should hide fullname in topic list and topic', async () => {
await Topics.post({
uid: hidingUser.uid,
title: 'Topic hidden',
content: 'lorem ipsum',
cid: testCid,
}, (err) => {
assert.ifError(err);
request(`${nconf.get('url')}/api/recent`, { json: true }, (err, res, body) => {
assert.ifError(err);
assert(!body.topics[0].user.hasOwnProperty('fullname'));
request(`${nconf.get('url')}/api/topic/${body.topics[0].slug}`, { json: true }, (err, res, body) => {
assert.ifError(err);
assert(!body.posts[0].user.hasOwnProperty('fullname'));
done();
});
});
});
const { body: body1 } = await request.get(`${nconf.get('url')}/api/recent`);
assert(!body1.topics[0].user.hasOwnProperty('fullname'));
const { body: body2 } = await request.get(`${nconf.get('url')}/api/topic/${body1.topics[0].slug}`);
assert(!body2.posts[0].user.hasOwnProperty('fullname'));
});
});

View File

@@ -141,26 +141,16 @@ describe('email confirmation (library methods)', () => {
describe('email confirmation (v3 api)', () => {
let userObj;
let jar;
const register = data => new Promise((resolve, reject) => {
helpers.registerUser(data, (err, jar, response, body) => {
if (err) {
return reject(err);
}
resolve({ jar, response, body });
});
});
before(async () => {
// If you're running this file directly, uncomment these lines
await register({
await helpers.registerUser({
username: 'fake-user',
password: 'derpioansdosa',
email: 'b@c.com',
gdpr_consent: true,
});
({ body: userObj, jar } = await register({
({ body: userObj, jar } = await helpers.registerUser({
username: 'email-test',
password: 'abcdef',
email: 'test@example.org',
@@ -174,43 +164,40 @@ describe('email confirmation (v3 api)', () => {
});
it('should not list their email', async () => {
const { res, body } = await helpers.request('get', `/api/v3/users/${userObj.uid}/emails`, {
const { response, body } = await helpers.request('get', `/api/v3/users/${userObj.uid}/emails`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(response.statusCode, 200);
assert.deepStrictEqual(body, JSON.parse('{"status":{"code":"ok","message":"OK"},"response":{"emails":[]}}'));
});
it('should not allow confirmation if they are not an admin', async () => {
const { res } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('test@example.org')}/confirm`, {
const { response } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('test@example.org')}/confirm`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 403);
assert.strictEqual(response.statusCode, 403);
});
it('should not confirm an email that is not pending or set', async () => {
await groups.join('administrators', userObj.uid);
const { res, body } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('fake@example.org')}/confirm`, {
const { response } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('fake@example.org')}/confirm`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 404);
assert.strictEqual(response.statusCode, 404);
await groups.leave('administrators', userObj.uid);
});
it('should confirm their email (using the pending validation)', async () => {
await groups.join('administrators', userObj.uid);
const { res, body } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('test@example.org')}/confirm`, {
const { response, body } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('test@example.org')}/confirm`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(response.statusCode, 200);
assert.deepStrictEqual(body, JSON.parse('{"status":{"code":"ok","message":"OK"},"response":{}}'));
await groups.leave('administrators', userObj.uid);
});
@@ -221,12 +208,12 @@ describe('email confirmation (v3 api)', () => {
({ jar } = await helpers.loginUser('email-test', 'abcdef')); // email removal logs out everybody
await groups.join('administrators', userObj.uid);
const { res, body } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('test@example.org')}/confirm`, {
const { response, body } = await helpers.request('post', `/api/v3/users/${userObj.uid}/emails/${encodeURIComponent('test@example.org')}/confirm`, {
jar,
json: true,
});
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(response.statusCode, 200);
assert.deepStrictEqual(body, JSON.parse('{"status":{"code":"ok","message":"OK"},"response":{}}'));
await groups.leave('administrators', userObj.uid);
});