import { auth } from "@/lib/auth"; import type { SessionUser } from "@/types"; /** * Read-only session lookup. Returns the SessionUser stashed on the * NextAuth session by `auth.ts::callbacks.session`, or null if there * is no authenticated session. */ export async function getSessionUser(): Promise { const session = await auth(); return (session as any)?.platformUser ?? null; } /** * Throws if there is no authenticated session. Otherwise returns the * SessionUser. Use at the top of any handler that requires a logged-in * user regardless of role. */ export async function requireSession(): Promise { const user = await getSessionUser(); if (!user) throw new Error("Unauthorized"); return user; } /** * Throws unless the caller has a platform-level role * (platform_admin or platform_operator). Use to gate /api/admin/* * routes — these handle ANY customer's org and must not be accessible * to customer-role users. */ export async function requirePlatformRole(): Promise { const user = await requireSession(); if (!user.isPlatform) throw new Error("Forbidden: platform role required"); return user; } // --------------------------------------------------------------------------- // Slice 5: role predicates and gates // --------------------------------------------------------------------------- // // Naming convention: `is*` are pure predicates over a SessionUser, // safe to call inline in JSX/server components. `require*` throw on // failure and are meant for the top of route handlers. /** * True when the user is a platform admin/operator OR holds the * `owner` customer role on their org. * * This is the single check for "can mutate". Platform users always * win because they administer all orgs cross-cut. Customer-side, only * `owner` may mutate; `user` (and any future read-only customer role) * cannot. */ export function canMutate(user: SessionUser): boolean { return user.isPlatform || user.roles.includes("owner"); } /** * True when the user holds the customer `owner` role on their org. * Excludes platform users — use {@link canMutate} when both should * be allowed. * * Useful for permissions that are specifically about "this customer's * own owner", e.g. "owner can invite users into their own org" — a * platform user shouldn't be casually inviting users into a customer * org, that's an admin-console action and goes through different * tooling. */ export function isCustomerOwner(user: SessionUser): boolean { return !user.isPlatform && user.roles.includes("owner"); } /** * Throws unless `canMutate(user) === true`. Use at the top of any * mutating customer-side handler. * * The thrown error message is intentionally generic — handlers * should catch and translate to a 403 JSON response so the client * doesn't see a stack trace. */ export async function requireOwnerRole(): Promise { const user = await requireSession(); if (!canMutate(user)) { throw new Error("Forbidden: owner role required"); } return user; }