feat: implement pivot-currency model, rbac smart tokens & fix circular imports

This commit is contained in:
2026-02-10 10:20:45 +00:00
parent 24d35fe0c1
commit e255fea3a5
117 changed files with 2247 additions and 3542 deletions

View File

@@ -5,7 +5,7 @@ from sqlalchemy import select
from app.db.session import get_db
from app.services.auth_service import AuthService
from app.core.security import create_access_token
from app.core.security import create_access_token, RANK_MAP
from app.schemas.auth import (
UserLiteRegister, Token, PasswordResetRequest,
UserKYCComplete, PasswordResetConfirm
@@ -17,7 +17,7 @@ router = APIRouter()
@router.post("/register-lite", response_model=Token, status_code=status.HTTP_201_CREATED)
async def register_lite(user_in: UserLiteRegister, db: AsyncSession = Depends(get_db)):
"""Step 1: Alapszintű regisztráció (Email + Jelszó)."""
"""Step 1: Alapszintű regisztráció. Az új felhasználó alapértelmezetten 'user' (Rank 10)."""
stmt = select(User).where(User.email == user_in.email)
result = await db.execute(stmt)
if result.scalar_one_or_none():
@@ -28,7 +28,17 @@ async def register_lite(user_in: UserLiteRegister, db: AsyncSession = Depends(ge
try:
user = await AuthService.register_lite(db, user_in)
token = create_access_token(data={"sub": str(user.id)})
# Kezdeti token generálása
token_data = {
"sub": str(user.id),
"role": "user",
"rank": 10,
"scope_level": "individual",
"scope_id": str(user.id)
}
token = create_access_token(data=token_data)
return {
"access_token": token,
"token_type": "bearer",
@@ -45,7 +55,7 @@ async def login(
db: AsyncSession = Depends(get_db),
form_data: OAuth2PasswordRequestForm = Depends()
):
"""Bejelentkezés és Access Token generálása."""
"""Bejelentkezés és okos JWT generálása RBAC adatokkal."""
user = await AuthService.authenticate(db, form_data.username, form_data.password)
if not user:
raise HTTPException(
@@ -53,7 +63,20 @@ async def login(
detail="Hibás e-mail cím vagy jelszó."
)
token = create_access_token(data={"sub": str(user.id)})
# Szerepkör string kinyerése és rang meghatározása a RANK_MAP-ből
role_name = user.role.value if hasattr(user.role, 'value') else str(user.role)
user_rank = RANK_MAP.get(role_name, 10)
token_data = {
"sub": str(user.id),
"role": role_name,
"rank": user_rank,
"scope_level": user.scope_level or "individual",
"scope_id": user.scope_id or str(user.id),
"region": user.region_code
}
token = create_access_token(data=token_data)
return {
"access_token": token,
"token_type": "bearer",
@@ -62,14 +85,11 @@ async def login(
@router.get("/verify-email")
async def verify_email(token: str, db: AsyncSession = Depends(get_db)):
"""E-mail megerősítése a kiküldött link alapján."""
"""E-mail megerősítése."""
success = await AuthService.verify_email(db, token)
if not success:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Érvénytelen vagy lejárt megerősítő token."
)
return {"message": "Email sikeresen megerősítve! Jöhet a profil kitöltése (KYC)."}
raise HTTPException(status_code=400, detail="Érvénytelen vagy lejárt token.")
return {"message": "Email sikeresen megerősítve!"}
@router.post("/complete-kyc")
async def complete_kyc(
@@ -77,38 +97,27 @@ async def complete_kyc(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""Step 2: Személyes adatok és okmányok rögzítése."""
"""Step 2: KYC adatok rögzítése és aktiválás."""
user = await AuthService.complete_kyc(db, current_user.id, kyc_in)
if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Felhasználó nem található.")
return {"status": "success", "message": "A profil adatok rögzítve, a rendszer aktiválva."}
raise HTTPException(status_code=404, detail="Felhasználó nem található.")
return {"status": "success", "message": "A profil aktiválva."}
@router.post("/forgot-password")
async def forgot_password(req: PasswordResetRequest, db: AsyncSession = Depends(get_db)):
"""Elfelejtett jelszó folyamat indítása biztonsági korlátokkal."""
"""Elfelejtett jelszó folyamat."""
result = await AuthService.initiate_password_reset(db, req.email)
if result == "cooldown":
raise HTTPException(status_code=429, detail="Kérjük várjon 2 percet az újabb kérés előtt.")
if result in ["hourly_limit", "daily_limit"]:
raise HTTPException(status_code=429, detail="Túllépte a napi/óránkénti próbálkozások számát.")
return {"message": "Amennyiben a megadott e-mail cím szerepel a rendszerünkben, kiküldtük a linket."}
raise HTTPException(status_code=429, detail="Kérjük várjon 2 percet.")
return {"message": "Amennyiben a cím létezik, a linket kiküldtük."}
@router.post("/reset-password")
async def reset_password(req: PasswordResetConfirm, db: AsyncSession = Depends(get_db)):
"""Új jelszó beállítása. Backend ellenőrzi az egyezőséget és a tokent."""
"""Új jelszó beállítása."""
if req.password != req.password_confirm:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="A két jelszó nem egyezik meg."
)
raise HTTPException(status_code=400, detail="A jelszavak nem egyeznek.")
success = await AuthService.reset_password(db, req.email, req.token, req.password)
if not success:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Érvénytelen adatok vagy lejárt token."
)
return {"message": "A jelszó sikeresen frissítve! Most már bejelentkezhet."}
raise HTTPException(status_code=400, detail="Hiba a jelszó frissítésekor.")
return {"message": "A jelszó sikeresen frissítve!"}