mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-01-01 05:10:42 +01:00
Compare commits
15 Commits
v1.15.1-be
...
v1.15.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2468b84b4 | ||
|
|
efb7d688f0 | ||
|
|
dc6e27730a | ||
|
|
567c5f2056 | ||
|
|
1460a7a84d | ||
|
|
769aba0aed | ||
|
|
72d1b3cdca | ||
|
|
ee4d90f68f | ||
|
|
f300c933a5 | ||
|
|
e250c3f1fb | ||
|
|
ba7b23ac48 | ||
|
|
2946bb1679 | ||
|
|
9c5c32d4a5 | ||
|
|
20f4fe085f | ||
|
|
c18f449190 |
71
CHANGELOG.md
71
CHANGELOG.md
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user