Files
pyssh-client/app/dialogs/session_dialog.py
Florian ef059647fa
Some checks failed
CI / lint (push) Has been cancelled
CI / tests (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / build (push) Has been cancelled
Reduire la taille du formulaire de session (connexion rapide SFTP/SSH)
Signale par l'utilisateur : le dialogue de "SFTP sur cette connexion" ne
tenait pas sur l'ecran. Deux causes distinctes, verifiees par capture d'ecran
avant de conclure (rendu hors-ecran + grab()) :

1. Bug reel, dans les deux dialogues qui ont une section Bastion (SessionDialog
   et GroupSettingsDialog) : leur QStackedWidget reservait par defaut la
   hauteur de sa PLUS GRANDE page (Configuration manuelle, 5 champs) meme
   quand la page vide "Aucun" etait affichee - environ 200px de vide pur,
   visible sur la premiere capture. Nouveau app/ui/auto_height_stack.py
   (AutoHeightStackedWidget) : le sizeHint suit la page COURANTE, pas la plus
   grande de toutes: -186px des l'ouverture, a bastion desactive.

2. Choix d'ergonomie : une connexion rapide sert a aller vite (souvent un
   serveur de test en DHCP, cf. le README) - Bastion/Keepalive/Reconnexion y
   sont maintenant repliees par defaut derriere un bouton "Options
   avancees...", toujours accessibles mais qui n'allongent plus le dialogue
   pour le cas courant. Se deplient automatiquement si "Heriter du groupe"
   ramene un bastion herite, pour ne pas cacher un changement silencieux.
   Le dialogue complet ("Nouvelle session" depuis le panneau lateral) est
   inchange : tout reste visible d'emblee, c'est une operation reflechie, pas
   une connexion a la volee.

Resultat mesure : dialogue de connexion rapide 1026px -> 492px replie (-52%),
876px si depliees (au lieu de 1026, meme contenu, sans le vide). Le dialogue
"Nouvelle session" complet passe de meme a 816px grace au seul correctif (1).

tests/test_session_dialog_compact.py (5 verifications) : sizeHint de
AutoHeightStackedWidget sur la page courante, repli par defaut en connexion
rapide, redimensionnement au depli, absence de repli hors connexion rapide,
depli automatique sur heritage de groupe. Suite complete revalidee (35/35),
ruff propre.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 08:54:31 +02:00

538 lines
25 KiB
Python

