Panneau lateral masquable et redimensionnable, plus de types de cles
Le panneau des sessions etait declare Movable|Floatable mais pas Closable : il n'avait donc aucun bouton de fermeture et ne pouvait pas etre masque. Sa largeur plancher etait par ailleurs imposee par son contenu, ce qui empechait de le retrecir vraiment. - ajout de DockWidgetClosable, largeur minimale ramenee a 120 px - bascule par F9, par le menu Affichage, ou par la croix du panneau, les trois restant synchronises - separateur elargi a 6 px et surligne au survol, pour etre attrape facilement Generation de cles : ajout d'ECDSA sur les courbes NIST P-256, P-384 et P-521, en plus d'Ed25519 et RSA. Ed25519 reste le choix par defaut : c'est aussi une courbe elliptique (Curve25519), plus courte et plus rapide, et sans les interrogations qui entourent le choix des parametres des courbes NIST. Chaque type est verifie relisible par paramiko, donc utilisable pour se connecter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ from PySide6.QtWidgets import (
|
||||
QDialogButtonBox, QApplication, QLabel,
|
||||
)
|
||||
|
||||
from app.key_manager import KeyManager, KEY_TYPE_ED25519, KEY_TYPE_RSA
|
||||
from app.key_manager import KeyManager, KEY_TYPE_ED25519, KEY_TYPE_RSA, KEY_TYPE_LABELS
|
||||
|
||||
|
||||
class GenerateKeyDialog(QDialog):
|
||||
@@ -22,8 +22,14 @@ class GenerateKeyDialog(QDialog):
|
||||
|
||||
self.name_edit = QLineEdit()
|
||||
self.type_combo = QComboBox()
|
||||
self.type_combo.addItem("Ed25519 (recommande)", KEY_TYPE_ED25519)
|
||||
self.type_combo.addItem("RSA", KEY_TYPE_RSA)
|
||||
for key_type, label in KEY_TYPE_LABELS.items():
|
||||
self.type_combo.addItem(label, key_type)
|
||||
self.type_combo.setToolTip(
|
||||
"Ed25519 : courbe elliptique moderne, courte et rapide — le choix par defaut.\n"
|
||||
"ECDSA (P-256/384/521) : egalement sur courbe elliptique, utile si une politique\n"
|
||||
"interne impose les courbes NIST.\n"
|
||||
"RSA : le plus universel, a privilegier pour un serveur tres ancien."
|
||||
)
|
||||
self.bits_spin = QSpinBox()
|
||||
self.bits_spin.setRange(2048, 8192)
|
||||
self.bits_spin.setSingleStep(1024)
|
||||
|
||||
@@ -9,7 +9,7 @@ from pathlib import Path
|
||||
|
||||
import paramiko
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa, ed25519
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa, ed25519, ec
|
||||
|
||||
from app.paths import DATA_DIR # noqa: E402 (chemins valables aussi en .exe)
|
||||
KEYS_FILE = DATA_DIR / "keys.json"
|
||||
@@ -17,6 +17,26 @@ DEFAULT_KEY_DIR = DATA_DIR / "keys"
|
||||
|
||||
KEY_TYPE_ED25519 = "ed25519"
|
||||
KEY_TYPE_RSA = "rsa"
|
||||
KEY_TYPE_ECDSA_P256 = "ecdsa-p256"
|
||||
KEY_TYPE_ECDSA_P384 = "ecdsa-p384"
|
||||
KEY_TYPE_ECDSA_P521 = "ecdsa-p521"
|
||||
|
||||
# Courbes elliptiques NIST utilisees par ECDSA. Ed25519 est aussi une courbe
|
||||
# elliptique (Curve25519) et reste le choix recommande : plus court, plus rapide,
|
||||
# et sans les interrogations qui entourent le choix des parametres des courbes NIST.
|
||||
ECDSA_CURVES = {
|
||||
KEY_TYPE_ECDSA_P256: ec.SECP256R1,
|
||||
KEY_TYPE_ECDSA_P384: ec.SECP384R1,
|
||||
KEY_TYPE_ECDSA_P521: ec.SECP521R1,
|
||||
}
|
||||
|
||||
KEY_TYPE_LABELS = {
|
||||
KEY_TYPE_ED25519: "Ed25519 (recommande)",
|
||||
KEY_TYPE_ECDSA_P256: "ECDSA — courbe NIST P-256",
|
||||
KEY_TYPE_ECDSA_P384: "ECDSA — courbe NIST P-384",
|
||||
KEY_TYPE_ECDSA_P521: "ECDSA — courbe NIST P-521",
|
||||
KEY_TYPE_RSA: "RSA (compatibilite maximale)",
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -97,6 +117,8 @@ class KeyManager:
|
||||
|
||||
if key_type == KEY_TYPE_ED25519:
|
||||
private_key = ed25519.Ed25519PrivateKey.generate()
|
||||
elif key_type in ECDSA_CURVES:
|
||||
private_key = ec.generate_private_key(ECDSA_CURVES[key_type]())
|
||||
else:
|
||||
private_key = rsa.generate_private_key(public_exponent=65537, key_size=rsa_bits)
|
||||
|
||||
|
||||
@@ -552,6 +552,14 @@ class MainWindow(QMainWindow):
|
||||
keys_menu.addAction(manage_keys_action)
|
||||
|
||||
view_menu = self.menuBar().addMenu("&Affichage")
|
||||
|
||||
self.toggle_sidebar_action = QAction("Panneau des sessions", self, checkable=True)
|
||||
self.toggle_sidebar_action.setChecked(True)
|
||||
self.toggle_sidebar_action.setShortcut(QKeySequence("F9"))
|
||||
self.toggle_sidebar_action.triggered.connect(self.toggle_sidebar)
|
||||
view_menu.addAction(self.toggle_sidebar_action)
|
||||
view_menu.addSeparator()
|
||||
|
||||
theme_menu = view_menu.addMenu("Theme")
|
||||
self.theme_group = QActionGroup(self)
|
||||
self.theme_group.setExclusive(True)
|
||||
@@ -574,7 +582,14 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def _build_sidebar(self) -> None:
|
||||
dock = QDockWidget("Sessions", self)
|
||||
dock.setFeatures(QDockWidget.DockWidgetMovable | QDockWidget.DockWidgetFloatable)
|
||||
# Closable : sans cette option, le panneau n'a pas de bouton de fermeture
|
||||
# et ne peut pas etre masque.
|
||||
dock.setFeatures(
|
||||
QDockWidget.DockWidgetMovable
|
||||
| QDockWidget.DockWidgetFloatable
|
||||
| QDockWidget.DockWidgetClosable
|
||||
)
|
||||
self.sessions_dock = dock
|
||||
|
||||
container = QWidget()
|
||||
layout = QVBoxLayout(container)
|
||||
@@ -632,8 +647,15 @@ class MainWindow(QMainWindow):
|
||||
self.tree.customContextMenuRequested.connect(self._on_tree_context_menu)
|
||||
layout.addWidget(self.tree)
|
||||
|
||||
# Sans minimum explicite, la largeur plancher est imposee par le contenu
|
||||
# (les noms de sessions), ce qui empeche de retrecir vraiment le panneau.
|
||||
container.setMinimumWidth(120)
|
||||
self.tree.setMinimumWidth(0)
|
||||
dock.setWidget(container)
|
||||
self.addDockWidget(Qt.LeftDockWidgetArea, dock)
|
||||
self.resizeDocks([dock], [300], Qt.Horizontal)
|
||||
# garde la case du menu Affichage synchronisee si on ferme le panneau par sa croix
|
||||
dock.visibilityChanged.connect(self.toggle_sidebar_action.setChecked)
|
||||
|
||||
def _build_tabs(self) -> None:
|
||||
self.tabs = QTabWidget()
|
||||
@@ -881,6 +903,13 @@ class MainWindow(QMainWindow):
|
||||
self.tabs.removeTab(index)
|
||||
|
||||
# ---------- cles / a propos ----------
|
||||
def toggle_sidebar(self, visible: bool | None = None) -> None:
|
||||
"""Affiche ou masque le panneau des sessions (F9)."""
|
||||
if visible is None:
|
||||
visible = not self.sessions_dock.isVisible()
|
||||
self.sessions_dock.setVisible(visible)
|
||||
self.toggle_sidebar_action.setChecked(visible)
|
||||
|
||||
def change_theme(self, theme: str) -> None:
|
||||
"""Applique un theme a chaud : feuille de style de l'interface + couleurs des
|
||||
terminaux deja ouverts (qui ont leur propre palette, hors QSS)."""
|
||||
|
||||
Reference in New Issue
Block a user