import os from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBasic, HTTPBasicCredentials from passlib.context import CryptContext from sqlalchemy import text security = HTTPBasic() pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") def verify_password(plain: str, hashed: str) -> bool: return pwd_context.verify(plain, hashed) def hash_password(plain: str) -> str: return pwd_context.hash(plain) def get_current_user(credentials: HTTPBasicCredentials = Depends(security)): from app.database import engine with engine.connect() as conn: row = conn.execute( text("SELECT hashed_password FROM auth.users WHERE username = :u"), {"u": credentials.username}, ).fetchone() if row is None or not verify_password(credentials.password, row[0]): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials", headers={"WWW-Authenticate": "Basic"}, ) return credentials.username def seed_users(): """Seed initial users from INITIAL_USERS env var if users table is empty.""" from app.database import engine initial_users_env = os.getenv("INITIAL_USERS", "") if not initial_users_env: return with engine.connect() as conn: count = conn.execute(text("SELECT COUNT(*) FROM auth.users")).scalar() if count and count > 0: return for pair in initial_users_env.split(","): pair = pair.strip() if ":" not in pair: continue username, password = pair.split(":", 1) username = username.strip() password = password.strip() if not username or not password: continue hashed = hash_password(password) conn.execute( text( "INSERT INTO auth.users (username, hashed_password) " "VALUES (:u, :h) ON CONFLICT (username) DO NOTHING" ), {"u": username, "h": hashed}, ) conn.commit()