"""ResolutionFlow system service account. This module manages the platform-level service account used as the author for system/default content (seeded trees, synced step library entries, etc.). The service account ID is resolved once at startup and cached on app.state so that sync operations can use it without a DB query per request. """ from __future__ import annotations import uuid import logging from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.admin_database import _admin_session_factory logger = logging.getLogger(__name__) SERVICE_ACCOUNT_EMAIL = "noreply@resolutionflow.com" SERVICE_ACCOUNT_NAME = "ResolutionFlow" # Well-known UUID for the platform account — owns all default/global content. # Created by migration 3a40fe11b427_create_global_content_tables. PLATFORM_ACCOUNT_ID = uuid.UUID("00000000-0000-0000-0000-000000000001") SYSTEM_ACCOUNT_NAME = "ResolutionFlow System" SYSTEM_ACCOUNT_DISPLAY_CODE = "RF-SYS-1" async def _ensure_system_account(db: AsyncSession) -> uuid.UUID: """Get or create the ResolutionFlow system account. Returns its ID.""" from app.models.account import Account from sqlalchemy import text result = await db.execute( select(Account).where(Account.display_code == SYSTEM_ACCOUNT_DISPLAY_CODE) ) account = result.scalar_one_or_none() if account is not None: return account.id new_account = Account( id=uuid.uuid4(), name=SYSTEM_ACCOUNT_NAME, display_code=SYSTEM_ACCOUNT_DISPLAY_CODE, ) db.add(new_account) await db.flush() logger.info(f"[service_account] Created system account (id={new_account.id})") return new_account.id async def ensure_service_account(db: AsyncSession) -> uuid.UUID: """Ensure the ResolutionFlow service account exists and return its ID. Idempotent — safe to call on every startup. This lookup must bypass RLS because startup runs before any request-scoped tenant context exists and the users table is tenant-isolated in Phase 4. The service account is normally created by Alembic migration 1490781700bc; the runtime create path remains as a self-healing fallback for environments that predate that seed. """ _ = db # Retained for call-site compatibility in app lifespan startup. from app.models.user import User async with _admin_session_factory() as admin_db: result = await admin_db.execute( select(User).where(User.email == SERVICE_ACCOUNT_EMAIL) ) user = result.scalar_one_or_none() if user is not None: if not user.is_service_account: user.is_service_account = True await admin_db.commit() return user.id account_id = await _ensure_system_account(admin_db) new_user = User( id=uuid.uuid4(), email=SERVICE_ACCOUNT_EMAIL, name=SERVICE_ACCOUNT_NAME, password_hash="!service-account-no-login", # bcrypt can't produce this prefix role="engineer", is_super_admin=False, is_team_admin=False, is_active=True, is_service_account=True, must_change_password=False, account_id=account_id, account_role="engineer", ) admin_db.add(new_user) await admin_db.commit() logger.info(f"[service_account] Created service account (id={new_user.id})") return new_user.id