mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-12 03:48:56 +00:00
fix(proxy): check project write permission before validating against team limits
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
7c0e586d78
commit
b56809b3b1
2 changed files with 35 additions and 10 deletions
|
|
@ -523,16 +523,6 @@ async def new_project(
|
|||
# Validate team exists and get team object with budget
|
||||
team_object = await _validate_team_exists(team_id=data.team_id, prisma_client=prisma_client)
|
||||
|
||||
# Validate project limits against team limits
|
||||
_check_team_project_limits(
|
||||
team_object=LiteLLM_TeamTable.model_validate(team_object.model_dump()),
|
||||
data=data,
|
||||
)
|
||||
|
||||
# Opt-in (default off): require rpm/tpm for every model added to the project.
|
||||
if general_settings.get("enforce_project_model_quota", False):
|
||||
_raise_on_missing_project_model_quota(data, _router_access_group_names(llm_router))
|
||||
|
||||
# Check if user has permission to create projects for this team
|
||||
# only team admins can create projects for their team
|
||||
has_permission = await _check_user_permission_for_project(
|
||||
|
|
@ -550,6 +540,16 @@ async def new_project(
|
|||
},
|
||||
)
|
||||
|
||||
# Validate project limits against team limits
|
||||
_check_team_project_limits(
|
||||
team_object=LiteLLM_TeamTable.model_validate(team_object.model_dump()),
|
||||
data=data,
|
||||
)
|
||||
|
||||
# Opt-in (default off): require rpm/tpm for every model added to the project.
|
||||
if general_settings.get("enforce_project_model_quota", False):
|
||||
_raise_on_missing_project_model_quota(data, _router_access_group_names(llm_router))
|
||||
|
||||
# Generate project_id if not provided
|
||||
if data.project_id is None:
|
||||
data.project_id = str(uuid.uuid4())
|
||||
|
|
|
|||
|
|
@ -1225,6 +1225,31 @@ async def test_new_project_flag_on_missing_rpm_tpm_returns_400(monkeypatch):
|
|||
assert "rpm/tpm quota" in str(exc_info.value)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_new_project_denies_non_admin_before_leaking_team_limits(monkeypatch):
|
||||
from litellm.proxy._types import LiteLLM_TeamTable
|
||||
from litellm_enterprise.proxy.management_endpoints import project_endpoints as pe
|
||||
|
||||
team = LiteLLM_TeamTable(team_id="test-team", models=["secret-model"], max_budget=42.0)
|
||||
monkeypatch.setattr(litellm.proxy.proxy_server, "prisma_client", mock.MagicMock())
|
||||
monkeypatch.setattr(litellm.proxy.proxy_server, "premium_user", True)
|
||||
monkeypatch.setattr(litellm.proxy.proxy_server, "general_settings", {})
|
||||
monkeypatch.setattr(pe, "_validate_team_exists", mock.AsyncMock(return_value=team))
|
||||
|
||||
with pytest.raises(ProxyException) as exc_info:
|
||||
await new_project(
|
||||
data=NewProjectRequest(team_id="test-team", models=["not-in-team"], max_budget=1000.0),
|
||||
http_request=Request(scope={"type": "http"}),
|
||||
user_api_key_dict=UserAPIKeyAuth(
|
||||
user_role=LitellmUserRoles.INTERNAL_USER, api_key="sk-user", user_id="stranger"
|
||||
),
|
||||
)
|
||||
|
||||
assert exc_info.value.code == "403"
|
||||
assert "secret-model" not in str(exc_info.value)
|
||||
assert "42" not in str(exc_info.value)
|
||||
|
||||
|
||||
def _project_update_mocks(monkeypatch, stored_metadata: dict) -> mock.MagicMock:
|
||||
existing_row = mock.MagicMock(
|
||||
team_id=None, budget_id=None, object_permission_id=None, metadata=stored_metadata
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue