snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
186
apps/meteor/server/lib/users/deleteUser.ts
Normal file
186
apps/meteor/server/lib/users/deleteUser.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import { Apps, AppEvents } from '@rocket.chat/apps';
|
||||
import { api } from '@rocket.chat/core-services';
|
||||
import { isUserFederated, type IUser } from '@rocket.chat/core-typings';
|
||||
import {
|
||||
Integrations,
|
||||
LivechatVisitors,
|
||||
LivechatDepartmentAgents,
|
||||
Messages,
|
||||
Rooms,
|
||||
Subscriptions,
|
||||
Users,
|
||||
ReadReceipts,
|
||||
ReadReceiptsArchive,
|
||||
LivechatUnitMonitors,
|
||||
ModerationReports,
|
||||
} from '@rocket.chat/models';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import { getUserSingleOwnedRooms } from './getUserSingleOwnedRooms';
|
||||
import { settings } from '../../settings';
|
||||
import { callbacks } from '../callbacks';
|
||||
import { i18n } from '../i18n';
|
||||
import { FileUpload } from '../media/file-upload';
|
||||
import {
|
||||
notifyOnRoomChangedById,
|
||||
notifyOnIntegrationChangedByUserId,
|
||||
notifyOnLivechatDepartmentAgentChanged,
|
||||
notifyOnUserChange,
|
||||
} from '../notifyListener';
|
||||
import { getSubscribedRoomsForUserWithDetails, shouldRemoveOrChangeOwner } from '../rooms/getRoomsWithSingleOwner';
|
||||
import { relinquishRoomOwnerships } from '../rooms/relinquishRoomOwnerships';
|
||||
import { updateGroupDMsName } from '../rooms/updateGroupDMsName';
|
||||
|
||||
export async function deleteUser(userId: string, confirmRelinquish = false, deletedBy?: IUser['_id']): Promise<{ deletedRooms: string[] }> {
|
||||
if (userId === 'rocket.cat') {
|
||||
throw new Meteor.Error('error-action-not-allowed', 'Deleting the rocket.cat user is not allowed', {
|
||||
method: 'deleteUser',
|
||||
action: 'Delete_user',
|
||||
});
|
||||
}
|
||||
|
||||
const user = await Users.findOneById(userId, {
|
||||
projection: { username: 1, avatarOrigin: 1, roles: 1, federated: 1 },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return { deletedRooms: [] };
|
||||
}
|
||||
|
||||
if (isUserFederated(user)) {
|
||||
throw new Meteor.Error('error-not-allowed', 'User participated in federation, this user can only be deactivated permanently', {
|
||||
method: 'deleteUser',
|
||||
});
|
||||
}
|
||||
|
||||
const subscribedRooms = await getSubscribedRoomsForUserWithDetails(userId);
|
||||
|
||||
if (shouldRemoveOrChangeOwner(subscribedRooms) && !confirmRelinquish) {
|
||||
const rooms = await getUserSingleOwnedRooms(subscribedRooms);
|
||||
throw new Meteor.Error('user-last-owner', '', rooms);
|
||||
}
|
||||
|
||||
let deletedRooms: string[] = [];
|
||||
// Users without username can't do anything, so there is nothing to remove
|
||||
if (user.username != null) {
|
||||
let userToReplaceWhenUnlinking: IUser | null = null;
|
||||
const nameAlias = i18n.t('Removed_User');
|
||||
deletedRooms = await relinquishRoomOwnerships(userId, subscribedRooms, true);
|
||||
|
||||
const messageErasureType = settings.get<'Delete' | 'Unlink' | 'Keep'>('Message_ErasureType');
|
||||
switch (messageErasureType) {
|
||||
case 'Delete':
|
||||
const store = FileUpload.getStore('Uploads');
|
||||
const cursor = Messages.findFilesByUserId(userId);
|
||||
|
||||
for await (const { file, files } of cursor) {
|
||||
const fileIds = files?.map(({ _id }) => _id) || [];
|
||||
for await (const fileId of fileIds) {
|
||||
if (fileId) {
|
||||
await store.deleteById(fileId);
|
||||
}
|
||||
}
|
||||
|
||||
if (file?._id && !fileIds.includes(file._id)) {
|
||||
await store.deleteById(file._id);
|
||||
}
|
||||
}
|
||||
|
||||
await Messages.removeByUserId(userId);
|
||||
await ReadReceipts.removeByUserId(userId);
|
||||
await ReadReceiptsArchive.removeByUserId(userId);
|
||||
|
||||
await ModerationReports.hideMessageReportsByUserId(
|
||||
userId,
|
||||
deletedBy || userId,
|
||||
deletedBy === userId ? 'user deleted own account' : 'user account deleted',
|
||||
'DELETE_USER',
|
||||
);
|
||||
|
||||
break;
|
||||
case 'Unlink':
|
||||
userToReplaceWhenUnlinking = await Users.findOneById('rocket.cat');
|
||||
if (!userToReplaceWhenUnlinking?._id || !userToReplaceWhenUnlinking?.username) {
|
||||
break;
|
||||
}
|
||||
await Messages.unlinkUserId(userId, userToReplaceWhenUnlinking?._id, userToReplaceWhenUnlinking?.username, nameAlias);
|
||||
break;
|
||||
}
|
||||
|
||||
await Rooms.updateGroupDMsRemovingUsernamesByUsername(user.username, userId); // Remove direct rooms with the user
|
||||
await Rooms.removeDirectRoomContainingUsername(user.username); // Remove direct rooms with the user
|
||||
|
||||
const rids = subscribedRooms.map((room) => room.rid);
|
||||
void notifyOnRoomChangedById(rids);
|
||||
|
||||
await Subscriptions.removeByUserId(userId);
|
||||
|
||||
// Remove user as livechat agent
|
||||
if (user.roles.includes('livechat-agent')) {
|
||||
const departmentAgents = await LivechatDepartmentAgents.findByAgentId(userId).toArray();
|
||||
|
||||
const { deletedCount } = await LivechatDepartmentAgents.removeByAgentId(userId);
|
||||
|
||||
if (deletedCount > 0) {
|
||||
departmentAgents.forEach((depAgent) => {
|
||||
void notifyOnLivechatDepartmentAgentChanged(
|
||||
{
|
||||
_id: depAgent._id,
|
||||
agentId: userId,
|
||||
departmentId: depAgent.departmentId,
|
||||
},
|
||||
'removed',
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (user.roles.includes('livechat-monitor')) {
|
||||
// Remove user as Unit Monitor
|
||||
await LivechatUnitMonitors.removeByMonitorId(userId);
|
||||
}
|
||||
|
||||
// This is for compatibility. Since we allowed any user to be contact manager b4, we need to have the same logic
|
||||
// for deletion.
|
||||
await LivechatVisitors.removeContactManagerByUsername(user.username);
|
||||
|
||||
// removes user's avatar
|
||||
if (user.avatarOrigin === 'upload' || user.avatarOrigin === 'url' || user.avatarOrigin === 'rest') {
|
||||
await FileUpload.getStore('Avatars').deleteByName(user.username);
|
||||
}
|
||||
|
||||
// Disables all the integrations which rely on the user being deleted.
|
||||
await Integrations.disableByUserId(userId);
|
||||
void notifyOnIntegrationChangedByUserId(userId);
|
||||
|
||||
// Don't broadcast user.deleted for Erasure Type of 'Keep' so that messages don't disappear from logged in sessions
|
||||
if (messageErasureType === 'Delete') {
|
||||
void api.broadcast('user.deleted', user, {
|
||||
messageErasureType,
|
||||
});
|
||||
}
|
||||
if (messageErasureType === 'Unlink' && userToReplaceWhenUnlinking) {
|
||||
void api.broadcast('user.deleted', user, {
|
||||
messageErasureType,
|
||||
replaceByUser: { _id: userToReplaceWhenUnlinking._id, username: userToReplaceWhenUnlinking?.username, alias: nameAlias },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Remove user from users database
|
||||
await Users.removeById(userId);
|
||||
|
||||
// App IPostUserDeleted event hook
|
||||
if (deletedBy) {
|
||||
await Apps.self?.triggerEvent(AppEvents.IPostUserDeleted, { user, performedBy: await Users.findOneById(deletedBy) });
|
||||
}
|
||||
|
||||
// update name and fname of group direct messages
|
||||
await updateGroupDMsName(user);
|
||||
|
||||
void notifyOnUserChange({ clientAction: 'removed', id: user._id });
|
||||
|
||||
await callbacks.run('afterDeleteUser', user);
|
||||
|
||||
return { deletedRooms };
|
||||
}
|
||||
Reference in New Issue
Block a user