fix(proxy): only apply blocked when explicitly supplied

The model default blocked=False was being written on every customer
update that omitted the field, silently unblocking blocked customers
when admins changed unrelated fields like alias or budget.

Only accept bool values for fields the caller explicitly supplied
(data.fields_set()), keeping the isinstance(v, bool) semantics for
explicit updates like blocked=True/False.

Adds a regression test: updating a blocked customer without the
blocked field must not reset the block.
This commit is contained in:
cat0825 2026-08-04 11:27:09 +08:00
parent 703159eaab
commit 463e9cd7ff
2 changed files with 28 additions and 1 deletions

View file

@ -553,7 +553,10 @@ async def update_end_user(
# get non default values for key
non_default_values = {}
for k, v in data_json.items():
if v is not None and (isinstance(v, bool) or v not in ([], {}, 0)):
if v is not None and (
(isinstance(v, bool) and k in data.fields_set())
or v not in ([], {}, 0)
):
non_default_values[k] = v
## Get end user table data ##

View file

@ -105,6 +105,30 @@ def test_update_customer_unblock(mock_prisma_client, mock_user_api_key_auth):
assert update_mock.call_args.kwargs["data"]["blocked"] is False
def test_update_customer_keeps_blocked_when_omitted(mock_prisma_client, mock_user_api_key_auth):
"""
Regression test: updating a blocked customer without supplying `blocked`
must NOT reset it to unblocked. `blocked=False` is the model default and
should only be applied when explicitly provided by the caller.
"""
mock_end_user = LiteLLM_EndUserTable(user_id="test-user-1", blocked=True)
updated_mock_end_user = LiteLLM_EndUserTable(user_id="test-user-1", blocked=True)
mock_prisma_client.db.litellm_endusertable.find_first = AsyncMock(return_value=mock_end_user)
mock_prisma_client.db.litellm_endusertable.update = AsyncMock(return_value=updated_mock_end_user)
response = client.post(
"/customer/update",
json={"user_id": "test-user-1", "alias": "Updated Test User"},
headers={"Authorization": "Bearer test-key"},
)
assert response.status_code == 200
update_mock = mock_prisma_client.db.litellm_endusertable.update
update_mock.assert_called_once()
assert "blocked" not in update_mock.call_args.kwargs["data"]
def test_update_customer_not_found(mock_prisma_client, mock_user_api_key_auth):
"""
Test that update_end_user raises a 404 ProxyException when user_id does not exist.