Skip to content

Commit 5c98cbf

Browse files
committed
fix(webapp): block user deletion while impersonating
1 parent 15333ce commit 5c98cbf

1 file changed

Lines changed: 33 additions & 8 deletions

File tree

apps/webapp/app/routes/admin._index.tsx

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
TableHeaderCell,
2121
TableRow,
2222
} from "~/components/primitives/Table";
23+
import { SimpleTooltip } from "~/components/primitives/Tooltip";
24+
import { useIsImpersonating } from "~/hooks/useOrganizations";
2325
import { useUser } from "~/hooks/useUser";
2426
import { adminGetUsers, redirectWithImpersonation } from "~/models/admin.server";
2527
import { deleteUser as deleteUserOnPlatform } from "~/services/platform.v3.server";
@@ -66,6 +68,13 @@ export async function action({ request }: ActionFunctionArgs) {
6668
return redirect("/");
6769
}
6870

71+
if (admin.isImpersonating) {
72+
return typedjson(
73+
{ error: "Stop impersonating before deleting users." },
74+
{ status: 400 }
75+
);
76+
}
77+
6978
const targetId = deleteAttempt.data.id;
7079

7180
if (targetId === admin.id) {
@@ -102,6 +111,7 @@ export async function action({ request }: ActionFunctionArgs) {
102111

103112
export default function AdminDashboardRoute() {
104113
const currentUser = useUser();
114+
const isImpersonating = useIsImpersonating();
105115
const { users, filters, page, pageCount, justDeleted } = useTypedLoaderData<typeof loader>();
106116
const actionData = useTypedActionData<typeof action>();
107117
const actionError =
@@ -226,14 +236,29 @@ export default function AdminDashboardRoute() {
226236
Impersonate
227237
</Button>
228238
</Form>
229-
{!isSelf && (
230-
<Button
231-
type="button"
232-
variant="danger/small"
233-
onClick={() => openDeleteDialog({ id: user.id, email: user.email })}
234-
>
235-
Delete
236-
</Button>
239+
{!isSelf && !user.admin && (
240+
isImpersonating ? (
241+
<SimpleTooltip
242+
button={
243+
// Wrap in a span so hover events still fire
244+
// when the underlying button is disabled.
245+
<span tabIndex={0}>
246+
<Button type="button" variant="danger/small" disabled>
247+
Delete
248+
</Button>
249+
</span>
250+
}
251+
content="Stop impersonating to delete users"
252+
/>
253+
) : (
254+
<Button
255+
type="button"
256+
variant="danger/small"
257+
onClick={() => openDeleteDialog({ id: user.id, email: user.email })}
258+
>
259+
Delete
260+
</Button>
261+
)
237262
)}
238263
</div>
239264
</TableCell>

0 commit comments

Comments
 (0)