M1+M2+M10 (CC2 R3): JWT auth + admin users + GDPR backend
- auth/auth_v2.py: JWT login/refresh/logout/me + bcrypt + tenant_id/role/tier claims - auth/admin_users.py: /api/admin/users CRUD + invite/role/suspend + bulk CSV - auth/gdpr.py: cookie consent + Art.20 export + Art.17 erasure + admin queue - auth/seed_demo.py: 3 demo tenants + 4 users (damir@pgz.hr / PGZ2026!) - Removed legacy /api/auth/login + /api/auth/me from pgz_sport_api.py - Wired auth/admin/gdpr routers into FastAPI 5/5 live curl tests pass: damir@pgz.hr login → JWT with tenant_id=1, role=pgz_admin, tier=0
This commit is contained in:
+25
-39
@@ -933,21 +933,7 @@ def google_auth(token: str = Body(..., embed=True)):
|
||||
except Exception as e:
|
||||
raise HTTPException(401, f"Google auth failed: {e}")
|
||||
|
||||
@app.get("/api/auth/me")
|
||||
def auth_me(authorization: Optional[str] = Header(None)):
|
||||
"""Get current user info from JWT."""
|
||||
if not authorization: return {"role": "viewer", "email": None, "name": None}
|
||||
token = authorization.replace("Bearer ", "").strip()
|
||||
# Try JWT first
|
||||
try:
|
||||
payload = _jwt.decode(token, JWT_SECRET, algorithms=["HS256"])
|
||||
return {"role": payload.get("role"), "email": payload.get("email"), "name": payload.get("name")}
|
||||
except Exception:
|
||||
pass
|
||||
# Legacy demo token
|
||||
if token == ADMIN_TOKEN:
|
||||
return {"role": "admin", "email": "demo@admin", "name": "Demo Admin"}
|
||||
return {"role": "viewer", "email": None, "name": None}
|
||||
# /api/auth/me handled by auth.auth_v2 router (M1)
|
||||
|
||||
# ==================== STATIC ====================
|
||||
import pathlib
|
||||
@@ -1422,6 +1408,29 @@ try:
|
||||
except Exception as e:
|
||||
print(f'[CRM/M9] obrasci router fail: {e}')
|
||||
|
||||
# === Round 3 / CC2 — M1 Auth + M2 Admin Users + M10 GDPR ===
|
||||
try:
|
||||
from auth.auth_v2 import router as auth_v2_router
|
||||
app.include_router(auth_v2_router)
|
||||
print('[AUTH/M1] auth_v2 router loaded (/api/auth/*)')
|
||||
except Exception as e:
|
||||
print(f'[AUTH/M1] auth_v2 router fail: {e}')
|
||||
|
||||
try:
|
||||
from auth.admin_users import router as admin_users_router
|
||||
app.include_router(admin_users_router)
|
||||
print('[AUTH/M2] admin_users router loaded (/api/admin/users/*)')
|
||||
except Exception as e:
|
||||
print(f'[AUTH/M2] admin_users router fail: {e}')
|
||||
|
||||
try:
|
||||
from auth.gdpr import router as gdpr_router, admin_router as gdpr_admin_router
|
||||
app.include_router(gdpr_router)
|
||||
app.include_router(gdpr_admin_router)
|
||||
print('[AUTH/M10] gdpr routers loaded (/api/gdpr/*, /api/admin/gdpr/*)')
|
||||
except Exception as e:
|
||||
print(f'[AUTH/M10] gdpr routers fail: {e}')
|
||||
|
||||
|
||||
|
||||
@app.get("/sport-3d")
|
||||
@@ -1511,30 +1520,7 @@ def get_user(token):
|
||||
return payload
|
||||
except: return None
|
||||
|
||||
# ── AUTH: Email/Password login ──────────────────────────────────
|
||||
@app.post("/api/auth/login")
|
||||
def login(body: dict = Body(...)):
|
||||
email = (body.get("email","")).lower().strip()
|
||||
pwd = body.get("password","")
|
||||
if not email or not pwd: raise HTTPException(400,"Email i lozinka obavezni")
|
||||
rows = fetch("SELECT * FROM pgz_sport.users WHERE LOWER(email)=%s AND aktivan=TRUE",[email])
|
||||
if not rows: raise HTTPException(401,"Neispravni podaci")
|
||||
u = rows[0]
|
||||
ph = hashlib.sha256(pwd.encode()).hexdigest()
|
||||
if u.get("password_hash") != ph: raise HTTPException(401,"Neispravni podaci")
|
||||
payload = {"uid":u["id"],"email":email,"name":u.get("full_name",email),
|
||||
"role":u.get("user_type","viewer"),"klub_id":u.get("klub_id"),
|
||||
"savez_id":u.get("savez_id"),"iat":int(__import__("time").time()),
|
||||
"exp":int(__import__("time").time())+86400*7}
|
||||
tok = _jwt.encode(payload, JWT_SECRET, algorithm="HS256")
|
||||
try:
|
||||
with db() as conn:
|
||||
cur=conn.cursor()
|
||||
cur.execute("UPDATE pgz_sport.users SET last_login=NOW() WHERE id=%s",[u["id"]])
|
||||
conn.commit()
|
||||
except: pass
|
||||
return {"token":tok,"role":payload["role"],"name":payload["name"],
|
||||
"email":email,"klub_id":payload["klub_id"],"savez_id":payload["savez_id"]}
|
||||
# ── AUTH: Email/Password login — handled by auth.auth_v2 router (M1) ──
|
||||
|
||||
# ── SPORTAS FULL PROFILE ─────────────────────────────────────────
|
||||
@app.get("/api/sportas/{clan_id}/profil")
|
||||
|
||||
Reference in New Issue
Block a user