# -*- coding: utf-8 -*- """ AZA Desktop – Windows-Firewall-Handling. Minimale lokale Regel fuer Backend auf 127.0.0.1:8000. """ import os import sys import subprocess _RULE_NAME = "AZA Desktop - Lokale Kommunikation" _BACKEND_PORT = 8000 def add_firewall_rule_if_needed() -> tuple[bool, str]: """ Legt eine minimale Windows-Firewall-Regel fuer das lokale AZA-Backend an. Nur fuer aza_desktop.exe, TCP 8000, Remote 127.0.0.1 (localhost). Gibt (erfolg, meldung) zurueck. """ if sys.platform != "win32": return True, "" exe_path = "" if getattr(sys, "frozen", False): exe_path = sys.executable else: # Dev: Basis-Pfad ermitteln base = os.path.dirname(os.path.abspath(__file__)) for _ in range(5): candidate = os.path.join(base, "aza_desktop.exe") if os.path.isfile(candidate): exe_path = candidate break parent = os.path.dirname(base) if parent == base: break base = parent if not exe_path or not os.path.isfile(exe_path): return False, "aza_desktop.exe nicht gefunden" exe_path = os.path.normpath(exe_path) # Regel loeschen falls vorhanden (idempotent) try: subprocess.run( [ "netsh", "advfirewall", "firewall", "delete", "rule", f"name={_RULE_NAME}", ], capture_output=True, timeout=10, creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0, ) except Exception: pass # Regel anlegen try: result = subprocess.run( [ "netsh", "advfirewall", "firewall", "add", "rule", f"name={_RULE_NAME}", "dir=in", "action=allow", f"program={exe_path}", f"localport={_BACKEND_PORT}", "protocol=tcp", "remoteip=127.0.0.1", ], capture_output=True, text=True, timeout=10, creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0, ) if result.returncode == 0: return True, "Firewall-Regel angelegt" err = (result.stderr or result.stdout or "").strip() return False, err or f"Rueckcode {result.returncode}" except subprocess.TimeoutExpired: return False, "Zeitueberschreitung" except Exception as e: return False, str(e) def check_firewall_rule() -> dict: """ Prueft ohne Adminrechte, ob die erwartete Firewall-Regel existiert. Gibt dict mit keys: exists, program_ok, port_ok, protocol_ok, remote_ok, detail """ result = { "exists": False, "program_ok": None, "port_ok": None, "protocol_ok": None, "remote_ok": None, "detail": "", } if sys.platform != "win32": result["detail"] = "Nur unter Windows relevant" return result try: proc = subprocess.run( [ "netsh", "advfirewall", "firewall", "show", "rule", f"name={_RULE_NAME}", "verbose", ], capture_output=True, text=True, timeout=10, creationflags=( subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0 ), ) except Exception as e: result["detail"] = f"Pruefung nicht moeglich: {e}" return result stdout = proc.stdout or "" if proc.returncode != 0 or _RULE_NAME not in stdout: result["detail"] = "Regel nicht vorhanden" return result result["exists"] = True lower = stdout.lower() if "tcp" in lower: result["protocol_ok"] = True else: result["protocol_ok"] = False if str(_BACKEND_PORT) in stdout: result["port_ok"] = True else: result["port_ok"] = False if "127.0.0.1" in stdout or "localsubnet" in lower: result["remote_ok"] = True else: result["remote_ok"] = False if "aza_desktop" in lower: result["program_ok"] = True else: result["program_ok"] = False problems = [] if not result["protocol_ok"]: problems.append("Protokoll") if not result["port_ok"]: problems.append("Port") if not result["remote_ok"]: problems.append("Remote-IP") if not result["program_ok"]: problems.append("Programm") if problems: result["detail"] = "Regel vorhanden, Details abweichend: " + ", ".join(problems) else: result["detail"] = "Regel korrekt (TCP 8000, localhost, programmgebunden)" return result def get_firewall_hint_text() -> str: """Ruiger Hinweistext fuer den Nutzer bei Firewall-Popup oder Backend-Problem.""" return ( "Windows hat eine Rueckfrage zur lokalen Netzwerkkommunikation von AZA angezeigt.\n\n" "AZA verwendet lokal laufende Komponenten auf diesem Computer. " "Die Rueckfrage betrifft die interne Kommunikation der App.\n\n" "Fuer den lokalen Betrieb von AZA koennen Sie fortfahren bzw. " "den Zugriff fuer private Netzwerke erlauben." )