133 lines
3.9 KiB
Python
133 lines
3.9 KiB
Python
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"))
|