fix(ui): rendre la saisie du code d'accès atteignable à tout moment (v0.8.2)
La fenêtre de saisie ne s'ouvrait qu'une fois, juste après une connexion manuelle réussie. Un refus, une fenêtre fermée, une reconnexion automatique — qui n'ouvre volontairement aucune modale — ou l'expiration de l'autorisation au bout de 12 h laissaient un tunnel monté, un réseau muet et aucun élément d'interface pour ressaisir un code. Un bouton sous l'action de connexion et une entrée de systray apparaissent désormais dès qu'une saisie a un sens : identifiant VPN configuré, tunnel monté, accès non ouvert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,14 @@ Ce projet suit le [Versionnage Sémantique](https://semver.org/lang/fr/).
|
||||
|
||||
---
|
||||
|
||||
## [0.8.2] — 2026-09-06
|
||||
|
||||
### Corrigé
|
||||
- **Aucun moyen de saisir son code une fois en quarantaine.** La fenêtre de saisie ne s'ouvrait qu'une fois, dans la seconde suivant une connexion manuelle réussie. Un code refusé, une fenêtre fermée, une reconnexion automatique — qui n'ouvre volontairement aucune fenêtre modale, personne n'étant devant l'écran — ou l'expiration de l'autorisation au bout de 12 h laissaient l'utilisateur avec un tunnel monté, un réseau muet et pour seule issue une déconnexion/reconnexion. Un bouton **« Saisir le code d'accès »** apparaît désormais sous le bouton de connexion, et une entrée équivalente dans le menu du systray, dès qu'une saisie a un sens : identifiant VPN configuré, tunnel monté, accès non ouvert. Ils disparaissent le reste du temps, sans rien changer aux installations sans authentification serveur.
|
||||
- Le bouton apparaissait et disparaissait correctement, mais son calcul de hauteur se relançait à chaque cycle de rafraîchissement lorsque la fenêtre était repliée dans le systray : `isVisible()` y est faux pour tout enfant d'une fenêtre masquée, quel que soit l'état demandé. C'est `isHidden()` qui porte cette intention.
|
||||
|
||||
---
|
||||
|
||||
## [0.8.1] — 2026-09-06
|
||||
|
||||
### Sécurité
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
## ──────────────────────────────────────────────
|
||||
|
||||
APP := wgsecure
|
||||
VERSION := 0.8.1
|
||||
VERSION := 0.8.2
|
||||
VENV := .venv
|
||||
PYTHON := $(VENV)/bin/python3
|
||||
PIP := $(VENV)/bin/pip
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 🛡️ WGSecure (WGS)
|
||||
|
||||
🚀 v0.8.1 · 🐍 · 🪟🐧 · 🔐 Accès validé par le serveur · 🛡️ WireGuard
|
||||
🚀 v0.8.2 · 🐍 · 🪟🐧 · 🔐 Accès validé par le serveur · 🛡️ WireGuard
|
||||
|
||||
**WGSecure** est une interface graphique multiplateforme (Windows & Linux) pour gérer une connexion WireGuard dont l'accès aux ressources distantes est ouvert par un code à 6 chiffres validé **par le serveur**.
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
__version__ = "0.8.1"
|
||||
__version__ = "0.8.2"
|
||||
APP_NAME = "WGSecure"
|
||||
APP_SHORT = "WGS"
|
||||
|
||||
+63
-3
@@ -238,6 +238,23 @@ class MainWindow(QMainWindow):
|
||||
self._btn_connect.clicked.connect(self._on_connect)
|
||||
s.addWidget(self._btn_connect)
|
||||
|
||||
# Saisie du code d'accès, atteignable à tout moment. Le tunnel monte
|
||||
# en quarantaine et l'autorisation obtenue expire d'elle-même : sans
|
||||
# ce bouton, un code refusé, une reconnexion automatique ou une
|
||||
# autorisation échue ne laissaient d'autre issue que se déconnecter
|
||||
# puis se reconnecter. Masqué tant qu'il n'y a rien à saisir, pour ne
|
||||
# rien ajouter aux installations sans authentification serveur.
|
||||
self._btn_access = QPushButton("🔐 Saisir le code d'accès")
|
||||
self._btn_access.setFixedHeight(32)
|
||||
self._btn_access.setStyleSheet(
|
||||
f"QPushButton {{ background: {theme.WARN_SOLID}; color: {theme.TEXT};"
|
||||
" font-size: 12px; font-weight: bold; border-radius: 6px; border: none; }"
|
||||
"QPushButton:hover { background: #d68910; }"
|
||||
)
|
||||
self._btn_access.clicked.connect(self.request_access_code)
|
||||
self._btn_access.setVisible(False)
|
||||
s.addWidget(self._btn_access)
|
||||
|
||||
# Repli/dépli du détail (transfert, config, bande passante, journal) :
|
||||
# rattaché à l'action qu'il détaille, pas à l'en-tête.
|
||||
self._btn_toggle_details = QPushButton("▾ Afficher le détail")
|
||||
@@ -599,6 +616,16 @@ class MainWindow(QMainWindow):
|
||||
self._access_open = False
|
||||
self._info_labels["access"].setText(self._access_state())
|
||||
|
||||
# Le bouton n'apparaît que lorsqu'il y a réellement un code à saisir ;
|
||||
# sa hauteur entre et sort du calcul de la fenêtre, d'où le
|
||||
# réajustement. `isHidden()` et non `isVisible()` : cette dernière est
|
||||
# fausse dès que la fenêtre est repliée dans le systray, ce qui
|
||||
# relancerait le calcul de hauteur à chaque cycle de rafraîchissement.
|
||||
needed = self.access_code_needed()
|
||||
if needed == self._btn_access.isHidden():
|
||||
self._btn_access.setVisible(needed)
|
||||
self._sync_window_height()
|
||||
|
||||
if connected:
|
||||
if self._connected_since is None:
|
||||
self._connected_since = QDateTime.currentDateTime()
|
||||
@@ -691,6 +718,38 @@ class MainWindow(QMainWindow):
|
||||
self._worker.finished.connect(self._worker.deleteLater)
|
||||
self._worker.start()
|
||||
|
||||
def access_code_needed(self) -> bool:
|
||||
"""Vrai quand une saisie de code aurait un sens ici et maintenant.
|
||||
|
||||
Trois conditions : un identifiant VPN configuré (sans quoi le serveur
|
||||
ne gère pas ce poste), un tunnel monté (l'API n'est joignable que par
|
||||
l'intérieur) et aucune autorisation en cours.
|
||||
"""
|
||||
return (bool((self._cfg.wg.get("vpn_username") or "").strip())
|
||||
and self._is_connected and not self._access_open)
|
||||
|
||||
def request_access_code(self):
|
||||
"""Ouvre la saisie du code, à la demande de l'utilisateur.
|
||||
|
||||
Distincte de l'enchaînement automatique après connexion : le tunnel
|
||||
monte en quarantaine et l'autorisation expire d'elle-même, il faut
|
||||
donc pouvoir revenir saisir un code à tout moment — après un refus,
|
||||
après une reconnexion automatique, ou une fois l'autorisation échue.
|
||||
"""
|
||||
if not self._is_connected:
|
||||
QMessageBox.information(
|
||||
self, "Tunnel inactif",
|
||||
"Le serveur d'authentification n'est joignable que depuis "
|
||||
"l'intérieur du tunnel.\nConnectez-vous d'abord.")
|
||||
return
|
||||
if not (self._cfg.wg.get("vpn_username") or "").strip():
|
||||
QMessageBox.information(
|
||||
self, "Aucun identifiant VPN",
|
||||
"Renseignez l'identifiant VPN connu du serveur dans le "
|
||||
"panneau Administrateur.")
|
||||
return
|
||||
self._authenticate_with_server(silent=False)
|
||||
|
||||
def _authenticate_with_server(self, silent: bool):
|
||||
"""Demande le code à 6 chiffres et le fait valider par le serveur.
|
||||
|
||||
@@ -703,9 +762,10 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
if silent:
|
||||
# Reconnexion automatique : ouvrir une fenêtre modale sans
|
||||
# personne devant l'écran laisserait l'application figée.
|
||||
self._add_log("Code d'accès requis — reconnectez-vous manuellement",
|
||||
"warning")
|
||||
# personne devant l'écran laisserait l'application figée. Le
|
||||
# bouton « Saisir le code d'accès » prend le relais.
|
||||
self._add_log("Code d'accès requis — bouton « Saisir le code "
|
||||
"d'accès »", "warning")
|
||||
return
|
||||
|
||||
dlg = AccessCodeDialog(self._cfg, self)
|
||||
|
||||
@@ -44,6 +44,15 @@ class SystemTray(QSystemTrayIcon):
|
||||
self._action_toggle.triggered.connect(self._toggle_connection)
|
||||
menu.addAction(self._action_toggle)
|
||||
|
||||
# Saisie du code d'accès : le tunnel monte en quarantaine et
|
||||
# l'autorisation expire d'elle-même, il faut donc pouvoir y revenir
|
||||
# sans passer par une déconnexion. Masquée quand elle n'a pas lieu
|
||||
# d'être — pas d'identifiant VPN, tunnel à l'arrêt, accès déjà ouvert.
|
||||
self._action_access = QAction("🔐 Saisir le code d'accès", self)
|
||||
self._action_access.triggered.connect(self._win.request_access_code)
|
||||
self._action_access.setVisible(False)
|
||||
menu.addAction(self._action_access)
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
# Panneau de configuration
|
||||
@@ -76,6 +85,11 @@ class SystemTray(QSystemTrayIcon):
|
||||
except Exception:
|
||||
self._connected = False
|
||||
|
||||
# La fenêtre principale est seule à savoir si le serveur a ouvert
|
||||
# l'accès : elle tient cet état, qu'aucune commande WireGuard ne
|
||||
# rapporte — le tunnel est identique, ouvert ou en quarantaine.
|
||||
self._action_access.setVisible(self._win.access_code_needed())
|
||||
|
||||
if self._connected:
|
||||
self.setIcon(icons.icon_connected())
|
||||
self.setToolTip("WGSecure — Connecté")
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
; ──────────────────────────────────────────────
|
||||
|
||||
#define MyAppName "WGSecure"
|
||||
#define MyAppVersion "0.8.1"
|
||||
#define MyAppVersion "0.8.2"
|
||||
#define MyAppPublisher "WGSecure"
|
||||
#define MyAppExeName "wgsecure.exe"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user