128 lines
5.1 KiB
TypeScript
128 lines
5.1 KiB
TypeScript
import { getTenantUserType } from "./db/tenants.db.server";
|
|
import { getTenantIdFromUrl, resolveTenantLookupSource } from "./services/.server/urlService";
|
|
import { getUserInfo, getValidatedServerSession } 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 "@hivegrid/config";
|
|
import { runWithRlsContext } from "@hivegrid/db/rls-context.server";
|
|
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 requireAdminConsoleIdentityForUser(request, userInfo);
|
|
}
|
|
|
|
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) {
|
|
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 requireAdminConsoleIdentity({ request }: { request: Request }) {
|
|
const userInfo = await getUserInfo(request);
|
|
return requireAdminConsoleIdentityForUser(request, userInfo);
|
|
}
|
|
|
|
async function requireAdminConsoleIdentityForUser(request: Request, userInfo: Awaited<ReturnType<typeof getUserInfo>>) {
|
|
if (!userInfo.userId) {
|
|
throw Response.json(UNAUTHORIZED_ENVELOPE, { status: 401 });
|
|
}
|
|
const validatedSession = await getValidatedServerSession(request, userInfo);
|
|
if (!validatedSession || !("adminUser" in validatedSession) || validatedSession.adminUserId !== userInfo.userId) {
|
|
throw Response.json(UNAUTHORIZED_ENVELOPE, { status: 401 });
|
|
}
|
|
return { ...userInfo, adminIdentity: validatedSession.adminUser };
|
|
}
|
|
|
|
export async function requireSuperAdmin({ request }: { request: Request }) {
|
|
const adminIdentity = await requireAdminConsoleIdentity({ request });
|
|
const isSuperAdmin = await getIsSuperAdminByUserId(adminIdentity.userId!);
|
|
if (!isSuperAdmin) {
|
|
throw Response.json(UNAUTHORIZED_ENVELOPE, { status: 401 });
|
|
}
|
|
return { ...adminIdentity, 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 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 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 }) {
|
|
try {
|
|
const identity = await requireAdminConsoleIdentity({ request });
|
|
return getIsSuperAdminByUserId(identity.userId!);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export async function getIsSuperAdminByUserId(userId: string) {
|
|
if (!userId) return false;
|
|
const isSuperAdmin = await runWithRlsContext({ userId }, () =>
|
|
db.adminUserRole.findFirst({
|
|
where: { adminUserId: userId, user: { status: "active" }, role: { systemKey: "super_admin", active: true } },
|
|
select: { adminUserId: true },
|
|
})
|
|
);
|
|
return isSuperAdmin !== null;
|
|
}
|
|
|
|
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;
|
|
}
|