import json import os import hashlib import secrets from typing import Any from app.utils.platform_utils import get_config_dir _CONFIG_FILE = "config.json" _DEFAULT: dict[str, Any] = { "version": "0.1.0", "admin_password_hash": "", "admin_salt": "", "mfa_enabled": False, "mfa_secret": "", "wg": { "interface_name": "wgs0", "server_endpoint": "", "server_port": 51820, "server_public_key": "", "client_private_key": "", "client_public_key": "", "client_address": "10.8.0.2/24", "dns": "1.1.1.1", "allowed_ips": "0.0.0.0/0", "keepalive": 25, }, "ui": { "minimize_to_tray": True, "autostart": False, "theme": "auto", }, } class Config: def __init__(self): self._path = os.path.join(get_config_dir(), _CONFIG_FILE) self._data: dict[str, Any] = {} self.load() def load(self): if os.path.exists(self._path): try: with open(self._path, "r", encoding="utf-8") as f: saved = json.load(f) self._data = self._merge(_DEFAULT, saved) except Exception: self._data = dict(_DEFAULT) else: self._data = dict(_DEFAULT) def save(self): os.makedirs(os.path.dirname(self._path), exist_ok=True) with open(self._path, "w", encoding="utf-8") as f: json.dump(self._data, f, indent=2, ensure_ascii=False) def _merge(self, base: dict, override: dict) -> dict: result = dict(base) for k, v in override.items(): if k in result and isinstance(result[k], dict) and isinstance(v, dict): result[k] = self._merge(result[k], v) else: result[k] = v return result def get(self, *keys: str, default=None) -> Any: node = self._data for k in keys: if not isinstance(node, dict) or k not in node: return default node = node[k] return node def set(self, *keys_and_value) -> None: *keys, value = keys_and_value node = self._data for k in keys[:-1]: node = node.setdefault(k, {}) node[keys[-1]] = value # -- Admin password -- def set_admin_password(self, password: str): if not password: # Mot de passe vide = suppression de la protection self._data["admin_salt"] = "" self._data["admin_password_hash"] = "" else: salt = secrets.token_hex(16) hashed = hashlib.sha256((salt + password).encode()).hexdigest() self._data["admin_salt"] = salt self._data["admin_password_hash"] = hashed self.save() def check_admin_password(self, password: str) -> bool: salt = self._data.get("admin_salt", "") stored = self._data.get("admin_password_hash", "") if not stored: return True # Pas encore de mot de passe configuré candidate = hashlib.sha256((salt + password).encode()).hexdigest() return secrets.compare_digest(candidate, stored) def has_admin_password(self) -> bool: return bool(self._data.get("admin_password_hash", "")) # -- Propriétés WireGuard -- @property def wg(self) -> dict: return self._data["wg"] @property def mfa_enabled(self) -> bool: return bool(self._data.get("mfa_enabled", False)) @mfa_enabled.setter def mfa_enabled(self, value: bool): self._data["mfa_enabled"] = value @property def mfa_secret(self) -> str: return self._data.get("mfa_secret", "") @mfa_secret.setter def mfa_secret(self, value: str): self._data["mfa_secret"] = value @property def configured(self) -> bool: wg = self._data["wg"] return bool(wg.get("server_endpoint") and wg.get("client_private_key"))