68 lines
2.1 KiB
Python
68 lines
2.1 KiB
Python
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()
|