"""Имя пользователя для событий подключения/аутентификации (колонка «Пользователь» в UI).""" from __future__ import annotations from typing import Any # События, где в details может быть учётная запись (SSH/RDP/sudo/logind и т.д.). ACTOR_USER_EVENT_TYPES: frozenset[str] = frozenset( { "ssh.login.success", "ssh.login.failed", "privilege.sudo.command", "session.logind.new", "session.logind.removed", "session.logind.failed", "rdp.login.success", "rdp.login.failed", "rdp.shadow.control.started", "rdp.shadow.control.stopped", "rdp.shadow.control.permission", "winrm.session.started", "smb.admin_share.access", "auth.explicit.credentials", "rdg.connection.success", "rdg.connection.disconnected", "rdg.connection.failed", } ) _SHADOW_DETAIL_KEYS = ("shadower_user", "user", "username") _DEFAULT_DETAIL_KEYS = ("user", "username") def _pick_detail_value(details: dict[str, Any], keys: tuple[str, ...]) -> str | None: for key in keys: val = details.get(key) if val is not None and str(val).strip() not in ("", "-"): return str(val).strip() return None def extract_event_actor_user(event_type: str, details: dict[str, Any] | None) -> str | None: """Вернуть имя пользователя для auth/connect событий или None (UI покажет «—»).""" if event_type not in ACTOR_USER_EVENT_TYPES: return None if not isinstance(details, dict): return None keys = _SHADOW_DETAIL_KEYS if event_type.startswith("rdp.shadow.") else _DEFAULT_DETAIL_KEYS return _pick_detail_value(details, keys)