feat(proxy): add general_settings.override_user_param to stamp authenticated user id

This commit is contained in:
Devin AI 2026-07-28 00:14:28 +00:00
parent 0cd588ad10
commit bd7e8db98d
4 changed files with 64 additions and 0 deletions

View file

@ -2378,6 +2378,10 @@ class ConfigGeneralSettings(LiteLLMPydanticObjectBase):
description="[DEPRECATED] Use 'user_header_mappings' instead. When set, the header value is treated as the end user id unless overridden by user_header_mappings.",
)
user_header_mappings: Optional[List[UserHeaderMapping]] = None
override_user_param: bool | None = Field(
None,
description="When True, the proxy overwrites any client-supplied 'user' param with the authenticated user id. Use when LiteLLM is the authentication boundary and clients must not be able to spoof 'user'.",
)
supported_db_objects: Optional[List[SupportedDBObjectType]] = Field(
None,
description="Fine-grained control over which object types to load from the database when store_model_in_db is True. Available types: 'models', 'mcp', 'guardrails', 'vector_stores', 'pass_through_endpoints', 'prompts', 'model_cost_map', 'tools', 'config_overrides'. If not set, all objects are loaded (default behavior).",

View file

@ -1465,6 +1465,10 @@ async def add_litellm_data_to_request(
if "user" not in data:
data["user"] = user
if general_settings is not None and general_settings.get("override_user_param") is True:
if user_api_key_dict.user_id is not None:
data["user"] = user_api_key_dict.user_id
if litellm.overwrite_user_with_key_hash is True:
stampable_hash = _stampable_key_hash(user_api_key_dict)
if stampable_hash is not None:

View file

@ -5421,3 +5421,54 @@ async def test_overwrite_user_with_key_hash_rejects_alias_without_marker(monkeyp
)
assert updated_data["user"] == "caller-chosen-id"
@pytest.mark.asyncio
async def test_override_user_param_clobbers_client_supplied_user():
user_api_key_dict = UserAPIKeyAuth(api_key="hashed-key", user_id="alice")
data = {"model": "gpt-4o", "user": "bob"}
updated_data = await add_litellm_data_to_request(
data=data,
request=_make_chat_request_mock(),
user_api_key_dict=user_api_key_dict,
proxy_config=MagicMock(),
general_settings={"override_user_param": True},
version="test-version",
)
assert updated_data["user"] == "alice"
@pytest.mark.asyncio
async def test_override_user_param_disabled_keeps_client_supplied_user():
user_api_key_dict = UserAPIKeyAuth(api_key="hashed-key", user_id="alice")
data = {"model": "gpt-4o", "user": "bob"}
updated_data = await add_litellm_data_to_request(
data=data,
request=_make_chat_request_mock(),
user_api_key_dict=user_api_key_dict,
proxy_config=MagicMock(),
general_settings={},
version="test-version",
)
assert updated_data["user"] == "bob"
@pytest.mark.asyncio
async def test_override_user_param_without_authenticated_user_id_keeps_client_value():
user_api_key_dict = UserAPIKeyAuth(api_key="hashed-key")
data = {"model": "gpt-4o", "user": "bob"}
updated_data = await add_litellm_data_to_request(
data=data,
request=_make_chat_request_mock(),
user_api_key_dict=user_api_key_dict,
proxy_config=MagicMock(),
general_settings={"override_user_param": True},
version="test-version",
)
assert updated_data["user"] == "bob"

View file

@ -22663,6 +22663,11 @@ export interface components {
* @description [BETA] OpenTelemetry support - this might change, use with caution.
*/
otel?: boolean | null;
/**
* Override User Param
* @description When True, the proxy overwrites any client-supplied 'user' param with the authenticated user id. Use when LiteLLM is the authentication boundary and clients must not be able to spoof 'user'.
*/
override_user_param?: boolean | null;
/**
* Pass Through Endpoints
* @description Set-up pass-through endpoints for provider-specific endpoints. Docs - https://docs.litellm.ai/docs/proxy/pass_through