"""Effective Windows domain admin credentials (host → DB → env).""" from __future__ import annotations from dataclasses import dataclass from sqlalchemy.orm import Session from app.config import get_settings from app.models.host import Host from app.models.ui_settings import UI_SETTINGS_ROW_ID, UiSettings @dataclass(frozen=True) class WinAdminConfig: user: str password: str source: str # host | env | db @property def configured(self) -> bool: return bool(self.user.strip() and self.password.strip()) def normalize_win_admin_user(user: str) -> str: """DOMAIN\\user — один обратный слэш; убрать лишнее экранирование из env/UI.""" text = user.strip() if not text: return "" text = text.replace("\\\\", "\\") if "\\" not in text and "@" not in text and "/" not in text: # NETBIOS\user без слэша — не трогаем (оператор допишет в UI) return text return text def _win_admin_from_env() -> WinAdminConfig: settings = get_settings() return WinAdminConfig( user=normalize_win_admin_user(settings.sac_win_admin_user), password=settings.sac_win_admin_password, source="env", ) def get_effective_win_admin_config(db: Session | None = None) -> WinAdminConfig: if db is None: from app.database import SessionLocal session = SessionLocal() try: return get_effective_win_admin_config(session) finally: session.close() row = db.get(UiSettings, UI_SETTINGS_ROW_ID) env_cfg = _win_admin_from_env() if row is None: return env_cfg user = normalize_win_admin_user((row.win_admin_user or "").strip() or env_cfg.user) password = (row.win_admin_password or "") or env_cfg.password has_db_values = bool((row.win_admin_user or "").strip() or (row.win_admin_password or "").strip()) return WinAdminConfig( user=user, password=password, source="db" if has_db_values else env_cfg.source, ) def upsert_win_admin_settings( db: Session, *, user: str | None = None, password: str | None = None, ) -> WinAdminConfig: row = db.get(UiSettings, UI_SETTINGS_ROW_ID) env_cfg = _win_admin_from_env() if row is None: row = UiSettings( id=UI_SETTINGS_ROW_ID, show_sidebar_system_stats=True, win_admin_user=env_cfg.user or None, win_admin_password=env_cfg.password or None, ) db.add(row) if user is not None and user.strip(): row.win_admin_user = normalize_win_admin_user(user) if password is not None and password.strip(): row.win_admin_password = password db.commit() db.refresh(row) return get_effective_win_admin_config(db) def get_effective_win_admin_for_host(db: Session, host: Host) -> WinAdminConfig: """Prefer per-host mgmt_* when both user and password are set; else global Settings/env.""" host_user = normalize_win_admin_user((host.mgmt_user or "").strip()) host_password = (host.mgmt_password or "").strip() if host_user and host_password: return WinAdminConfig(user=host_user, password=host_password, source="host") global_cfg = get_effective_win_admin_config(db) # Allow host to override only the username, still using global password (rare). if host_user and global_cfg.password.strip(): return WinAdminConfig(user=host_user, password=global_cfg.password, source="host") return global_cfg