#!/usr/bin/env python3 """ Vollständiges Beispiel für die Integration des Lizenzservers in eine Python-Anwendung """ import requests import hashlib import platform import uuid import json import os import time import threading from datetime import datetime, timedelta from pathlib import Path class LicenseManager: def __init__(self, api_key, app_version="1.0.0"): self.api_key = api_key self.app_version = app_version self.server_url = "https://api-software-undso.z5m7q9dk3ah2v1plx6ju.com" self.headers = {"Authorization": f"Bearer {api_key}"} # Cache-Verzeichnis self.cache_dir = Path.home() / ".myapp" / "license" self.cache_dir.mkdir(parents=True, exist_ok=True) self.cache_file = self.cache_dir / "license.json" # Lizenz-Status self.license_key = None self.activation_id = None self.is_valid = False self.expires_at = None # Heartbeat Thread self.heartbeat_thread = None self.stop_heartbeat = False def get_machine_id(self): """Eindeutige Maschinen-ID basierend auf MAC-Adresse""" mac = uuid.getnode() return f"MAC-{mac:012X}" def get_hardware_hash(self): """Hardware-Fingerprint aus verschiedenen Systeminfos""" components = [ self.get_machine_id(), platform.processor(), platform.system(), platform.machine(), platform.node() ] combined = "-".join(components) return hashlib.sha256(combined.encode()).hexdigest() def save_license_cache(self): """Lizenzinfo lokal speichern für Offline-Betrieb""" cache_data = { "license_key": self.license_key, "activation_id": self.activation_id, "expires_at": self.expires_at.isoformat() if self.expires_at else None, "hardware_hash": self.get_hardware_hash(), "last_verified": datetime.now().isoformat() } with open(self.cache_file, 'w') as f: json.dump(cache_data, f) def load_license_cache(self): """Gespeicherte Lizenz laden""" if not self.cache_file.exists(): return None try: with open(self.cache_file, 'r') as f: return json.load(f) except: return None def activate_license(self, license_key): """Neue Lizenz aktivieren""" data = { "license_key": license_key, "machine_id": self.get_machine_id(), "hardware_hash": self.get_hardware_hash(), "os_info": { "os": platform.system(), "version": platform.version(), "release": platform.release(), "machine": platform.machine() }, "app_version": self.app_version } try: response = requests.post( f"{self.server_url}/api/license/activate", headers=self.headers, json=data, timeout=10, verify=True ) if response.status_code == 200: result = response.json() if result.get("success"): self.license_key = license_key self.activation_id = result.get("activation_id") self.is_valid = True if result.get("expires_at"): self.expires_at = datetime.fromisoformat( result["expires_at"].replace("Z", "+00:00") ) self.save_license_cache() self.start_heartbeat() return True, result.get("message", "Lizenz aktiviert") else: return False, result.get("message", "Aktivierung fehlgeschlagen") else: return False, f"Server-Fehler: {response.status_code}" except requests.exceptions.RequestException as e: return False, f"Verbindungsfehler: {str(e)}" def verify_license(self): """Lizenz verifizieren (Heartbeat)""" if not self.license_key or not self.activation_id: return False, "Keine aktive Lizenz" data = { "license_key": self.license_key, "machine_id": self.get_machine_id(), "hardware_hash": self.get_hardware_hash(), "activation_id": self.activation_id } try: response = requests.post( f"{self.server_url}/api/license/verify", headers=self.headers, json=data, timeout=10, verify=True ) if response.status_code == 200: result = response.json() self.is_valid = result.get("valid", False) if self.is_valid: self.save_license_cache() # Update-Check if result.get("requires_update"): print(f"Update verfügbar: {result.get('update_url')}") return self.is_valid, result.get("message", "") else: return False, f"Server-Fehler: {response.status_code}" except requests.exceptions.RequestException: # Offline-Modus: Cache prüfen return self.verify_offline() def verify_offline(self): """Offline-Verifizierung mit Grace Period""" cache = self.load_license_cache() if not cache: return False, "Keine gecachte Lizenz vorhanden" # Hardware-Hash prüfen if cache.get("hardware_hash") != self.get_hardware_hash(): # Grace Period bei Hardware-Änderung last_verified = datetime.fromisoformat(cache.get("last_verified")) grace_period = timedelta(days=7) if datetime.now() - last_verified > grace_period: return False, "Hardware geändert - Grace Period abgelaufen" # Ablaufdatum prüfen if cache.get("expires_at"): expires_at = datetime.fromisoformat(cache.get("expires_at")) if datetime.now() > expires_at: return False, "Lizenz abgelaufen" self.license_key = cache.get("license_key") self.activation_id = cache.get("activation_id") self.is_valid = True return True, "Offline-Modus (gecachte Lizenz)" def check_for_updates(self): """Nach Updates suchen""" if not self.license_key: return None data = { "current_version": self.app_version, "license_key": self.license_key } try: response = requests.post( f"{self.server_url}/api/version/check", headers=self.headers, json=data, timeout=10, verify=True ) if response.status_code == 200: return response.json() except: pass return None def heartbeat_worker(self): """Background-Thread für regelmäßige Lizenzprüfung""" while not self.stop_heartbeat: time.sleep(900) # 15 Minuten if self.stop_heartbeat: break valid, message = self.verify_license() if not valid: print(f"Lizenz-Warnung: {message}") # Hier könnte die App reagieren (z.B. Features deaktivieren) def start_heartbeat(self): """Heartbeat-Thread starten""" if self.heartbeat_thread and self.heartbeat_thread.is_alive(): return self.stop_heartbeat = False self.heartbeat_thread = threading.Thread( target=self.heartbeat_worker, daemon=True ) self.heartbeat_thread.start() def stop_heartbeat_thread(self): """Heartbeat-Thread beenden""" self.stop_heartbeat = True if self.heartbeat_thread: self.heartbeat_thread.join(timeout=1) def get_license_info(self): """Aktuelle Lizenzinformationen""" return { "valid": self.is_valid, "license_key": self.license_key[:4] + "****" if self.license_key else None, "expires_at": self.expires_at.isoformat() if self.expires_at else None, "machine_id": self.get_machine_id() } # Beispiel-Anwendung def main(): # API-Key sollte sicher gespeichert werden (z.B. verschlüsselt) API_KEY = os.environ.get("LICENSE_API_KEY", "your-api-key-here") # License Manager initialisieren license_mgr = LicenseManager(API_KEY, app_version="1.0.0") # Versuche gecachte Lizenz zu laden cache = license_mgr.load_license_cache() if cache: print("Gecachte Lizenz gefunden, verifiziere...") valid, message = license_mgr.verify_license() if valid: print(f"✓ Lizenz gültig: {message}") else: print(f"✗ Lizenz ungültig: {message}") # Neue Lizenz erforderlich license_key = input("Bitte Lizenzschlüssel eingeben: ") success, message = license_mgr.activate_license(license_key) if success: print(f"✓ {message}") else: print(f"✗ {message}") return else: # Erste Aktivierung print("Keine Lizenz gefunden.") license_key = input("Bitte Lizenzschlüssel eingeben: ") success, message = license_mgr.activate_license(license_key) if success: print(f"✓ {message}") else: print(f"✗ {message}") return # Update-Check print("\nPrüfe auf Updates...") update_info = license_mgr.check_for_updates() if update_info and update_info.get("update_available"): print(f"Update verfügbar: {update_info.get('latest_version')}") if update_info.get("is_mandatory"): print("⚠️ Dies ist ein Pflicht-Update!") # Lizenzinfo anzeigen info = license_mgr.get_license_info() print(f"\nLizenz-Status:") print(f"- Gültig: {info['valid']}") print(f"- Ablauf: {info['expires_at']}") print(f"- Maschine: {info['machine_id']}") # App läuft... print("\n✓ Anwendung gestartet mit gültiger Lizenz") try: # Simuliere App-Laufzeit while True: time.sleep(1) except KeyboardInterrupt: print("\nBeende Anwendung...") license_mgr.stop_heartbeat_thread() if __name__ == "__main__": main()