test2 / auth.py
simikkk's picture
Update auth.py
65c84d0 verified
Raw
History Blame Contribute Delete
7.15 kB
"""
OmniParse AI - Authentication Module
Handles user registration, login, session management, and API key generation.
Uses bcrypt for password hashing and delegates all storage to database.py.
Late-imports database functions inside call-bodies to avoid circular imports
(auth ↔ database).
"""
import secrets
import bcrypt
from validation import validate_email, validate_name, validate_password, sanitize_string
# ---------------------------------------------------------------------------
# Password helpers
# ---------------------------------------------------------------------------
def hash_pw(password: str) -> str:
"""Hash *password* with bcrypt (12 rounds) and return the hash as a UTF-8 string."""
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password.encode("utf-8"), salt)
return hashed.decode("utf-8")
def verify_pw(password: str, hashed: str) -> bool:
"""Return True if *password* matches the bcrypt *hashed* string."""
try:
return bcrypt.checkpw(password.encode("utf-8"), hashed.encode("utf-8"))
except Exception:
return False
# ---------------------------------------------------------------------------
# Token / API-key generators
# ---------------------------------------------------------------------------
def gen_token() -> str:
"""Generate a cryptographically random session token (URL-safe, 32 bytes)."""
return secrets.token_urlsafe(32)
def gen_api_key() -> str:
"""Generate a unique API key with a recognizable prefix."""
return "op_live_" + secrets.token_hex(20)
# ---------------------------------------------------------------------------
# User creation
# ---------------------------------------------------------------------------
def create_user(email: str, name: str, password: str, plan: str = "free"):
"""Register a new user after validating all inputs.
Args:
email: User email address.
name: Display name.
password: Plain-text password (will be hashed).
plan: Subscription plan (default "free").
Returns:
(user_dict, "") on success.
(None, "error message") on failure.
"""
# --- Input validation ---
valid, err = validate_email(email)
if not valid:
return None, err
valid, err = validate_name(name)
if not valid:
return None, err
valid, errs = validate_password(password)
if not valid:
return None, "; ".join(errs)
email = sanitize_string(email).lower()
name = sanitize_string(name)
if plan not in ("free", "basic", "pro", "enterprise"):
return None, "Invalid plan."
# --- Late import to avoid circular dependency ---
from database import get_user_by_email, create_user as db_create_user
# --- Check uniqueness ---
existing = get_user_by_email(email)
if existing is not None:
return None, "An account with this email already exists."
# --- Hash & store ---
hashed = hash_pw(password)
user = db_create_user(email=email, name=name, password=hashed, plan=plan)
return user, ""
# ---------------------------------------------------------------------------
# User lookups
# ---------------------------------------------------------------------------
def get_user_by_email(email: str):
"""Fetch a user by email. Returns user dict or None."""
from database import get_user_by_email as db_get_user_by_email
return db_get_user_by_email(email)
def get_user_by_id(uid: str):
"""Fetch a user by ID. Returns user dict or None."""
from database import get_user_by_id as db_get_user_by_id
return db_get_user_by_id(uid)
# ---------------------------------------------------------------------------
# User update
# ---------------------------------------------------------------------------
def update_user(uid: str, fields: dict):
"""Update user fields. Returns the updated user dict or None on failure."""
from database import update_user as db_update_user
return db_update_user(uid, fields)
# ---------------------------------------------------------------------------
# Session management
# ---------------------------------------------------------------------------
def create_session(user_id: str) -> str:
"""Create a new session for *user_id* and return the session token."""
from database import create_session as db_create_session
token = gen_token()
db_create_session(user_id=user_id, token=token)
return token
def get_session_user(token: str):
"""Look up the user associated with *token*.
Returns:
user_dict if the session is valid and not expired, else None.
"""
from database import get_session_user as db_get_session_user
return db_get_session_user(token)
def delete_session(token: str) -> None:
"""Delete a session token (logout)."""
from database import delete_session as db_delete_session
db_delete_session(token)
# ---------------------------------------------------------------------------
# Authentication (login)
# ---------------------------------------------------------------------------
def authenticate_user(email: str, password: str, ip: str = ""):
"""Verify credentials (session is created separately by the caller).
Args:
email: User email.
password: Plain-text password.
ip: Client IP address for rate-limiting.
Returns:
(user_dict, "") on success.
(None, "error message") on failure.
"""
# --- Input validation ---
valid, err = validate_email(email)
if not valid:
return None, err
if not isinstance(password, str) or not password:
return None, "Password is required."
email = sanitize_string(email).lower()
# --- Rate limit check ---
if ip:
from rate_limiter import check_auth_rate
if not check_auth_rate(ip):
return None, "Too many login attempts. Please try again later."
# --- Look up user ---
from database import get_user_by_email as db_get_user_by_email
user = db_get_user_by_email(email)
if user is None:
return None, "Invalid email or password."
# --- Verify password ---
stored_hash = user.get("password", "")
if not stored_hash:
return None, "Invalid email or password."
if not verify_pw(password, stored_hash):
return None, "Invalid email or password."
return user, ""
# ---------------------------------------------------------------------------
# Demo account bootstrap
# ---------------------------------------------------------------------------
def ensure_demo_account():
"""Create a demo account if one does not already exist."""
from database import get_user_by_email, create_user as db_create_user
existing = get_user_by_email("demo@omniparse.ai")
if existing is not None:
return
try:
hashed = hash_pw("demo1234")
db_create_user(
email="demo@omniparse.ai",
name="Demo User",
password=hashed,
plan="pro",
)
except Exception as e:
print(f"[INFO] Demo account not created: {e}")