You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodebb/src/user/bans.js

144 lines
4.5 KiB
JavaScript

'use strict';
const winston = require('winston');
const meta = require('../meta');
const emailer = require('../emailer');
const db = require('../database');
const groups = require('../groups');
const privileges = require('../privileges');
module.exports = function (User) {
User.bans = {};
User.bans.ban = async function (uid, until, reason) {
// "until" (optional) is unix timestamp in milliseconds
// "reason" (optional) is a string
until = until || 0;
reason = reason || '';
const now = Date.now();
until = parseInt(until, 10);
if (isNaN(until)) {
throw new Error('[[error:ban-expiry-missing]]');
}
const banKey = `uid:${uid}:ban:${now}`;
const banData = {
uid: uid,
timestamp: now,
expire: until > now ? until : 0,
};
if (reason) {
banData.reason = reason;
}
// Leaving all other system groups to have privileges constrained to the "banned-users" group
const systemGroups = groups.systemGroups.filter(group => group !== groups.BANNED_USERS);
await groups.leave(systemGroups, uid);
await groups.join(groups.BANNED_USERS, uid);
await db.sortedSetAdd('users:banned', now, uid);
await db.sortedSetAdd(`uid:${uid}:bans:timestamp`, now, banKey);
await db.setObject(banKey, banData);
await User.setUserField(uid, 'banned:expire', banData.expire);
if (until > now) {
await db.sortedSetAdd('users:banned:expire', until, uid);
} else {
await db.sortedSetRemove('users:banned:expire', uid);
}
// Email notification of ban
const username = await User.getUserField(uid, 'username');
const siteTitle = meta.config.title || 'NodeBB';
const data = {
subject: `[[email:banned.subject, ${siteTitle}]]`,
username: username,
until: until ? (new Date(until)).toUTCString().replace(/,/g, '\\,') : false,
reason: reason,
};
await emailer.send('banned', uid, data).catch(err => winston.error(`[emailer.send] ${err.stack}`));
return banData;
};
User.bans.unban = async function (uids) {
uids = Array.isArray(uids) ? uids : [uids];
const userData = await User.getUsersFields(uids, ['email:confirmed']);
await db.setObject(uids.map(uid => `user:${uid}`), { 'banned:expire': 0 });
/* eslint-disable no-await-in-loop */
for (const user of userData) {
const systemGroupsToJoin = [
'registered-users',
(parseInt(user['email:confirmed'], 10) === 1 ? 'verified-users' : 'unverified-users'),
];
await groups.leave(groups.BANNED_USERS, user.uid);
// An unbanned user would lost its previous "Global Moderator" status
await groups.join(systemGroupsToJoin, user.uid);
}
await db.sortedSetRemove(['users:banned', 'users:banned:expire'], uids);
};
User.bans.isBanned = async function (uids) {
const isArray = Array.isArray(uids);
uids = isArray ? uids : [uids];
const result = await User.bans.unbanIfExpired(uids);
return isArray ? result.map(r => r.banned) : result[0].banned;
};
User.bans.canLoginIfBanned = async function (uid) {
let canLogin = true;
const { banned } = (await User.bans.unbanIfExpired([uid]))[0];
// Group privilege overshadows individual one
if (banned) {
canLogin = await privileges.global.canGroup('local:login', groups.BANNED_USERS);
}
if (banned && !canLogin) {
// Checking a single privilege of user
canLogin = await groups.isMember(uid, 'cid:0:privileges:local:login');
}
return canLogin;
};
User.bans.unbanIfExpired = async function (uids) {
// loading user data will unban if it has expired -barisu
const userData = await User.getUsersFields(uids, ['banned:expire']);
return User.bans.calcExpiredFromUserData(userData);
};
User.bans.calcExpiredFromUserData = async function (userData) {
const isArray = Array.isArray(userData);
userData = isArray ? userData : [userData];
const banned = await groups.isMembers(userData.map(u => u.uid), groups.BANNED_USERS);
userData = userData.map((userData, index) => ({
banned: banned[index],
'banned:expire': userData && userData['banned:expire'],
banExpired: userData && userData['banned:expire'] <= Date.now() && userData['banned:expire'] !== 0,
}));
return isArray ? userData : userData[0];
};
User.bans.filterBanned = async function (uids) {
const isBanned = await User.bans.isBanned(uids);
return uids.filter((uid, index) => !isBanned[index]);
};
User.bans.getReason = async function (uid) {
if (parseInt(uid, 10) <= 0) {
return '';
}
const keys = await db.getSortedSetRevRange(`uid:${uid}:bans:timestamp`, 0, 0);
if (!keys.length) {
return '';
}
const banObj = await db.getObject(keys[0]);
return banObj && banObj.reason ? banObj.reason : '';
};
};