Compare commits

...

18 Commits

Author SHA1 Message Date
Barış Soner Uşaklı
1fad6f6804 fix: mongo username/pwd 2021-09-22 09:18:46 -04:00
SAES:RPG
a07b898864 Fix [MONGODB DRIVER] Warning: bulk operation remove has been deprecated, please use delete (#9746)
Co-authored-by: Brophy <paul.brophy@bastage.net>
2021-09-22 09:17:41 -04:00
Barış Soner Uşaklı
d1a7a71003 fix: remove unused translator 2021-09-21 17:16:55 -04:00
Barış Soner Uşaklı
e92f5fc561 fix: ban info test 2021-09-21 17:16:47 -04:00
Barış Soner Uşaklı
3675d343ef merge 2021-09-21 17:15:37 -04:00
Barış Soner Uşaklı
e6c9585ae6 fix: show local time for ban 2021-09-21 16:12:04 -04:00
Barış Soner Uşaklı
f57291ef5f fix: escape moderation note before adding to dom 2021-09-20 11:07:27 -04:00
Barış Soner Uşaklı
9556da11d4 feat: merge 2021-09-20 11:06:55 -04:00
Julian Lam
fdf8231800 feat: add github workflow file for docker push :shipit: 2021-09-17 13:45:29 -04:00
Barış Soner Uşaklı
a8748e137f fix: up timeout for psql tests 2021-09-17 12:29:30 -04:00
Barış Soner Uşaklı
89af582951 fix: xss on flags page via ban reason 2021-09-17 11:55:49 -04:00
Barış Soner Uşaklı
9410a73aaa feat: als (#9749)
* feat: als

* fix: up test timeout

* fix: don't overwrite caller if it already exists

* fix: up test timeout for psql
2021-08-30 10:28:30 -04:00
Renovate Bot
b2652ae697 fix(deps): update dependency nodebb-plugin-markdown to v8.14.1 2021-07-07 16:09:50 -04:00
Misty (Bot)
9f90bdcd59 chore: incrementing version number - v1.17.2 2021-07-07 18:44:40 +00:00
Misty (Bot)
b002d25421 Merge commit '8e0561f2264c46e54c08c6ddd0bc8a7e5511d3d3' into v1.17.x 2021-07-07 18:44:27 +00:00
Misty (Bot)
902da54029 chore: incrementing version number - v1.17.1 2021-05-26 15:40:53 +00:00
Misty (Bot)
7c0da8fcbb Merge commit '925883bf4c586dfce1ddf7948158be714acda1ed' into v1.17.x 2021-05-26 15:40:45 +00:00
Misty (Bot)
6b4ef46ead chore: incrementing version number - v1.17.0 2021-04-22 00:37:31 +00:00
18 changed files with 142 additions and 43 deletions

52
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Run Docker
# Controls when the workflow will run
on:
push:
branches:
- 'master'
- 'v*.x'
tags:
- 'v*'
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: nodebb/docker
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest
- name: Build and push Docker images
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}

View File

@@ -2,7 +2,7 @@
"name": "nodebb",
"license": "GPL-3.0",
"description": "NodeBB Forum",
"version": "1.17.1",
"version": "1.17.2",
"homepage": "http://www.nodebb.org",
"repository": {
"type": "git",
@@ -88,7 +88,7 @@
"nodebb-plugin-dbsearch": "5.0.2",
"nodebb-plugin-emoji": "^3.5.0",
"nodebb-plugin-emoji-android": "2.0.5",
"nodebb-plugin-markdown": "8.14.0",
"nodebb-plugin-markdown": "8.14.1",
"nodebb-plugin-mentions": "2.13.11",
"nodebb-plugin-spam-be-gone": "0.7.9",
"nodebb-rewards-essentials": "0.1.5",
@@ -182,4 +182,4 @@
"url": "https://github.com/barisusakli"
}
]
}
}

View File

@@ -21,7 +21,7 @@ define('forum/account/info', ['forum/account/header', 'components', 'forum/accou
app.alertSuccess('[[user:info.moderation-note.success]]');
var timestamp = Date.now();
var data = [{
note: note,
note: utils.escapeHTML(note),
user: app.user,
timestamp: timestamp,
timestampISO: utils.toISOString(timestamp),

View File

@@ -1,7 +1,7 @@
'use strict';
define('forum/login', ['jquery-form'], function () {
define('forum/login', ['translator', 'jquery-form'], function (translator) {
var Login = {
_capsState: false,
};
@@ -44,17 +44,22 @@ define('forum/login', ['jquery-form'], function () {
window.location.href = pathname + '?' + qs;
},
error: function (data) {
var message = data.responseText;
var errInfo = data.responseJSON;
if (data.status === 403 && data.responseText === 'Forbidden') {
window.location.href = config.relative_path + '/login?error=csrf-invalid';
} else {
errorEl.find('p').translateText(data.responseText);
errorEl.show();
submitEl.removeClass('disabled');
} else if (errInfo && errInfo.hasOwnProperty('banned_until')) {
message = errInfo.banned_until ?
translator.compile('error:user-banned-reason-until', (new Date(errInfo.banned_until).toLocaleString()), errInfo.reason) :
'[[error:user-banned-reason, ' + errInfo.reason + ']]';
}
errorEl.find('p').translateText(message);
errorEl.show();
submitEl.removeClass('disabled');
// Select the entire password if that field has focus
if ($('#password:focus').length) {
$('#password').select();
}
// Select the entire password if that field has focus
if ($('#password:focus').length) {
$('#password').select();
}
},
});

View File

@@ -208,15 +208,20 @@ socket = window.socket;
}
function onEventBanned(data) {
var message = data.until ? '[[error:user-banned-reason-until, ' + utils.toISOString(data.until) + ', ' + data.reason + ']]' : '[[error:user-banned-reason, ' + data.reason + ']]';
bootbox.alert({
title: '[[error:user-banned]]',
message: message,
closeButton: false,
callback: function () {
window.location.href = config.relative_path + '/';
},
require(['translator'], function (translator) {
var message = data.until ?
translator.compile('error:user-banned-reason-until', (new Date(data.until).toLocaleString()), data.reason) :
'[[error:user-banned-reason, ' + data.reason + ']]';
translator.translate(message, function (message) {
bootbox.alert({
title: '[[error:user-banned]]',
message: message,
closeButton: false,
callback: function () {
window.location.href = config.relative_path + '/';
},
});
});
});
}

7
src/als.js Normal file
View File

@@ -0,0 +1,7 @@
'use strict';
const { AsyncLocalStorage } = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();
module.exports = asyncLocalStorage;

View File

@@ -15,7 +15,6 @@ const user = require('../user');
const plugins = require('../plugins');
const utils = require('../utils');
const slugify = require('../slugify');
const translator = require('../translator');
const helpers = require('./helpers');
const privileges = require('../privileges');
const sockets = require('../socket.io');
@@ -263,7 +262,7 @@ function continueLogin(strategy, req, res, next) {
passport.authenticate(strategy, async (err, userData, info) => {
if (err) {
plugins.hooks.fire('action:login.continue', { req, strategy, userData, error: err });
return helpers.noScriptErrors(req, res, err.message, 403);
return helpers.noScriptErrors(req, res, err.data || err.message, 403);
}
if (!userData) {
@@ -423,8 +422,7 @@ authenticationController.localLogin = async function (req, username, password, n
userData.isAdminOrGlobalMod = isAdminOrGlobalMod;
if (!canLoginIfBanned) {
const banMesage = await getBanInfo(uid);
return next(new Error(banMesage));
return next(await getBanError(uid));
}
// Doing this after the ban check, because user's privileges might change after a ban expires
@@ -483,19 +481,19 @@ authenticationController.logout = async function (req, res, next) {
}
};
async function getBanInfo(uid) {
async function getBanError(uid) {
try {
const banInfo = await user.getLatestBanInfo(uid);
if (!banInfo.reason) {
banInfo.reason = await translator.translate('[[user:info.banned-no-reason]]');
banInfo.reason = '[[user:info.banned-no-reason]]';
}
return banInfo.banned_until ?
`[[error:user-banned-reason-until, ${banInfo.banned_until_readable}, ${banInfo.reason}]]` :
`[[error:user-banned-reason, ${banInfo.reason}]]`;
const err = new Error(banInfo.reason);
err.data = banInfo;
return err;
} catch (err) {
if (err.message === 'no-ban-info') {
return '[[error:user-banned]]';
return new Error('[[error:user-banned]]');
}
throw err;
}

View File

@@ -11,6 +11,7 @@ const privileges = require('../privileges');
const categories = require('../categories');
const plugins = require('../plugins');
const meta = require('../meta');
const middlewareHelpers = require('../middleware/helpers');
const helpers = module.exports;
@@ -19,7 +20,10 @@ const url = nconf.get('url');
helpers.noScriptErrors = async function (req, res, error, httpStatus) {
if (req.body.noscript !== 'true') {
return res.status(httpStatus).send(error);
if (typeof error === 'string') {
return res.status(httpStatus).send(error);
}
return res.status(httpStatus).json(error);
}
const middleware = require('../middleware');
const httpStatusString = httpStatus.toString();
@@ -124,7 +128,17 @@ helpers.notAllowed = async function (req, res, error) {
if (req.loggedIn || req.uid === -1) {
if (res.locals.isAPI) {
helpers.formatApiResponse(403, res, error);
if (req.originalUrl.startsWith(`${relative_path}/api/v3`)) {
helpers.formatApiResponse(403, res, error);
} else {
res.status(403).json({
path: req.path.replace(/^\/api/, ''),
loggedIn: req.loggedIn,
error: error,
title: '[[global:403.title]]',
bodyClass: middlewareHelpers.buildBodyClass(req, res),
});
}
} else {
const middleware = require('../middleware');
await middleware.buildHeaderAsync(req, res);

View File

@@ -12,7 +12,7 @@ connection.getConnectionString = function (mongo) {
let usernamePassword = '';
const uri = mongo.uri || '';
if (mongo.username && mongo.password) {
usernamePassword = `${nconf.get('mongo:username')}:${encodeURIComponent(nconf.get('mongo:password'))}@`;
usernamePassword = `${mongo.username}:${encodeURIComponent(mongo.password)}@`;
} else if (!uri.includes('@') || !uri.slice(uri.indexOf('://') + 3, uri.indexOf('@'))) {
winston.warn('You have no mongo username/password setup!');
}

View File

@@ -57,7 +57,7 @@ module.exports = function (module) {
return;
}
const bulk = module.client.collection('objects').initializeUnorderedBulkOp();
data.forEach(item => bulk.find({ _key: item[0], value: String(item[1]) }).remove());
data.forEach(item => bulk.find({ _key: item[0], value: String(item[1]) }).delete());
await bulk.execute();
};
};

View File

@@ -776,7 +776,7 @@ async function mergeBanHistory(history, targetUid, uids) {
meta: [
{
key: '[[user:banned]]',
value: cur.reason,
value: validator.escape(String(cur.reason)),
labelClass: 'danger',
},
{

View File

@@ -96,6 +96,10 @@ Hooks.fire = async function (hook, params) {
winston.warn(`[plugins] Unknown hookType: ${hookType}, hook : ${hook}`);
return;
}
if (params && typeof params === 'object' && !params.hasOwnProperty('caller')) {
const als = require('../als');
params.caller = als.getStore();
}
const result = await hookTypeToMethod[hookType](hook, hookList, params);
if (hook !== 'action:plugins.firehook' && hook !== 'filter:plugins.firehook') {

View File

@@ -73,7 +73,8 @@ function onConnection(socket) {
onConnect(socket);
socket.onAny((event, ...args) => {
const payload = { data: [event].concat(args) };
onMessage(socket, payload);
const als = require('../als');
als.run({ uid: socket.uid }, onMessage, socket, payload);
});
socket.on('disconnect', () => {

View File

@@ -171,6 +171,10 @@ function setupExpressApp(app) {
app.use(middleware.addHeaders);
app.use(middleware.processRender);
auth.initialize(app, middleware);
const als = require('./als');
app.use((req, res, next) => {
als.run({ uid: req.uid }, next);
});
app.use(middleware.autoLocale); // must be added after auth middlewares are added
const toobusy = require('toobusy-js');

View File

@@ -487,7 +487,15 @@ describe('authentication', () => {
loginUser(bannedUser.username, bannedUser.pw, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 403);
assert.equal(body, '[[error:user-banned-reason, spammer]]');
delete body.timestamp;
assert.deepStrictEqual(body, {
banned_until: 0,
banned_until_readable: '',
expiry: 0,
expiry_readable: '',
reason: 'spammer',
uid: 6,
});
user.bans.unban(bannedUser.uid, (err) => {
assert.ifError(err);
const expiry = Date.now() + 10000;
@@ -496,7 +504,8 @@ describe('authentication', () => {
loginUser(bannedUser.username, bannedUser.pw, (err, res, body) => {
assert.ifError(err);
assert.equal(res.statusCode, 403);
assert.equal(body, `[[error:user-banned-reason-until, ${utils.toISOString(expiry)}, No reason given.]]`);
assert(body.banned_until);
assert(body.reason, '[[user:info.banned-no-reason]]');
done();
});
});

View File

@@ -334,7 +334,7 @@ describe('Sorted Set methods', () => {
});
it('should work with big arrays (length > 100) ', async function () {
this.timeout(50000);
this.timeout(100000);
const keys = [];
for (let i = 0; i < 400; i++) {
/* eslint-disable no-await-in-loop */

View File

@@ -87,7 +87,7 @@ describe('emailer', () => {
assert.equal(output, text);
done();
});
}, 500);
}, 2000);
});
});
});

View File

@@ -359,7 +359,7 @@ describe('Notifications', () => {
assert(nids.includes(nid));
done();
});
}, 1500);
}, 3000);
});
});
});