diff --git a/packages/features/ee/users/components/UsersTable.tsx b/packages/features/ee/users/components/UsersTable.tsx index 2137fde721..233b4d4eab 100644 --- a/packages/features/ee/users/components/UsersTable.tsx +++ b/packages/features/ee/users/components/UsersTable.tsx @@ -21,7 +21,7 @@ import { Table, TextField, } from "@calcom/ui"; -import { Edit, Lock, Trash, User, VenetianMask } from "@calcom/ui/components/icon"; +import { Edit, Lock, Trash, User, VenetianMask, Shield } from "@calcom/ui/components/icon"; import { withLicenseRequired } from "../../common/components/LicenseRequired"; @@ -88,6 +88,12 @@ function UsersTableBare() { }, }); + const removeTwoFactor = trpc.viewer.admin.removeTwoFactor.useMutation({ + onSuccess: () => { + showToast("2FA has been removed", "success"); + }, + }); + const lockUserAccount = trpc.viewer.admin.lockUserAccount.useMutation({ onSuccess: ({ userId, locked }) => { showToast(locked ? "User was locked" : "User was unlocked", "success"); @@ -236,6 +242,13 @@ function UsersTableBare() { }, icon: VenetianMask, }, + { + id: "remove-2fa", + label: "Remove 2FA", + color: "destructive", + onClick: () => removeTwoFactor.mutate({ userId: user.id }), + icon: Shield, + }, { id: "delete", label: "Delete", diff --git a/packages/trpc/server/routers/viewer/admin/_router.ts b/packages/trpc/server/routers/viewer/admin/_router.ts index 3a31b10297..9bb381d29c 100644 --- a/packages/trpc/server/routers/viewer/admin/_router.ts +++ b/packages/trpc/server/routers/viewer/admin/_router.ts @@ -4,6 +4,7 @@ import { authedAdminProcedure } from "../../../procedures/authedProcedure"; import { router, importHandler } from "../../../trpc"; import { ZListMembersSchema } from "./listPaginated.schema"; import { ZAdminLockUserAccountSchema } from "./lockUserAccount.schema"; +import { ZAdminRemoveTwoFactor } from "./removeTwoFactor.schema"; import { ZAdminPasswordResetSchema } from "./sendPasswordReset.schema"; const NAMESPACE = "admin"; @@ -39,4 +40,11 @@ export const adminRouter = router({ data: { enabled, updatedBy: user.id }, }); }), + removeTwoFactor: authedAdminProcedure.input(ZAdminRemoveTwoFactor).mutation(async (opts) => { + const handler = await importHandler( + namespaced("removeTwoFactor"), + () => import("./removeTwoFactor.handler") + ); + return handler(opts); + }), }); diff --git a/packages/trpc/server/routers/viewer/admin/removeTwoFactor.handler.ts b/packages/trpc/server/routers/viewer/admin/removeTwoFactor.handler.ts new file mode 100644 index 0000000000..c3fbab5fab --- /dev/null +++ b/packages/trpc/server/routers/viewer/admin/removeTwoFactor.handler.ts @@ -0,0 +1,32 @@ +import { prisma } from "@calcom/prisma"; + +import type { TrpcSessionUser } from "../../../trpc"; +import type { TAdminRemoveTwoFactor } from "./removeTwoFactor.schema"; + +type GetOptions = { + ctx: { + user: NonNullable; + }; + input: TAdminRemoveTwoFactor; +}; + +const removeTwoFactorHandler = async ({ input }: GetOptions) => { + const { userId } = input; + await prisma.user.update({ + where: { + id: userId, + }, + data: { + backupCodes: null, + twoFactorEnabled: false, + twoFactorSecret: null, + }, + }); + + return { + success: true, + userId, + }; +}; + +export default removeTwoFactorHandler; diff --git a/packages/trpc/server/routers/viewer/admin/removeTwoFactor.schema.ts b/packages/trpc/server/routers/viewer/admin/removeTwoFactor.schema.ts new file mode 100644 index 0000000000..8260a22ccb --- /dev/null +++ b/packages/trpc/server/routers/viewer/admin/removeTwoFactor.schema.ts @@ -0,0 +1,7 @@ +import { z } from "zod"; + +export const ZAdminRemoveTwoFactor = z.object({ + userId: z.number(), +}); + +export type TAdminRemoveTwoFactor = z.infer;