mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-05 20:56:13 -05:00
Allow dev user to impersonate
This commit is contained in:
parent
cbf95d0cd1
commit
edd8512704
|
|
@ -3,6 +3,9 @@ export const ADMIN_ID = process.env.NODE_ENV === "test" ? 1 : 274;
|
|||
|
||||
// Panda Scep Acing Baja Michi
|
||||
export const STAFF_IDS = [11329, 9719, 9342, 20774, 23094];
|
||||
// hfcRed
|
||||
export const DEV_IDS = [27883];
|
||||
|
||||
export const STAFF_DISCORD_IDS = [
|
||||
"138757634500067328",
|
||||
"184478601171828737",
|
||||
|
|
|
|||
|
|
@ -1,14 +1,25 @@
|
|||
import type { LoaderFunctionArgs } from "react-router";
|
||||
import { isImpersonating } from "~/features/auth/core/user.server";
|
||||
import {
|
||||
getRealUserId,
|
||||
isImpersonating,
|
||||
requireUser,
|
||||
} from "~/features/auth/core/user.server";
|
||||
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
||||
import { requireRole } from "~/modules/permissions/guards.server";
|
||||
import { isAdmin, isDev, isStaff } from "~/modules/permissions/utils";
|
||||
import { parseSafeSearchParams } from "~/utils/remix.server";
|
||||
import { adminActionSearchParamsSchema } from "../admin-schemas";
|
||||
import { DANGEROUS_CAN_ACCESS_DEV_CONTROLS } from "../core/dev-controls";
|
||||
|
||||
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||
if (!DANGEROUS_CAN_ACCESS_DEV_CONTROLS) {
|
||||
requireRole("STAFF");
|
||||
const user = requireUser();
|
||||
const realUserId = await getRealUserId(request);
|
||||
const userToCheck =
|
||||
realUserId && realUserId !== user.id ? { id: realUserId } : user;
|
||||
|
||||
if (!isAdmin(userToCheck) && !isStaff(userToCheck) && !isDev(userToCheck)) {
|
||||
throw new Response("Forbidden", { status: 403 });
|
||||
}
|
||||
}
|
||||
|
||||
const parsedSearchParams = parseSafeSearchParams({
|
||||
|
|
|
|||
|
|
@ -47,6 +47,17 @@ export const meta: MetaFunction = (args) => {
|
|||
};
|
||||
|
||||
export default function AdminPage() {
|
||||
const isStaff = useHasRole("STAFF");
|
||||
|
||||
// is dev user or is someone impersonating another user (allow them to stop)
|
||||
if (!isStaff) {
|
||||
return (
|
||||
<Main>
|
||||
<Impersonate />
|
||||
</Main>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Main>
|
||||
<SendouTabs>
|
||||
|
|
@ -109,12 +120,15 @@ function FriendCodeLookUp() {
|
|||
function AdminActions() {
|
||||
const isStaff = useHasRole("STAFF");
|
||||
const isAdmin = useHasRole("ADMIN");
|
||||
const isDev = useHasRole("DEV");
|
||||
|
||||
return (
|
||||
<div className="stack lg">
|
||||
{DANGEROUS_CAN_ACCESS_DEV_CONTROLS ? <Seed /> : null}
|
||||
{DANGEROUS_CAN_ACCESS_DEV_CONTROLS ? <TestAdminNotification /> : null}
|
||||
{DANGEROUS_CAN_ACCESS_DEV_CONTROLS || isAdmin ? <Impersonate /> : null}
|
||||
{DANGEROUS_CAN_ACCESS_DEV_CONTROLS || isAdmin || isDev ? (
|
||||
<Impersonate />
|
||||
) : null}
|
||||
|
||||
{isStaff ? <LinkPlayer /> : null}
|
||||
{isStaff ? <GiveArtist /> : null}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ import type { ActionFunction, LoaderFunction } from "react-router";
|
|||
import { redirect } from "react-router";
|
||||
import { z } from "zod";
|
||||
import { DANGEROUS_CAN_ACCESS_DEV_CONTROLS } from "~/features/admin/core/dev-controls";
|
||||
import { requireUser } from "~/features/auth/core/user.server";
|
||||
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
||||
import { requireRole } from "~/modules/permissions/guards.server";
|
||||
import { isAdmin, isStaff } from "~/modules/permissions/utils";
|
||||
import { logger } from "~/utils/logger";
|
||||
import {
|
||||
canAccessLohiEndpoint,
|
||||
|
|
@ -74,19 +75,36 @@ export const logInAction: ActionFunction = async ({ request }) => {
|
|||
|
||||
export const impersonateAction: ActionFunction = async ({ request }) => {
|
||||
if (!DANGEROUS_CAN_ACCESS_DEV_CONTROLS) {
|
||||
requireRole("ADMIN");
|
||||
const user = requireUser();
|
||||
if (!user.roles.includes("ADMIN") && !user.roles.includes("DEV")) {
|
||||
throw new Response("Forbidden", { status: 403 });
|
||||
}
|
||||
|
||||
if (user.roles.includes("DEV") && !user.roles.includes("ADMIN")) {
|
||||
const url = new URL(request.url);
|
||||
const targetId = Number(url.searchParams.get("id"));
|
||||
if (isAdmin({ id: targetId }) || isStaff({ id: targetId })) {
|
||||
throw new Response("Forbidden", { status: 403 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const session = await authSessionStorage.getSession(
|
||||
request.headers.get("Cookie"),
|
||||
);
|
||||
|
||||
const realUserId = session.get(SESSION_KEY);
|
||||
|
||||
const url = new URL(request.url);
|
||||
const rawId = url.searchParams.get("id");
|
||||
|
||||
const userId = Number(url.searchParams.get("id"));
|
||||
if (!rawId || Number.isNaN(userId)) throw new Response(null, { status: 400 });
|
||||
|
||||
logger.info(
|
||||
`Impersonation: user ${realUserId} started impersonating user ${userId}`,
|
||||
);
|
||||
|
||||
session.set(IMPERSONATED_SESSION_KEY, userId);
|
||||
|
||||
throw redirect(ADMIN_PAGE, {
|
||||
|
|
@ -99,6 +117,13 @@ export const stopImpersonatingAction: ActionFunction = async ({ request }) => {
|
|||
request.headers.get("Cookie"),
|
||||
);
|
||||
|
||||
const realUserId = session.get(SESSION_KEY);
|
||||
const impersonatedUserId = session.get(IMPERSONATED_SESSION_KEY);
|
||||
|
||||
logger.info(
|
||||
`Impersonation: user ${realUserId} stopped impersonating user ${impersonatedUserId}`,
|
||||
);
|
||||
|
||||
session.unset(IMPERSONATED_SESSION_KEY);
|
||||
|
||||
throw redirect(ADMIN_PAGE, {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { IMPERSONATED_SESSION_KEY } from "./authenticator.server";
|
||||
import { IMPERSONATED_SESSION_KEY, SESSION_KEY } from "./authenticator.server";
|
||||
import { authSessionStorage } from "./session.server";
|
||||
import { type AuthenticatedUser, getUserContext } from "./user-context.server";
|
||||
|
||||
|
|
@ -24,3 +24,13 @@ export async function isImpersonating(request: Request) {
|
|||
|
||||
return Boolean(session.get(IMPERSONATED_SESSION_KEY));
|
||||
}
|
||||
|
||||
export async function getRealUserId(
|
||||
request: Request,
|
||||
): Promise<number | undefined> {
|
||||
const session = await authSessionStorage.getSession(
|
||||
request.headers.get("Cookie"),
|
||||
);
|
||||
|
||||
return session.get(SESSION_KEY) as number | undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { UserWithPlusTier } from "~/db/tables";
|
||||
import { userDiscordIdIsAged } from "~/utils/users";
|
||||
import type { Role } from "./types";
|
||||
import { isAdmin, isStaff, isSupporter } from "./utils";
|
||||
import { isAdmin, isDev, isStaff, isSupporter } from "./utils";
|
||||
|
||||
export function userRoles(
|
||||
user: Pick<
|
||||
|
|
@ -26,6 +26,10 @@ export function userRoles(
|
|||
result.push("STAFF");
|
||||
}
|
||||
|
||||
if (isDev(user)) {
|
||||
result.push("DEV");
|
||||
}
|
||||
|
||||
if (typeof user.patronTier === "number") {
|
||||
result.push("MINOR_SUPPORT");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,5 +14,6 @@ export type Role =
|
|||
| "CALENDAR_EVENT_ADDER"
|
||||
| "TOURNAMENT_ADDER"
|
||||
| "API_ACCESSER"
|
||||
| "DEV"
|
||||
| "SUPPORTER" // patrons of "Supporter" tier or higher
|
||||
| "MINOR_SUPPORT"; // patrons of "Support" tier or higher
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { ADMIN_ID, STAFF_IDS } from "~/features/admin/admin-constants";
|
||||
import { ADMIN_ID, DEV_IDS, STAFF_IDS } from "~/features/admin/admin-constants";
|
||||
|
||||
export function isAdmin(user?: { id: number }) {
|
||||
return user?.id === ADMIN_ID;
|
||||
|
|
@ -10,6 +10,12 @@ export function isStaff(user?: { id: number }) {
|
|||
return STAFF_IDS.includes(user.id);
|
||||
}
|
||||
|
||||
export function isDev(user?: { id: number }) {
|
||||
if (!user) return false;
|
||||
|
||||
return DEV_IDS.includes(user.id);
|
||||
}
|
||||
|
||||
export function isSupporter(user?: { patronTier: number | null }) {
|
||||
return typeof user?.patronTier === "number" && user.patronTier >= 2;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user