mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
fix(proxy): fail closed when fallback authorization lookup errors
A non-ProxyException from the team, project or access-group lookup used to escape the fallback loop and replace the provider's error. Treat it as a denial and log it. Also drop the unrelated reformatting of test_router.py and test_fallback_event_handlers.py so both diffs are additions only.
This commit is contained in:
parent
3ea501430b
commit
d18bfe176e
4 changed files with 632 additions and 239 deletions
|
|
@ -12,6 +12,7 @@ from typing import Final
|
|||
|
||||
from pydantic import BaseModel, ValidationError
|
||||
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.proxy._types import ProxyException, UserAPIKeyAuth
|
||||
from litellm.proxy.auth.auth_checks import can_key_call_resolved_model
|
||||
from litellm.router import Router
|
||||
|
|
@ -31,6 +32,9 @@ async def is_model_authorized_for_token(*, model: str, valid_token: UserAPIKeyAu
|
|||
)
|
||||
except ProxyException:
|
||||
return False
|
||||
except Exception as e: # noqa: BLE001 # fail closed: a lookup failure must neither run the fallback nor replace the provider error
|
||||
verbose_proxy_logger.warning("Skipping fallback to model=%s: authorization lookup failed: %s", model, e)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,23 @@ async def test_is_model_authorized_for_token_follows_the_key_access_groups():
|
|||
assert await is_model_authorized_for_token(model="secret-model", valid_token=token, llm_router=router) is False
|
||||
|
||||
|
||||
class _RouterWithBrokenAccessGroupLookup(Router):
|
||||
def get_model_access_groups(self, *args, **kwargs):
|
||||
raise RuntimeError("access group store unavailable")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_model_authorized_for_token_fails_closed_when_the_lookup_breaks():
|
||||
router = _RouterWithBrokenAccessGroupLookup(model_list=_router().model_list)
|
||||
|
||||
assert (
|
||||
await is_model_authorized_for_token(
|
||||
model="open-model", valid_token=_key_limited_to("open-group"), llm_router=router
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("metadata_field", ["metadata", "litellm_metadata"])
|
||||
async def test_router_fallback_access_check_authorizes_the_key_carried_in_request_metadata(metadata_field: str):
|
||||
|
|
|
|||
|
|
@ -573,7 +573,9 @@ async def test_run_async_fallback_keeps_a_request_override_distinct_from_the_bar
|
|||
with pytest.raises(RuntimeError, match="fallback model also failed"):
|
||||
await run_async_fallback(
|
||||
litellm_router=router,
|
||||
fallback_model_group=[{"model": "already-attempted", "messages": [{"role": "user", "content": "shorter"}]}],
|
||||
fallback_model_group=[
|
||||
{"model": "already-attempted", "messages": [{"role": "user", "content": "shorter"}]}
|
||||
],
|
||||
original_model_group="primary-model",
|
||||
original_exception=RuntimeError("original failed"),
|
||||
max_fallbacks=3,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue