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.

289 lines
9.1 KiB
JavaScript

'use strict';
const util = require('util');
const nconf = require('nconf');
const db = require('../../database');
const api = require('../../api');
const groups = require('../../groups');
const meta = require('../../meta');
const privileges = require('../../privileges');
const user = require('../../user');
const utils = require('../../utils');
const helpers = require('../helpers');
const Users = module.exports;
const hasAdminPrivilege = async (uid, privilege) => {
const ok = await privileges.admin.can(`admin:${privilege}`, uid);
if (!ok) {
throw new Error('[[error:no-privileges]]');
}
};
Users.redirectBySlug = async (req, res) => {
const uid = await user.getUidByUserslug(req.params.userslug);
if (uid) {
const path = req.path.split('/').slice(3).join('/');
const urlObj = new URL(nconf.get('url') + req.url);
res.redirect(308, nconf.get('relative_path') + encodeURI(`/api/v3/users/${uid}/${path}${urlObj.search}`));
} else {
helpers.formatApiResponse(404, res);
}
};
Users.create = async (req, res) => {
await hasAdminPrivilege(req.uid, 'users');
const userObj = await api.users.create(req, req.body);
helpers.formatApiResponse(200, res, userObj);
};
Users.exists = async (req, res) => {
helpers.formatApiResponse(200, res);
};
Users.get = async (req, res) => {
const userData = await user.getUserData(req.params.uid);
const publicUserData = await user.hidePrivateData(userData, req.uid);
helpers.formatApiResponse(200, res, publicUserData);
};
Users.update = async (req, res) => {
const userObj = await api.users.update(req, { ...req.body, uid: req.params.uid });
helpers.formatApiResponse(200, res, userObj);
};
Users.delete = async (req, res) => {
await api.users.delete(req, { ...req.params, password: req.body.password });
helpers.formatApiResponse(200, res);
};
Users.deleteContent = async (req, res) => {
await api.users.deleteContent(req, { ...req.params, password: req.body.password });
helpers.formatApiResponse(200, res);
};
Users.deleteAccount = async (req, res) => {
await api.users.deleteAccount(req, { ...req.params, password: req.body.password });
helpers.formatApiResponse(200, res);
};
Users.deleteMany = async (req, res) => {
await hasAdminPrivilege(req.uid, 'users');
await api.users.deleteMany(req, req.body);
helpers.formatApiResponse(200, res);
};
Users.changePicture = async (req, res) => {
await api.users.changePicture(req, { ...req.body, uid: req.params.uid });
helpers.formatApiResponse(200, res);
};
Users.updateSettings = async (req, res) => {
const settings = await api.users.updateSettings(req, { ...req.body, uid: req.params.uid });
helpers.formatApiResponse(200, res, settings);
};
Users.changePassword = async (req, res) => {
await api.users.changePassword(req, { ...req.body, uid: req.params.uid });
helpers.formatApiResponse(200, res);
};
Users.follow = async (req, res) => {
await api.users.follow(req, req.params);
helpers.formatApiResponse(200, res);
};
Users.unfollow = async (req, res) => {
await api.users.unfollow(req, req.params);
helpers.formatApiResponse(200, res);
};
Users.ban = async (req, res) => {
await api.users.ban(req, { ...req.body, uid: req.params.uid });
helpers.formatApiResponse(200, res);
};
Users.unban = async (req, res) => {
await api.users.unban(req, { ...req.body, uid: req.params.uid });
helpers.formatApiResponse(200, res);
};
Users.generateToken = async (req, res) => {
await hasAdminPrivilege(req.uid, 'settings');
if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid, 10)) {
return helpers.formatApiResponse(401, res);
}
const settings = await meta.settings.get('core.api');
settings.tokens = settings.tokens || [];
const newToken = {
token: utils.generateUUID(),
uid: req.user.uid,
description: req.body.description || '',
timestamp: Date.now(),
};
settings.tokens.push(newToken);
await meta.settings.set('core.api', settings);
helpers.formatApiResponse(200, res, newToken);
};
Users.deleteToken = async (req, res) => {
await hasAdminPrivilege(req.uid, 'settings');
if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid, 10)) {
return helpers.formatApiResponse(401, res);
}
const settings = await meta.settings.get('core.api');
const beforeLen = settings.tokens.length;
settings.tokens = settings.tokens.filter(tokenObj => tokenObj.token !== req.params.token);
if (beforeLen !== settings.tokens.length) {
await meta.settings.set('core.api', settings);
helpers.formatApiResponse(200, res);
} else {
helpers.formatApiResponse(404, res);
}
};
const getSessionAsync = util.promisify((sid, callback) => {
db.sessionStore.get(sid, (err, sessionObj) => callback(err, sessionObj || null));
});
Users.revokeSession = async (req, res) => {
// Only admins or global mods (besides the user themselves) can revoke sessions
if (parseInt(req.params.uid, 10) !== req.uid && !await user.isAdminOrGlobalMod(req.uid)) {
return helpers.formatApiResponse(404, res);
}
const sids = await db.getSortedSetRange(`uid:${req.params.uid}:sessions`, 0, -1);
let _id;
for (const sid of sids) {
/* eslint-disable no-await-in-loop */
const sessionObj = await getSessionAsync(sid);
if (sessionObj && sessionObj.meta && sessionObj.meta.uuid === req.params.uuid) {
_id = sid;
break;
}
}
if (!_id) {
throw new Error('[[error:no-session-found]]');
}
await user.auth.revokeSession(_id, req.params.uid);
helpers.formatApiResponse(200, res);
};
Users.invite = async (req, res) => {
const { emails, groupsToJoin = [] } = req.body;
if (!emails || !Array.isArray(groupsToJoin)) {
return helpers.formatApiResponse(400, res, new Error('[[error:invalid-data]]'));
}
// For simplicity, this API route is restricted to self-use only. This can change if needed.
if (parseInt(req.user.uid, 10) !== parseInt(req.params.uid, 10)) {
return helpers.formatApiResponse(403, res, new Error('[[error:no-privileges]]'));
}
const canInvite = await privileges.users.hasInvitePrivilege(req.uid);
if (!canInvite) {
return helpers.formatApiResponse(403, res, new Error('[[error:no-privileges]]'));
}
const { registrationType } = meta.config;
const isAdmin = await user.isAdministrator(req.uid);
if (registrationType === 'admin-invite-only' && !isAdmin) {
return helpers.formatApiResponse(403, res, new Error('[[error:no-privileges]]'));
}
const inviteGroups = (await groups.getUserInviteGroups(req.uid)).map(group => group.name);
const cannotInvite = groupsToJoin.some(group => !inviteGroups.includes(group));
if (groupsToJoin.length > 0 && cannotInvite) {
return helpers.formatApiResponse(403, res, new Error('[[error:no-privileges]]'));
}
const max = meta.config.maximumInvites;
const emailsArr = emails.split(',').map(email => email.trim()).filter(Boolean);
for (const email of emailsArr) {
/* eslint-disable no-await-in-loop */
let invites = 0;
if (max) {
invites = await user.getInvitesNumber(req.uid);
}
if (!isAdmin && max && invites >= max) {
return helpers.formatApiResponse(403, res, new Error(`[[error:invite-maximum-met, ${invites}, ${max}]]`));
}
await user.sendInvitationEmail(req.uid, email, groupsToJoin);
}
return helpers.formatApiResponse(200, res);
};
Users.getInviteGroups = async function (req, res) {
if (parseInt(req.params.uid, 10) !== parseInt(req.user.uid, 10)) {
return helpers.formatApiResponse(401, res);
}
const userInviteGroups = await groups.getUserInviteGroups(req.params.uid);
return helpers.formatApiResponse(200, res, userInviteGroups.map(group => group.displayName));
};
Users.listEmails = async (req, res) => {
const [isPrivileged, { showemail }] = await Promise.all([
user.isPrivileged(req.uid),
user.getSettings(req.params.uid),
]);
const isSelf = req.uid === parseInt(req.params.uid, 10);
if (isSelf || isPrivileged || showemail) {
const emails = await db.getSortedSetRangeByScore('email:uid', 0, 500, req.params.uid, req.params.uid);
helpers.formatApiResponse(200, res, { emails });
} else {
helpers.formatApiResponse(204, res);
}
};
Users.getEmail = async (req, res) => {
const [isPrivileged, { showemail }, exists] = await Promise.all([
user.isPrivileged(req.uid),
user.getSettings(req.params.uid),
db.isSortedSetMember('email:uid', req.params.email.toLowerCase()),
]);
const isSelf = req.uid === parseInt(req.params.uid, 10);
if (exists && (isSelf || isPrivileged || showemail)) {
helpers.formatApiResponse(204, res);
} else {
helpers.formatApiResponse(404, res);
}
};
Users.confirmEmail = async (req, res) => {
const [pending, current, canManage] = await Promise.all([
user.email.isValidationPending(req.params.uid, req.params.email),
user.getUserField(req.params.uid, 'email'),
privileges.admin.can('admin:users', req.uid),
]);
if (!canManage) {
return helpers.notAllowed(req, res);
}
if (pending) { // has active confirmation request
const code = await db.get(`confirm:byUid:${req.params.uid}`);
await user.email.confirmByCode(code, req.session.id);
helpers.formatApiResponse(200, res);
} else if (current && current === req.params.email) { // email in user hash (i.e. email passed into user.create)
await user.email.confirmByUid(req.params.uid);
helpers.formatApiResponse(200, res);
} else {
helpers.formatApiResponse(404, res);
}
};