generated from CMU-313/NodeBB-S24
-
Notifications
You must be signed in to change notification settings - Fork 236
Expand file tree
/
Copy pathapproval.js
More file actions
167 lines (152 loc) · 7.07 KB
/
approval.js
File metadata and controls
167 lines (152 loc) · 7.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
'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, ${userData.username + "1"}]]');
}
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 = [].concat(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);
}
};
};