Kakashiix26 commited on
Commit
fa36140
·
verified ·
1 Parent(s): 0123d27

fix: allowlist role authoritative on every authed request

Browse files
auth/__pycache__/__init__.cpython-314.pyc ADDED
Binary file (153 Bytes). View file
 
auth/__pycache__/crypto.cpython-314.pyc ADDED
Binary file (2.31 kB). View file
 
auth/__pycache__/dependencies.cpython-314.pyc ADDED
Binary file (2 kB). View file
 
auth/__pycache__/roles.cpython-314.pyc ADDED
Binary file (2.77 kB). View file
 
auth/__pycache__/utils.cpython-314.pyc ADDED
Binary file (3.64 kB). View file
 
auth/dependencies.py CHANGED
@@ -4,6 +4,7 @@ FastAPI dependency for authenticated routes.
4
  from fastapi import Depends, Header, HTTPException, status
5
  from sqlalchemy.orm import Session
6
 
 
7
  from auth.utils import decode_access_token
8
  from db.database import get_db
9
  from db.models import User
@@ -23,4 +24,12 @@ def get_current_user(
23
  user = db.query(User).filter(User.id == user_id).first()
24
  if not user:
25
  raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
 
 
 
 
 
 
 
 
26
  return user
 
4
  from fastapi import Depends, Header, HTTPException, status
5
  from sqlalchemy.orm import Session
6
 
7
+ from auth.roles import ensure_role
8
  from auth.utils import decode_access_token
9
  from db.database import get_db
10
  from db.models import User
 
24
  user = db.query(User).filter(User.id == user_id).first()
25
  if not user:
26
  raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
27
+ # The email allowlist is authoritative on EVERY authenticated request — not
28
+ # just login — so downstream gates (e.g. premium generation) never see a
29
+ # stale role='user' for an allowlisted SUPER_ADMIN. Idempotent after the
30
+ # first upgrade. Non-fatal if the write fails (still returns the user).
31
+ try:
32
+ ensure_role(user, db)
33
+ except Exception:
34
+ db.rollback()
35
  return user
auth/roles.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Role resolution — the SUPER_ADMIN allowlist is the single source of truth.
3
+
4
+ Lives in the `auth` layer (not `api`) so BOTH the auth endpoints and the
5
+ `get_current_user` dependency can apply it. Previously the allowlist upgrade
6
+ only ran during login/`/me`, so a request authenticated with a token whose DB
7
+ row still said `role='user'` (e.g. a SUPER_ADMIN email that logged in before the
8
+ upgrade persisted) was treated as a normal user by downstream gates like the
9
+ premium generation flow. Applying it in `get_current_user` makes every
10
+ authenticated request authoritative.
11
+ """
12
+ import os
13
+
14
+ from sqlalchemy.orm import Session
15
+
16
+ from db.models import User
17
+
18
+ # ponytail: small allowlist drives the SUPER_ADMIN role (env-overridable, comma-separated).
19
+ SUPER_ADMIN_EMAILS: set[str] = {
20
+ e.strip().lower()
21
+ for e in os.environ.get("SUPER_ADMIN_EMAILS", "proff.pratiksingh@gmail.com").split(",")
22
+ if e.strip()
23
+ }
24
+
25
+
26
+ def resolve_role(email: str) -> str:
27
+ """Role is fully determined by the email allowlist (case-insensitive)."""
28
+ return "SUPER_ADMIN" if (email or "").lower() in SUPER_ADMIN_EMAILS else "user"
29
+
30
+
31
+ def ensure_role(user: User, db: Session) -> str:
32
+ """Set the user's role from the allowlist, persisting an upgrade to
33
+ SUPER_ADMIN once. Idempotent: after the first upgrade it only reads."""
34
+ role = resolve_role(user.email)
35
+ if getattr(user, "role", None) != role and role == "SUPER_ADMIN":
36
+ user.role = role
37
+ db.commit()
38
+ return role