-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdeps.py
More file actions
111 lines (93 loc) · 3.85 KB
/
Copy pathdeps.py
File metadata and controls
111 lines (93 loc) · 3.85 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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import uuid
from collections.abc import AsyncGenerator
from typing import Annotated
from fastapi import Depends, HTTPException, Request, status
from fastapi.security import OAuth2PasswordBearer
from pydantic import ValidationError
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import settings
from app.core.db import AsyncSessionLocal
from app.core.messages.error_message import ErrorMessages
from app.core.security import verify_token
from app.models.user import User
from app.repositories.token_blacklist import is_token_blacklisted
from app.repositories.user import get_user_by_id
from app.schemas.token import TokenPayload
from app.schemas.user import SystemRole
reusable_oauth2 = OAuth2PasswordBearer(
tokenUrl=f"{settings.API_V1_STR}/auth/login", auto_error=False
)
async def get_db() -> AsyncGenerator[AsyncSession, None]:
"""Database session dependency."""
async with AsyncSessionLocal() as session:
yield session
async def get_current_user(
request: Request,
db: Annotated[AsyncSession, Depends(get_db)],
bearer_token: Annotated[str | None, Depends(reusable_oauth2)] = None,
) -> User:
"""Resolve the JWT to a User, allowing accounts in the deletion grace window.
This intentionally does NOT reject ``is_active=False`` users — that check
moved to ``get_current_active_user`` so deactivated users can still hit
``/users/me`` and ``/users/me/reactivate`` during the grace period.
"""
token = request.cookies.get("access_token") or bearer_token
if not token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ErrorMessages.INVALID_TOKEN,
headers={"WWW-Authenticate": "Bearer"},
)
try:
if await is_token_blacklisted(token):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ErrorMessages.INVALID_TOKEN,
headers={"WWW-Authenticate": "Bearer"},
)
token_subject = verify_token(token)
if token_subject is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ErrorMessages.INVALID_TOKEN,
headers={"WWW-Authenticate": "Bearer"},
)
token_data = TokenPayload(sub=token_subject)
except (ValidationError, ValueError):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ErrorMessages.INVALID_TOKEN,
headers={"WWW-Authenticate": "Bearer"},
)
user = await get_user_by_id(db, user_id=uuid.UUID(token_data.sub))
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=ErrorMessages.USER_NOT_FOUND
)
return user
def get_current_active_user(
current_user: Annotated[User, Depends(get_current_user)],
) -> User:
"""Require the caller's account to be active (not in deletion grace)."""
if not current_user.is_active:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ErrorMessages.USER_INACTIVE,
)
return current_user
def get_current_superuser(
current_user: Annotated[User, Depends(get_current_active_user)],
) -> User:
"""Require the caller to be an active system admin."""
if current_user.role != SystemRole.ADMIN:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=ErrorMessages.INSUFFICIENT_PERMISSIONS,
)
return current_user
# Type aliases for dependency injection
SessionDep = Annotated[AsyncSession, Depends(get_db)]
TokenDep = Annotated[str, Depends(reusable_oauth2)]
CurrentUser = Annotated[User, Depends(get_current_user)]
CurrentActiveUser = Annotated[User, Depends(get_current_active_user)]
CurrentSuperUser = Annotated[User, Depends(get_current_superuser)]