mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(identity): add LITELLM_OAUTH2_UNKNOWN_ROLE_DEFAULT escape hatch for OAuth2 unknown roles
OAuth2 role mapping is fail-closed by default; unknown IdP roles raise ValueError instead of silently granting INTERNAL_USER access. Operators whose IdP returns custom role names not in LitellmUserRoles can set LITELLM_OAUTH2_UNKNOWN_ROLE_DEFAULT=<role> to map unknown roles to that fallback. An invalid env value raises on first use so misconfiguration is loud, not silently downgraded. Resolves the reviewer split: Veria flagged the original INTERNAL_USER default as fail-open; Greptile flagged the pure-raise as breaking deployments with custom IdP role names. The opt-in flag preserves the secure default and gives operators an explicit escape hatch.
This commit is contained in:
parent
36e5ed2048
commit
368bfabedc
2 changed files with 44 additions and 1 deletions
|
|
@ -6,10 +6,20 @@ an already-validated response payload into the carrier.
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth
|
||||
|
||||
UNKNOWN_ROLE_DEFAULT_ENV = "LITELLM_OAUTH2_UNKNOWN_ROLE_DEFAULT"
|
||||
|
||||
|
||||
def _unknown_role_fallback() -> Optional[LitellmUserRoles]:
|
||||
raw = os.getenv(UNKNOWN_ROLE_DEFAULT_ENV)
|
||||
if not raw:
|
||||
return None
|
||||
return LitellmUserRoles(raw)
|
||||
|
||||
|
||||
def build_user_api_key_auth_from_oauth2_response(
|
||||
*,
|
||||
|
|
@ -29,6 +39,11 @@ def build_user_api_key_auth_from_oauth2_response(
|
|||
|
||||
Active-token validation, scope checks, and token-not-active rejection
|
||||
happen upstream in ``Oauth2Handler``; this builder trusts its input.
|
||||
|
||||
Unknown IdP role values are rejected by default. Set
|
||||
``LITELLM_OAUTH2_UNKNOWN_ROLE_DEFAULT`` to a valid
|
||||
``LitellmUserRoles`` value to map unknown roles to that fallback;
|
||||
an invalid env value raises on first use so misconfiguration is loud.
|
||||
"""
|
||||
user_id: Optional[str] = response_data.get(user_id_field_name)
|
||||
raw_role = response_data.get(user_role_field_name)
|
||||
|
|
@ -41,7 +56,11 @@ def build_user_api_key_auth_from_oauth2_response(
|
|||
try:
|
||||
user_role = LitellmUserRoles(raw_role)
|
||||
except ValueError as e:
|
||||
raise ValueError(f"Invalid OAuth2 role: {raw_role!r}") from e
|
||||
fallback = _unknown_role_fallback()
|
||||
if fallback is not None:
|
||||
user_role = fallback
|
||||
else:
|
||||
raise ValueError(f"Invalid OAuth2 role: {raw_role!r}") from e
|
||||
|
||||
return UserAPIKeyAuth(
|
||||
api_key=token,
|
||||
|
|
|
|||
|
|
@ -66,6 +66,30 @@ def test_missing_role_field_stays_none():
|
|||
assert uak.user_role is None
|
||||
|
||||
|
||||
def test_unknown_idp_role_uses_env_fallback_when_set(monkeypatch):
|
||||
monkeypatch.setenv("LITELLM_OAUTH2_UNKNOWN_ROLE_DEFAULT", "internal_user")
|
||||
uak = build_user_api_key_auth_from_oauth2_response(
|
||||
token="t", response_data={"sub": "u", "role": "custom-idp-role"}
|
||||
)
|
||||
assert uak.user_role == LitellmUserRoles.INTERNAL_USER
|
||||
|
||||
|
||||
def test_unknown_idp_role_fallback_with_invalid_env_value_fails(monkeypatch):
|
||||
monkeypatch.setenv("LITELLM_OAUTH2_UNKNOWN_ROLE_DEFAULT", "not-a-real-role")
|
||||
with pytest.raises(ValueError):
|
||||
build_user_api_key_auth_from_oauth2_response(
|
||||
token="t", response_data={"sub": "u", "role": "custom-idp-role"}
|
||||
)
|
||||
|
||||
|
||||
def test_known_idp_role_ignores_env_fallback(monkeypatch):
|
||||
monkeypatch.setenv("LITELLM_OAUTH2_UNKNOWN_ROLE_DEFAULT", "internal_user")
|
||||
uak = build_user_api_key_auth_from_oauth2_response(
|
||||
token="t", response_data={"sub": "u", "role": "proxy_admin"}
|
||||
)
|
||||
assert uak.user_role == LitellmUserRoles.PROXY_ADMIN
|
||||
|
||||
|
||||
def test_token_is_hashed_into_token_field():
|
||||
"""The api_key is hashed by the UserAPIKeyAuth validator; the
|
||||
OAuth2 builder must not bypass that path."""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue