mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-03-04 19:41:16 +01:00
feat: remote account banning, #13904
This commit is contained in:
@@ -246,7 +246,7 @@ define('admin/manage/users', [
|
|||||||
bootbox.confirm((uids.length > 1 ? '[[admin/manage/users:alerts.confirm-ban-multi]]' : '[[admin/manage/users:alerts.confirm-ban]]'), function (confirm) {
|
bootbox.confirm((uids.length > 1 ? '[[admin/manage/users:alerts.confirm-ban-multi]]' : '[[admin/manage/users:alerts.confirm-ban]]'), function (confirm) {
|
||||||
if (confirm) {
|
if (confirm) {
|
||||||
Promise.all(uids.map(function (uid) {
|
Promise.all(uids.map(function (uid) {
|
||||||
return api.put('/users/' + uid + '/ban');
|
return api.put('/users/' + encodeURIComponent(uid) + '/ban');
|
||||||
})).then(() => {
|
})).then(() => {
|
||||||
onSuccess('[[admin/manage/users:alerts.ban-success]]', '.ban', true);
|
onSuccess('[[admin/manage/users:alerts.ban-success]]', '.ban', true);
|
||||||
}).catch(alerts.error);
|
}).catch(alerts.error);
|
||||||
@@ -284,7 +284,7 @@ define('admin/manage/users', [
|
|||||||
) : 0;
|
) : 0;
|
||||||
|
|
||||||
Promise.all(uids.map(function (uid) {
|
Promise.all(uids.map(function (uid) {
|
||||||
return api.put('/users/' + uid + '/ban', {
|
return api.put('/users/' + encodeURIComponent(uid) + '/ban', {
|
||||||
until: until,
|
until: until,
|
||||||
reason: formData.reason,
|
reason: formData.reason,
|
||||||
});
|
});
|
||||||
@@ -326,7 +326,7 @@ define('admin/manage/users', [
|
|||||||
|
|
||||||
|
|
||||||
Promise.all(uids.map(function (uid) {
|
Promise.all(uids.map(function (uid) {
|
||||||
return api.del('/users/' + uid + '/ban', {
|
return api.del('/users/' + encodeURIComponent(uid) + '/ban', {
|
||||||
reason: formData.reason || '',
|
reason: formData.reason || '',
|
||||||
});
|
});
|
||||||
})).then(() => {
|
})).then(() => {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ define('forum/account/moderate', [
|
|||||||
const until = formData.length > 0 ? (
|
const until = formData.length > 0 ? (
|
||||||
Date.now() + (formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1))
|
Date.now() + (formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1))
|
||||||
) : 0;
|
) : 0;
|
||||||
api.put('/users/' + theirid + '/ban', {
|
api.put('/users/' + encodeURIComponent(theirid) + '/ban', {
|
||||||
until: until,
|
until: until,
|
||||||
reason: formData.reason || '',
|
reason: formData.reason || '',
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
@@ -37,7 +37,7 @@ define('forum/account/moderate', [
|
|||||||
tpl: 'modals/unban',
|
tpl: 'modals/unban',
|
||||||
title: '[[user:unban-account]]',
|
title: '[[user:unban-account]]',
|
||||||
onSubmit: function (formData) {
|
onSubmit: function (formData) {
|
||||||
api.del('/users/' + theirid + '/ban', {
|
api.del('/users/' + encodeURIComponent(theirid) + '/ban', {
|
||||||
reason: formData.reason || '',
|
reason: formData.reason || '',
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
ajaxify.refresh();
|
ajaxify.refresh();
|
||||||
|
|||||||
@@ -111,6 +111,12 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => {
|
|||||||
let { pid: mainPid, tid, uid: authorId, timestamp, title, content, sourceContent, _activitypub } = mainPost;
|
let { pid: mainPid, tid, uid: authorId, timestamp, title, content, sourceContent, _activitypub } = mainPost;
|
||||||
const hasTid = !!tid;
|
const hasTid = !!tid;
|
||||||
|
|
||||||
|
const authorBanned = await user.bans.isBanned(authorId);
|
||||||
|
if (!hasTid && authorBanned) { // New topics only
|
||||||
|
activitypub.helpers.log('[notes/assert] OP is banned, not asserting topic.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const cid = hasTid ? await topics.getTopicField(tid, 'cid') : options.cid || -1;
|
const cid = hasTid ? await topics.getTopicField(tid, 'cid') : options.cid || -1;
|
||||||
let crosspostCid = false;
|
let crosspostCid = false;
|
||||||
|
|
||||||
@@ -263,8 +269,19 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uids = Array.from(unprocessed.reduce((uids, post) => {
|
||||||
|
uids.add(post.uid);
|
||||||
|
return uids;
|
||||||
|
}, new Set()));
|
||||||
|
const isBanned = await user.bans.isBanned(uids);
|
||||||
|
const banned = uids.filter((_, idx) => isBanned[idx]);
|
||||||
|
|
||||||
let added = [];
|
let added = [];
|
||||||
await Promise.all(unprocessed.map(async (post) => {
|
await Promise.all(unprocessed.map(async (post) => {
|
||||||
|
if (banned.includes(post.uid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { to, cc } = post._activitypub;
|
const { to, cc } = post._activitypub;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const db = require('../database');
|
const db = require('../database');
|
||||||
|
const user = require('../user');
|
||||||
const meta = require('../meta');
|
const meta = require('../meta');
|
||||||
const activitypub = require('../activitypub');
|
const activitypub = require('../activitypub');
|
||||||
const analytics = require('../analytics');
|
const analytics = require('../analytics');
|
||||||
@@ -64,6 +65,12 @@ middleware.assertPayload = helpers.try(async function (req, res, next) {
|
|||||||
// Checks the validity of the incoming payload against the sender and rejects on failure
|
// Checks the validity of the incoming payload against the sender and rejects on failure
|
||||||
activitypub.helpers.log('[middleware/activitypub] Validating incoming payload...');
|
activitypub.helpers.log('[middleware/activitypub] Validating incoming payload...');
|
||||||
|
|
||||||
|
// Reject from banned users
|
||||||
|
const isBanned = await user.bans.isBanned(req.uid);
|
||||||
|
if (isBanned) {
|
||||||
|
return res.sendStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
// Sanity-check payload schema
|
// Sanity-check payload schema
|
||||||
const required = ['id', 'type', 'actor', 'object'];
|
const required = ['id', 'type', 'actor', 'object'];
|
||||||
if (!required.every(prop => req.body.hasOwnProperty(prop))) {
|
if (!required.every(prop => req.body.hasOwnProperty(prop))) {
|
||||||
|
|||||||
@@ -430,6 +430,45 @@ describe('Notes', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('from a banned account', () => {
|
||||||
|
before(async function () {
|
||||||
|
const { id: bannedUid } = helpers.mocks.person();
|
||||||
|
this.bannedUid = bannedUid;
|
||||||
|
|
||||||
|
const { note } = helpers.mocks.note({
|
||||||
|
attributedTo: bannedUid,
|
||||||
|
});
|
||||||
|
|
||||||
|
const uid = await user.create({ username: utils.generateUUID() });
|
||||||
|
const { id: boosterUid } = helpers.mocks.person();
|
||||||
|
await db.sortedSetAdd(`followersRemote:${boosterUid}`, Date.now(), uid);
|
||||||
|
const { activity } = helpers.mocks.announce({
|
||||||
|
actor: boosterUid,
|
||||||
|
object: note,
|
||||||
|
});
|
||||||
|
this.activity = activity;
|
||||||
|
this.pid = note.id;
|
||||||
|
|
||||||
|
await user.bans.ban(bannedUid, 0, 'testing');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should list the remote user as banned, when queried', async function () {
|
||||||
|
const isBanned = await user.bans.isBanned(this.bannedUid);
|
||||||
|
assert.strictEqual(isBanned, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Can't actually test the middleware because I can't sign a request for a test
|
||||||
|
|
||||||
|
it('should not assert a note if authored by a banned user (boosted by third-party)', async function () {
|
||||||
|
await activitypub.inbox.announce({
|
||||||
|
body: this.activity,
|
||||||
|
});
|
||||||
|
|
||||||
|
const exists = await posts.exists(this.pid);
|
||||||
|
assert.strictEqual(exists, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Create', () => {
|
describe('Create', () => {
|
||||||
let uid;
|
let uid;
|
||||||
let cid;
|
let cid;
|
||||||
|
|||||||
Reference in New Issue
Block a user