This commit is contained in:
devin-ai-integration[bot] 2026-09-23 14:43:08 +00:00 • committed by GitHub
commit 64cac3e599
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 77 additions and 22 deletions

View file

@ -1319,6 +1319,14 @@ class JWTHandler:
await self.http_handler.close()
def _is_team_auto_join_denied(error: Exception) -> bool:
if isinstance(error, ProxyException):
return error.code == "403"
if isinstance(error, HTTPException):
return error.status_code == 403
return False
class JWTAuthManager:
"""Manages JWT authentication and authorization operations"""
@ -2011,25 +2019,28 @@ class JWTAuthManager:
),
team_id=team_object.team_id,
)
# add user to team - make this non-blocking to avoid authentication failures
try:
await team_member_add(
data=data,
user_api_key_dict=UserAPIKeyAuth(
user_role=LitellmUserRoles.PROXY_ADMIN
), # [TODO]: expose an internal service role, for better tracking
)
user_api_key_dict=UserAPIKeyAuth(user_role=LitellmUserRoles.PROXY_ADMIN),
) # [TODO]: expose an internal service role, for better tracking
verbose_proxy_logger.debug(
"Successfully added user %s to team %s", user_object.user_id, team_object.team_id
)
except ProxyException as e:
if e.type == ProxyErrorTypes.team_member_already_in_team:
except Exception as e:
if _is_team_auto_join_denied(e):
raise
if isinstance(e, ProxyException) and e.type == ProxyErrorTypes.team_member_already_in_team:
verbose_proxy_logger.debug(
"User %s is already a member of team %s", user_object.user_id, team_object.team_id
)
return
else:
raise e
verbose_proxy_logger.warning(
"JWT auto-join: could not add user %s to team %s, continuing the request: %s",
user_object.user_id,
team_object.team_id,
e,
)
return
@staticmethod

View file

@ -116,30 +116,74 @@ async def test_map_user_to_teams_handles_already_in_team_exception():
@pytest.mark.asyncio
async def test_map_user_to_teams_reraises_other_proxy_exceptions():
"""Test that other ProxyException types are re-raised"""
@pytest.mark.parametrize(
"side_effect",
[
ProxyException(
message="Some other error",
type=ProxyErrorTypes.internal_server_error,
param=None,
code="500",
),
HTTPException(
status_code=500,
detail={"error": "Unique constraint failed on the fields: (`user_id`,`team_id`)"},
),
RuntimeError("db pool timeout"),
],
)
async def test_map_user_to_teams_swallows_non_auth_failures(side_effect):
"""Test that non-authorization failures do not reject the request"""
# Setup test data
user = LiteLLM_UserTable(user_id="test_user_1")
team = LiteLLM_TeamTable(team_id="test_team_1", members_with_roles=[])
# Create a ProxyException with a different error type
other_exception = ProxyException(
message="Some other error",
type=ProxyErrorTypes.internal_server_error,
param="some_param",
code="500",
)
with patch( # test-quality-ok: map_user_to_teams imports team_member_add locally
"litellm.proxy.management_endpoints.team_endpoints.team_member_add",
new_callable=AsyncMock,
side_effect=side_effect,
) as mock_add:
with patch( # test-quality-ok: assert the warning emitted for swallowed failures
"litellm.proxy.auth.handle_jwt.verbose_proxy_logger"
) as mock_logger:
result = await JWTAuthManager.map_user_to_teams(user_object=user, team_object=team)
assert result is None
mock_add.assert_called_once()
mock_logger.warning.assert_called_once()
@pytest.mark.asyncio
@pytest.mark.parametrize(
"side_effect, exception_type",
[
(
ProxyException(
message="forbidden",
type=ProxyErrorTypes.auth_error,
param=None,
code="403",
),
ProxyException,
),
(HTTPException(status_code=403, detail="forbidden"), HTTPException),
],
)
async def test_map_user_to_teams_reraises_authorization_denials(side_effect, exception_type):
"""Test that authorization denials reject the request"""
user = LiteLLM_UserTable(user_id="test_user_1")
team = LiteLLM_TeamTable(team_id="test_team_1", members_with_roles=[])
# Mock team_member_add to raise the exception
with patch(
"litellm.proxy.management_endpoints.team_endpoints.team_member_add",
new_callable=AsyncMock,
side_effect=other_exception,
side_effect=side_effect,
) as mock_add:
# This should re-raise the exception
with pytest.raises(ProxyException) as exc_info:
with pytest.raises(exception_type):
await JWTAuthManager.map_user_to_teams(user_object=user, team_object=team)
mock_add.assert_called_once()
@pytest.mark.asyncio
async def test_map_user_to_teams_null_inputs():