deerflow-code/offline-backend-20260512/backend/app/gateway/auth/disabled_mode.py
2026-09-07 18:24:55 +08:00

76 lines
2.4 KiB
Python

"""Dev-only bypass when ``DEER_FLOW_AUTH_DISABLED`` is set.
Never enable in production: all API and LangGraph routes run as an assumed user.
"""
from __future__ import annotations
import os
from datetime import UTC
from uuid import UUID
from sqlalchemy import select
from app.gateway.auth.models import User
from deerflow.persistence.engine import get_session_factory
from deerflow.persistence.user.model import UserRow
_FALLBACK_ADMIN_ID = UUID("00000000-0000-4000-8000-000000000001")
# Must satisfy pydantic EmailStr (avoid reserved/special-use domains like .local).
_FALLBACK_EMAIL = "anonymous@example.com"
_cached_assumed_user: User | None = None
def is_auth_disabled() -> bool:
"""Return True when auth gate should be bypassed (development / local tooling)."""
return os.environ.get("DEER_FLOW_AUTH_DISABLED", "").strip().lower() in ("1", "true", "yes", "on")
def _row_to_user(row: UserRow) -> User:
created = row.created_at
if created.tzinfo is None:
created = created.replace(tzinfo=UTC)
return User(
id=UUID(row.id),
email=row.email,
password_hash=row.password_hash,
system_role=row.system_role, # type: ignore[arg-type]
created_at=created,
oauth_provider=row.oauth_provider,
oauth_id=row.oauth_id,
needs_setup=row.needs_setup,
token_version=row.token_version,
)
async def get_assumed_user_for_disabled_auth() -> User:
"""Resolve the user assumed for every request when auth is disabled.
Prefer the first admin row in the database so thread ownership matches an
existing account. If the DB is empty or unavailable, use a stable synthetic
admin (restart the gateway after creating a real admin to pick them up).
"""
global _cached_assumed_user
if _cached_assumed_user is not None:
return _cached_assumed_user
sf = get_session_factory()
if sf is not None:
async with sf() as session:
stmt = select(UserRow).where(UserRow.system_role == "admin").limit(1)
row = (await session.execute(stmt)).scalar_one_or_none()
if row is not None:
_cached_assumed_user = _row_to_user(row)
return _cached_assumed_user
_cached_assumed_user = User(
id=_FALLBACK_ADMIN_ID,
email=_FALLBACK_EMAIL,
password_hash=None,
system_role="admin",
needs_setup=False,
token_version=0,
)
return _cached_assumed_user