import type { Context } from "hono"; import type { AuthUser } from "./auth.ts"; import type { Db } from "./db.ts"; import { respondApiError, routeLabel, type RpcEnvelope } from "./http_errors.ts"; import { hasPermission } from "./permissions.ts"; import { projectDocPerm } from "./iam_catalog.ts"; import { assertProjectAccess, getUserScope, type UserScope } from "./user_scope.ts"; export async function denyUnlessProjectScope( c: Context, projectId: number | null | undefined, ): Promise { if (projectId == null || !Number.isFinite(projectId)) return true; const user = c.get("user") as AuthUser; if (user.realm === "platform" || user.is_owner) return true; const scope = await getUserScope(user); if (assertProjectAccess(scope, projectId)) return true; respondApiError(c, "FORBIDDEN", "Obra fuera de su alcance", { route: routeLabel(c), project_id: projectId, }); return false; } export function filterProjectsByScope( items: T[], scope: UserScope, ): T[] { if (scope.allProjects) return items; const allowed = new Set(scope.projectIds); return items.filter((p) => allowed.has(Number(p.id))); } export async function filterProjectListEnvelope( c: Context, env: RpcEnvelope<{ projects?: Array<{ id: number }> }>, ): Promise }>> { const user = c.get("user") as AuthUser; if (!env.ok || !env.data?.projects || user.realm === "platform" || user.is_owner) return env; const scope = await getUserScope(user); return { ...env, data: { ...env.data, projects: filterProjectsByScope(env.data.projects, scope), }, }; } export async function projectDocCategory( db: Db, typeCode: string, ): Promise { const row = await db.prepare( "SELECT category FROM project_document_types WHERE code = ?", ).get(typeCode) as { category: string } | undefined; return row?.category ?? "otro"; } export async function denyUnlessProjectDocView( c: Context, db: Db, typeCode: string, ): Promise { const user = c.get("user") as AuthUser; if (user.realm === "platform" || user.is_owner) return true; const category = await projectDocCategory(db, typeCode); if (await hasPermission(user, projectDocPerm(category, "view"))) return true; respondApiError(c, "FORBIDDEN", "Sin permiso para este tipo de documento de obra", { route: routeLabel(c), type_code: typeCode, category, }); return false; } export async function denyUnlessProjectDocCreate( c: Context, db: Db, typeCode: string, ): Promise { const user = c.get("user") as AuthUser; if (user.realm === "platform" || user.is_owner) return true; const category = await projectDocCategory(db, typeCode); if (await hasPermission(user, projectDocPerm(category, "create"))) return true; respondApiError(c, "FORBIDDEN", "Sin permiso para subir este tipo de documento", { route: routeLabel(c), type_code: typeCode, category, }); return false; } export async function filterProjectDocumentsForUser( user: AuthUser, db: Db, documents: Array<{ type_code?: string; type?: string }>, ): Promise { if (user.realm === "platform" || user.is_owner) return documents; const out: typeof documents = []; for (const doc of documents) { const typeCode = String(doc.type_code ?? doc.type ?? ""); const category = await projectDocCategory(db, typeCode); if (await hasPermission(user, projectDocPerm(category, "view"))) out.push(doc); } return out; } export async function denyUnlessWarehouseCentral(c: Context): Promise { const user = c.get("user") as AuthUser; if (user.realm === "platform" || user.is_owner) return true; const scope = await getUserScope(user); if (scope.warehouseCentral) return true; respondApiError(c, "FORBIDDEN", "Sin acceso al almacén central", { route: routeLabel(c) }); return false; }