from __future__ import annotations from datetime import datetime, timezone from sqlalchemy.orm import Session from app.modules.core.iam.models import User from app.modules.core.iam.scope import UserScope, ensure_manageable_existing_user class LifecycleError(Exception): pass def utcnow_naive() -> datetime: return datetime.now(timezone.utc).replace(tzinfo=None) def ensure_not_self(actor: User, target: User, action_label: str) -> None: if actor.id == target.id: raise LifecycleError(f"You cannot {action_label} your own account.") def ensure_not_bootstrap_admin(target: User) -> None: if (target.email or '').strip().lower() == 'admin@auditfirm.local': raise LifecycleError('Bootstrap system admin cannot be modified by this action.') def ensure_manageable_lifecycle(scope: UserScope, db: Session, actor: User, target: User, action_label: str) -> None: ensure_manageable_existing_user(db, scope, target) ensure_not_self(actor, target, action_label) def activate_user(user: User) -> None: user.is_active = True if user.deleted_at is not None: user.deleted_at = None def deactivate_user(user: User) -> None: user.is_active = False def enable_login(user: User) -> None: user.allow_login = True def disable_login(user: User) -> None: user.allow_login = False def lock_user(user: User) -> None: user.is_locked = True user.locked_at_utc = utcnow_naive() def unlock_user(user: User) -> None: user.is_locked = False user.locked_at_utc = None def soft_delete_user(user: User) -> None: user.deleted_at = utcnow_naive() user.is_active = False user.allow_login = False user.is_locked = True if user.locked_at_utc is None: user.locked_at_utc = utcnow_naive() def restore_user(user: User) -> None: user.deleted_at = None user.is_active = True user.allow_login = True user.is_locked = False user.locked_at_utc = None