diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index 0f8dffaa29..1c338a0975 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -510,7 +510,7 @@ define('admin/manage/users', [ if (confirm) { Promise.all( uids.map( - uid => api.del(`/users/${uid}${path}`, {}).then(() => { + uid => api.del(`/users/${encodeURIComponent(uid)}${path}`, {}).then(() => { if (path !== '/content') { removeRow(uid); } diff --git a/src/activitypub/actors.js b/src/activitypub/actors.js index aa093802dd..158ee9b893 100644 --- a/src/activitypub/actors.js +++ b/src/activitypub/actors.js @@ -71,6 +71,8 @@ Actors.assert = async (ids, options = {}) => { // winston.verbose(`[activitypub/actors] Asserting ${ids.length} actor(s)`); + // NOTE: MAKE SURE EVERY DB ADDITION HAS A CORRESPONDING REMOVAL IN ACTORS.REMOVE! + const urlMap = new Map(); const followersUrlMap = new Map(); const pubKeysMap = new Map(); @@ -207,3 +209,29 @@ Actors.getLocalFollowersCount = async (id) => { return await db.sortedSetCard(`followersRemote:${id}`); }; + +Actors.remove = async (id) => { + const exists = await db.isSortedSetMember('usersRemote:lastCrawled', id); + if (!exists) { + return false; + } + + let { username, fullname, url, followersUrl } = await user.getUserFields(id, ['username', 'fullname', 'url', 'followersUrl']); + username = username.toLowerCase(); + + await Promise.all([ + db.sortedSetRemoveBulk([ + ['ap.preferredUsername:sorted', `${username}:${id}`], + ['ap.name:sorted', `${fullname.toLowerCase()}:${id}`], + ]), + db.deleteObjectField('handle:uid', username), + db.deleteObjectField('followersUrl:uid', followersUrl), + db.deleteObjectField('remoteUrl:uid', url), + db.delete(`userRemote:${id}:keys`), + ]); + + await Promise.all([ + db.delete(`userRemote:${id}`), + db.sortedSetRemove('usersRemote:lastCrawled', id), + ]); +}; diff --git a/src/api/users.js b/src/api/users.js index 6b3398db9d..924dd1daa0 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -515,7 +515,7 @@ async function isPrivilegedOrSelfAndPasswordMatch(caller, data) { async function processDeletion({ uid, method, password, caller }) { const isTargetAdmin = await user.isAdministrator(uid); - const isSelf = parseInt(uid, 10) === parseInt(caller.uid, 10); + const isSelf = String(uid) === String(caller.uid); const hasAdminPrivilege = await privileges.admin.can('admin:users', caller.uid); if (isSelf && meta.config.allowAccountDelete !== 1) { diff --git a/src/middleware/assert.js b/src/middleware/assert.js index 67a63b0344..6b20667379 100644 --- a/src/middleware/assert.js +++ b/src/middleware/assert.js @@ -29,8 +29,8 @@ Assert.user = helpers.try(async (req, res, next) => { const uid = req.params.uid || res.locals.uid; if ( - (utils.isNumber(uid) && await user.exists(uid)) || - (uid.indexOf('@') !== -1 && await activitypub.helpers.query(uid)) + ((utils.isNumber(uid) || activitypub.helpers.isUri(uid)) && await user.exists(uid)) || + (uid.indexOf('@') !== -1 && await user.existsBySlug(uid)) ) { return next(); } diff --git a/src/user/delete.js b/src/user/delete.js index 9efd8802ae..c6eb13809f 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -13,7 +13,9 @@ const topics = require('../topics'); const groups = require('../groups'); const messaging = require('../messaging'); const plugins = require('../plugins'); +const activitypub = require('../activitypub'); const batch = require('../batch'); +const utils = require('../utils'); module.exports = function (User) { const deletesInProgress = {}; @@ -24,7 +26,7 @@ module.exports = function (User) { }; User.deleteContent = async function (callerUid, uid) { - if (parseInt(uid, 10) <= 0) { + if (utils.isNumber(uid) && parseInt(uid, 10) <= 0) { throw new Error('[[error:invalid-uid]]'); } if (deletesInProgress[uid]) { @@ -61,7 +63,7 @@ module.exports = function (User) { let deleteIds = []; await batch.processSortedSet('post:queue', async (ids) => { const data = await db.getObjects(ids.map(id => `post:queue:${id}`)); - const userQueuedIds = data.filter(d => parseInt(d.uid, 10) === parseInt(uid, 10)).map(d => d.id); + const userQueuedIds = data.filter(d => String(d.uid) === String(uid)).map(d => d.id); deleteIds = deleteIds.concat(userQueuedIds); }, { batch: 500 }); await async.eachSeries(deleteIds, posts.removeFromQueue); @@ -90,7 +92,7 @@ module.exports = function (User) { deletesInProgress[uid] = 'user.deleteAccount'; await removeFromSortedSets(uid); - const userData = await db.getObject(`user:${uid}`); + const userData = await db.getObject(utils.isNumber(uid) ? `user:${uid}` : `userRemote:${uid}`); if (!userData || !userData.username) { delete deletesInProgress[uid]; @@ -153,6 +155,7 @@ module.exports = function (User) { flags.resolveFlag('user', uid, uid), User.reset.cleanByUid(uid), User.email.expireValidation(uid), + activitypub.actors.remove(uid), ]); await db.deleteAll([ `followers:${uid}`, `following:${uid}`, `user:${uid}`,