Files
pyssh-client/app/dialogs/session_dialog.py
Florian c39ef9c038 Qualite : pyproject.toml (ruff/pytest/pyright) + CI Gitea (lint/tests/build)
pyproject.toml devient le point d'entree unique de configuration des outils
(pytest.ini supprime, sa config y est deplacee). Les dependances restent dans
requirements.txt/requirements-dev.txt (deja utilises par setup.bat) plutot que
dupliquees ici : ce projet se distribue par PyInstaller, pas par "pip install".

ruff : configure puis passe sur tout app/ + tests/ (154 corrections
automatiques, purement mecaniques - tri des imports, imports multiples sur une
ligne separes, alias depreciees) suivies de 7 corrections manuelles ciblees :
  - app/ssh_connection.py : `raise ... from None` pour le refus explicite de
    passphrase (ce n'est pas un echec a diagnostiquer).
  - app/controllers/workspace_manager.py : zip() sans strict= documente en
    commentaire (deliberement tolerant a un workspace legerement perime).
  - tests/test_ai_redaction.py : nom de variable ambigu renomme.
  - E702 (construction compacte "widget = X(); widget.setY(...)" dans
    sftp_panel.py/tunnel_dialog.py) et E402 (quelques tests qui importent un
    symbole juste avant la section qui l'utilise) : ignores explicitement, ce
    sont des styles deliberes preexistants, pas des oublis.
`ruff check app main.py tests` est desormais propre.

pyright : mode "basic" (le code n'a jamais ete type). Plus de 200 signalements
sur ~300 etaient le meme faux positif connu de l'ecosysteme PySide6 (acces
plat aux enums Qt, ex. Qt.UserRole, valide a l'execution mais que les stubs ne
reconnaissent que sous leur forme imbriquee) : desactive avec justification en
commentaire. Reste un socle de 77 signalements reels (surtout des acces
Optional non filtres dans app/ui/tabs.py et app/ssh_connection.py) : non
corrige dans cette phase - resserrer le typage la ou pyright a raison est un
travail a part, pas une consequence automatique de l'ajout de l'outil. La CI
le lance en mode informatif (continue-on-error) tant que ce socle n'est pas
resorbe.

.gitea/workflows/ci.yml : lint (ruff, bloquant), tests (pytest, bloquant),
typecheck (pyright, informatif), build (PyInstaller, sur main uniquement,
artefact du dossier portable). Necessite un runner Gitea Actions Windows
enregistre - PySide6/pywinpty/Credential Manager/ACL NTFS sont Windows
uniquement, comme l'application elle-meme ; non verifiable depuis ce poste.

Suite complete revalidee apres coup (33/33) pour confirmer qu'aucune des 154
corrections automatiques n'a modifie de comportement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 07:46:50 +02:00

506 lines
24 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,
QStackedWidget,
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,
)
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)
layout.addWidget(self._build_bastion_group())
layout.addWidget(self._build_keepalive_group())
layout.addWidget(self._build_reconnect_group())
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)
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)
# ---------- 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 = QStackedWidget()
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