-
Notifications
You must be signed in to change notification settings - Fork 77
Expand file tree
/
Copy pathdeps.py
More file actions
84 lines (66 loc) · 2.63 KB
/
deps.py
File metadata and controls
84 lines (66 loc) · 2.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
"""FastAPI dependencies for authentication (via Next.js internal API)."""
import os
from typing import Annotated
import httpx
from fastapi import Depends, Header, HTTPException, status
from shared.enums import MemberRole
# Configuration for internal API (Python → Next.js)
TRACEROOT_UI_URL = os.getenv("TRACEROOT_UI_URL", "http://localhost:3000")
INTERNAL_API_SECRET = os.getenv("INTERNAL_API_SECRET", "")
class ProjectAccessInfo:
"""Information about user's access to a project."""
def __init__(self, project_id: str, user_id: str, role: str):
self.project_id = project_id
self.user_id = user_id
self.role = role
async def get_project_access(
project_id: str,
x_user_id: Annotated[str | None, Header()] = None,
) -> ProjectAccessInfo:
"""
Validate user has access to a project via Next.js internal API.
The frontend should pass:
- x-user-id: User's unique ID (from session)
Raises 401 if missing user ID, 403 if no access, 404 if project not found.
"""
if not x_user_id:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing x-user-id header",
)
# Validate access via Next.js internal API
try:
async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.post(
f"{TRACEROOT_UI_URL}/api/internal/validate-project-access",
json={"userId": x_user_id, "projectId": project_id},
headers={"X-Internal-Secret": INTERNAL_API_SECRET},
)
except httpx.RequestError as e:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=f"Authentication service unavailable: {e}",
) from e
if response.status_code == 401:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Unauthorized",
)
if response.status_code != 200:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Authentication service error",
)
data = response.json()
if not data.get("hasAccess"):
error = data.get("error", "No access to this project")
status_code = (
status.HTTP_404_NOT_FOUND if "not found" in error.lower() else status.HTTP_403_FORBIDDEN
)
raise HTTPException(status_code=status_code, detail=error)
return ProjectAccessInfo(
project_id=project_id,
user_id=x_user_id,
role=data.get("role", MemberRole.VIEWER),
)
ProjectAccess = Annotated[ProjectAccessInfo, Depends(get_project_access)]