feat(customers): add initial admin bootstrap and csv import
This commit is contained in:
parent
c816e9869d
commit
ecab0fe6b6
27 changed files with 1197 additions and 34 deletions
82
backend/hermes/app/services/initial_admin_bootstrap.py
Normal file
82
backend/hermes/app/services/initial_admin_bootstrap.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
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})
|
||||
Loading…
Add table
Add a link
Reference in a new issue