Compare commits

..

15 Commits

Author SHA1 Message Date
Misty (Bot)
a2468b84b4 chore: incrementing version number - v1.15.1
(cherry picked from commit 2790a3a3fe)
Signed-off-by: Misty (Bot) <deploy@nodebb.org>
2020-11-11 22:51:06 +00:00
Misty (Bot)
efb7d688f0 chore: update changelog for v1.15.1 2020-11-11 22:51:06 +00:00
Barış Soner Uşaklı
dc6e27730a Merge branch 'master' of https://github.com/NodeBB/NodeBB 2020-11-11 12:48:32 -05:00
Barış Soner Uşaklı
567c5f2056 fix: #8869, dont escape category title,description twice 2020-11-11 12:48:22 -05:00
Renovate Bot
1460a7a84d fix(deps): update dependency nodebb-plugin-mentions to v2.13.4 2020-11-11 15:52:59 +00:00
Julian Lam
769aba0aed fix: refresh flags list on bulk action success 2020-11-11 10:15:49 -05:00
Renovate Bot
72d1b3cdca fix(deps): update dependency diff to v5 2020-11-10 14:43:20 -05:00
Julian Lam
ee4d90f68f fix: test breakage from f300c933a5 2020-11-10 14:39:57 -05:00
Julian Lam
f300c933a5 refactor: move session revocation route to write api 2020-11-10 14:27:38 -05:00
renovate[bot]
e250c3f1fb fix(deps): update dependency nodebb-theme-persona to v10.2.67 (#8847)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-10 13:18:52 -05:00
renovate[bot]
ba7b23ac48 fix(deps): update dependency json2csv to v5.0.4 (#8865)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-10 13:15:10 -05:00
renovate[bot]
2946bb1679 fix(deps): update dependency postcss to v8.1.7 (#8866)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-10 13:14:25 -05:00
Barış Soner Uşaklı
9c5c32d4a5 feat: #8864, add action:events.log 2020-11-10 11:29:15 -05:00
renovate[bot]
20f4fe085f fix(deps): update dependency nodebb-theme-slick to v1.2.40 (#8863)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-10 11:10:11 -05:00
renovate[bot]
c18f449190 fix(deps): update dependency nodebb-plugin-mentions to v2.13.3 (#8862)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2020-11-10 10:50:21 -05:00
11 changed files with 148 additions and 67 deletions

View File

@@ -1,3 +1,74 @@
#### v1.15.1 (2020-11-11)
##### Chores
* incrementing version number - v1.15.1-beta.0 (e033da8a)
* fallback l10n for admin-settings-api (8368c25b)
* **deps:**
* update dependency eslint to v7.13.0 (bcbc0854)
* update dependency eslint-config-airbnb-base to v14.2.1 (d227fe9f)
* update commitlint monorepo to v11 (90bcfa6d)
##### New Features
* #8864, add action:events.log (9c5c32d4)
* show db info side by side (62c0454c)
* add language keys for admin-settings-api (d32e4e02)
* #8824, cache refactor (#8851) (f1f9b225)
* move mkdirp to beforeBuild so it doesnt get called twice (6255874e)
* group exists API call in write api (d2631922)
* user exist route in write api (1446cec7)
* new shorthand route /api/v3/users/bySlug/:userslug (60e1e99b)
* allow passwords with length > 73 characters (#8818) (512f6de6)
* #8821, allow guest topic views (9e3eb5d4)
##### Bug Fixes
* #8869, dont escape category title,description twice (567c5f20)
* refresh flags list on bulk action success (769aba0a)
* test breakage from f300c933a50263039a57811f8cc716df39a138b0 (ee4d90f6)
* remove some unnecessary jquery wrappers (9f7902ef)
* send back jquery object to keep backwards compat (978f1ee0)
* use header/footer cache in prod (a0164b1c)
* add missing maxAge to cache (05a92885)
* clear header-cache after each suite (3f5f38dd)
* show msg on fail (255cf43e)
* spec (fe63c6ae)
* guest header/footer cache (2e446392)
* #8846, possible fix (74951f59)
* winston error message (16d03975)
* permanent redirect on user api redirect shorthand (6b196a20)
* user exist route needs no authentication (f2bb42c0)
* #8840, don't crash if /compose route is called with no query params (c61dee4b)
* XSS in event:banned messaging modal (f68bce86)
* #8838, fix chat dropdown timestamps (78ee8332)
* #8836, truncate fullname (76cd5b0f)
* #8827, do not require admin:users privilege to ban users (891a1ea2)
* **deps:**
* update dependency nodebb-plugin-mentions to v2.13.4 (1460a7a8)
* update dependency diff to v5 (72d1b3cd)
* update dependency nodebb-theme-persona to v10.2.67 (#8847) (e250c3f1)
* update dependency json2csv to v5.0.4 (#8865) (ba7b23ac)
* update dependency postcss to v8.1.7 (#8866) (2946bb16)
* update dependency nodebb-theme-slick to v1.2.40 (#8863) (20f4fe08)
* update dependency nodebb-plugin-mentions to v2.13.3 (#8862) (c18f4491)
* update dependency benchpressjs to v2.1.0 (14ba6383)
* update dependency benchpressjs to v2.0.9 (381a32ab)
* update dependency mongodb to v3.6.3 (#8841) (7e273e77)
* update dependency nodebb-theme-persona to v10.2.66 (#8839) (00f90cd9)
* update dependency nodebb-plugin-mentions to v2.13.2 (#8835) (064c99cd)
* update dependency postcss to v8.1.6 (e0cf9740)
* update dependency @nodebb/socket.io-adapter-mongo to v3.1.1 (#8831) (40eb658b)
* update dependency @nodebb/mubsub to v1.7.1 (#8830) (7b8a5567)
* update dependency postcss to v8.1.5 (9f5ef9d0)
* **#8828:** web install templates now compiled (#8832) (de5a21f1)
* **acp:** max-height for plugin menu list (eec630f1)
##### Refactors
* move session revocation route to write api (f300c933)
* change Benchpress.parse to .render (#8856) (e128264b)
#### v1.15.0 (2020-11-04)
##### Breaking Changes

View File

@@ -2,7 +2,7 @@
"name": "nodebb",
"license": "GPL-3.0",
"description": "NodeBB Forum",
"version": "1.15.1-beta.0",
"version": "1.15.1",
"homepage": "http://www.nodebb.org",
"repository": {
"type": "git",
@@ -63,7 +63,7 @@
"cropperjs": "^1.5.6",
"csurf": "^1.11.0",
"daemon": "^1.1.0",
"diff": "^4.0.2",
"diff": "^5.0.0",
"express": "^4.17.1",
"express-session": "^1.17.0",
"express-useragent": "^1.0.13",
@@ -77,7 +77,7 @@
"jquery-serializeobject": "1.0.0",
"jquery-ui": "1.12.1",
"jsesc": "3.0.2",
"json2csv": "5.0.3",
"json2csv": "5.0.4",
"jsonwebtoken": "^8.5.1",
"less": "^3.11.1",
"lodash": "^4.17.15",
@@ -98,13 +98,13 @@
"nodebb-plugin-emoji": "^3.3.0",
"nodebb-plugin-emoji-android": "2.0.0",
"nodebb-plugin-markdown": "8.12.1",
"nodebb-plugin-mentions": "2.13.2",
"nodebb-plugin-mentions": "2.13.4",
"nodebb-plugin-soundpack-default": "1.0.0",
"nodebb-plugin-spam-be-gone": "0.7.3",
"nodebb-rewards-essentials": "0.1.4",
"nodebb-theme-lavender": "5.0.14",
"nodebb-theme-persona": "10.2.66",
"nodebb-theme-slick": "1.2.39",
"nodebb-theme-persona": "10.2.67",
"nodebb-theme-slick": "1.2.40",
"nodebb-theme-vanilla": "11.3.0",
"nodebb-widget-essentials": "4.1.2",
"nodemailer": "^6.4.6",
@@ -114,7 +114,7 @@
"passport-local": "1.0.0",
"pg": "^8.0.2",
"pg-cursor": "^2.1.9",
"postcss": "8.1.6",
"postcss": "8.1.7",
"postcss-clean": "1.1.0",
"promise-polyfill": "^8.1.3",
"prompt": "^1.0.0",

View File

@@ -1,7 +1,7 @@
'use strict';
define('forum/account/sessions', ['forum/account/header', 'components'], function (header, components) {
define('forum/account/sessions', ['forum/account/header', 'components', 'api'], function (header, components, api) {
var Sessions = {};
Sessions.init = function () {
@@ -17,15 +17,9 @@ define('forum/account/sessions', ['forum/account/header', 'components'], functio
if (uuid) {
// This is done via DELETE because a user shouldn't be able to
// revoke his own session! This is what logout is for
$.ajax({
url: config.relative_path + '/api/user/' + ajaxify.data.userslug + '/session/' + uuid,
method: 'delete',
headers: {
'x-csrf-token': config.csrf_token,
},
}).done(function () {
api.del(`/users/${ajaxify.data.uid}/sessions/${uuid}`, {}).then(() => {
parentEl.remove();
}).fail(function (err) {
}).catch((err) => {
try {
var errorObj = JSON.parse(err.responseText);
if (errorObj.loggedIn === false) {

View File

@@ -153,6 +153,7 @@ define('forum/flags/list', ['components', 'Chart'], function (components, Chart)
});
if (fulfilled) {
app.alertSuccess('[[flags:bulk-success, ' + fulfilled + ']]');
ajaxify.refresh();
}
errors.forEach(function (res) {

View File

@@ -1,8 +1,5 @@
'use strict';
const util = require('util');
const db = require('../../database');
const user = require('../../user');
const helpers = require('../helpers');
const accountHelpers = require('./helpers');
@@ -21,39 +18,3 @@ sessionController.get = async function (req, res, next) {
res.render('account/sessions', userData);
};
const getSessionAsync = util.promisify(function (sid, callback) {
db.sessionStore.get(sid, (err, sessionObj) => callback(err, sessionObj || null));
});
sessionController.revoke = async function (req, res, next) {
if (!req.params.hasOwnProperty('uuid')) {
return next();
}
try {
const uid = await user.getUidByUserslug(req.params.userslug);
if (!uid) {
throw new Error('[[error:no-session-found]]');
}
const sids = await db.getSortedSetRange('uid:' + uid + ':sessions', 0, -1);
let _id;
for (const sid of sids) {
/* eslint-disable no-await-in-loop */
const sessionObj = await getSessionAsync(sid);
if (sessionObj && sessionObj.meta && sessionObj.meta.uuid === req.params.uuid) {
_id = sid;
break;
}
}
if (!_id) {
throw new Error('[[error:no-session-found]]');
}
await user.auth.revokeSession(_id, uid);
} catch (err) {
return res.status(500).send(err.message);
}
res.sendStatus(200);
};

View File

@@ -146,14 +146,17 @@ function addTags(categoryData, res) {
{
name: 'title',
content: categoryData.name,
noEscape: true,
},
{
property: 'og:title',
content: categoryData.name,
noEscape: true,
},
{
name: 'description',
content: categoryData.description,
noEscape: true,
},
{
property: 'og:type',

View File

@@ -1,5 +1,8 @@
'use strict';
const util = require('util');
const db = require('../../database');
const api = require('../../api');
const user = require('../../user');
const meta = require('../../meta');
@@ -120,3 +123,32 @@ Users.deleteToken = async (req, res) => {
helpers.formatApiResponse(404, res);
}
};
const getSessionAsync = util.promisify(function (sid, callback) {
db.sessionStore.get(sid, (err, sessionObj) => callback(err, sessionObj || null));
});
Users.revokeSession = async (req, res) => {
// Only admins or global mods (besides the user themselves) can revoke sessions
if (parseInt(req.params.uid, 10) !== req.uid && !await user.isAdminOrGlobalMod(req.uid)) {
return helpers.formatApiResponse(404, res);
}
const sids = await db.getSortedSetRange('uid:' + req.params.uid + ':sessions', 0, -1);
let _id;
for (const sid of sids) {
/* eslint-disable no-await-in-loop */
const sessionObj = await getSessionAsync(sid);
if (sessionObj && sessionObj.meta && sessionObj.meta.uuid === req.params.uuid) {
_id = sid;
break;
}
}
if (!_id) {
throw new Error('[[error:no-session-found]]');
}
await user.auth.revokeSession(_id, req.params.uid);
helpers.formatApiResponse(200, res);
};

View File

@@ -8,6 +8,7 @@ const db = require('./database');
const batch = require('./batch');
const user = require('./user');
const utils = require('./utils');
const plugins = require('./plugins');
const events = module.exports;
@@ -78,6 +79,7 @@ events.log = async function (data) {
], data.timestamp, eid),
db.setObject('event:' + eid, data),
]);
plugins.fireHook('action:events.log', { data: data });
};
events.getEvents = async function (filter, start, stop, from, to) {

View File

@@ -1,5 +1,8 @@
'use strict';
const winston = require('winston');
const nconf = require('nconf');
var helpers = require('./helpers');
var setupPageRoute = helpers.setupPageRoute;
@@ -39,7 +42,14 @@ module.exports = function (app, middleware, controllers) {
setupPageRoute(app, '/user/:userslug/consent', middleware, accountMiddlewares, controllers.accounts.consent.get);
setupPageRoute(app, '/user/:userslug/blocks', middleware, accountMiddlewares, controllers.accounts.blocks.getBlocks);
setupPageRoute(app, '/user/:userslug/sessions', middleware, accountMiddlewares, controllers.accounts.sessions.get);
app.delete('/api/user/:userslug/session/:uuid', [middleware.exposeUid, middleware.ensureSelfOrGlobalPrivilege], controllers.accounts.sessions.revoke);
app.delete('/api/user/:userslug/session/:uuid', [middleware.exposeUid], function (req, res, next) {
// TODO: Remove this entire route in v1.16.0
winston.warn('[router] `/api/user/:userslug/session/:uuid` has been deprecated, use `DELETE /api/v3/users/:uid/sessions/:uuid` or `DELETE /api/v3/users/bySlug/:userslug/sessions/:uuid` instead');
if (!res.locals.uid) {
return next();
}
res.redirect(`${nconf.get('relative_path')}/api/v3/users/${res.locals.uid}/sessions/${req.params.uuid}`);
});
setupPageRoute(app, '/notifications', middleware, [middleware.authenticate], controllers.accounts.notifications.get);
setupPageRoute(app, '/user/:userslug/chats/:roomid?', middleware, middlewares, controllers.accounts.chats.get);

View File

@@ -35,6 +35,8 @@ function authenticatedRoutes() {
setupApiRoute(router, 'post', '/:uid/tokens', [...middlewares, middleware.assert.user], controllers.write.users.generateToken);
setupApiRoute(router, 'delete', '/:uid/tokens/:token', [...middlewares, middleware.assert.user], controllers.write.users.deleteToken);
setupApiRoute(router, 'delete', '/:uid/sessions/:uuid', [...middlewares, middleware.assert.user], controllers.write.users.revokeSession);
// Shorthand route to access user routes by userslug
router.all('/+bySlug/:userslug*?', [], controllers.write.users.redirectBySlug);
}

View File

@@ -813,20 +813,19 @@ describe('Controllers', function () {
});
it('should fail if user doesn\'t exist', function (done) {
request.del(nconf.get('url') + '/api/user/doesnotexist/session/1112233', {
request.del(`${nconf.get('url')}/api/v3/users/doesnotexist/sessions/1112233`, {
jar: jar,
headers: {
'x-csrf-token': csrf_token,
},
}, function (err, res, body) {
assert.ifError(err);
assert.equal(res.statusCode, 403);
assert.deepEqual(JSON.parse(body), {
response: {},
status: {
code: 'forbidden',
message: 'You are not authorised to make this call',
},
assert.strictEqual(res.statusCode, 404);
const parsedResponse = JSON.parse(body);
assert.deepStrictEqual(parsedResponse.response, {});
assert.deepStrictEqual(parsedResponse.status, {
code: 'not-found',
message: '[[error:no-user]]',
});
done();
});
@@ -839,15 +838,21 @@ describe('Controllers', function () {
db.sessionStore.get(sid, function (err, sessionObj) {
assert.ifError(err);
request.del(nconf.get('url') + '/api/user/revokeme/session/' + sessionObj.meta.uuid, {
request.del(`${nconf.get('url')}/api/v3/users/${uid}/sessions/${sessionObj.meta.uuid}`, {
jar: jar,
headers: {
'x-csrf-token': csrf_token,
},
}, function (err, res, body) {
assert.ifError(err);
assert.equal(res.statusCode, 200);
assert.equal(body, 'OK');
assert.strictEqual(res.statusCode, 200);
assert.deepStrictEqual(JSON.parse(body), {
status: {
code: 'ok',
message: 'OK',
},
response: {},
});
done();
});
});