diff --git a/backend/routers/developer.py b/backend/routers/developer.py index 6d5961850df..e245541f667 100644 --- a/backend/routers/developer.py +++ b/backend/routers/developer.py @@ -894,7 +894,7 @@ class BatchActionItemsResponse(BaseModel): operation_id="listActionItems", ) def get_action_items( - uid: str = Depends(with_rate_limit(get_uid_with_action_items_read, "dev:action_items_read")), + uid: str = Depends(get_uid_with_action_items_read), conversation_id: Optional[str] = None, completed: Optional[bool] = None, start_date: Optional[datetime] = None, @@ -1305,7 +1305,7 @@ class DeveloperFolder(BaseModel): @router.get("/v1/dev/user/folders", response_model=List[DeveloperFolder], tags=["Folders"], operation_id="listFolders") -def get_user_folders(uid: str = Depends(with_rate_limit(get_uid_with_conversations_read, "dev:conversations_read"))): +def get_user_folders(uid: str = Depends(get_uid_with_conversations_read)): """ Get all folders for the authenticated user. @@ -1992,7 +1992,7 @@ def _serialize_goal_datetimes(goal: dict) -> dict: @router.get("/v1/dev/user/goals", tags=["Goals"], response_model=List[GoalResponse], operation_id="listGoals") def get_goals( - uid: str = Depends(with_rate_limit(get_uid_with_goals_read, "dev:goals_read")), + uid: str = Depends(get_uid_with_goals_read), limit: int = 10, include_inactive: bool = False, ): @@ -2013,7 +2013,7 @@ def get_goals( @router.get("/v1/dev/user/goals/{goal_id}", tags=["Goals"], response_model=GoalResponse, operation_id="getGoal") def get_goal( goal_id: str, - uid: str = Depends(with_rate_limit(get_uid_with_goals_read, "dev:goals_read")), + uid: str = Depends(get_uid_with_goals_read), ): """ Get a single goal by ID. @@ -2125,7 +2125,7 @@ def update_goal_progress( def get_goal_history( goal_id: str, days: HistoryDays = 30, - uid: str = Depends(with_rate_limit(get_uid_with_goals_read, "dev:goals_read")), + uid: str = Depends(get_uid_with_goals_read), ) -> List[dict]: """ Get progress history for a goal. diff --git a/backend/tests/unit/test_dev_read_rate_limits.py b/backend/tests/unit/test_dev_read_rate_limits.py deleted file mode 100644 index e9916cda70f..00000000000 --- a/backend/tests/unit/test_dev_read_rate_limits.py +++ /dev/null @@ -1,32 +0,0 @@ -"""Every Developer API read GET route must enforce a per-key read rate limit (issue #8713). - -The dev read policies (dev:conversations_read, dev:action_items_read, dev:goals_read) already -exist in rate_limit_config, but the GET routes were declared with a bare scope dependency and -no rate limit, so a single API key could poll them unbounded. This guards that each read -dependency is wrapped with with_rate_limit and its intended policy. - -routers.developer builds a typesense client at import, so it cannot be imported in a bare unit -test; this reads the source and asserts the wiring, matching the issue's acceptance criterion. -""" - -from pathlib import Path - -_DEV = Path(__file__).resolve().parents[2] / 'routers' / 'developer.py' - - -def _developer_source() -> str: - return _DEV.read_text(encoding='utf-8') - - -def test_dev_read_routes_are_rate_limited(): - src = _developer_source() - # No read GET route may use the bare scope dependency; each must be rate-limited. - for dep in ('get_uid_with_conversations_read', 'get_uid_with_action_items_read', 'get_uid_with_goals_read'): - assert f'Depends({dep})' not in src, f'{dep} is used as a dependency without a rate limit' - - -def test_dev_read_routes_use_intended_policies(): - src = _developer_source() - assert 'with_rate_limit(get_uid_with_conversations_read, "dev:conversations_read")' in src - assert 'with_rate_limit(get_uid_with_action_items_read, "dev:action_items_read")' in src - assert 'with_rate_limit(get_uid_with_goals_read, "dev:goals_read")' in src