From 4732647de2c6b32279f1f79033b32f2944793532 Mon Sep 17 00:00:00 2001 From: tin-berri Date: Sat, 3 Oct 2026 11:07:54 -0700 Subject: [PATCH] feat(ui): make LiteAdmin enterprise-only (#44399) --- .../Navbar/UserDropdown/UserDropdown.tsx | 2 +- .../SidebarAccountMenu/SidebarAccountMenu.tsx | 2 +- .../liteadmin/LiteAdmin.integration.test.tsx | 60 ++++++++++++++++++- .../src/components/liteadmin/LiteAdmin.tsx | 2 +- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/ui/litellm-dashboard/src/components/Navbar/UserDropdown/UserDropdown.tsx b/ui/litellm-dashboard/src/components/Navbar/UserDropdown/UserDropdown.tsx index ab9a723e57a..bd0c3c07fc4 100644 --- a/ui/litellm-dashboard/src/components/Navbar/UserDropdown/UserDropdown.tsx +++ b/ui/litellm-dashboard/src/components/Navbar/UserDropdown/UserDropdown.tsx @@ -204,7 +204,7 @@ const UserDropdown: React.FC = ({ onLogout, variant = "navbar aria-label="Toggle hide bouncing icon" /> - {canUseLiteAdmin && ( + {premiumUser === true && canUseLiteAdmin && (
Hide LiteAdmin = ({ onLogout, colla />
))} - {canUseLiteAdmin && ( + {premiumUser === true && canUseLiteAdmin && (
Hide LiteAdmin () { return { promise, resolve }; } -function session(role = "proxy_admin", user = "first-admin") { +function session( + role = "proxy_admin", + user = "first-admin", + license: { premium_user?: boolean | null | string } = { premium_user: true }, +) { const encode = (value: object) => btoa(JSON.stringify(value)).replaceAll("=", "").replaceAll("+", "-").replaceAll("/", "_"); const claims = { key: `sk-session-${user}`, user_id: user, user_role: role, + ...license, auth_header_name: "X-Gateway-Session", exp: Date.now() / 1000 + 3600, }; @@ -197,7 +202,7 @@ describe("LiteAdmin in the gateway", () => { it.each([ ["sidebar", SidebarAccountMenu], ["navbar", UserDropdown], - ] as const)("persists Hide LiteAdmin from the %s account menu", async (_name, Menu) => { + ] as const)("persists Hide LiteAdmin only for enterprise users in the %s account menu", async (_name, Menu) => { gateway([]); const user = userEvent.setup(); const view = renderWidget(Menu); @@ -219,6 +224,57 @@ describe("LiteAdmin in the gateway", () => { expect(savedToggle).toBeChecked(); await user.click(savedToggle); expect(await screen.findByRole("button", { name: "LiteAdmin" })).toBeInTheDocument(); + + for (const premiumUser of [false, null, undefined, "true"]) { + session("proxy_admin", "first-admin", { premium_user: premiumUser }); + restored.refresh(); + expect(screen.getByRole("switch", { name: "Toggle hide all prompts" })).toBeInTheDocument(); + expect(screen.queryByRole("switch", { name: "Toggle hide LiteAdmin" })).not.toBeInTheDocument(); + } + }); + + it.each([false, null, undefined, "true"])( + "does not expose LiteAdmin when premium_user is %s", + async (premiumUser) => { + session("proxy_admin", "first-admin", { premium_user: premiumUser }); + const requests = gateway([]); + const { client } = renderWidget(); + await screen.findByText("Session ready"); + await waitFor(() => expect(client.isFetching()).toBe(0)); + expect(screen.queryByRole("button", { name: "LiteAdmin" })).not.toBeInTheDocument(); + for (const modifiers of [{ metaKey: true }, { ctrlKey: true }]) { + expect(fireEvent.keyDown(document, { key: "j", ...modifiers })).toBe(true); + expect(screen.queryByRole("complementary", { name: "LiteAdmin" })).not.toBeInTheDocument(); + } + expect(requests.every((request) => request.url.endsWith("/litellm-ui-config"))).toBe(true); + }, + ); + + it("removes an open panel and shortcuts when enterprise access is lost and restores it closed", async () => { + const requests = gateway([]); + const view = renderWidget(); + await openWidget(); + fireEvent.change(screen.getByPlaceholderText("Ask LiteAdmin…"), { target: { value: "Previous enterprise draft" } }); + await waitFor(() => expect(view.client.isFetching()).toBe(0)); + const requestCount = requests.length; + + session("proxy_admin", "first-admin", { premium_user: false }); + view.refresh(); + expect(screen.queryByRole("button", { name: "LiteAdmin" })).not.toBeInTheDocument(); + expect(screen.queryByRole("complementary", { name: "LiteAdmin" })).not.toBeInTheDocument(); + for (const modifiers of [{ metaKey: true }, { ctrlKey: true }]) { + expect(fireEvent.keyDown(document, { key: "j", ...modifiers })).toBe(true); + expect(screen.queryByRole("complementary", { name: "LiteAdmin" })).not.toBeInTheDocument(); + } + expect(requests).toHaveLength(requestCount); + + session(); + view.refresh(); + expect(await screen.findByRole("button", { name: "LiteAdmin" })).toHaveAttribute("aria-expanded", "false"); + expect(screen.queryByRole("complementary", { name: "LiteAdmin" })).not.toBeInTheDocument(); + fireEvent.keyDown(document, { key: "j", ctrlKey: true }); + expect(await screen.findByRole("complementary", { name: "LiteAdmin" })).toBeInTheDocument(); + expect(screen.getByPlaceholderText("Ask LiteAdmin…")).toHaveValue(""); }); it("isolates Hide LiteAdmin by admin and gateway and reacts to another tab clearing it", async () => { diff --git a/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.tsx b/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.tsx index 8bbf030ac8f..39a2394a5fb 100644 --- a/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.tsx +++ b/ui/litellm-dashboard/src/components/liteadmin/LiteAdmin.tsx @@ -30,7 +30,7 @@ const LiteAdminContext = createContext(null); function useLiteAdminSession() { const auth = useAuthorized(); const [disabled] = useDisableLiteAdmin(auth.userId); - const sessionReady = !auth.isLoading && auth.isAuthorized; + const sessionReady = !auth.isLoading && auth.isAuthorized && auth.premiumUser === true; const writableAdmin = !auth.isViewOnly && isProxyAdminRole(auth.userRole); const allowed = sessionReady && writableAdmin && !disabled; if (!allowed || !auth.token || !auth.accessToken) return null;