""" Lizenzverwaltungsfunktionalität für den Social Media Account Generator. """ import os import json import time import uuid import hmac import hashlib import logging import requests from datetime import datetime, timedelta from typing import Dict, List, Any, Optional, Tuple, Union logger = logging.getLogger("license_manager") class LicenseManager: """Klasse zur Verwaltung von Softwarelizenzen.""" CONFIG_FILE = os.path.join("config", "license.json") LICENSE_SERVER_URL = "https://api.example.com/license" # Platzhalter - in der Produktion anpassen def __init__(self): """Initialisiert den LicenseManager und lädt die Konfiguration.""" self.license_data = self.load_license_data() self.machine_id = self.get_machine_id() # Stelle sicher, dass das Konfigurationsverzeichnis existiert os.makedirs(os.path.dirname(self.CONFIG_FILE), exist_ok=True) # Prüfe die Lizenz beim Start self.verify_license() def load_license_data(self) -> Dict[str, Any]: """Lädt die Lizenzdaten aus der Konfigurationsdatei.""" if not os.path.exists(self.CONFIG_FILE): return { "key": "", "activation_date": "", "expiry_date": "", "status": "inactive", "status_text": "Keine Lizenz aktiviert", "features": [], "last_online_check": "", "signature": "" } try: with open(self.CONFIG_FILE, "r", encoding="utf-8") as f: license_data = json.load(f) logger.info(f"Lizenzdaten geladen: Status '{license_data.get('status', 'unbekannt')}'") return license_data except Exception as e: logger.error(f"Fehler beim Laden der Lizenzdaten: {e}") return { "key": "", "activation_date": "", "expiry_date": "", "status": "inactive", "status_text": "Fehler beim Laden der Lizenz", "features": [], "last_online_check": "", "signature": "" } def save_license_data(self) -> bool: """Speichert die Lizenzdaten in die Konfigurationsdatei.""" try: with open(self.CONFIG_FILE, "w", encoding="utf-8") as f: json.dump(self.license_data, f, indent=2) logger.info("Lizenzdaten gespeichert") return True except Exception as e: logger.error(f"Fehler beim Speichern der Lizenzdaten: {e}") return False def get_license_info(self) -> Dict[str, Any]: """Gibt die aktuellen Lizenzdaten zurück.""" return self.license_data def get_machine_id(self) -> str: """ Generiert eine eindeutige Maschinen-ID. Returns: Eindeutige Maschinen-ID """ try: # Versuche, eine eindeutige Hardware-ID zu generieren # In der Produktion sollte dies mit einer robusteren Methode ersetzt werden machine_id_file = os.path.join("config", ".machine_id") if os.path.exists(machine_id_file): # Bestehende ID laden with open(machine_id_file, "r") as f: return f.read().strip() else: # Neue ID generieren machine_id = str(uuid.uuid4()) with open(machine_id_file, "w") as f: f.write(machine_id) return machine_id except Exception as e: logger.error(f"Fehler bei der Generierung der Maschinen-ID: {e}") # Fallback: UUID auf Basis der aktuellen Zeit return str(uuid.uuid5(uuid.NAMESPACE_DNS, f"fallback-{time.time()}")) def is_licensed(self) -> bool: """ Überprüft, ob eine gültige Lizenz vorhanden ist. Returns: True, wenn eine gültige Lizenz vorhanden ist, sonst False """ # Prüfe den Status der Lizenz if self.license_data["status"] not in ["active", "trial"]: return False # Prüfe, ob die Lizenz abgelaufen ist if self.license_data["expiry_date"]: try: expiry_date = datetime.fromisoformat(self.license_data["expiry_date"]) if datetime.now() > expiry_date: logger.warning("Lizenz ist abgelaufen") self.license_data["status"] = "expired" self.license_data["status_text"] = "Lizenz abgelaufen" self.save_license_data() return False except Exception as e: logger.error(f"Fehler beim Parsen des Ablaufdatums: {e}") return False # Prüfe, ob regelmäßige Online-Verifizierung erforderlich ist if self.license_data["last_online_check"]: try: last_check = datetime.fromisoformat(self.license_data["last_online_check"]) max_offline_days = 7 # Maximale Tage ohne Online-Check if datetime.now() > last_check + timedelta(days=max_offline_days): logger.warning(f"Letzte Online-Überprüfung ist mehr als {max_offline_days} Tage her") # Versuche, eine Online-Überprüfung durchzuführen if not self.online_verification(): self.license_data["status"] = "verification_required" self.license_data["status_text"] = "Online-Überprüfung erforderlich" self.save_license_data() return False except Exception as e: logger.error(f"Fehler bei der Überprüfung der Online-Verifizierung: {e}") # Prüfe die Signatur (in der Produktion sollte dies erweitert werden) if not self.verify_signature(): logger.warning("Ungültige Lizenzsignatur") self.license_data["status"] = "invalid" self.license_data["status_text"] = "Ungültige Lizenz (manipuliert)" self.save_license_data() return False return True def verify_license(self) -> bool: """ Überprüft die aktuelle Lizenz. Returns: True, wenn die Lizenz gültig ist, sonst False """ # Lizenzschlüssel vorhanden? if not self.license_data["key"]: logger.info("Kein Lizenzschlüssel vorhanden") self.license_data["status"] = "inactive" self.license_data["status_text"] = "Keine Lizenz aktiviert" self.save_license_data() return False return self.is_licensed() def create_signature(self, data: str) -> str: """ Erstellt eine Signatur für die angegebenen Daten. Args: data: Zu signierende Daten Returns: Signatur als Hexadezimalstring """ # In der Produktion sollte ein sicherer Schlüssel verwendet werden secret_key = "development_secret_key" # HMAC-SHA256-Signatur erstellen signature = hmac.new( secret_key.encode(), data.encode(), hashlib.sha256 ).hexdigest() return signature def verify_signature(self) -> bool: """ Überprüft die Signatur der Lizenzdaten. Returns: True, wenn die Signatur gültig ist, sonst False """ if not self.license_data["signature"]: return False # Daten für die Signaturprüfung vorbereiten data_to_verify = f"{self.license_data['key']}|{self.machine_id}|{self.license_data['activation_date']}|{self.license_data['expiry_date']}" # Signatur erstellen computed_signature = self.create_signature(data_to_verify) # Signatur vergleichen return computed_signature == self.license_data["signature"] def online_verification(self) -> bool: """ Führt eine Online-Überprüfung der Lizenz durch. Returns: True, wenn die Überprüfung erfolgreich war, sonst False """ if not self.license_data["key"]: return False try: # Daten für die Lizenzüberprüfung verification_data = { "license_key": self.license_data["key"], "machine_id": self.machine_id, "product_version": "1.0.0", # In der Produktion aus einer Konfiguration laden "timestamp": time.time() } # Anfrage an den Lizenzserver senden response = requests.post( self.LICENSE_SERVER_URL + "/verify", json=verification_data, timeout=10 ) if response.status_code == 200: result = response.json() if result.get("status") == "active": # Lizenz ist gültig logger.info("Online-Lizenzüberprüfung erfolgreich") # Aktualisiere das Datum der letzten Überprüfung self.license_data["last_online_check"] = datetime.now().isoformat() self.save_license_data() return True else: # Lizenz ist ungültig logger.warning(f"Lizenz ungültig: {result.get('message', 'Unbekannter Fehler')}") self.license_data["status"] = result.get("status", "invalid") self.license_data["status_text"] = result.get("message", "Lizenz ungültig") self.save_license_data() return False else: logger.warning(f"Fehler bei der Online-Überprüfung: HTTP {response.status_code}") return False except requests.RequestException as e: logger.error(f"Netzwerkfehler bei der Online-Überprüfung: {e}") # Bei Verbindungsproblemen sollte die lokale Lizenz weiterhin gültig bleiben # In der Produktion kann hier eine Begrenzung der Offline-Zeit implementiert werden return True except Exception as e: logger.error(f"Unerwarteter Fehler bei der Online-Überprüfung: {e}") return False def activate_license(self, license_key: str) -> Tuple[bool, str]: """ Aktiviert eine Lizenz mit dem angegebenen Schlüssel. Args: license_key: Zu aktivierender Lizenzschlüssel Returns: (Erfolg, Nachricht) """ if not license_key: return False, "Bitte geben Sie einen Lizenzschlüssel ein." try: # In der Produktionsumgebung sollte hier eine Online-Aktivierung erfolgen # Für Entwicklungszwecke implementieren wir eine einfache lokale Aktivierung # Simulierte Online-Aktivierung activation_data = { "license_key": license_key, "machine_id": self.machine_id, "product_version": "1.0.0", "timestamp": time.time() } # Nur für Entwicklung: Prüfe, ob der Lizenzschlüssel bekannt ist if license_key.startswith("DEV-"): # Entwicklungslizenzen haben unbegrenzte Laufzeit expiry_date = (datetime.now() + timedelta(days=365)).isoformat() activation_response = { "status": "active", "message": "Entwicklungslizenz aktiviert", "activation_date": datetime.now().isoformat(), "expiry_date": expiry_date, "features": ["all"] } elif license_key.startswith("TRIAL-"): # Trial-Lizenzen haben begrenzte Laufzeit expiry_date = (datetime.now() + timedelta(days=30)).isoformat() activation_response = { "status": "trial", "message": "Trial-Lizenz aktiviert (30 Tage)", "activation_date": datetime.now().isoformat(), "expiry_date": expiry_date, "features": ["basic"] } else: # Alle anderen Schlüssel simulieren eine Online-Aktivierung try: # Anfrage an den Lizenzserver senden response = requests.post( self.LICENSE_SERVER_URL + "/activate", json=activation_data, timeout=10 ) if response.status_code == 200: activation_response = response.json() else: logger.warning(f"Fehler bei der Lizenzaktivierung: HTTP {response.status_code}") return False, f"Fehler bei der Lizenzaktivierung: HTTP {response.status_code}" except requests.RequestException as e: logger.error(f"Netzwerkfehler bei der Lizenzaktivierung: {e}") return False, f"Netzwerkfehler bei der Lizenzaktivierung: {e}" except Exception as e: logger.error(f"Unerwarteter Fehler bei der Lizenzaktivierung: {e}") return False, f"Unerwarteter Fehler bei der Lizenzaktivierung: {e}" # Lizenzdaten aktualisieren self.license_data["key"] = license_key self.license_data["status"] = activation_response.get("status", "inactive") self.license_data["status_text"] = activation_response.get("message", "Unbekannter Status") self.license_data["activation_date"] = activation_response.get("activation_date", datetime.now().isoformat()) self.license_data["expiry_date"] = activation_response.get("expiry_date", "") self.license_data["features"] = activation_response.get("features", []) self.license_data["last_online_check"] = datetime.now().isoformat() # Signatur erstellen data_to_sign = f"{self.license_data['key']}|{self.machine_id}|{self.license_data['activation_date']}|{self.license_data['expiry_date']}" self.license_data["signature"] = self.create_signature(data_to_sign) # Lizenzdaten speichern self.save_license_data() logger.info(f"Lizenz '{license_key}' erfolgreich aktiviert: {self.license_data['status_text']}") return True, self.license_data["status_text"] except Exception as e: error_msg = f"Fehler bei der Lizenzaktivierung: {e}" logger.error(error_msg) return False, error_msg def deactivate_license(self) -> Tuple[bool, str]: """ Deaktiviert die aktuelle Lizenz. Returns: (Erfolg, Nachricht) """ if not self.license_data["key"]: return False, "Keine Lizenz aktiviert" old_key = self.license_data["key"] try: # Online-Deaktivierung simulieren deactivation_data = { "license_key": self.license_data["key"], "machine_id": self.machine_id, "timestamp": time.time() } # Anfrage für die Produktionsumgebung # response = requests.post( # self.LICENSE_SERVER_URL + "/deactivate", # json=deactivation_data, # timeout=10 # ) # Lizenzdaten zurücksetzen self.license_data = { "key": "", "activation_date": "", "expiry_date": "", "status": "inactive", "status_text": "Keine Lizenz aktiviert", "features": [], "last_online_check": "", "signature": "" } # Lizenzdaten speichern self.save_license_data() logger.info(f"Lizenz '{old_key}' erfolgreich deaktiviert") return True, "Lizenz erfolgreich deaktiviert" except Exception as e: error_msg = f"Fehler bei der Lizenzdeaktivierung: {e}" logger.error(error_msg) return False, error_msg def has_feature(self, feature_name: str) -> bool: """ Überprüft, ob die aktuelle Lizenz eine bestimmte Funktion unterstützt. Args: feature_name: Name der zu überprüfenden Funktion Returns: True, wenn die Funktion unterstützt wird, sonst False """ if not self.is_licensed(): return False # "all" bedeutet, dass alle Funktionen unterstützt werden if "all" in self.license_data["features"]: return True return feature_name in self.license_data["features"]