mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
Merge 821cbe9c85 into 22b36cbcf6
This commit is contained in:
commit
855709580f
2 changed files with 106 additions and 5 deletions
|
|
@ -13,7 +13,7 @@ vi.mock("../utils/roles", async (importOriginal) => {
|
||||||
rolesWithWriteAccess: ["admin", "internal"],
|
rolesWithWriteAccess: ["admin", "internal"],
|
||||||
rolesAllowedToViewWriteScopedPages: ["admin", "internal", "admin_viewer"],
|
rolesAllowedToViewWriteScopedPages: ["admin", "internal", "admin_viewer"],
|
||||||
isAdminRole: (role: string) => role === "admin" || role === "admin_viewer",
|
isAdminRole: (role: string) => role === "admin" || role === "admin_viewer",
|
||||||
isUserTeamAdminForAnyTeam: () => false,
|
isUserTeamAdminForAnyTeam: actual.isUserTeamAdminForAnyTeam,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -24,7 +24,7 @@ vi.mock("next/navigation", () => ({
|
||||||
useRouter: () => ({ push: vi.fn() }),
|
useRouter: () => ({ push: vi.fn() }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { mockUseAuthorized, mockUseOrganizations } = vi.hoisted(() => {
|
const { mockUseAuthorized, mockUseOrganizations, mockUseTeams } = vi.hoisted(() => {
|
||||||
const mockUseAuthorized = vi.fn(() => ({
|
const mockUseAuthorized = vi.fn(() => ({
|
||||||
userId: "test-user-id",
|
userId: "test-user-id",
|
||||||
accessToken: "test-access-token",
|
accessToken: "test-access-token",
|
||||||
|
|
@ -43,7 +43,13 @@ const { mockUseAuthorized, mockUseOrganizations } = vi.hoisted(() => {
|
||||||
error: null,
|
error: null,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return { mockUseAuthorized, mockUseOrganizations };
|
const mockUseTeams = vi.fn(() => ({
|
||||||
|
data: [] as Array<{ members_with_roles: Array<{ user_id: string; role: string }> }>,
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return { mockUseAuthorized, mockUseOrganizations, mockUseTeams };
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
|
vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
|
||||||
|
|
@ -55,7 +61,7 @@ vi.mock("@/app/(dashboard)/hooks/organizations/useOrganizations", () => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("@/app/(dashboard)/hooks/teams/useTeams", () => ({
|
vi.mock("@/app/(dashboard)/hooks/teams/useTeams", () => ({
|
||||||
useTeams: () => ({ data: [], isLoading: false, error: null }),
|
useTeams: mockUseTeams,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("@/app/(dashboard)/hooks/uiConfig/useUIConfig", () => {
|
vi.mock("@/app/(dashboard)/hooks/uiConfig/useUIConfig", () => {
|
||||||
|
|
@ -103,6 +109,30 @@ const placementsOf = (page: string): string[] =>
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const teamAdminAuthorization = {
|
||||||
|
userId: "team-admin-user-id",
|
||||||
|
accessToken: "test-access-token",
|
||||||
|
userRole: "internal",
|
||||||
|
isViewOnly: false,
|
||||||
|
token: "test-token",
|
||||||
|
userEmail: "teamadmin@example.com",
|
||||||
|
premiumUser: false,
|
||||||
|
disabledPersonalKeyCreation: false,
|
||||||
|
showSSOBanner: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const teamMemberAuthorization = {
|
||||||
|
userId: "team-member-user-id",
|
||||||
|
accessToken: "test-access-token",
|
||||||
|
userRole: "internal",
|
||||||
|
isViewOnly: false,
|
||||||
|
token: "test-token",
|
||||||
|
userEmail: "teamuser@example.com",
|
||||||
|
premiumUser: false,
|
||||||
|
disabledPersonalKeyCreation: false,
|
||||||
|
showSSOBanner: false,
|
||||||
|
};
|
||||||
|
|
||||||
describe("Sidebar (leftnav)", () => {
|
describe("Sidebar (leftnav)", () => {
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
|
|
@ -111,6 +141,12 @@ describe("Sidebar (leftnav)", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
mockUseAuthorized.mockReset();
|
mockUseAuthorized.mockReset();
|
||||||
mockUseOrganizations.mockReset();
|
mockUseOrganizations.mockReset();
|
||||||
|
mockUseTeams.mockReset();
|
||||||
|
mockUseTeams.mockReturnValue({
|
||||||
|
data: [],
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
mockUseThemeImpl = unbrandedTheme;
|
mockUseThemeImpl = unbrandedTheme;
|
||||||
navState.pathname = "/ui/api-keys";
|
navState.pathname = "/ui/api-keys";
|
||||||
});
|
});
|
||||||
|
|
@ -515,6 +551,65 @@ describe("Sidebar (leftnav)", () => {
|
||||||
expect(screen.getByText("Organizations")).toBeInTheDocument();
|
expect(screen.getByText("Organizations")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows Projects to an internal user who administers a team", () => {
|
||||||
|
mockUseAuthorized.mockReturnValue(teamAdminAuthorization);
|
||||||
|
mockUseTeams.mockReturnValue({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
members_with_roles: [{ user_id: "team-admin-user-id", role: "admin" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
renderWithProviders(<Sidebar {...defaultProps} enableProjectsUI />);
|
||||||
|
|
||||||
|
expect(screen.getByRole("link", { name: /Projects/ })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hides Projects when the feature flag is disabled for a team admin", () => {
|
||||||
|
mockUseAuthorized.mockReturnValue(teamAdminAuthorization);
|
||||||
|
mockUseTeams.mockReturnValue({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
members_with_roles: [{ user_id: "team-admin-user-id", role: "admin" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
renderWithProviders(<Sidebar {...defaultProps} enableProjectsUI={false} />);
|
||||||
|
|
||||||
|
expect(screen.queryByRole("link", { name: /Projects/ })).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hides Projects from an internal user who is not a team admin", () => {
|
||||||
|
mockUseAuthorized.mockReturnValue(teamMemberAuthorization);
|
||||||
|
|
||||||
|
renderWithProviders(<Sidebar {...defaultProps} enableProjectsUI />);
|
||||||
|
|
||||||
|
expect(screen.queryByRole("link", { name: /Projects/ })).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("applies the internal-user page allowlist to Projects for team admins", () => {
|
||||||
|
mockUseAuthorized.mockReturnValue(teamAdminAuthorization);
|
||||||
|
mockUseTeams.mockReturnValue({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
members_with_roles: [{ user_id: "team-admin-user-id", role: "admin" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
renderWithProviders(<Sidebar {...defaultProps} enableProjectsUI enabledPagesInternalUsers={["teams"]} />);
|
||||||
|
|
||||||
|
expect(screen.queryByRole("link", { name: /Projects/ })).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it("marks the nav item for the current route active", () => {
|
it("marks the nav item for the current route active", () => {
|
||||||
navState.pathname = "/ui/logs";
|
navState.pathname = "/ui/logs";
|
||||||
renderWithProviders(<Sidebar {...defaultProps} />);
|
renderWithProviders(<Sidebar {...defaultProps} />);
|
||||||
|
|
|
||||||
|
|
@ -475,7 +475,13 @@ const Sidebar_: React.FC<SidebarProps> = ({
|
||||||
if (!isAdmin && enabledPagesInternalUsers != null) return enabledPagesInternalUsers.includes(item.page);
|
if (!isAdmin && enabledPagesInternalUsers != null) return enabledPagesInternalUsers.includes(item.page);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (item.key === "projects" && !enableProjectsUI) return false;
|
if (item.key === "projects") {
|
||||||
|
if (!enableProjectsUI) return false;
|
||||||
|
const hasRoleAccess = isAdmin || isOrgAdmin || isTeamAdmin;
|
||||||
|
if (!hasRoleAccess) return false;
|
||||||
|
if (!isAdmin && enabledPagesInternalUsers != null) return enabledPagesInternalUsers.includes(item.page);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
!isAdmin &&
|
!isAdmin &&
|
||||||
item.key === "agents" &&
|
item.key === "agents" &&
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue