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/index.js

303 lines
8.6 KiB
JavaScript

'use strict';
const _ = require('lodash');
const groups = require('../groups');
const plugins = require('../plugins');
const db = require('../database');
const privileges = require('../privileges');
const categories = require('../categories');
const meta = require('../meta');
const utils = require('../utils');
const User = module.exports;
User.email = require('./email');
User.notifications = require('./notifications');
User.reset = require('./reset');
User.digest = require('./digest');
require('./data')(User);
require('./auth')(User);
require('./bans')(User);
require('./create')(User);
require('./posts')(User);
require('./topics')(User);
require('./categories')(User);
require('./follow')(User);
require('./profile')(User);
require('./admin')(User);
require('./delete')(User);
require('./settings')(User);
require('./search')(User);
require('./jobs')(User);
require('./picture')(User);
require('./approval')(User);
require('./invite')(User);
require('./password')(User);
require('./info')(User);
require('./online')(User);
require('./blocks')(User);
require('./uploads')(User);
User.exists = async function (uid) {
return await db.isSortedSetMember('users:joindate', uid);
};
User.existsBySlug = async function (userslug) {
const exists = await User.getUidByUserslug(userslug);
return !!exists;
};
User.getUidsFromSet = async function (set, start, stop) {
if (set === 'users:online') {
const count = parseInt(stop, 10) === -1 ? stop : stop - start + 1;
const now = Date.now();
return await db.getSortedSetRevRangeByScore(set, start, count, '+inf', now - (meta.config.onlineCutoff * 60000));
}
return await db.getSortedSetRevRange(set, start, stop);
};
User.getUsersFromSet = async function (set, uid, start, stop) {
const uids = await User.getUidsFromSet(set, start, stop);
return await User.getUsers(uids, uid);
};
User.getUsersWithFields = async function (uids, fields, uid) {
let results = await plugins.fireHook('filter:users.addFields', { fields: fields });
results.fields = _.uniq(results.fields);
const userData = await User.getUsersFields(uids, results.fields);
results = await plugins.fireHook('filter:userlist.get', { users: userData, uid: uid });
return results.users;
};
User.getUsers = async function (uids, uid) {
return await User.getUsersWithFields(uids, [
'uid', 'username', 'userslug', 'picture', 'status',
'postcount', 'reputation', 'email:confirmed', 'lastonline',
'flags', 'banned', 'banned:expire', 'joindate',
], uid);
};
User.getStatus = function (userData) {
if (userData.uid <= 0) {
return 'offline';
}
const isOnline = (Date.now() - userData.lastonline) < (meta.config.onlineCutoff * 60000);
return isOnline ? (userData.status || 'online') : 'offline';
};
User.getUidByUsername = async function (username) {
if (!username) {
return 0;
}
return await db.sortedSetScore('username:uid', username);
};
User.getUidsByUsernames = async function (usernames) {
return await db.sortedSetScores('username:uid', usernames);
};
User.getUidByUserslug = async function (userslug) {
if (!userslug) {
return 0;
}
return await db.sortedSetScore('userslug:uid', userslug);
};
User.getUsernamesByUids = async function (uids) {
const users = await User.getUsersFields(uids, ['username']);
return users.map(user => user.username);
};
User.getUsernameByUserslug = async function (slug) {
const uid = await User.getUidByUserslug(slug);
return await User.getUserField(uid, 'username');
};
User.getUidByEmail = async function (email) {
return await db.sortedSetScore('email:uid', email.toLowerCase());
};
User.getUidsByEmails = async function (emails) {
emails = emails.map(email => email && email.toLowerCase());
return await db.sortedSetScores('email:uid', emails);
};
User.getUsernameByEmail = async function (email) {
const uid = await db.sortedSetScore('email:uid', String(email).toLowerCase());
return await User.getUserField(uid, 'username');
};
User.isModerator = async function (uid, cid) {
return await privileges.users.isModerator(uid, cid);
};
User.isModeratorOfAnyCategory = async function (uid) {
const cids = await User.getModeratedCids(uid);
return Array.isArray(cids) ? !!cids.length : false;
};
User.isAdministrator = async function (uid) {
return await privileges.users.isAdministrator(uid);
};
User.isGlobalModerator = async function (uid) {
return await privileges.users.isGlobalModerator(uid);
};
User.getPrivileges = async function (uid) {
return await utils.promiseParallel({
isAdmin: User.isAdministrator(uid),
isGlobalModerator: User.isGlobalModerator(uid),
isModeratorOfAnyCategory: User.isModeratorOfAnyCategory(uid),
});
};
User.isPrivileged = async function (uid) {
const results = await User.getPrivileges(uid);
return results ? (results.isAdmin || results.isGlobalModerator || results.isModeratorOfAnyCategory) : false;
};
User.isAdminOrGlobalMod = async function (uid) {
const [isAdmin, isGlobalMod] = await Promise.all([
User.isAdministrator(uid),
User.isGlobalModerator(uid),
]);
return isAdmin || isGlobalMod;
};
User.isAdminOrSelf = async function (callerUid, uid) {
await isSelfOrMethod(callerUid, uid, User.isAdministrator);
};
User.isAdminOrGlobalModOrSelf = async function (callerUid, uid) {
await isSelfOrMethod(callerUid, uid, User.isAdminOrGlobalMod);
};
User.isPrivilegedOrSelf = async function (callerUid, uid) {
await isSelfOrMethod(callerUid, uid, User.isPrivileged);
};
async function isSelfOrMethod(callerUid, uid, method) {
if (parseInt(callerUid, 10) === parseInt(uid, 10)) {
return;
}
const isPass = await method(callerUid);
if (!isPass) {
throw new Error('[[error:no-privileges]]');
}
}
User.getAdminsandGlobalMods = async function () {
const results = await groups.getMembersOfGroups(['administrators', 'Global Moderators']);
return await User.getUsersData(_.union.apply(_, results));
};
User.getAdminsandGlobalModsandModerators = async function () {
const results = await Promise.all([
groups.getMembers('administrators', 0, -1),
groups.getMembers('Global Moderators', 0, -1),
User.getModeratorUids(),
]);
return await User.getUsersData(_.union.apply(_, results));
};
User.getModeratorUids = async function () {
const cids = await categories.getAllCidsFromSet('categories:cid');
const uids = await categories.getModeratorUids(cids);
return _.union(...uids);
};
User.getModeratedCids = async function (uid) {
if (parseInt(uid, 10) <= 0) {
return [];
}
const cids = await categories.getAllCidsFromSet('categories:cid');
const isMods = await User.isModerator(uid, cids);
return cids.filter((cid, index) => cid && isMods[index]);
};
User.addInterstitials = function (callback) {
plugins.registerHook('core', {
hook: 'filter:register.interstitial',
method: [
// GDPR information collection/processing consent + email consent
async function (data) {
if (!meta.config.gdpr_enabled || (data.userData && data.userData.gdpr_consent)) {
return data;
}
if (!data.userData) {
throw new Error('[[error:invalid-data]]');
}
if (data.userData.uid) {
const consented = await db.getObjectField('user:' + data.userData.uid, 'gdpr_consent');
if (parseInt(consented, 10)) {
return data;
}
}
data.interstitials.push({
template: 'partials/gdpr_consent',
data: {
digestFrequency: meta.config.dailyDigestFreq,
digestEnabled: meta.config.dailyDigestFreq !== 'off',
},
callback: function (userData, formData, next) {
if (formData.gdpr_agree_data === 'on' && formData.gdpr_agree_email === 'on') {
userData.gdpr_consent = true;
}
next(userData.gdpr_consent ? null : new Error('[[register:gdpr_consent_denied]]'));
},
});
return data;
},
// Forum Terms of Use
async function (data) {
if (!data.userData) {
throw new Error('[[error:invalid-data]]');
}
if (!meta.config.termsOfUse || data.userData.acceptTos) {
// no ToS or ToS accepted, nothing to do
return data;
}
if (data.userData.uid) {
const accepted = await db.getObjectField('user:' + data.userData.uid, 'acceptTos');
if (parseInt(accepted, 10)) {
return data;
}
}
const termsOfUse = await plugins.fireHook('filter:parse.post', {
postData: {
content: meta.config.termsOfUse || '',
},
});
data.interstitials.push({
template: 'partials/acceptTos',
data: {
termsOfUse: termsOfUse.postData.content,
},
callback: function (userData, formData, next) {
if (formData['agree-terms'] === 'on') {
userData.acceptTos = true;
}
next(userData.acceptTos ? null : new Error('[[register:terms_of_use_error]]'));
},
});
return data;
},
],
});
callback();
};
require('../promisify')(User);