Save SSH passwords in Windows Credential Manager and auto-type at prompt

This commit is contained in:
megaproxy 2026-05-25 20:08:31 +01:00
parent 872fb0e80e
commit 1c243b3f3f
11 changed files with 538 additions and 38 deletions

View file

@ -3,7 +3,8 @@
use base64::{engine::general_purpose::STANDARD as B64, Engine as _};
use tauri::{AppHandle, Manager};
use crate::hosts::{self, SshHost};
use crate::creds;
use crate::hosts::{self, SshHost, SshHostView};
use crate::pty::{list_wsl_distros, PaneId, PtyManager, SpawnSpec};
const WORKSPACE_FILE: &str = "workspace.json";
@ -92,11 +93,47 @@ pub async fn load_workspace(app: AppHandle) -> Result<Option<String>, String> {
}
#[tauri::command]
pub async fn list_ssh_hosts(app: AppHandle) -> Result<Vec<SshHost>, String> {
hosts::load(&app).map_err(|e| e.to_string())
pub async fn list_ssh_hosts(app: AppHandle) -> Result<Vec<SshHostView>, String> {
let raw = hosts::load(&app).map_err(|e| e.to_string())?;
Ok(raw
.into_iter()
.map(|h| {
let has_password = creds::has(&h.id);
SshHostView { host: h, has_password }
})
.collect())
}
#[tauri::command]
pub async fn save_ssh_hosts(app: AppHandle, hosts: Vec<SshHost>) -> Result<(), String> {
// Sweep orphaned credentials: any host id that existed before this call
// but isn't in the new list gets its keyring entry deleted. Saves the
// frontend from having to diff and call delete_host_password itself.
if let Ok(prior) = crate::hosts::load(&app) {
let new_ids: std::collections::HashSet<&str> =
hosts.iter().map(|h| h.id.as_str()).collect();
for old in &prior {
if !new_ids.contains(old.id.as_str()) {
if let Err(e) = creds::delete(&old.id) {
tracing::warn!("orphan credential cleanup failed for {}: {e}", old.id);
}
}
}
}
crate::hosts::save(&app, &hosts).map_err(|e| e.to_string())
}
#[tauri::command]
pub async fn set_host_password(host_id: String, password: String) -> Result<(), String> {
creds::set(&host_id, &password).map_err(|e| e.to_string())
}
#[tauri::command]
pub async fn delete_host_password(host_id: String) -> Result<(), String> {
creds::delete(&host_id).map_err(|e| e.to_string())
}
#[tauri::command]
pub async fn has_host_password(host_id: String) -> Result<bool, String> {
Ok(creds::has(&host_id))
}