Passwort nutzen: Auto-Login via sshpass + Keychain, Editor mit Anzeige/Loeschen, Doku

This commit is contained in:
x3
2026-09-21 23:21:07 +02:00
parent 7dc6ff75d2
commit 4fd711474c
7 changed files with 113 additions and 11 deletions
+1 -1
View File
@@ -104,7 +104,7 @@ struct ContentView: View {
}
}
.sheet(item: $editingProfile) { profile in
ProfileEditorView(profile: profile) { saved in
ProfileEditorView(profile: profile, isNew: !store.profiles.contains(where: { $0.id == profile.id })) { saved in
if store.profiles.contains(where: { $0.id == saved.id }) {
store.update(saved)
} else {
@@ -164,7 +164,7 @@ struct EmbeddedTerminalView: View {
if checking { return "Prüfe \(profile.host):\(profile.port) …" }
switch term.state {
case .never: return canConnect ? "Bereit — verbindet beim Öffnen." : "Host/Benutzer fehlt."
case .running: return "Verbunden (PTY, TERM=xterm-256color)."
case .running: return term.autoLogin ? "Verbunden (Auto-Login via Keychain)." : "Verbunden (PTY, TERM=xterm-256color)."
case .done(_, let msg): return msg
}
}
@@ -4,18 +4,21 @@ struct ProfileEditorView: View {
@Environment(\.dismiss) var dismiss
@EnvironmentObject var store: ProfileStore
@State var profile: ConnectionProfile
var isNew: Bool
var onSave: (ConnectionProfile) -> Void
@State private var password: String = ""
@State private var savePassword: Bool = false
@State private var storedPasswordPresent: Bool = false
init(profile: ConnectionProfile, onSave: @escaping (ConnectionProfile) -> Void) {
init(profile: ConnectionProfile, isNew: Bool, onSave: @escaping (ConnectionProfile) -> Void) {
_profile = State(initialValue: profile)
self.isNew = isNew
self.onSave = onSave
}
var body: some View {
VStack(spacing: 0) {
Text(profile.id.uuidString.prefix(8) == ConnectionProfile().id.uuidString.prefix(8) ? "Profil bearbeiten" : "Profil").font(.headline).padding()
Text(isNew ? "Neues Profil" : "Profil bearbeiten").font(.headline).padding()
Form {
Section("Allgemein") {
TextField("Name", text: $profile.name)
@@ -44,8 +47,17 @@ struct ProfileEditorView: View {
Text("Kein Key").tag(UUID?.none)
ForEach(store.sshKeys) { k in Text(k.name).tag(UUID?.some(k.id)) }
}
SecureField("Passwort (optional, Keychain)", text: $password)
if storedPasswordPresent && password.isEmpty {
HStack {
Image(systemName: "checkmark.circle.fill").foregroundStyle(.green)
Text("Passwort in Keychain gespeichert")
Spacer()
Button("Löschen", role: .destructive) { deleteStoredPassword() }
}
}
SecureField(storedPasswordPresent ? "Neues Passwort …" : "Passwort (optional, Keychain)", text: $password)
Toggle("Passwort in Keychain speichern", isOn: $savePassword)
Text(sshpassHint).font(.caption).foregroundStyle(.secondary)
}
}
.formStyle(.grouped)
@@ -68,12 +80,30 @@ struct ProfileEditorView: View {
}
.padding()
}
.frame(width: 520, height: 620)
.frame(width: 520, height: 640)
.onAppear {
if let acct = profile.passwordAccount {
password = KeychainHelper.load(account: acct) ?? ""
savePassword = !password.isEmpty
if let acct = profile.passwordAccount,
let stored = KeychainHelper.load(account: acct), !stored.isEmpty {
storedPasswordPresent = true
savePassword = true
}
}
}
var sshpassHint: String {
if SSHService.findSshpass() != nil {
return "sshpass gefunden → Auto-Login mit Keychain-Passwort aktiv, kein Tippen nötig."
} else {
return "Ohne sshpass: Passwort wird beim Verbinden im Terminal abgefragt. Für Auto-Login: brew install sshpass."
}
}
func deleteStoredPassword() {
if let acct = profile.passwordAccount {
KeychainHelper.delete(account: acct)
}
password = ""
savePassword = false
storedPasswordPresent = false
}
}