Globální vyhledávání obchází RBAC scope, který výpisy respektují
Symptom
Sekce “Symptom”Uživatel s omezeným rozsahem (např. obchodník vidící jen svoje klienty) přes globální vyhledávač najde a otevře i cizí záznamy — klienty, smlouvy, faktury — které v běžných výpisech nevidí.
Root cause
Sekce “Root cause”Scope filtr (WHERE ownerId = me apod.) byl pečlivě aplikovaný v každém list endpointu (/api/clients, /api/contracts, …), ale globální vyhledávání byl samostatný endpoint, který dotazoval všechny entity jen s requirePermission(READ) a BEZ scope filtru. Právo „číst” ještě neznamená „číst vše” — rozsah (own/all) se musí promítnout i sem.
Snadno se přehlédne, protože:
- vyhledávání je zvláštní endpoint, ne jeden z list endpointů,
- projektová kontrola RBAC obvykle testuje list stránky, ne fulltext,
- funkčně to „funguje” (vrací výsledky), jen vrací i cizí.
Řešení
Sekce “Řešení”Do vyhledávacího endpointu přenést stejný scope filtr jako mají výpisy, PER MODUL:
const clientWhere = auth.scope === "own" ? { OR: [{ ownerId: me }, { leads: { some: { assignedToId: me } } }] } : {};const contractWhere = contractsPerm?.scope === "own" ? { OR: [{ creatorId: me }, { client: { ownerId: me } }] } : {};// … a když uživatel na modul právo NEMÁ, sekci vůbec nevracetPozor: scope se liší per entita (klient=owner, smlouva=creator/owner, …) — nestačí filtrovat jen hlavní entitu.
Jak to poznat příště
Sekce “Jak to poznat příště”- Audit RBAC musí explicitně zahrnout vyhledávání, exporty, dashboardové agregace a „nedávné”/„související” widgety — všude, kde se čtou data napříč entitami
- Pravidlo: „kde list filtruje scope, musí filtrovat i každý jiný způsob, jak se ke stejným datům dostat”