import { getTenantUserType } from "./db/tenants.db.server"; import { getTenantIdFromUrl, resolveTenantLookupSource } from "./services/.server/urlService"; import { getUserInfo } from "./session.server"; import { db } from "./db.server"; import { getUserRoleInTenant } from "./db/permissions/userRoles.db.server"; import { Params } from "react-router"; import { DefaultAppRoles } from "~/application/dtos/shared/DefaultAppRoles"; import { loadRequiredWebConfig } from "@toolctrl/config"; import { getHostRoutingContextFromRequest } from "./app/HostRouting.server"; const webConfig = loadRequiredWebConfig(); export type AuthErrorCode = "UNAUTHORIZED" | "FORBIDDEN"; export function authErrorEnvelope(code: AuthErrorCode, messageKey: string) { return { error: { code, messageKey } }; } const UNAUTHORIZED_ENVELOPE = authErrorEnvelope("UNAUTHORIZED", "errors.unauthorized"); const FORBIDDEN_ADMINS_ONLY_ENVELOPE = authErrorEnvelope("FORBIDDEN", "errors.adminsOnly"); export async function requireAuth({ request, params, adminOnly }: { request: Request; params: { tenant?: string }; adminOnly?: boolean }) { const userInfo = await getUserInfo(request); const hostContext = getHostRoutingContextFromRequest(request); if (hostContext === "admin") { return await requireSuperAdmin({ request }); } const tenantLookupSource = resolveTenantLookupSource({ request, params }); if (tenantLookupSource.source === "app-route-param") { const tenantId = await getTenantIdFromUrl({ tenant: tenantLookupSource.tenant }); if (!userInfo.userId || !tenantId) { throw Response.json(UNAUTHORIZED_ENVELOPE, { status: 401 }); } const member = await getTenantUserType(userInfo.userId, tenantId); if (!member) { const isSuperAdmin = await getIsSuperAdminByUserId(userInfo.userId); if (!isSuperAdmin) { throw Response.json(UNAUTHORIZED_ENVELOPE, { status: 401 }); } } } if (adminOnly) { const isSuperUserOrAdmin = await getIsSuperUserOrAdmin({ request, params }); if (!isSuperUserOrAdmin && webConfig.nodeEnv !== "development") { throw Response.json(FORBIDDEN_ADMINS_ONLY_ENVELOPE, { status: 401 }); } } return { ...userInfo }; } export async function requireSuperAdmin({ request }: { request: Request }) { const userInfo = await getUserInfo(request); if (!userInfo.userId) { throw Response.json(UNAUTHORIZED_ENVELOPE, { status: 401 }); } const isSuperAdmin = await db.adminUser.findUnique({ where: { userId: userInfo.userId } }); if (!isSuperAdmin) { throw Response.json(UNAUTHORIZED_ENVELOPE, { status: 401 }); } return { ...userInfo, isSuperAdmin }; } export async function getIsSuperUserOrAdmin({ request, params }: { request: Request; params: Params }) { const { userId } = await getUserInfo(request); const tenantId = await getTenantIdFromUrl(params); if (!userId || !tenantId) return false; const isSuperAdmin = await getIsSuperAdmin({ request }); if (isSuperAdmin) return true; const isSuperUser = await getUserRoleInTenant(userId, tenantId, DefaultAppRoles.SuperUser); if (isSuperUser) return true; const isAdmin = await getUserRoleInTenant(userId, tenantId, DefaultAppRoles.Admin); if (isAdmin) return true; return false; } export async function getIsSuperUserOrAdminByIds({ userId, tenantId }: { userId: string; tenantId: string }) { if (!userId || !tenantId) return false; const isSuperAdmin = await db.adminUser.findUnique({ where: { userId: userId } }); if (isSuperAdmin) return true; const isSuperUser = await getUserRoleInTenant(userId, tenantId, DefaultAppRoles.SuperUser); if (isSuperUser) return true; const isAdmin = await getUserRoleInTenant(userId, tenantId, DefaultAppRoles.Admin); if (isAdmin) return true; return false; } export async function getIsSuperAdmin({ request }: { request: Request }) { const { userId } = await getUserInfo(request); if (!userId) return false; return getIsSuperAdminByUserId(userId); } export async function getIsSuperAdminByUserId(userId: string) { if (!userId) return false; const isSuperAdmin = await db.adminUser.findUnique({ where: { userId } }); return !!isSuperAdmin; } export async function requireSuperUserOrAdmin({ request, params }: { request: Request; params: Params }) { const isSuperUserOrAdmin = await getIsSuperUserOrAdmin({ request, params }); if (!isSuperUserOrAdmin) { throw Response.json(UNAUTHORIZED_ENVELOPE, { status: 401 }); } return true; }