mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
Merge ca2dc46d0f into 22a349ee70
This commit is contained in:
commit
223f1dbec9
5 changed files with 49 additions and 8 deletions
|
|
@ -10,10 +10,18 @@ export interface OrganizationListFilters {
|
|||
org_alias?: string | null;
|
||||
}
|
||||
|
||||
export const useOrganizations = (filters?: OrganizationListFilters): UseQueryResult<Organization[]> => {
|
||||
export interface UseOrganizationsOptions {
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
export const useOrganizations = (
|
||||
filters?: OrganizationListFilters,
|
||||
options?: UseOrganizationsOptions,
|
||||
): UseQueryResult<Organization[]> => {
|
||||
const { accessToken, userId, userRole } = useAuthorized();
|
||||
const orgId = filters?.org_id || null;
|
||||
const orgAlias = filters?.org_alias || null;
|
||||
const enabled = options?.enabled ?? true;
|
||||
return useQuery<Organization[]>({
|
||||
queryKey: organizationKeys.list(
|
||||
orgId || orgAlias
|
||||
|
|
@ -21,7 +29,7 @@ export const useOrganizations = (filters?: OrganizationListFilters): UseQueryRes
|
|||
: {},
|
||||
),
|
||||
queryFn: async () => await organizationListCall(accessToken!, orgId, orgAlias),
|
||||
enabled: Boolean(accessToken && userId && userRole),
|
||||
enabled: enabled && Boolean(accessToken && userId && userRole),
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
"use client";
|
||||
|
||||
import { hasCapability, type Capability } from "@/utils/capabilities";
|
||||
import { capabilityRequiresOrgAdmin, hasCapability, type Capability } from "@/utils/capabilities";
|
||||
|
||||
import useAuthorized from "./useAuthorized";
|
||||
import useIsOrgAdmin from "./useIsOrgAdmin";
|
||||
|
||||
const useCan = (capability: Capability): boolean => {
|
||||
const { userRole } = useAuthorized();
|
||||
const isOrgAdmin = useIsOrgAdmin();
|
||||
const isOrgAdmin = useIsOrgAdmin({ enabled: capabilityRequiresOrgAdmin(capability) });
|
||||
return hasCapability(userRole, capability, isOrgAdmin);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,15 @@ import { isOrgAdminForAnyOrg, isOrgAdminSessionRole } from "@/utils/roles";
|
|||
import { useOrganizations } from "./organizations/useOrganizations";
|
||||
import useAuthorized from "./useAuthorized";
|
||||
|
||||
const useIsOrgAdmin = (): boolean => {
|
||||
export interface UseIsOrgAdminOptions {
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
const useIsOrgAdmin = (options?: UseIsOrgAdminOptions): boolean => {
|
||||
const { userId, userRole } = useAuthorized();
|
||||
const { data: organizations } = useOrganizations();
|
||||
return isOrgAdminSessionRole(userRole) || isOrgAdminForAnyOrg(organizations, userId);
|
||||
const enabled = options?.enabled ?? true;
|
||||
const { data: organizations } = useOrganizations(undefined, { enabled });
|
||||
return isOrgAdminSessionRole(userRole) || (enabled && isOrgAdminForAnyOrg(organizations, userId));
|
||||
};
|
||||
|
||||
export default useIsOrgAdmin;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { hasCapability, rolesWithCapability, type Capability } from "./capabilities";
|
||||
import { capabilityRequiresOrgAdmin, hasCapability, rolesWithCapability, type Capability } from "./capabilities";
|
||||
import { effectiveSessionRole } from "./roles";
|
||||
|
||||
const ADMIN_ROLES = ["Admin", "Admin Viewer", "proxy_admin", "proxy_admin_viewer"];
|
||||
|
|
@ -150,3 +150,29 @@ describe("rolesWithCapability", () => {
|
|||
expect(hasCapability(removed, "viewToolPolicies")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// Callers use this to skip the organization-list fetch that backs the org-admin
|
||||
// check when the capability cannot be reopened by an org-admin membership. That
|
||||
// keeps role-gated pages like Memory / Workflows / Guardrails Monitor from
|
||||
// firing a network request just to compute a denial.
|
||||
describe("capabilityRequiresOrgAdmin", () => {
|
||||
it("is true only for viewDeletedTeams, the sole capability that opts org admins in", () => {
|
||||
expect(capabilityRequiresOrgAdmin("viewDeletedTeams")).toBe(true);
|
||||
});
|
||||
|
||||
it.each<Capability>([
|
||||
"viewToolPolicies",
|
||||
"viewAuditLogs",
|
||||
"viewPolicies",
|
||||
"viewPrompts",
|
||||
"viewOrganizationUsage",
|
||||
"viewAgentUsage",
|
||||
"viewGlobalSpend",
|
||||
"viewWorkflowRuns",
|
||||
"viewMemory",
|
||||
"viewGuardrailUsage",
|
||||
"viewProxyWideCostData",
|
||||
])("is false for %s so useCan can skip the org-list fetch for it", (capability) => {
|
||||
expect(capabilityRequiresOrgAdmin(capability)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ const ORG_ADMIN_CAPABILITIES: ReadonlySet<Capability> = new Set<Capability>([
|
|||
"viewOrganizationUsage",
|
||||
]);
|
||||
|
||||
export const capabilityRequiresOrgAdmin = (capability: Capability): boolean => ORG_ADMIN_CAPABILITIES.has(capability);
|
||||
|
||||
export const hasCapability = (
|
||||
userRole: string | null | undefined,
|
||||
capability: Capability,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue