RemminaMac 0.1.0: SSH/RDP-Client für macOS (SwiftUI, Remmina-Layout, Profile, SSH-Keys, Docs, Release-Skripte)
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
|
||||
struct SSHKeysView: View {
|
||||
@EnvironmentObject var store: ProfileStore
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@State private var showingAdd = false
|
||||
@State private var newName = ""
|
||||
@State private var newPath = ""
|
||||
@State private var newPassphrase = ""
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("SSH-Keys verwalten").font(.headline).padding()
|
||||
List {
|
||||
ForEach(store.sshKeys) { key in
|
||||
VStack(alignment: .leading) {
|
||||
Text(key.name).font(.headline)
|
||||
Text(key.privateKeyPath).font(.caption).foregroundStyle(.secondary).textSelection(.enabled)
|
||||
}
|
||||
.contextMenu {
|
||||
Button("Löschen", role: .destructive) {
|
||||
store.sshKeys.removeAll { $0.id == key.id }
|
||||
store.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
.onDelete { idx in
|
||||
store.sshKeys.remove(atOffsets: idx)
|
||||
store.save()
|
||||
}
|
||||
}
|
||||
if showingAdd {
|
||||
Form {
|
||||
TextField("Name", text: $newName)
|
||||
HStack {
|
||||
TextField("Privater Key-Pfad (~/.ssh/id_ed25519)", text: $newPath)
|
||||
Button("Wählen…") { chooseFile() }
|
||||
}
|
||||
SecureField("Passphrase (Keychain)", text: $newPassphrase)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
HStack {
|
||||
Button("Import aus ~/.ssh") { importSSHDir() }
|
||||
Button(showingAdd ? "Abbrechen" : "Hinzufügen") { showingAdd.toggle() }
|
||||
Spacer()
|
||||
if showingAdd {
|
||||
Button("Speichern") { saveNew() }.keyboardShortcut(.defaultAction).disabled(newName.isEmpty || newPath.isEmpty)
|
||||
} else {
|
||||
Button("Neuen Key generieren (ssh-keygen)") { generateKey() }
|
||||
Button("Fertig") { dismiss() }.keyboardShortcut(.defaultAction)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.frame(width: 600, height: 500)
|
||||
}
|
||||
|
||||
func chooseFile() {
|
||||
let panel = NSOpenPanel()
|
||||
panel.canChooseFiles = true
|
||||
panel.allowsMultipleSelection = false
|
||||
panel.directoryURL = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".ssh")
|
||||
if panel.runModal() == .OK, let url = panel.url {
|
||||
newPath = url.path
|
||||
if newName.isEmpty { newName = url.lastPathComponent }
|
||||
}
|
||||
}
|
||||
|
||||
func saveNew() {
|
||||
var entry = StoredSSHKey(name: newName, privateKeyPath: newPath, publicKeyPath: newPath + ".pub")
|
||||
if !newPassphrase.isEmpty {
|
||||
entry.passphraseAccount = "sshkey-\(entry.id.uuidString)"
|
||||
_ = KeychainHelper.save(newPassphrase, account: entry.passphraseAccount!)
|
||||
}
|
||||
store.sshKeys.append(entry)
|
||||
store.save()
|
||||
newName = ""; newPath = ""; newPassphrase = ""; showingAdd = false
|
||||
}
|
||||
|
||||
func importSSHDir() {
|
||||
let sshDir = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".ssh").path
|
||||
guard let files = try? FileManager.default.contentsOfDirectory(atPath: sshDir) else { return }
|
||||
for f in files where !f.hasSuffix(".pub") && !f.contains("known_hosts") && !f.contains("config") {
|
||||
let full = (sshDir as NSString).appendingPathComponent(f)
|
||||
if store.sshKeys.contains(where: { $0.privateKeyPath == full }) { continue }
|
||||
store.sshKeys.append(StoredSSHKey(name: f, privateKeyPath: full))
|
||||
}
|
||||
store.save()
|
||||
}
|
||||
|
||||
func generateKey() {
|
||||
let task = Process()
|
||||
task.executableURL = URL(fileURLWithPath: "/usr/bin/ssh-keygen")
|
||||
let outPath = FileManager.default.homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".ssh/remminamac_\(Int(Date().timeIntervalSince1970))").path
|
||||
task.arguments = ["-t", "ed25519", "-f", outPath, "-N", ""]
|
||||
try? task.run()
|
||||
task.waitUntilExit()
|
||||
store.sshKeys.append(StoredSSHKey(name: URL(fileURLWithPath: outPath).lastPathComponent, privateKeyPath: outPath, publicKeyPath: outPath + ".pub"))
|
||||
store.save()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user