diff --git a/app/components/UserDialogs.tsx b/app/components/UserDialogs.tsx index 1d568bd654..db6ec7fd07 100644 --- a/app/components/UserDialogs.tsx +++ b/app/components/UserDialogs.tsx @@ -71,7 +71,7 @@ export function UserDeleteDialog({ user, onSubmit }: Props) { danger > {t( - "Are you sure you want to permanently delete {{ userName }}? This operation is unrecoverable, consider suspending the user instead.", + "Are you sure you want to permanently delete {{ userName }}? This operation is unrecoverable. Any API keys, webhooks, and integrations they created will stop working — consider suspending the user instead.", { userName: user.name, } diff --git a/server/queues/processors/UserDeletedProcessor.test.ts b/server/queues/processors/UserDeletedProcessor.test.ts index 8c1267ac4f..8a6cf74d61 100644 --- a/server/queues/processors/UserDeletedProcessor.test.ts +++ b/server/queues/processors/UserDeletedProcessor.test.ts @@ -1,11 +1,11 @@ import UserAuthentication from "@server/models/UserAuthentication"; -import { buildUser } from "@server/test/factories"; +import { buildUser, buildWebhookSubscription } from "@server/test/factories"; import UserDeletedProcessor from "./UserDeletedProcessor"; const ip = "127.0.0.1"; describe("UserDeletedProcessor", () => { - test("should remove relationships", async () => { + it("should remove relationships", async () => { const user = await buildUser(); expect( await UserAuthentication.count({ @@ -32,4 +32,24 @@ describe("UserDeletedProcessor", () => { }) ).toBe(0); }); + + it("should disable webhook subscriptions created by the user", async () => { + const user = await buildUser(); + const webhook = await buildWebhookSubscription({ + teamId: user.teamId, + createdById: user.id, + }); + + const processor = new UserDeletedProcessor(); + await processor.perform({ + name: "users.delete", + userId: user.id, + actorId: user.id, + teamId: user.teamId, + ip, + }); + + await webhook.reload(); + expect(webhook.enabled).toEqual(false); + }); }); diff --git a/server/queues/processors/UserDeletedProcessor.ts b/server/queues/processors/UserDeletedProcessor.ts index e0730a5437..31ea63a61c 100644 --- a/server/queues/processors/UserDeletedProcessor.ts +++ b/server/queues/processors/UserDeletedProcessor.ts @@ -6,6 +6,7 @@ import { Subscription, UserAuthentication, UserMembership, + WebhookSubscription, } from "@server/models"; import { sequelize } from "@server/storage/database"; import type { Event as TEvent, UserEvent } from "@server/types"; @@ -59,6 +60,16 @@ export default class UserDeletedProcessor extends BaseProcessor { }, transaction, }); + await WebhookSubscription.update( + { enabled: false }, + { + where: { + createdById: event.userId, + enabled: true, + }, + transaction, + } + ); }); } } diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index 09e6ba9493..cc5585f8e1 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -542,7 +542,7 @@ "Viewers can only view and comment on documents.": "Viewers can only view and comment on documents.", "Are you sure you want to make {{ userName }} a {{ role }}?": "Are you sure you want to make {{ userName }} a {{ role }}?", "I understand, delete": "I understand, delete", - "Are you sure you want to permanently delete {{ userName }}? This operation is unrecoverable, consider suspending the user instead.": "Are you sure you want to permanently delete {{ userName }}? This operation is unrecoverable, consider suspending the user instead.", + "Are you sure you want to permanently delete {{ userName }}? This operation is unrecoverable. Any API keys, webhooks, and integrations they created will stop working — consider suspending the user instead.": "Are you sure you want to permanently delete {{ userName }}? This operation is unrecoverable. Any API keys, webhooks, and integrations they created will stop working — consider suspending the user instead.", "Are you sure you want to suspend {{ userName }}? Suspended users will be prevented from logging in.": "Are you sure you want to suspend {{ userName }}? Suspended users will be prevented from logging in.", "New name": "New name", "Name can't be empty": "Name can't be empty",