179 lines
5.2 KiB
Python
179 lines
5.2 KiB
Python
# -*- 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."
|
||
)
|