Passwort nutzen: Auto-Login via sshpass + Keychain, Editor mit Anzeige/Loeschen, Doku
This commit is contained in:
@@ -39,6 +39,43 @@ enum SSHService {
|
||||
NSPasteboard.general.clearContents()
|
||||
NSPasteboard.general.setString(cmd, forType: .string)
|
||||
}
|
||||
|
||||
// MARK: - Passwort / Auto-Login
|
||||
|
||||
/// Gespeichertes Login-Secret: erst Profil-Passwort, dann Key-Passphrase (Keychain, nie JSON).
|
||||
static func loginSecret(for profile: ConnectionProfile, key: StoredSSHKey?) -> String? {
|
||||
if let acct = profile.passwordAccount,
|
||||
let pw = KeychainHelper.load(account: acct), !pw.isEmpty {
|
||||
return pw
|
||||
}
|
||||
if let key, let acct = key.passphraseAccount,
|
||||
let pp = KeychainHelper.load(account: acct), !pp.isEmpty {
|
||||
return pp
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
static func findSshpass() -> String? {
|
||||
for p in ["/opt/homebrew/bin/sshpass", "/usr/local/bin/sshpass", "/usr/bin/sshpass"] {
|
||||
if FileManager.default.isExecutableFile(atPath: p) { return p }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// true = beim Verbinden wird ohne Tippen eingeloggt (sshpass + Keychain-Secret).
|
||||
static func willAutoLogin(profile: ConnectionProfile, key: StoredSSHKey?) -> Bool {
|
||||
loginSecret(for: profile, key: key) != nil && findSshpass() != nil
|
||||
}
|
||||
|
||||
/// Reine Funktion (testbar): mit Secret+sshpass -> sshpass -e … + SSHPASS-Env,
|
||||
/// sonst direkter Aufruf. Passwort steht NIE in der Befehlszeile (nur im Env).
|
||||
static func sessionConfig(executable: String, args: [String], password: String?, sshpassPath: String?)
|
||||
-> (executable: String, arguments: [String], extraEnv: [String: String], autoLogin: Bool) {
|
||||
if let password, !password.isEmpty, let sshpass = sshpassPath, !sshpass.isEmpty {
|
||||
return (sshpass, ["-e", executable] + args, ["SSHPASS": password], true)
|
||||
}
|
||||
return (executable, args, [:], false)
|
||||
}
|
||||
}
|
||||
|
||||
enum RDPService {
|
||||
|
||||
@@ -20,9 +20,11 @@ enum PTYState: Equatable {
|
||||
final class PTYSession: ObservableObject {
|
||||
@Published private(set) var lines: [[StyledText]] = [[]]
|
||||
@Published private(set) var state: PTYState = .never
|
||||
@Published private(set) var autoLogin = false
|
||||
|
||||
var executable = ""
|
||||
var arguments: [String] = []
|
||||
var customEnv: [String: String] = [:]
|
||||
|
||||
private var masterFD: Int32 = -1
|
||||
private var process: Process?
|
||||
@@ -48,6 +50,10 @@ final class PTYSession: ObservableObject {
|
||||
self.arguments = arguments
|
||||
}
|
||||
|
||||
func setAutoLogin(_ value: Bool) {
|
||||
autoLogin = value
|
||||
}
|
||||
|
||||
func start() {
|
||||
cleanupSync()
|
||||
epoch += 1
|
||||
@@ -79,6 +85,7 @@ final class PTYSession: ObservableObject {
|
||||
var env = ProcessInfo.processInfo.environment
|
||||
env["TERM"] = "xterm-256color"
|
||||
if env["LANG"] == nil { env["LANG"] = "de_DE.UTF-8" }
|
||||
for (k, v) in customEnv { env[k] = v }
|
||||
p.environment = env
|
||||
// closeOnDealloc=false: Parent schließt slave explizit nach run().
|
||||
p.standardInput = FileHandle(fileDescriptor: slave)
|
||||
@@ -282,11 +289,22 @@ final class TerminalManager: ObservableObject {
|
||||
func session(for profile: ConnectionProfile, key: StoredSSHKey?) -> PTYSession {
|
||||
if let existing = sessions[profile.id] { return existing }
|
||||
let s = PTYSession()
|
||||
let baseExe: String
|
||||
let baseArgs: [String]
|
||||
if profile.proto == .sftp {
|
||||
s.configure(executable: "/usr/bin/sftp", arguments: ["-P", "\(profile.port)"] + (key.map { ["-i", $0.privateKeyPath] } ?? []) + ["\(profile.username)@\(profile.host)"])
|
||||
baseExe = "/usr/bin/sftp"
|
||||
baseArgs = ["-P", "\(profile.port)"] + (key.map { ["-i", $0.privateKeyPath] } ?? []) + ["\(profile.username)@\(profile.host)"]
|
||||
} else {
|
||||
s.configure(executable: "/usr/bin/ssh", arguments: SSHService.sshArguments(for: profile, key: key))
|
||||
baseExe = "/usr/bin/ssh"
|
||||
baseArgs = SSHService.sshArguments(for: profile, key: key)
|
||||
}
|
||||
// Auto-Login nur mit Keychain-Secret + sshpass, sonst interaktiver Prompt im Terminal
|
||||
let cfg = SSHService.sessionConfig(executable: baseExe, args: baseArgs,
|
||||
password: SSHService.loginSecret(for: profile, key: key),
|
||||
sshpassPath: SSHService.findSshpass())
|
||||
s.configure(executable: cfg.executable, arguments: cfg.arguments)
|
||||
s.customEnv = cfg.extraEnv
|
||||
s.setAutoLogin(cfg.autoLogin)
|
||||
sessions[profile.id] = s
|
||||
return s
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user