--- description: RBAC is required for every apps/web product module (menu, moduleKey, action flags) globs: apps/web/src/apps/main/**/*.{ts,tsx} alwaysApply: false --- # Web module RBAC Every authenticated product module in `apps/web` must be gated by `GET /auth/me` permissions. Copy Privileges (`system/privileges`) — do not invent a second RBAC path. Catalog keys live in `api.md` §4 (`PRIVILEGES`, `CONFIGURATION.BRANCH`, `SALES.ORDER`, …). `isSuperadmin` bypasses the matrix (adapter returns `defaultPrivileges`). ## Required wiring (do all four) 1. **`moduleKey`** on `ModuleConfigEntity` equals the catalog `code` (e.g. `CONFIGURATION.BRANCH`). 2. **Menu leaf** in `layouts/data/menu.data.ts` sets the same `moduleKey`. `filterMenuByViewPrivilege` hides the item when `ALLOW_VIEW` is false. 3. **Routes** wrap in `EnterpriseModuleProvider` so missing `ALLOW_VIEW` shows forbidden (no all-true flash). 4. **Do not** re-check create/edit/delete in page JSX. Foundations already hide actions from `PrivilegeEntity`. ```ts // BAD — custom hide/show, or menu without moduleKey if (!user.permissions.BRANCHES?.create) return null; { key: 'branches', path: '/app/system/branches/index' } // GOOD export const branchesModuleConfig = { moduleKey: 'CONFIGURATION.BRANCH', /* ... */ }; { key: 'system-branches', path: '/app/system/branches/index', moduleKey: 'CONFIGURATION.BRANCH' } ``` ## Flag map (`mapUserPrivileges`) | API flag | UI | |---|---| | `view` | `ALLOW_VIEW` (menu + module chrome) | | `create` | `ALLOW_CREATE` (create + duplicate) | | `update` | `ALLOW_EDIT` + `ALLOW_ACTIVATE` + `ALLOW_DEACTIVATE` | | `delete` | `ALLOW_DELETE` | | `import` | `ALLOW_IMPORT` | Missing flag → `false`. Cycles/plans: key follows `purpose` (`SALES.CYCLE` / `LOGISTICS.PLAN`), not a generic `CYCLES` key. Reference: [apps/web/src/core/lib/map-user-privileges.ts](apps/web/src/core/lib/map-user-privileges.ts), [filter-menu-by-view-privilege.ts](apps/web/src/core/lib/filter-menu-by-view-privilege.ts).