mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-16 23:41:43 +00:00
The Auto-Routers tab was proxy-admin only, while Add Model on the same page already admits team admins. The asymmetry was not a policy decision; the auto-router create form simply never mounted a team selector, so a team admin's submit was unscoped and POST /model/new rejects an unscoped create from any non-proxy-admin. Mounting the shared TeamDropdown closes it, and the tab now takes the same audience as its sibling. Fixing that surfaced a second, larger problem. The dashboard decided who may edit or delete a deployment with `(userRole === "Admin" || created_by === userID) && db_model`, but `created_by` is written at creation and never read by any backend auth check. The API authorizes on team-admin membership of model_info.team_id, so the dashboard was wrong in both directions: it hid controls from team admins the API accepts, and offered them to former team admins the API rejects. Verified against a live proxy; a model created by the proxy admin was PATCHed and DELETEd 200 by a team admin who did not create it, while the same key got 403 on another team's row and on an unscoped row. Both questions now have one owner in utils/modelPermissions.ts, deliberately shaped as a mirror of ModelManagementAuthChecks. Creation returns a tagged union rather than a pair of booleans, so "may not create" and "may create unscoped" cannot be confused, and the five places that had each invented their own spelling (the models page, the auto-routers tab and panel, the auto-router form, and both branches of AddModelForm) call it instead. Row affordances are now per row rather than per tab, because opening the tab to team admins puts routers they cannot act on in the same list. Note for reviewers: collapsing AddModelForm onto the shared owner changes behaviour for org_admin and Admin Viewer who also admin a team. They previously got the optional team selector, because all_admin_roles counts them as admins, and could submit an unscoped create that the API always 403s; they now get the required selector. Also corrects stale copy left by the auto-router move. The exclude_auto_routers API description named a dashboard page, which went stale inside a single PR; it now describes the concept so it cannot drift with the UI again. The eslint-suppressions prune includes one entry for caching/_components/cache_dashboard.tsx, which this branch does not touch. Its baseline was already stale; the gate measures the whole tree, so it could not be left behind. |
||
|---|---|---|
| .. | ||
| budgetUtils.ts | ||
| cookieUtils.test.ts | ||
| cookieUtils.ts | ||
| dataUtils.test.ts | ||
| dataUtils.ts | ||
| debounceConstants.ts | ||
| entityLinks.ts | ||
| errorPatterns.ts | ||
| errorUtils.test.ts | ||
| errorUtils.ts | ||
| jwtUtils.test.ts | ||
| jwtUtils.ts | ||
| keyExpiryUtils.test.ts | ||
| keyExpiryUtils.ts | ||
| keyUpdateUtils.test.ts | ||
| keyUpdateUtils.ts | ||
| licenseUtils.test.ts | ||
| licenseUtils.ts | ||
| localStorageUtils.test.ts | ||
| localStorageUtils.ts | ||
| maskedSecretUtils.ts | ||
| mcpHeaderUtils.test.ts | ||
| mcpHeaderUtils.ts | ||
| mcpTokenStore.test.ts | ||
| mcpTokenStore.ts | ||
| mcpToolCrudClassification.test.ts | ||
| mcpToolCrudClassification.ts | ||
| migratedPages.test.ts | ||
| migratedPages.ts | ||
| modelPermissions.test.ts | ||
| modelPermissions.ts | ||
| pkce.ts | ||
| proxyUtils.test.ts | ||
| proxyUtils.ts | ||
| returnUrlUtils.test.ts | ||
| returnUrlUtils.ts | ||
| roles.test.ts | ||
| roles.ts | ||
| secureStorage.ts | ||
| tabRoutes.test.ts | ||
| tabRoutes.ts | ||
| teamUtils.test.ts | ||
| teamUtils.ts | ||
| textUtils.test.ts | ||
| textUtils.ts | ||