"""Boite de dialogue de creation/edition d'une session SSH.""" from __future__ import annotations from PySide6.QtWidgets import ( QDialog, QFormLayout, QLineEdit, QSpinBox, QComboBox, QDialogButtonBox, QHBoxLayout, QPushButton, QTextEdit, QFileDialog, QWidget, QVBoxLayout, QCheckBox, QMessageBox, ) from app.session_manager import Session, AUTH_AUTO, AUTH_KEY, AUTH_PASSWORD, AUTH_AGENT from app.key_manager import KeyManager class SessionDialog(QDialog): """Formulaire de session. En mode `connect_label`, il sert de connexion rapide : on saisit les infos et on se connecte directement, l'enregistrement etant optionnel.""" def __init__(self, key_manager: KeyManager, session: Session | None = None, parent=None, connect_label: str = "", title: str = ""): super().__init__(parent) self.key_manager = key_manager self.session = session or Session() self.quick_mode = bool(connect_label) self.setWindowTitle(title or ("Nouvelle session" if session is None else "Modifier la session")) self.setMinimumWidth(420) layout = QVBoxLayout(self) form = QFormLayout() self.name_edit = QLineEdit(self.session.name) self.group_edit = QLineEdit(self.session.group) self.group_edit.setPlaceholderText("ex: Production/Web") self.host_edit = QLineEdit(self.session.host) self.port_spin = QSpinBox() self.port_spin.setRange(1, 65535) self.port_spin.setValue(self.session.port or 22) self.user_edit = QLineEdit(self.session.username) self.auth_combo = QComboBox() self.auth_combo.addItem("Automatique (comme la commande ssh)", AUTH_AUTO) self.auth_combo.addItem("Cle SSH choisie", AUTH_KEY) self.auth_combo.addItem("Mot de passe (saisi a la connexion)", AUTH_PASSWORD) self.auth_combo.addItem("Agent SSH", AUTH_AGENT) self.auth_combo.setToolTip( "Automatique : utilise l'agent SSH et vos cles ~/.ssh comme le fait la commande ssh,\n" "et demande un mot de passe seulement si aucune cle ne convient." ) idx = self.auth_combo.findData(self.session.auth_type) self.auth_combo.setCurrentIndex(idx if idx >= 0 else 0) self.key_combo = QComboBox() self._reload_keys() self.browse_key_btn = QPushButton("Parcourir...") key_row = QWidget() key_layout = QHBoxLayout(key_row) key_layout.setContentsMargins(0, 0, 0, 0) key_layout.addWidget(self.key_combo, 1) key_layout.addWidget(self.browse_key_btn) self.notes_edit = QTextEdit(self.session.notes) self.notes_edit.setFixedHeight(60) form.addRow("Nom :", self.name_edit) form.addRow("Dossier :", self.group_edit) form.addRow("Hote :", self.host_edit) form.addRow("Port :", self.port_spin) form.addRow("Utilisateur :", self.user_edit) form.addRow("Authentification :", self.auth_combo) form.addRow("Cle privee :", key_row) form.addRow("Notes :", self.notes_edit) layout.addLayout(form) self.save_check = QCheckBox("Enregistrer dans mes sessions (optionnel)") if self.quick_mode: # decoche par defaut : les serveurs de test changent d'IP (DHCP), les # enregistrer systematiquement encombre la liste pour rien self.save_check.setChecked(False) layout.addWidget(self.save_check) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) if self.quick_mode: buttons.button(QDialogButtonBox.Ok).setText(connect_label) buttons.accepted.connect(self._on_accept) buttons.rejected.connect(self.reject) layout.addWidget(buttons) self.auth_combo.currentIndexChanged.connect(self._update_key_row) self.browse_key_btn.clicked.connect(self._browse_key) self._update_key_row() if self.session.key_path: existing = self.key_combo.findData(self.session.key_path) if existing >= 0: self.key_combo.setCurrentIndex(existing) else: self.key_combo.addItem(self.session.key_path, self.session.key_path) self.key_combo.setCurrentIndex(self.key_combo.count() - 1) def _reload_keys(self) -> None: self.key_combo.clear() for entry in self.key_manager.keys: self.key_combo.addItem(f"{entry.name} ({entry.key_type})", entry.private_path) def _update_key_row(self) -> None: is_key_auth = self.auth_combo.currentData() == AUTH_KEY self.key_combo.setEnabled(is_key_auth) self.browse_key_btn.setEnabled(is_key_auth) def _browse_key(self) -> None: path, _ = QFileDialog.getOpenFileName(self, "Choisir une cle privee") if path: self.key_combo.addItem(path, path) self.key_combo.setCurrentIndex(self.key_combo.count() - 1) def should_save(self) -> bool: """En connexion rapide, indique si la session doit aussi etre enregistree.""" return self.save_check.isChecked() if self.quick_mode else True def _on_accept(self) -> None: if not self.host_edit.text().strip(): QMessageBox.warning(self, "Hote manquant", "Indiquez l'adresse du serveur (nom d'hote ou IP).") self.host_edit.setFocus() return if not self.user_edit.text().strip(): QMessageBox.warning(self, "Utilisateur manquant", "Indiquez le nom d'utilisateur.") self.user_edit.setFocus() return if self.auth_combo.currentData() == AUTH_KEY and not self.key_combo.currentData(): QMessageBox.warning( self, "Cle manquante", "Aucune cle privee n'est selectionnee.\n\n" "Choisissez une cle (bouton « Parcourir... »), ou passez l'authentification " "sur « Automatique » — qui utilisera l'agent SSH et vos cles ~/.ssh comme le " "fait la commande ssh.", ) self.auth_combo.setFocus() return self.session.name = self.name_edit.text().strip() or self.host_edit.text().strip() self.session.group = self.group_edit.text().strip() self.session.host = self.host_edit.text().strip() self.session.port = self.port_spin.value() self.session.username = self.user_edit.text().strip() self.session.auth_type = self.auth_combo.currentData() self.session.key_path = self.key_combo.currentData() or "" self.session.notes = self.notes_edit.toPlainText() self.accept() def result_session(self) -> Session: return self.session