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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
const assert = require('assert'); const assert = require('assert');
const async = require('async'); const async = require('async');
const request = require('request');
const nconf = require('nconf'); const nconf = require('nconf');
const db = require('./mocks/databasemock'); const db = require('./mocks/databasemock');
@@ -12,6 +12,7 @@ const categories = require('../src/categories');
const user = require('../src/user'); const user = require('../src/user');
const search = require('../src/search'); const search = require('../src/search');
const privileges = require('../src/privileges'); const privileges = require('../src/privileges');
const request = require('../src/request');
describe('Search', () => { describe('Search', () => {
let phoebeUid; let phoebeUid;
@@ -104,25 +105,19 @@ describe('Search', () => {
], done); ], 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 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`; 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) => { await privileges.global.give(['groups:search:content'], 'guests');
assert.ifError(err);
request({ const { body } = await request.get(nconf.get('url') + qs);
url: nconf.get('url') + qs,
json: true,
}, (err, response, body) => {
assert.ifError(err);
assert(body); assert(body);
assert.equal(body.matchCount, 1); assert.equal(body.matchCount, 1);
assert.equal(body.posts.length, 1); assert.equal(body.posts.length, 1);
assert.equal(body.posts[0].pid, post1Data.pid); assert.equal(body.posts[0].pid, post1Data.pid);
assert.equal(body.posts[0].uid, phoebeUid); assert.equal(body.posts[0].uid, phoebeUid);
privileges.global.rescind(['groups:search:content'], 'guests', done); await privileges.global.rescind(['groups:search:content'], 'guests');
});
});
}); });
it('should search for a user', (done) => { it('should search for a user', (done) => {
@@ -254,15 +249,11 @@ describe('Search', () => {
], done); ], 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'; const qs = '/api/search?term=cucumber&in=titlesposts&searchOnly=1';
privileges.global.give(['groups:search:content'], 'guests', (err) => { await privileges.global.give(['groups:search:content'], 'guests');
assert.ifError(err);
request({ const { body } = await request.get(nconf.get('url') + qs);
url: nconf.get('url') + qs,
json: true,
}, (err, response, body) => {
assert.ifError(err);
assert(body); assert(body);
assert(body.hasOwnProperty('matchCount')); assert(body.hasOwnProperty('matchCount'));
assert(body.hasOwnProperty('pagination')); assert(body.hasOwnProperty('pagination'));
@@ -270,24 +261,15 @@ describe('Search', () => {
assert(body.hasOwnProperty('posts')); assert(body.hasOwnProperty('posts'));
assert(!body.hasOwnProperty('categories')); assert(!body.hasOwnProperty('categories'));
privileges.global.rescind(['groups:search:content'], 'guests', done); 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'; const qs = '/api/search';
privileges.global.give(['groups:search:content'], 'guests', (err) => { await privileges.global.give(['groups:search:content'], 'guests');
assert.ifError(err); const { response, body } = await request.get(nconf.get('url') + qs);
request({
url: nconf.get('url') + qs,
json: true,
}, (err, response, body) => {
assert.ifError(err);
assert(body); assert(body);
assert.strictEqual(response.statusCode, 200); assert.strictEqual(response.statusCode, 200);
privileges.global.rescind(['groups:search:content'], 'guests', done); await privileges.global.rescind(['groups:search:content'], 'guests');
});
});
}); });
}); });

View File

@@ -11,9 +11,7 @@ const sleep = util.promisify(setTimeout);
const assert = require('assert'); const assert = require('assert');
const async = require('async'); const async = require('async');
const nconf = require('nconf'); const nconf = require('nconf');
const request = require('request');
const cookies = request.jar();
const db = require('./mocks/databasemock'); const db = require('./mocks/databasemock');
const user = require('../src/user'); const user = require('../src/user');
@@ -52,35 +50,11 @@ describe('socket.io', () => {
}); });
it('should connect and auth properly', (done) => { it('should connect and auth properly', async () => {
request.get({ const { response, csrf_token } = await helpers.loginUser('admin', 'adminpwd');
url: `${nconf.get('url')}/api/config`, io = await helpers.connectSocketIO(response, csrf_token);
jar: cookies, assert(io);
json: true, assert(io.emit);
}, (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 return error for unknown event', (done) => { it('should return error for unknown event', (done) => {

View File

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

View File

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

View File

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

View File

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