Session-Caching, Domain-/Gruppen-Skripte; KeePass-DB aus Repo entfernen

- inwx_common.py: Login mit Session-Cache, KP_PW-Option
- inwx_list/add: Domain als Argument, gemeinsamer Helfer
- inwx_domains(_owner).py: Domains alphabetisch, NS, Gruppen via citeq-TXT (DNS)
- hosting.kdbx aus Tracking genommen und in .gitignore

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 15:57:10 +02:00
parent d8e051d4b3
commit e31bdeb59d
8 changed files with 556 additions and 114 deletions
+159
View File
@@ -0,0 +1,159 @@
#!/usr/bin/env python3
"""Gemeinsame Helfer für die INWX-Skripte.
Kapselt das Laden der Zugangsdaten aus KeePassXC sowie den Login mit
Session-Caching: Nach dem ersten Login wird das Session-Cookie von INWX
lokal zwischengespeichert. Folgeaufrufe nutzen diese Session wieder und
benötigen daher weder das KeePass-Master-Passwort noch einen neuen Login,
solange die Session bei INWX noch gültig ist.
"""
import json
import os
import subprocess
import sys
import requests
# Import-Fix für die Library-Struktur (Linux/Mac)
try:
from INWX.Domrobot import ApiClient
except ImportError:
from inwx.Domrobot import ApiClient
# --- KONFIGURATION ---
API_URL = 'https://api.domrobot.com'
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
KP_DB_PATH = os.path.join(BASE_DIR, "hosting.kdbx")
KP_ENTRY_NAME = "inwx"
# Datei mit der zwischengespeicherten Session (enthält nur ein kurzlebiges
# Session-Cookie, niemals das Passwort).
SESSION_CACHE_PATH = os.path.join(BASE_DIR, ".inwx_session")
def get_creds():
"""Holt Benutzername und Passwort aus KeePassXC.
Das KeePass-Master-Passwort wird interaktiv abgefragt. Ist die
Umgebungsvariable KP_PW gesetzt, wird deren Wert stattdessen automatisch
an keepassxc-cli übergeben (praktisch in nicht-interaktiven Umgebungen).
"""
try:
# -s sorgt dafür, dass das Passwort nicht als 'PROTECTED' ausgegeben wird
cmd = ["keepassxc-cli", "show", "-s", KP_DB_PATH, KP_ENTRY_NAME]
master_pw = os.environ.get("KP_PW")
if master_pw:
# Master-Passwort über stdin an keepassxc-cli durchreichen.
result = subprocess.run(
cmd, input=master_pw + "\n",
capture_output=True, text=True,
)
if result.returncode != 0:
print(f"❌ KeePass-Fehler: {result.stderr.strip()}")
return None, None
output = result.stdout
else:
# Kein KP_PW gesetzt -> keepassxc-cli fragt interaktiv nach.
output = subprocess.check_output(cmd, text=True)
creds = {}
for line in output.splitlines():
if ":" in line:
k, v = line.split(":", 1)
creds[k.strip().lower()] = v.strip()
user = creds.get("username") or creds.get("benutzername") or creds.get("user")
password = creds.get("password") or creds.get("passwort")
return user, password
except Exception as e:
print(f"❌ KeePass-Fehler: {e}")
return None, None
def _load_cached_cookies(api):
"""Lädt gespeicherte Session-Cookies in den ApiClient. True bei Erfolg."""
if not os.path.exists(SESSION_CACHE_PATH):
return False
try:
with open(SESSION_CACHE_PATH, "r") as f:
cookies = json.load(f)
if not cookies:
return False
api.api_session.cookies = requests.utils.cookiejar_from_dict(cookies)
return True
except Exception:
return False
def _save_cached_cookies(api):
"""Speichert die Session-Cookies (Datei nur für den Benutzer lesbar, 0600)."""
try:
cookies = requests.utils.dict_from_cookiejar(api.api_session.cookies)
fd = os.open(SESSION_CACHE_PATH, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, "w") as f:
json.dump(cookies, f)
except Exception as e:
print(f"⚠️ Konnte Session nicht zwischenspeichern: {e}")
def _clear_cached_session():
"""Entfernt eine (abgelaufene) zwischengespeicherte Session."""
try:
os.remove(SESSION_CACHE_PATH)
except FileNotFoundError:
pass
except Exception:
pass
def _do_login(api):
"""Frischer Login via KeePass-Zugangsdaten; speichert die Session. True bei Erfolg."""
user, password = get_creds()
if not user or not password:
return False
print(f"Versuche Login für: {user}")
login_res = api.login(user, password)
if login_res['code'] != 1000:
print(f"❌ Login fehlgeschlagen: {login_res['msg']} (Code: {login_res['code']})")
return False
_save_cached_cookies(api)
print("✅ Login erfolgreich (Session zwischengespeichert).")
return True
def get_client():
"""Liefert einen einsatzbereiten ApiClient.
Nutzt eine zwischengespeicherte Session, falls vorhanden. Ob diese noch
gültig ist, wird erst beim ersten echten Aufruf via call_api() geprüft
so sparen wir uns eine zusätzliche Anfrage. Ist keine Session vorhanden,
wird sofort via KeePass eingeloggt.
"""
api = ApiClient(api_url=API_URL)
if _load_cached_cookies(api):
print("️ Verwende zwischengespeicherte Session.")
return api
if not _do_login(api):
sys.exit(1)
return api
def call_api(api, method, params=None):
"""Wie ApiClient.call_api, meldet sich aber bei abgelaufener Session neu an.
Liefert INWX einen Autorisierungsfehler (Code 22002299), wird die
zwischengespeicherte Session verworfen, via KeePass neu eingeloggt und
der Aufruf einmal wiederholt.
"""
params = params or {}
res = api.call_api(method, params)
code = res.get('code')
if code is not None and 2200 <= code < 2300:
print("️ Session abgelaufen, melde neu an...")
_clear_cached_session()
if not _do_login(api):
sys.exit(1)
res = api.call_api(method, params)
return res