"""Boite de dialogue de creation/edition d'une session SSH."""
from __future__ import annotations
from PySide6.QtCore import Qt
from PySide6.QtWidgets import (
QCheckBox,
QComboBox,
QCompleter,
QDialog,
QDialogButtonBox,
QFileDialog,
QFormLayout,
QGroupBox,
QHBoxLayout,
QLineEdit,
QMessageBox,
QPushButton,
QSpinBox,
QTextEdit,
QVBoxLayout,
QWidget,
)
from app.credentials import CredentialStore, NullCredentialStore, session_credential_key
from app.groups import GroupSettingsStore
from app.key_manager import KeyManager
from app.openssh.config import load_openssh_hosts
from app.session_manager import (
AUTH_AGENT,
AUTH_AUTO,
AUTH_KEY,
AUTH_PASSWORD,
BASTION_MANUAL,
BASTION_NONE,
BASTION_OPENSSH,
BASTION_SESSION,
PASSWORD_STORAGE_NONE,
PASSWORD_STORAGE_SECURE,
BastionConfig,
Session,
SessionStore,
)
from app.ui.auto_height_stack import AutoHeightStackedWidget
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 = "",
session_store: SessionStore | None = None, group_store: GroupSettingsStore | None = None,
credential_store: CredentialStore | None = None,
):
super().__init__(parent)
self.key_manager = key_manager
self.session_store = session_store
self.group_store = group_store
self.credential_store = credential_store or NullCredentialStore()
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(460)
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.inherit_group_btn = QPushButton("↓ Heriter du groupe")
self.inherit_group_btn.setToolTip(
"Pre-remplit ce formulaire avec les valeurs par defaut enregistrees pour ce dossier"
" (Parametres du groupe..., dans le panneau des sessions)."
)
self.inherit_group_btn.clicked.connect(self._apply_group_defaults)
group_row = QWidget()
group_layout = QHBoxLayout(group_row)
group_layout.setContentsMargins(0, 0, 0, 0)
group_layout.addWidget(self.group_edit, 1)
group_layout.addWidget(self.inherit_group_btn)
self.tags_edit = QLineEdit(", ".join(self.session.tags))
self.tags_edit.setPlaceholderText("ex: prod, docker, client-x (separes par des virgules)")
if self.session_store is not None:
completer = QCompleter(self.session_store.tags(), self.tags_edit)
completer.setCaseSensitivity(Qt.CaseInsensitive)
self.tags_edit.setCompleter(completer)
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 :", group_row)
form.addRow("Tags :", self.tags_edit)
self.remember_password_check = QCheckBox("Memoriser le mot de passe de maniere securisee")
self.remember_password_check.setChecked(self.session.password_storage == PASSWORD_STORAGE_SECURE)
if self.credential_store.is_available():
self.remember_password_check.setToolTip(
"Stocke dans le trousseau securise du systeme (Windows Credential Manager), "
"jamais en clair dans les fichiers de PySSH."
)
else:
self.remember_password_check.setEnabled(False)
self.remember_password_check.setChecked(False)
self.remember_password_check.setToolTip(
"Aucun trousseau securise disponible sur ce systeme : le mot de passe sera "
"toujours redemande a la connexion."
)
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("", self.remember_password_check)
form.addRow("Notes :", self.notes_edit)
self.form = form
layout.addLayout(form)
advanced_container = QWidget()
advanced_layout = QVBoxLayout(advanced_container)
advanced_layout.setContentsMargins(0, 0, 0, 0)
advanced_layout.addWidget(self._build_bastion_group())
advanced_layout.addWidget(self._build_keepalive_group())
advanced_layout.addWidget(self._build_reconnect_group())
if self.quick_mode:
# Repliees par defaut : une connexion rapide sert avant tout a
# aller vite (souvent un serveur de test en DHCP) - bastion,
# keepalive et reconnexion restent accessibles sans allonger le
# dialogue pour le cas courant, ou ils ne servent pas.
self.advanced_toggle = QPushButton("▸ Options avancees (bastion, keepalive, reconnexion)")
self.advanced_toggle.setCheckable(True)
self.advanced_toggle.setFlat(True)
self.advanced_toggle.toggled.connect(self._on_advanced_toggled)
layout.addWidget(self.advanced_toggle)
advanced_container.setVisible(False)
layout.addWidget(advanced_container)
self.advanced_container = advanced_container
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 _on_advanced_toggled(self, checked: bool) -> None:
self.advanced_container.setVisible(checked)
self.advanced_toggle.setText(
"▾ Options avancees (bastion, keepalive, reconnexion)" if checked
else "▸ Options avancees (bastion, keepalive, reconnexion)"
)
self.adjustSize()
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)
self.form.setRowVisible(self.remember_password_check, self.auth_combo.currentData() == AUTH_PASSWORD)
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)
# ---------- heritage des parametres du groupe ----------
def _apply_group_defaults(self) -> None:
group_path = self.group_edit.text().strip()
if not group_path:
QMessageBox.information(self, "Heriter du groupe", "Indiquez d'abord un dossier.")
return
settings = self.group_store.get(group_path) if self.group_store else None
if settings is None:
QMessageBox.information(
self, "Heriter du groupe",
f"Aucun parametre par defaut enregistre pour « {group_path} ».\n\n"
"Definissez-en depuis le panneau des sessions : clic droit sur le dossier "
"-> Parametres du groupe...",
)
return
if settings.username:
self.user_edit.setText(settings.username)
if settings.port:
self.port_spin.setValue(settings.port)
auth_idx = self.auth_combo.findData(settings.auth_type)
if auth_idx >= 0:
self.auth_combo.setCurrentIndex(auth_idx)
if settings.key_path:
existing = self.key_combo.findData(settings.key_path)
if existing >= 0:
self.key_combo.setCurrentIndex(existing)
else:
self.key_combo.addItem(settings.key_path, settings.key_path)
self.key_combo.setCurrentIndex(self.key_combo.count() - 1)
if settings.bastion.mode != BASTION_NONE:
mode_idx = self.bastion_mode_combo.findData(settings.bastion.mode)
if mode_idx >= 0:
self.bastion_mode_combo.setCurrentIndex(mode_idx)
if settings.bastion.mode == BASTION_SESSION:
found = self.bastion_session_combo.findData(settings.bastion.session_id)
if found >= 0:
self.bastion_session_combo.setCurrentIndex(found)
elif settings.bastion.mode == BASTION_OPENSSH:
found = self.bastion_openssh_combo.findData(settings.bastion.openssh_alias)
if found >= 0:
self.bastion_openssh_combo.setCurrentIndex(found)
elif settings.bastion.mode == BASTION_MANUAL:
self.bastion_host_edit.setText(settings.bastion.host)
self.bastion_port_spin.setValue(settings.bastion.port or 22)
self.bastion_user_edit.setText(settings.bastion.username)
self.keepalive_check.setChecked(settings.keepalive_enabled)
self.keepalive_interval_spin.setValue(settings.keepalive_interval or 30)
self.keepalive_count_spin.setValue(settings.keepalive_count_max or 3)
# les reglages avances viennent d'etre modifies : les montrer, sans quoi
# ils resteraient caches derriere le repli "Options avancees"
if self.quick_mode and not self.advanced_toggle.isChecked():
self.advanced_toggle.setChecked(True)
# ---------- Bastion / Jump Host ----------
def _build_bastion_group(self) -> QGroupBox:
"""Un seul saut pour l'instant (voir BastionConfig) : Aucun / une session
PySSH existante / un Host de ~/.ssh/config / une configuration manuelle."""
bastion = self.session.bastion
group = QGroupBox("Bastion / Jump Host")
v = QVBoxLayout(group)
self.bastion_mode_combo = QComboBox()
self.bastion_mode_combo.addItem("Aucun", BASTION_NONE)
self.bastion_mode_combo.addItem("Une session PySSH existante", BASTION_SESSION)
self.bastion_mode_combo.addItem("Un Host de ~/.ssh/config", BASTION_OPENSSH)
self.bastion_mode_combo.addItem("Configuration manuelle", BASTION_MANUAL)
v.addWidget(self.bastion_mode_combo)
self.bastion_stack = AutoHeightStackedWidget()
v.addWidget(self.bastion_stack)
self.bastion_stack.addWidget(QWidget()) # BASTION_NONE : rien a afficher
session_page = QWidget()
session_form = QFormLayout(session_page)
session_form.setContentsMargins(0, 4, 0, 0)
self.bastion_session_combo = QComboBox()
others = [
s for s in (self.session_store.sessions if self.session_store else [])
if s.id != self.session.id
]
if others:
for s in sorted(others, key=lambda s: s.name.lower()):
self.bastion_session_combo.addItem(f"{s.name} ({s.username}@{s.host})", s.id)
else:
self.bastion_session_combo.addItem("(aucune autre session enregistree)", "")
self.bastion_session_combo.setEnabled(False)
session_form.addRow("Session :", self.bastion_session_combo)
self.bastion_stack.addWidget(session_page)
openssh_page = QWidget()
openssh_form = QFormLayout(openssh_page)
openssh_form.setContentsMargins(0, 4, 0, 0)
self.bastion_openssh_combo = QComboBox()
hosts = load_openssh_hosts()
if hosts:
for h in hosts:
self.bastion_openssh_combo.addItem(f"{h.alias} ({h.display_target})", h.alias)
else:
self.bastion_openssh_combo.addItem("(aucun host dans ~/.ssh/config)", "")
self.bastion_openssh_combo.setEnabled(False)
openssh_form.addRow("Host :", self.bastion_openssh_combo)
self.bastion_stack.addWidget(openssh_page)
manual_page = QWidget()
manual_form = QFormLayout(manual_page)
manual_form.setContentsMargins(0, 4, 0, 0)
self.bastion_host_edit = QLineEdit(bastion.host if bastion.mode == BASTION_MANUAL else "")
self.bastion_port_spin = QSpinBox()
self.bastion_port_spin.setRange(1, 65535)
self.bastion_port_spin.setValue(bastion.port or 22)
self.bastion_user_edit = QLineEdit(bastion.username if bastion.mode == BASTION_MANUAL else "")
self.bastion_auth_combo = QComboBox()
self.bastion_auth_combo.addItem("Automatique", AUTH_AUTO)
self.bastion_auth_combo.addItem("Cle SSH choisie", AUTH_KEY)
self.bastion_auth_combo.addItem("Mot de passe (saisi a la connexion)", AUTH_PASSWORD)
self.bastion_auth_combo.addItem("Agent SSH", AUTH_AGENT)
self.bastion_key_combo = QComboBox()
for entry in self.key_manager.keys:
self.bastion_key_combo.addItem(f"{entry.name} ({entry.key_type})", entry.private_path)
self.bastion_browse_key_btn = QPushButton("Parcourir...")
bastion_key_row = QWidget()
bastion_key_layout = QHBoxLayout(bastion_key_row)
bastion_key_layout.setContentsMargins(0, 0, 0, 0)
bastion_key_layout.addWidget(self.bastion_key_combo, 1)
bastion_key_layout.addWidget(self.bastion_browse_key_btn)
manual_form.addRow("Hote :", self.bastion_host_edit)
manual_form.addRow("Port :", self.bastion_port_spin)
manual_form.addRow("Utilisateur :", self.bastion_user_edit)
manual_form.addRow("Authentification :", self.bastion_auth_combo)
manual_form.addRow("Cle privee :", bastion_key_row)
self.bastion_stack.addWidget(manual_page)
self.bastion_auth_combo.currentIndexChanged.connect(self._update_bastion_key_row)
self.bastion_browse_key_btn.clicked.connect(self._browse_bastion_key)
self.bastion_mode_combo.currentIndexChanged.connect(self.bastion_stack.setCurrentIndex)
mode_idx = self.bastion_mode_combo.findData(bastion.mode)
self.bastion_mode_combo.setCurrentIndex(mode_idx if mode_idx >= 0 else 0)
self.bastion_stack.setCurrentIndex(self.bastion_mode_combo.currentIndex())
if bastion.mode == BASTION_SESSION:
found = self.bastion_session_combo.findData(bastion.session_id)
if found >= 0:
self.bastion_session_combo.setCurrentIndex(found)
elif bastion.mode == BASTION_OPENSSH:
found = self.bastion_openssh_combo.findData(bastion.openssh_alias)
if found >= 0:
self.bastion_openssh_combo.setCurrentIndex(found)
elif bastion.mode == BASTION_MANUAL:
auth_idx = self.bastion_auth_combo.findData(bastion.auth_type)
self.bastion_auth_combo.setCurrentIndex(auth_idx if auth_idx >= 0 else 0)
if bastion.key_path:
existing = self.bastion_key_combo.findData(bastion.key_path)
if existing >= 0:
self.bastion_key_combo.setCurrentIndex(existing)
else:
self.bastion_key_combo.addItem(bastion.key_path, bastion.key_path)
self.bastion_key_combo.setCurrentIndex(self.bastion_key_combo.count() - 1)
self._update_bastion_key_row()
return group
def _update_bastion_key_row(self) -> None:
is_key_auth = self.bastion_auth_combo.currentData() == AUTH_KEY
self.bastion_key_combo.setEnabled(is_key_auth)
self.bastion_browse_key_btn.setEnabled(is_key_auth)
def _browse_bastion_key(self) -> None:
path, _ = QFileDialog.getOpenFileName(self, "Choisir une cle privee (bastion)")
if path:
self.bastion_key_combo.addItem(path, path)
self.bastion_key_combo.setCurrentIndex(self.bastion_key_combo.count() - 1)
def _result_bastion(self) -> BastionConfig:
mode = self.bastion_mode_combo.currentData()
bastion = BastionConfig(mode=mode)
if mode == BASTION_SESSION:
bastion.session_id = self.bastion_session_combo.currentData() or ""
elif mode == BASTION_OPENSSH:
bastion.openssh_alias = self.bastion_openssh_combo.currentData() or ""
elif mode == BASTION_MANUAL:
bastion.host = self.bastion_host_edit.text().strip()
bastion.port = self.bastion_port_spin.value()
bastion.username = self.bastion_user_edit.text().strip()
bastion.auth_type = self.bastion_auth_combo.currentData()
bastion.key_path = self.bastion_key_combo.currentData() or ""
return bastion
# ---------- Keepalive ----------
def _build_keepalive_group(self) -> QGroupBox:
session = self.session
group = QGroupBox("Keepalive")
form = QFormLayout(group)
self.keepalive_check = QCheckBox("Active")
self.keepalive_check.setChecked(session.keepalive_enabled)
self.keepalive_interval_spin = QSpinBox()
self.keepalive_interval_spin.setRange(5, 3600)
self.keepalive_interval_spin.setSuffix(" s")
self.keepalive_interval_spin.setValue(session.keepalive_interval or 30)
self.keepalive_count_spin = QSpinBox()
self.keepalive_count_spin.setRange(1, 20)
self.keepalive_count_spin.setValue(session.keepalive_count_max or 3)
form.addRow(self.keepalive_check)
form.addRow("Intervalle :", self.keepalive_interval_spin)
form.addRow("Nombre d'echecs avant abandon :", self.keepalive_count_spin)
self.keepalive_check.toggled.connect(self.keepalive_interval_spin.setEnabled)
self.keepalive_check.toggled.connect(self.keepalive_count_spin.setEnabled)
self.keepalive_interval_spin.setEnabled(session.keepalive_enabled)
self.keepalive_count_spin.setEnabled(session.keepalive_enabled)
return group
# ---------- Reconnexion automatique ----------
def _build_reconnect_group(self) -> QGroupBox:
session = self.session
group = QGroupBox("Reconnexion automatique")
form = QFormLayout(group)
self.reconnect_check = QCheckBox("Reconnexion automatique")
self.reconnect_check.setChecked(session.reconnect_enabled)
self.reconnect_delay_spin = QSpinBox()
self.reconnect_delay_spin.setRange(1, 300)
self.reconnect_delay_spin.setSuffix(" s")
self.reconnect_delay_spin.setValue(session.reconnect_initial_delay or 5)
self.reconnect_attempts_spin = QSpinBox()
self.reconnect_attempts_spin.setRange(0, 1000)
self.reconnect_attempts_spin.setSpecialValueText("Illimite")
self.reconnect_attempts_spin.setValue(session.reconnect_max_attempts)
form.addRow(self.reconnect_check)
form.addRow("Delai initial :", self.reconnect_delay_spin)
form.addRow("Tentatives maximum (0 = illimite) :", self.reconnect_attempts_spin)
self.reconnect_check.toggled.connect(self.reconnect_delay_spin.setEnabled)
self.reconnect_check.toggled.connect(self.reconnect_attempts_spin.setEnabled)
self.reconnect_delay_spin.setEnabled(session.reconnect_enabled)
self.reconnect_attempts_spin.setEnabled(session.reconnect_enabled)
return group
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
bastion_mode = self.bastion_mode_combo.currentData()
if bastion_mode == BASTION_SESSION and not self.bastion_session_combo.currentData():
QMessageBox.warning(self, "Bastion incomplet", "Choisissez une session PySSH a utiliser comme bastion.")
return
if bastion_mode == BASTION_OPENSSH and not self.bastion_openssh_combo.currentData():
QMessageBox.warning(self, "Bastion incomplet", "Choisissez un Host de ~/.ssh/config a utiliser comme bastion.")
return
if bastion_mode == BASTION_MANUAL and not self.bastion_host_edit.text().strip():
QMessageBox.warning(self, "Bastion incomplet", "Indiquez l'adresse du bastion.")
self.bastion_host_edit.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()
seen = set()
tags = []
for raw in self.tags_edit.text().split(","):
tag = raw.strip()
if tag and tag.lower() not in seen:
seen.add(tag.lower())
tags.append(tag)
self.session.tags = tags
remember = self.auth_combo.currentData() == AUTH_PASSWORD and self.remember_password_check.isChecked()
new_storage = PASSWORD_STORAGE_SECURE if remember else PASSWORD_STORAGE_NONE
if self.session.password_storage == PASSWORD_STORAGE_SECURE and new_storage == PASSWORD_STORAGE_NONE:
# la memorisation vient d'etre desactivee (ou l'authentification changee) :
# ne pas laisser un secret perimer dans le trousseau
self.credential_store.delete_secret(session_credential_key(self.session.id))
self.session.password_storage = new_storage
self.session.bastion = self._result_bastion()
self.session.keepalive_enabled = self.keepalive_check.isChecked()
self.session.keepalive_interval = self.keepalive_interval_spin.value()
self.session.keepalive_count_max = self.keepalive_count_spin.value()
self.session.reconnect_enabled = self.reconnect_check.isChecked()
self.session.reconnect_initial_delay = self.reconnect_delay_spin.value()
self.session.reconnect_max_attempts = self.reconnect_attempts_spin.value()
self.accept()
def result_session(self) -> Session:
return self.session