fix(ui): align pass-through headers and authentication defaults (#48081)

This commit is contained in:
moe-berri 2026-10-23 15:38:32 -07:00 • committed by GitHub
parent 22cfee1534
commit 7a75cf273e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 59 additions and 100 deletions

View file

@ -4,8 +4,6 @@ import PassThroughSettings from "@/components/PassThroughSettings/PassThroughSet
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
export default function PassThroughPanel() {
const { accessToken, userRole, userId: userID, premiumUser } = useAuthorized();
return (
<PassThroughSettings accessToken={accessToken} userRole={userRole} userID={userID} premiumUser={premiumUser} />
);
const { accessToken, userRole, userId: userID } = useAuthorized();
return <PassThroughSettings accessToken={accessToken} userRole={userRole} userID={userID} />;
}

View file

@ -19,7 +19,6 @@ interface PassThroughSettingsProps {
accessToken: string | null;
userRole: string | null;
userID: string | null;
premiumUser?: boolean;
}
export interface passThroughItem {
@ -37,7 +36,7 @@ export interface passThroughItem {
is_from_config?: boolean;
}
const PassThroughSettings: React.FC<PassThroughSettingsProps> = ({ accessToken, userRole, userID, premiumUser }) => {
const PassThroughSettings: React.FC<PassThroughSettingsProps> = ({ accessToken, userRole, userID }) => {
const [generalSettings, setGeneralSettings] = useState<passThroughItem[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [selectedEndpointId, setSelectedEndpointId] = useState<string | null>(null);
@ -116,7 +115,6 @@ const PassThroughSettings: React.FC<PassThroughSettingsProps> = ({ accessToken,
onClose={() => setSelectedEndpointId(null)}
accessToken={accessToken}
isAdmin={userRole === "Admin" || userRole === "admin"}
premiumUser={premiumUser}
onEndpointUpdated={handleEndpointUpdated}
/>
);
@ -133,7 +131,6 @@ const PassThroughSettings: React.FC<PassThroughSettingsProps> = ({ accessToken,
accessToken={accessToken}
setPassThroughItems={setGeneralSettings}
passThroughItems={generalSettings}
premiumUser={premiumUser}
/>
<PassThroughEndpointsTable

View file

@ -19,14 +19,9 @@ const setup = () => userEvent.setup({ pointerEventsCheck: PointerEventsCheckLeve
type User = ReturnType<typeof setup>;
const renderForm = (premiumUser = true) =>
const renderForm = () =>
renderWithProviders(
<AddPassThroughEndpoint
accessToken="test-token"
passThroughItems={[]}
setPassThroughItems={vi.fn()}
premiumUser={premiumUser}
/>,
<AddPassThroughEndpoint accessToken="test-token" passThroughItems={[]} setPassThroughItems={vi.fn()} />,
);
const openModal = async (user: User) => {
@ -82,7 +77,7 @@ const reopenedPayload = {
include_subpath: true,
methods: undefined,
default_query_params: undefined,
auth: undefined,
auth: true,
timeout: undefined,
cost_per_request: undefined,
};
@ -93,7 +88,7 @@ describe("add_pass_through submit payload", () => {
createPassThroughEndpoint.mockResolvedValue({ endpoints: [{ id: "generated-id", path: "/bria" }] });
});
it("sends exactly the antd payload, once, for a minimal endpoint", async () => {
it("sends the configured headers and numeric fields once", async () => {
const user = setup();
renderForm();
await openModal(user);
@ -114,7 +109,7 @@ describe("add_pass_through submit payload", () => {
include_subpath: true,
methods: undefined,
default_query_params: undefined,
auth: undefined,
auth: true,
timeout: "900",
cost_per_request: "1.5",
});
@ -161,32 +156,37 @@ describe("add_pass_through submit payload", () => {
expect(lastPayload().methods).toStrictEqual(["POST"]);
});
it("sends auth true once the premium security toggle is switched on", async () => {
it("creates an authenticated endpoint without adding headers", async () => {
const user = setup();
renderForm(true);
renderForm();
await openModal(user);
await fillRequiredFields(user);
const switches = screen.getAllByRole("switch");
expect(switches).toHaveLength(2);
await user.click(switches[1]);
fireEvent.change(screen.getByPlaceholderText("bria"), { target: { value: "example" } });
fireEvent.change(screen.getByPlaceholderText("https://engine.prod.bria-api.com"), {
target: { value: "https://example.com" },
});
expect(screen.getByRole("switch", { name: "Require LiteLLM authentication" })).toBeChecked();
await submit(user);
await waitFor(() => expect(createPassThroughEndpoint).toHaveBeenCalled());
expect(lastPayload().auth).toBe(true);
await waitFor(() => expect(createPassThroughEndpoint).toHaveBeenCalledOnce());
expect(lastPayload()).toMatchObject({ path: "/example", headers: {}, auth: true });
});
it("omits the auth key entirely for a non-premium user", async () => {
it("saves authentication off with all headers removed and resets it on reopening", async () => {
const user = setup();
renderForm(false);
renderForm();
await openModal(user);
await fillRequiredFields(user);
await user.click(screen.getByRole("button", { name: "Remove header 1" }));
await user.click(screen.getByRole("switch", { name: "Require LiteLLM authentication" }));
await submit(user);
await waitFor(() => expect(createPassThroughEndpoint).toHaveBeenCalled());
expect(lastPayload()).not.toHaveProperty("auth");
await waitFor(() => expect(createPassThroughEndpoint).toHaveBeenCalledOnce());
expect(lastPayload()).toMatchObject({ headers: {}, auth: false });
await openModal(user);
expect(screen.getByRole("switch", { name: "Require LiteLLM authentication" })).toBeChecked();
expect(screen.queryByPlaceholderText("Header Name")).not.toBeInTheDocument();
});
it("drops include_subpath to false when the toggle is switched off", async () => {
@ -212,7 +212,6 @@ describe("add_pass_through submit payload", () => {
expect(await screen.findByText("Path is required")).toBeInTheDocument();
expect(screen.getByText("Target URL is required")).toBeInTheDocument();
expect(screen.getByText("Please configure the headers")).toBeInTheDocument();
expect(createPassThroughEndpoint).not.toHaveBeenCalled();
});

View file

@ -41,11 +41,9 @@ const passThroughFormSchema = z.object({
.pipe(z.url({ error: "Please enter a valid URL" })),
methods: z.array(z.string()).optional(),
include_subpath: z.boolean(),
headers: keyValuePairsSchema.refine((pairs) => pairs.some(([name]) => name !== ""), {
error: "Please configure the headers",
}),
headers: keyValuePairsSchema,
default_query_params: keyValuePairsSchema.optional(),
auth: z.boolean().optional(),
auth: z.boolean(),
timeout: z.string().optional(),
cost_per_request: z.string().optional(),
});
@ -59,7 +57,7 @@ const emptyFormValues = {
include_subpath: true,
headers: [],
default_query_params: undefined,
auth: undefined,
auth: true,
timeout: undefined,
cost_per_request: undefined,
} as unknown as PassThroughFormValues;
@ -88,14 +86,12 @@ interface AddFallbacksProps {
accessToken: string;
passThroughItems: passThroughItem[];
setPassThroughItems: React.Dispatch<React.SetStateAction<passThroughItem[]>>;
premiumUser?: boolean;
}
const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
accessToken,
setPassThroughItems,
passThroughItems,
premiumUser = false,
}) => {
const [isModalVisible, setIsModalVisible] = useState(false);
const [isLoading, setIsLoading] = useState(false);
@ -123,7 +119,7 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
include_subpath: values.include_subpath,
headers: toRecord(values.headers),
default_query_params: optionalRecord(values.default_query_params),
...(premiumUser ? { auth: values.auth } : {}),
auth: values.auth,
timeout: values.timeout,
cost_per_request: values.cost_per_request,
...(Object.keys(guardrails).length > 0 ? { guardrails } : {}),
@ -268,14 +264,12 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
control={form.control}
name="headers"
label={labelWithHint(
"Authentication Headers",
"Headers (Optional)",
"Authentication and other headers to forward with requests",
)}
description={
<>
<span className="mb-1 block font-medium">
Add authentication tokens and other required headers
</span>
<span className="mb-1 block font-medium">Add headers only if required by the target API</span>
<span className="block">Common examples: auth_token, Authorization, x-api-key</span>
</>
}
@ -313,13 +307,7 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
</Card>
<FormField control={form.control} name="auth">
{({ value, onChange }) => (
<PassThroughSecuritySection
premiumUser={premiumUser}
authEnabled={value ?? false}
onAuthChange={onChange}
/>
)}
{({ value, onChange }) => <PassThroughSecuritySection authEnabled={value} onAuthChange={onChange} />}
</FormField>
<PassThroughGuardrailsSection accessToken={accessToken} value={guardrails} onChange={setGuardrails} />

View file

@ -4,41 +4,18 @@ import { Card } from "@/components/ui/card";
import { Switch } from "@/components/ui/switch";
export interface PassThroughSecuritySectionProps {
premiumUser: boolean;
authEnabled: boolean;
onAuthChange: (checked: boolean) => void;
}
const PassThroughSecuritySection: React.FC<PassThroughSecuritySectionProps> = ({
premiumUser,
authEnabled,
onAuthChange,
}) => {
const PassThroughSecuritySection: React.FC<PassThroughSecuritySectionProps> = ({ authEnabled, onAuthChange }) => {
return (
<Card className="block p-6">
<h3 className="mb-2 text-lg font-semibold text-foreground">Security</h3>
<p className="mb-4 text-sm text-muted-foreground">
When enabled, requests to this endpoint will require a valid LiteLLM Virtual Key
</p>
{premiumUser ? (
<Switch checked={authEnabled} onCheckedChange={onAuthChange} />
) : (
<div>
<div className="mb-3 flex items-center">
<Switch disabled checked={false} />
<span className="ml-2 text-sm text-muted-foreground">Authentication (Premium)</span>
</div>
<div className="rounded-lg border border-warning/20 bg-warning/10 p-3">
<p className="text-sm text-warning">
Setting authentication for pass-through endpoints is a LiteLLM Enterprise feature. Get a trial key{" "}
<a href="https://www.litellm.ai/#pricing" target="_blank" rel="noopener noreferrer" className="underline">
here
</a>
.
</p>
</div>
</div>
)}
<Switch aria-label="Require LiteLLM authentication" checked={authEnabled} onCheckedChange={onAuthChange} />
</Card>
);
};

View file

@ -31,16 +31,8 @@ const endpoint = {
methods: ["GET"],
};
const renderView = (premiumUser = true, data = endpoint) =>
renderWithProviders(
<PassThroughInfoView
endpointData={data}
onClose={vi.fn()}
accessToken="test-token"
isAdmin
premiumUser={premiumUser}
/>,
);
const renderView = (data = endpoint) =>
renderWithProviders(<PassThroughInfoView endpointData={data} onClose={vi.fn()} accessToken="test-token" isAdmin />);
const openEditForm = async (user: User) => {
await user.click(screen.getByRole("tab", { name: "Settings" }));
@ -122,20 +114,34 @@ describe("pass_through_info update payload", () => {
expect(lastPayload().headers).toStrictEqual({ "x-api-key": "k" });
});
it("sends auth undefined for a non-premium user", async () => {
it.each([true, false])("loads auth %s and persists the opposite choice", async (auth) => {
const user = setup();
renderView(false);
renderView({ ...endpoint, headers: {}, auth });
await openEditForm(user);
const toggle = screen.getByRole("switch", { name: "Require LiteLLM authentication" });
if (auth) {
expect(toggle).toBeChecked();
} else {
expect(toggle).not.toBeChecked();
}
await user.click(toggle);
await save(user);
await waitFor(() => expect(updatePassThroughEndpoint).toHaveBeenCalled());
expect(lastPayload().auth).toBeUndefined();
await waitFor(() => expect(updatePassThroughEndpoint).toHaveBeenCalledOnce());
expect(lastPayload()).toMatchObject({ headers: {}, auth: !auth });
await openEditForm(user);
const savedToggle = screen.getByRole("switch", { name: "Require LiteLLM authentication" });
if (auth) {
expect(savedToggle).not.toBeChecked();
} else {
expect(savedToggle).toBeChecked();
}
});
it("sends methods undefined when the endpoint has none", async () => {
const user = setup();
renderView(true, { ...endpoint, methods: [] });
renderView({ ...endpoint, methods: [] });
await openEditForm(user);
await save(user);

View file

@ -91,7 +91,6 @@ export interface PassThroughInfoProps {
onClose: () => void;
accessToken: string | null;
isAdmin: boolean;
premiumUser?: boolean;
onEndpointUpdated?: () => void;
}
@ -139,7 +138,6 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
onClose,
accessToken,
isAdmin,
premiumUser = false,
onEndpointUpdated,
}) => {
const [endpointData, setEndpointData] = useState<PassThroughEndpoint | null>(initialEndpointData);
@ -157,7 +155,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
include_subpath: initialEndpointData.include_subpath || false,
cost_per_request: initialEndpointData.cost_per_request,
timeout: initialEndpointData.timeout,
auth: initialEndpointData.auth || false,
auth: initialEndpointData.auth ?? true,
},
});
@ -189,7 +187,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
include_subpath: values.include_subpath,
cost_per_request: values.cost_per_request,
timeout: values.timeout,
auth: premiumUser ? values.auth : undefined,
auth: values.auth,
methods: values.methods.length > 0 ? values.methods : undefined,
guardrails: guardrails && Object.keys(guardrails).length > 0 ? guardrails : undefined,
};
@ -472,11 +470,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
<FormField control={form.control} name="auth">
{({ value, onChange }) => (
<PassThroughSecuritySection
premiumUser={premiumUser}
authEnabled={value}
onAuthChange={onChange}
/>
<PassThroughSecuritySection authEnabled={value} onAuthChange={onChange} />
)}
</FormField>