diff --git a/ui/litellm-dashboard/src/components/templates/key_edit_view.integration.test.tsx b/ui/litellm-dashboard/src/components/templates/key_edit_view.integration.test.tsx index 11ac2b67e88..901b98407c5 100644 --- a/ui/litellm-dashboard/src/components/templates/key_edit_view.integration.test.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.integration.test.tsx @@ -1704,6 +1704,44 @@ describe("KeyEditView", () => { expect(screen.queryByRole("option", { name: /Beta/ })).not.toBeInTheDocument(); }); + it("should let a non-admin move a key to their team in another organization", async () => { + const onSubmitMock = vi.fn().mockResolvedValue(undefined); + + renderWithProviders( + {}} + onSubmit={onSubmitMock} + accessToken="" + userID="" + userRole="Internal User" + premiumUser={false} + />, + ); + + await waitFor(() => { + expect(screen.getByLabelText("Organization")).toHaveValue("Engineering"); + }); + + await userEvent.click(screen.getByRole("combobox", { name: "Team" })); + await userEvent.click(await screen.findByRole("option", { name: /Beta/ })); + + await waitFor(() => { + expect(screen.getByLabelText("Organization")).toHaveValue("Sales"); + }); + + await userEvent.click(screen.getByRole("button", { name: /save changes/i })); + + await waitFor(() => { + expect(onSubmitMock).toHaveBeenCalled(); + }); + expect(onSubmitMock.mock.calls[0][0]).toMatchObject({ organization_id: "org-2", team_id: "team-b" }); + }); + it("should re-scope teams when the edited key switches to another organization", async () => { const sharedProps = { teams: [ diff --git a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx index 5b61805de05..3e844d38d68 100644 --- a/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx +++ b/ui/litellm-dashboard/src/components/templates/key_edit_view.tsx @@ -146,6 +146,7 @@ function KeyEditSession({ const { data: uiSettingsData } = useUISettings(); const enableProjectsUI = Boolean(uiSettingsData?.values?.enable_projects_ui); const hasProject = Boolean(keyData.project_id); + const canChangeOrganization = userRole === "Admin" && !hasProject; const detachProject = hasProject && form.watch("project_id") === null; const canDetachProject = canDetachKeyProject(team, organizations, userID, userRole); @@ -352,9 +353,10 @@ function KeyEditSession({ })), ]; - const visibleTeams = selectedOrganizationId - ? teams?.filter((t) => t.organization_id === selectedOrganizationId) - : teams; + const visibleTeams = + canChangeOrganization && selectedOrganizationId + ? teams?.filter((t) => t.organization_id === selectedOrganizationId) + : teams; const teamOptionsFor = (selectedTeamId: string | null): SearchSelectOption[] => { const selectedTeamOutsideFilter = teams?.find((t) => t.team_id === selectedTeamId && !visibleTeams?.includes(t)); @@ -795,7 +797,7 @@ function KeyEditSession({ value={value} organizations={organizations} loading={isOrganizationsLoading} - disabled={userRole !== "Admin" || hasProject} + disabled={!canChangeOrganization} onChange={(orgId) => handleOrganizationChange(onChange, orgId)} /> )}