Skip to content

fix(ui): omit unchanged allowed_routes on key update - #27148

Open
krrish-berri-2 wants to merge 2 commits into
litellm_internal_stagingfrom
cursor/fix-non-admin-key-edit-allowed-routes-27005-b201
Open

fix(ui): omit unchanged allowed_routes on key update#27148
krrish-berri-2 wants to merge 2 commits into
litellm_internal_stagingfrom
cursor/fix-non-admin-key-edit-allowed-routes-27005-b201

Conversation

@krrish-berri-2

@krrish-berri-2 krrish-berri-2 commented May 5, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Fixes #27005

Pre-Submission checklist

  • I have Added testing in the tests/ directory — 3 new tests added in key_info_view.test.tsx
  • My PR passes all unit tests — all 3817 UI tests pass (383 test files)
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Type

🐛 Bug Fix

Changes

Non-admin team admins cannot save key edit settings because the UI always submits the current allowed_routes value in the update payload, triggering the backend permission check (_check_allowed_routes_caller_permission) added in PR #25445.

This fix follows the established precedent from commit 2c41f3c29 (the policies field fix): strip allowed_routes from the update payload when the form value equals the previously persisted value, so non-admin editors don't trip the backend "setting allowed_routes" permission check on a no-op save. Genuine route changes (including clears) still pass through.

What changed

key_info_view.tsx:

  • Added normalizeStringList and areStringListsEqual helper functions for robust comparison of route list values (handles arrays, comma-separated strings, whitespace normalization)
  • Added a check in handleKeyUpdate that strips allowed_routes from the update payload when the submitted value matches the existing key's value

key_info_view.test.tsx:

  • Added allowed_routes payload normalization test suite with 3 tests:
    1. Drops allowed_routes when submitted value matches existing key (the bug fix)
    2. Drops empty allowed_routes when the key previously had no route override
    3. Keeps allowed_routes when the user genuinely clears an existing route override

Root cause

PR #16034 (UI Key Type select) made the edit form always submit allowed_routes. PR #25445 added _check_allowed_routes_caller_permission to block non-admins from setting allowed_routes. The collision means any non-admin saving a key (even without changes) gets a 403.

Screenshots / Proof of Fix

Backend verification

Confirmed the backend bug: a team admin sending unchanged allowed_routes in the update payload gets 403:

curl /key/update with allowed_routes: ["llm_api_routes"] → 403 "Only proxy admins can set allowed_routes"
curl /key/update WITHOUT allowed_routes → SUCCESS (key_alias updated)

Test results (all 28 tests pass, including 3 new allowed_routes tests)

Test results showing all 28 tests passing

Video walkthrough of tests running

test_results_allowed_routes_fix.mp4

To show artifacts inline, enable in settings.

Slack Thread

Open in Web Open in Cursor 

Non-admin team admins could not save key edit settings because the UI
always submitted the current allowed_routes value in the update payload,
triggering the backend permission check added in PR #25445.

This follows the established precedent from commit 2c41f3c (policies
field fix): strip allowed_routes from the update payload when the form
value equals the previously persisted value so non-admin editors don't
trip the backend 'setting allowed_routes' permission check on a no-op
save. Genuine route changes (including clears) still pass through.

Adds normalizeStringList and areStringListsEqual helpers for robust
comparison of the route list values.

Closes #27005

Co-authored-by: Krrish Dholakia <krrish-berri-2@users.noreply.github.com>
@CLAassistant

CLAassistant commented May 5, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ shivamrawat1
❌ cursoragent
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a 403 error for non-admin team admins saving key settings by stripping allowed_routes from the update payload when the value is unchanged, following the established pattern from the policies field fix. The implementation and tests are clean and well-targeted.

Confidence Score: 4/5

Safe to merge; the fix is well-scoped and backed by tests, with only a minor order-sensitivity edge case.

Only P2 findings — the positional comparison in areStringListsEqual could cause an unnecessary backend call if route order differs between server response and form submission, but this is an edge case that doesn't affect correctness of the primary bug fix.

No files require special attention.

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/components/templates/key_info_view.tsx Adds normalizeStringList/areStringListsEqual helpers and strips allowed_routes from the update payload when unchanged; order-sensitive comparison may occasionally send unnecessary updates if route order differs.
ui/litellm-dashboard/src/components/templates/key_info_view.test.tsx Adds 3 focused tests covering the main normalization cases (unchanged routes dropped, empty-to-empty dropped, clear kept); tests only exercise the array path of normalizeStringList, leaving the comma-separated-string branch untested.

Reviews (1): Last reviewed commit: "fix(ui): omit unchanged allowed_routes o..." | Re-trigger Greptile

Comment on lines +67 to +74
const areStringListsEqual = (left: unknown, right: unknown): boolean => {
const normalizedLeft = normalizeStringList(left);
const normalizedRight = normalizeStringList(right);
return (
normalizedLeft.length === normalizedRight.length &&
normalizedLeft.every((entry, index) => entry === normalizedRight[index])
);
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 areStringListsEqual compares lists positionally (index-by-index). If the backend returns ["b", "a"] and the form submits ["a", "b"] — same set, different order — the two lists are deemed unequal and the update is sent, which would still trip the backend permission check for a no-op save. Sorting before comparison makes the equality check order-independent, matching the semantic intent of route lists.

Suggested change
const areStringListsEqual = (left: unknown, right: unknown): boolean => {
const normalizedLeft = normalizeStringList(left);
const normalizedRight = normalizeStringList(right);
return (
normalizedLeft.length === normalizedRight.length &&
normalizedLeft.every((entry, index) => entry === normalizedRight[index])
);
};
const areStringListsEqual = (left: unknown, right: unknown): boolean => {
const normalizedLeft = normalizeStringList(left).sort();
const normalizedRight = normalizeStringList(right).sort();
return (
normalizedLeft.length === normalizedRight.length &&
normalizedLeft.every((entry, index) => entry === normalizedRight[index])
);
};

@codecov

codecov Bot commented May 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@marty-sullivan

Copy link
Copy Markdown
Contributor

@krrish-berri-2 note that this seemed to only partially fix #27005 - still seeing this bug in v1.90.0, check my new comment in the linked issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Non-admin team admins cannot save Key Edit Settings — UI sends allowed_routes, but UpdateKeyRequest has no key_type to fall back to

6 participants