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.
168 lines
6.1 KiB
JavaScript
168 lines
6.1 KiB
JavaScript
'use strict';
|
|
|
|
const validator = require('validator');
|
|
const winston = require('winston');
|
|
const cronJob = require('cron').CronJob;
|
|
|
|
const db = require('../database');
|
|
const meta = require('../meta');
|
|
const emailer = require('../emailer');
|
|
const notifications = require('../notifications');
|
|
const groups = require('../groups');
|
|
const utils = require('../utils');
|
|
const slugify = require('../slugify');
|
|
const plugins = require('../plugins');
|
|
|
|
module.exports = function (User) {
|
|
new cronJob('0 * * * *', (() => {
|
|
User.autoApprove();
|
|
}), null, true);
|
|
|
|
User.addToApprovalQueue = async function (userData) {
|
|
userData.username = userData.username.trim();
|
|
userData.userslug = slugify(userData.username);
|
|
await canQueue(userData);
|
|
const hashedPassword = await User.hashPassword(userData.password);
|
|
const data = {
|
|
username: userData.username,
|
|
email: userData.email,
|
|
ip: userData.ip,
|
|
hashedPassword: hashedPassword,
|
|
};
|
|
const results = await plugins.hooks.fire('filter:user.addToApprovalQueue', { data: data, userData: userData });
|
|
await db.setObject(`registration:queue:name:${userData.username}`, results.data);
|
|
await db.sortedSetAdd('registration:queue', Date.now(), userData.username);
|
|
await sendNotificationToAdmins(userData.username);
|
|
};
|
|
|
|
async function canQueue(userData) {
|
|
await User.isDataValid(userData);
|
|
const usernames = await db.getSortedSetRange('registration:queue', 0, -1);
|
|
if (usernames.includes(userData.username)) {
|
|
throw new Error('[[error:username-taken]]');
|
|
}
|
|
const keys = usernames.filter(Boolean).map(username => `registration:queue:name:${username}`);
|
|
const data = await db.getObjectsFields(keys, ['email']);
|
|
const emails = data.map(data => data && data.email).filter(Boolean);
|
|
if (userData.email && emails.includes(userData.email)) {
|
|
throw new Error('[[error:email-taken]]');
|
|
}
|
|
}
|
|
|
|
async function sendNotificationToAdmins(username) {
|
|
const notifObj = await notifications.create({
|
|
type: 'new-register',
|
|
bodyShort: `[[notifications:new_register, ${username}]]`,
|
|
nid: `new_register:${username}`,
|
|
path: '/admin/manage/registration',
|
|
mergeId: 'new_register',
|
|
});
|
|
await notifications.pushGroup(notifObj, 'administrators');
|
|
}
|
|
|
|
User.acceptRegistration = async function (username) {
|
|
const userData = await db.getObject(`registration:queue:name:${username}`);
|
|
if (!userData) {
|
|
throw new Error('[[error:invalid-data]]');
|
|
}
|
|
const creation_time = await db.sortedSetScore('registration:queue', username);
|
|
const uid = await User.create(userData);
|
|
await User.setUserFields(uid, {
|
|
password: userData.hashedPassword,
|
|
'password:shaWrapped': 1,
|
|
});
|
|
await removeFromQueue(username);
|
|
await markNotificationRead(username);
|
|
await plugins.hooks.fire('filter:register.complete', { uid: uid });
|
|
await emailer.send('registration_accepted', uid, {
|
|
username: username,
|
|
subject: `[[email:welcome-to, ${meta.config.title || meta.config.browserTitle || 'NodeBB'}]]`,
|
|
template: 'registration_accepted',
|
|
uid: uid,
|
|
}).catch(err => winston.error(`[emailer.send] ${err.stack}`));
|
|
const total = await db.incrObjectFieldBy('registration:queue:approval:times', 'totalTime', Math.floor((Date.now() - creation_time) / 60000));
|
|
const counter = await db.incrObjectField('registration:queue:approval:times', 'counter');
|
|
await db.setObjectField('registration:queue:approval:times', 'average', total / counter);
|
|
return uid;
|
|
};
|
|
|
|
async function markNotificationRead(username) {
|
|
const nid = `new_register:${username}`;
|
|
const uids = await groups.getMembers('administrators', 0, -1);
|
|
const promises = uids.map(uid => notifications.markRead(nid, uid));
|
|
await Promise.all(promises);
|
|
}
|
|
|
|
User.rejectRegistration = async function (username) {
|
|
await removeFromQueue(username);
|
|
await markNotificationRead(username);
|
|
};
|
|
|
|
async function removeFromQueue(username) {
|
|
await Promise.all([
|
|
db.sortedSetRemove('registration:queue', username),
|
|
db.delete(`registration:queue:name:${username}`),
|
|
]);
|
|
}
|
|
|
|
User.shouldQueueUser = async function (ip) {
|
|
const { registrationApprovalType } = meta.config;
|
|
if (registrationApprovalType === 'admin-approval') {
|
|
return true;
|
|
} else if (registrationApprovalType === 'admin-approval-ip') {
|
|
const count = await db.sortedSetCard(`ip:${ip}:uid`);
|
|
return !!count;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
User.getRegistrationQueue = async function (start, stop) {
|
|
const data = await db.getSortedSetRevRangeWithScores('registration:queue', start, stop);
|
|
const keys = data.filter(Boolean).map(user => `registration:queue:name:${user.value}`);
|
|
let users = await db.getObjects(keys);
|
|
users = users.filter(Boolean).map((user, index) => {
|
|
user.timestampISO = utils.toISOString(data[index].score);
|
|
user.email = validator.escape(String(user.email));
|
|
user.usernameEscaped = validator.escape(String(user.username));
|
|
delete user.hashedPassword;
|
|
return user;
|
|
});
|
|
await Promise.all(users.map(async (user) => {
|
|
// temporary: see http://www.stopforumspam.com/forum/viewtopic.php?id=6392
|
|
// need to keep this for getIPMatchedUsers
|
|
user.ip = user.ip.replace('::ffff:', '');
|
|
await getIPMatchedUsers(user);
|
|
user.customActions = user.customActions || [];
|
|
/*
|
|
// then spam prevention plugins, using the "filter:user.getRegistrationQueue" hook can be like:
|
|
user.customActions.push({
|
|
title: '[[spam-be-gone:report-user]]',
|
|
id: 'report-spam-user-' + user.username,
|
|
class: 'btn-warning report-spam-user',
|
|
icon: 'fa-flag'
|
|
});
|
|
*/
|
|
}));
|
|
|
|
const results = await plugins.hooks.fire('filter:user.getRegistrationQueue', { users: users });
|
|
return results.users;
|
|
};
|
|
|
|
async function getIPMatchedUsers(user) {
|
|
const uids = await User.getUidsFromSet(`ip:${user.ip}:uid`, 0, -1);
|
|
user.ipMatch = await User.getUsersFields(uids, ['uid', 'username', 'picture']);
|
|
}
|
|
|
|
User.autoApprove = async function () {
|
|
if (meta.config.autoApproveTime <= 0) {
|
|
return;
|
|
}
|
|
const users = await db.getSortedSetRevRangeWithScores('registration:queue', 0, -1);
|
|
const now = Date.now();
|
|
for (const user of users.filter(user => now - user.score >= meta.config.autoApproveTime * 3600000)) {
|
|
// eslint-disable-next-line no-await-in-loop
|
|
await User.acceptRegistration(user.value);
|
|
}
|
|
};
|
|
};
|