Skip to content

Commit 4e1ebb8

Browse files
committed
Fix potential resource exhaustion in TDEI HTTP client
1 parent 2499c24 commit 4e1ebb8

2 files changed

Lines changed: 50 additions & 24 deletions

File tree

api/core/security.py

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,25 @@
2222
maxsize=1000, ttl=60 * 60
2323
)
2424

25+
# Shared HTTP client for TDEI backend calls. Initialized by main.py lifespan.
26+
_tdei_client: httpx.AsyncClient | None = None
27+
28+
29+
def init_tdei_client() -> None:
30+
global _tdei_client
31+
_tdei_client = httpx.AsyncClient(
32+
base_url=settings.TDEI_BACKEND_URL,
33+
timeout=httpx.Timeout(connect=10, read=30, write=30, pool=10),
34+
)
35+
36+
37+
async def close_tdei_client() -> None:
38+
global _tdei_client
39+
if _tdei_client is not None:
40+
await _tdei_client.aclose()
41+
_tdei_client = None
42+
43+
2544
security = HTTPBearer()
2645

2746

@@ -119,6 +138,7 @@ def get_task_db_session(
119138
) -> AsyncSession:
120139
return session
121140

141+
122142
async def validate_token(
123143
credentials: HTTPAuthorizationCredentials = Depends(security),
124144
osm_db_session: AsyncSession = Depends(get_osm_db_session),
@@ -185,32 +205,30 @@ async def _validate_token_uncached(
185205
r.user_name = payload.get("preferred_username", "unknown")
186206

187207
# get user's project groups and roles from TDEI
188-
pg_base_url = f"{settings.TDEI_BACKEND_URL}/project-group-roles/{user_id}"
189208
pgs = []
190-
async with httpx.AsyncClient() as http_client:
191-
response = await http_client.get(
192-
pg_base_url,
193-
headers=headers,
194-
params={"page_no": 1, "page_size": 1000},
195-
)
209+
response = await _tdei_client.get(
210+
f"project-group-roles/{user_id}",
211+
headers=headers,
212+
params={"page_no": 1, "page_size": 1000},
213+
)
214+
215+
# token is not valid or server unavailable
216+
if response.status_code != 200:
217+
raise credentials_exception
218+
219+
try:
220+
pg_data = response.json()
221+
except Exception:
222+
raise credentials_exception
196223

197-
# token is not valid or server unavailable
198-
if response.status_code != 200:
199-
raise credentials_exception
200-
201-
try:
202-
pg_data = response.json()
203-
except Exception:
204-
raise credentials_exception
205-
206-
for i in pg_data:
207-
pgs.append(
208-
UserInfoPGMembership(
209-
project_group_id=i["tdei_project_group_id"],
210-
project_group_name=i["project_group_name"],
211-
tdeiRoles=i["roles"],
212-
)
224+
for i in pg_data:
225+
pgs.append(
226+
UserInfoPGMembership(
227+
project_group_id=i["tdei_project_group_id"],
228+
project_group_name=i["project_group_name"],
229+
tdeiRoles=i["roles"],
213230
)
231+
)
214232

215233
r.projectGroups = pgs
216234

api/main.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
from api.core.config import settings
1616
from api.core.database import get_task_session
1717
from api.core.logging import get_logger, setup_logging
18-
from api.core.security import UserInfo, validate_token
18+
from api.core.security import (
19+
UserInfo,
20+
close_tdei_client,
21+
init_tdei_client,
22+
validate_token,
23+
)
1924
from api.src.teams.routes import router as teams_router
2025
from api.src.workspaces.repository import WorkspaceRepository
2126
from api.src.workspaces.routes import router as workspaces_router
@@ -51,12 +56,14 @@ async def lifespan(_app: FastAPI):
5156
# 2 hour timeout for long-running OSM imports:
5257
timeout=httpx.Timeout(connect=10, read=7200, write=7200, pool=10),
5358
)
59+
init_tdei_client()
5460

5561
yield # App runs
5662

5763
# Run after app cleanup:
5864
await _osm_client.aclose()
5965
_osm_client = None
66+
await close_tdei_client()
6067

6168

6269
app = FastAPI(
@@ -79,6 +86,7 @@ async def lifespan(_app: FastAPI):
7986
app.include_router(teams_router, prefix="/api/v1")
8087
app.include_router(workspaces_router, prefix="/api/v1")
8188

89+
8290
@app.get("/health")
8391
async def health_check():
8492
"""Health check endpoint. Used for Docker."""

0 commit comments

Comments
 (0)