import logging from pydantic import EmailStr, TypeAdapter, ValidationError from sqlalchemy import select from sqlalchemy.orm import Session from app.core.config import settings from app.core.security import hash_password from app.models.user import User from app.repositories.rbac_repository import RbacRepository from app.services.audit_service import write_audit_log logger = logging.getLogger(__name__) email_adapter = TypeAdapter(EmailStr) def bootstrap_initial_admin(db: Session) -> None: active_user_exists = db.scalar(select(User.id).where(User.is_active.is_(True)).limit(1)) if active_user_exists is not None: logger.info("initial_admin.skipped_active_user_exists") return username = (settings.initial_admin_username or "").strip() email = (settings.initial_admin_email or "").strip() password = settings.initial_admin_password or "" if not username or not email or not password: logger.warning( "initial_admin.not_configured", extra={"reason": "missing_required_initial_admin_environment"}, ) return try: normalized_email = str(email_adapter.validate_python(email)) except ValidationError: logger.warning("initial_admin.invalid_email") return administrator_role = RbacRepository.get_role_by_name(db, "administrator") if administrator_role is None: logger.warning("initial_admin.missing_administrator_role") return existing_user = db.scalar(select(User).where((User.username == username) | (User.email == normalized_email))) if existing_user is not None: logger.info("initial_admin.skipped_user_already_exists") return admin = User( first_name=settings.initial_admin_first_name.strip(), last_name=settings.initial_admin_last_name.strip(), username=username, email=normalized_email, password_hash=hash_password(password), role="administrator", role_id=administrator_role.id, is_active=True, ) db.add(admin) db.commit() db.refresh(admin) write_audit_log( db, action="users.initial_admin_bootstrap", entity_type="users", entity_id=admin.id, entity_label=admin.username, actor_username="system", after_data={ "id": admin.id, "username": admin.username, "email": admin.email, "role": admin.role, "role_id": admin.role_id, "is_active": admin.is_active, }, metadata={"source": "initial_admin_bootstrap"}, ) logger.info("initial_admin.created", extra={"user_id": admin.id})