44 lines
1.6 KiB
Python
44 lines
1.6 KiB
Python
import uuid
|
|
from datetime import datetime
|
|
|
|
from fastapi import Depends, HTTPException, Request, status
|
|
from fastapi.security import OAuth2PasswordBearer
|
|
from slowapi import Limiter
|
|
from slowapi.util import get_remote_address
|
|
from sqlalchemy.orm import Session
|
|
|
|
from core.auth import decode_access_token
|
|
from core.database import get_db
|
|
from core.models import User as UserModel
|
|
|
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
|
|
|
|
# Shared rate limiter — registered on app.state in main.py
|
|
limiter = Limiter(key_func=get_remote_address)
|
|
|
|
|
|
def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)) -> UserModel:
|
|
user_id = decode_access_token(token)
|
|
if not user_id:
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
|
|
user = db.query(UserModel).filter(UserModel.id == uuid.UUID(user_id)).first()
|
|
if not user:
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
|
|
# Throttle to one write per 5 minutes so every authenticated request doesn't hammer the DB
|
|
now = datetime.now()
|
|
if not user.last_active_at or (now - user.last_active_at).total_seconds() > 300:
|
|
user.last_active_at = now
|
|
db.commit()
|
|
return user
|
|
|
|
|
|
# Per-user key for rate limiting authenticated endpoints — prevents shared IPs (NAT/VPN)
|
|
# from having their limits pooled. Falls back to remote IP for unauthenticated requests.
|
|
def get_user_id_from_request(request: Request) -> str:
|
|
auth = request.headers.get("Authorization", "")
|
|
if auth.startswith("Bearer "):
|
|
user_id = decode_access_token(auth[7:])
|
|
if user_id:
|
|
return f"user:{user_id}"
|
|
return get_remote_address(request)
|