Commit Graph

3 Commits

Author SHA1 Message Date
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
Florian
c08037a638 feat: ajouter la prise en charge de ~/.ssh/config
Support minimum : Host, HostName, User, Port, IdentityFile, ProxyJump,
ForwardAgent, ServerAliveInterval, ServerAliveCountMax.

- app/openssh/config.py : load_openssh_hosts() lit ~/.ssh/config via
  paramiko.SSHConfig (gere nativement les blocs "Host *" fusionnes, les alias
  multiples sur une meme ligne Host, et l'expansion ~ des IdentityFile).
  Fichier absent = liste vide, pas une erreur. session_from_openssh_host()
  convertit un Host en Session PySSH (groupe "OpenSSH", cle si IdentityFile
  presente sinon authentification auto).
- Nouvelle section "Configuration OpenSSH" dans le panneau lateral
  (SessionController), sous Sessions SSH : double-clic ou menu contextuel pour
  se connecter directement ou importer comme session (editeur pre-rempli,
  ajustable avant enregistrement) ; rafraichissement via le menu ⚙ ou le menu
  contextuel de l'hote.
- ConnectionController.open_openssh_host() ouvre une connexion directe sans
  enregistrer de session, comme une connexion rapide.

Le ProxyJump est detecte, affiche (badge "· via <bastion>", tooltip) et reporte
dans les notes d'une session importee, mais pas encore applique automatiquement
a la connexion : ce sera l'objet de la Phase 3 (Bastion / Jump Host), qui
prendra en charge toutes ses sources (OpenSSH, session PySSH, config manuelle)
via un mecanisme commun.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 01:49:59 +02:00
Florian
47005b1e26 refactor: decouper main_window.py en controllers/services/ui
main_window.py concentrait la fenetre principale, l'arbre des sessions, les 4
classes d'onglets, la detection de commandes ssh et le multi-send : 983 lignes
et une seule classe qui pilotait toute l'application.

- app/services/ssh_target.py : detection d'une cible ssh dans un terminal (pur,
  sans dependance Qt)
- app/ui/tabs.py : BaseTerminalTab / SessionTabWidget / SFTPTabWidget /
  LocalTerminalTabWidget (deplaces tels quels)
- app/controllers/session_controller.py : panneau lateral (arbre sessions +
  terminaux locaux) et CRUD des sessions
- app/controllers/connection_controller.py : cycle de vie des onglets
  (ouverture, connexion rapide, renommage, fermeture)
- app/controllers/broadcast_manager.py : bouton et logique du mode Multi-Send

main_window.py ne fait plus que construire ces composants et les relier par
signaux (983 -> ~200 lignes). Comportement inchange ; MainWindow.tabs et
detect_ssh_target restent importables depuis app.main_window pour la
compatibilite avec les tests existants.

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