Files
homekeeper/api/app/auth.py
T
2026-07-06 19:25:38 +02:00

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()