105 lines
4.3 KiB
Swift
105 lines
4.3 KiB
Swift
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()
|
|
}
|
|
}
|