Commit Graph

3 Commits

Author SHA1 Message Date
Florian
9216f636bd feat: ajouter le Credential Vault (memorisation securisee optionnelle)
Par defaut, inchange : les mots de passe sont toujours redemandes a chaque
connexion. Nouvelle case a cocher optionnelle dans l'editeur de session,
visible seulement en authentification par mot de passe : "Memoriser le mot
de passe de maniere securisee".

- app/credentials.py : abstraction CredentialStore (set_secret/get_secret/
  delete_secret/is_available), pour supporter plus tard d'autres systemes que
  Windows. WindowsCredentialStore utilise Windows Credential Manager
  (CredWrite/CredRead/CredDelete) via pywin32-ctypes - deja present dans
  l'environnement (dependance de PyInstaller), mais pas encore declare comme
  dependance d'execution : ajoute a requirements.txt (marque sys_platform ==
  "win32"), sinon la fonctionnalite aurait silencieusement ete indisponible
  sur une installation propre. NullCredentialStore en repli si indisponible :
  jamais d'ecriture en clair a la place.
- Session.password_storage ("none" par defaut | "secure") : seulement le mode
  est stocke dans sessions.json, jamais le secret. Le mot de passe est ecrit/
  lu dans le trousseau via une cle applicative (session:<id>), et supprime
  automatiquement si l'utilisateur desactive la memorisation ou supprime la
  session.
- Un bastion en mode "session PySSH" reutilise un mot de passe deja memorise
  pour cette session (lecture seule depuis ce chemin : l'ecriture ne se fait
  que via l'ouverture directe de la session concernee).

tests/test_credentials.py : NullCredentialStore, aller-retour reel avec
Windows Credential Manager (nettoye et verifie apres coup, aucune entree
residuelle), integration ConnectionController (memorise puis reutilise sans
redemander), suppression en cascade, reutilisation par un bastion, et
verification que le secret n'apparait jamais dans la serialisation JSON de
la session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 03:21:32 +02:00
Florian
a3c72ade0b feat: ajouter les Workspaces (enregistrer/restaurer l'etat des onglets)
Un workspace capture les onglets ouverts (sessions SSH, terminaux locaux,
splits avec leurs tailles) et les restaure plus tard. Ne reference les
sessions que par id : aucun mot de passe stocke, redemande a la restauration
exactement comme une ouverture normale.

- app/workspace.py : Workspace/WorkspaceTab/WorkspaceItem + WorkspaceStore
  (meme pattern que SessionStore/KeyManager : JSON atomique, schema_version).
- app/controllers/workspace_manager.py : capture() lit l'etat reel des onglets
  de ConnectionController (types de widgets, layout et tailles d'un
  SplitContainer) ; restore() rouvre chaque onglet via
  ConnectionController.build_session_tab()/build_sftp_tab()/new_local_terminal()
  (factorises hors de open_session()/open_sftp_session() pour etre reutilisables
  ici) puis reconstruit les splits avec split_tab(). Un element non restaurable
  (session supprimee depuis) est signale (compteur "ignores") plutot que de
  faire echouer toute la restauration.
- WorkspaceDialog : creer / enregistrer (ecraser) / ouvrir / renommer /
  supprimer, plus la case "Restaurer automatiquement le dernier workspace
  ouvert au demarrage" (desactivee par defaut).
- app/app_settings.py : preferences globales (theme, taille de police,
  dernier workspace) consolidees dans un seul module au lieu d'un mecanisme
  de lecture/ecriture duplique par prefernce dans theme.py.
- SplitContainer expose layout_kind et get_layout_sizes()/set_layout_sizes()
  (parcours recursif des QSplitter imbriques) pour la capture/restauration
  fidele d'une grille 2x2.

tests/test_workspace.py : capture (simple + split), sauvegarde/rechargement
JSON, restauration complete et partielle, renommer/supprimer/re-enregistrer,
restauration automatique - avec app_settings.SETTINGS_FILE redirige vers un
fichier temporaire pour ne jamais toucher les vrais reglages de l'utilisateur.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 02:47:07 +02:00
Florian
c2bde19f59 feat: ajouter le support ProxyJump / bastion (jump host)
Une session peut desormais passer par un jump host avant d'atteindre sa
cible, avec 3 sources possibles (limite a un seul saut pour l'instant) :
une session PySSH existante, un Host de ~/.ssh/config, ou une configuration
manuelle (hote/port/utilisateur/authentification/cle).

- session_manager.py : BastionConfig (mode + parametres), champ
  Session.bastion. Jamais de mot de passe stocke : un bastion en mode "mot
  de passe" le redemande a la connexion, comme le fait deja Session.auth_type.
- app/bastion.py : resolve_bastion_target() convertit un BastionConfig en
  parametres de connexion concrets quelle que soit sa source, et leve une
  erreur claire si la reference est cassee (session supprimee, alias OpenSSH
  introuvable, config manuelle incomplete) plutot que d'echouer plus tard
  avec un message obscur.
- ssh_connection.py : la logique de connexion (auth, cle d'hote modifiee,
  mot de passe de secours en mode auto) est factorisee dans _connect_host(),
  reutilisee a l'identique pour le bastion et pour la cible.
  _open_bastion_channel() connecte d'abord le bastion puis ouvre un canal
  direct-tcpip vers la cible, passe en sock= a la connexion finale (mecanisme
  ProxyJump standard de paramiko). Le bastion a son propre SSHClient, ferme
  proprement a la fin de la connexion.
- SessionDialog : section "Bastion / Jump Host" (choix de la source + champs
  associes, caches/affiches via un QStackedWidget).
- ConnectionController resout le bastion et demande son mot de passe (si
  nécessaire) avant d'ouvrir l'onglet ; erreur affichee immediatement si la
  reference est invalide plutot que de lancer une connexion vouee a l'echec.
- app/openssh/config.py : un Host avec ProxyJump produit maintenant une
  Session dont le bastion reference cet alias (mode BASTION_OPENSSH) : le
  scenario proxmox-prod / bastion de la Phase 2 fonctionne de bout en bout.

tests/test_bastion.py (resolution des 3 sources + erreurs, et ouverture du
canal via un bastion entierement mocke, sans reseau reel) et
tests/test_session_dialog_bastion.py (UI : construction, pre-remplissage a
l'edition, resultat par mode).

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