mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-05-08 16:38:58 +02:00
WIP
This commit is contained in:
122
Sources/SecretAgent/App.swift
Normal file
122
Sources/SecretAgent/App.swift
Normal file
@@ -0,0 +1,122 @@
|
||||
import Cocoa
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import SecureEnclaveSecretKit
|
||||
import SmartCardSecretKit
|
||||
import SecretAgentKit
|
||||
import Brief
|
||||
import Observation
|
||||
import Common
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct SecretAgent: App {
|
||||
|
||||
@MainActor private let storeList: SecretStoreList = {
|
||||
let list = SecretStoreList()
|
||||
let cryptoKit = SecureEnclave.Store()
|
||||
let migrator = SecureEnclave.CryptoKitMigrator()
|
||||
try? migrator.migrate(to: cryptoKit)
|
||||
list.add(store: cryptoKit)
|
||||
list.add(store: SmartCard.Store())
|
||||
return list
|
||||
}()
|
||||
private let updater = Updater(checkOnLaunch: true)
|
||||
private let notifier = Notifier()
|
||||
private let authenticationHandler = AuthenticationHandler()
|
||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(directory: URL.publicKeyDirectory)
|
||||
|
||||
@State var pending: ([[SignatureRequest]], (Set<SignatureRequest>) async throws -> Void)?
|
||||
@Environment(\.openWindow) var openWindow
|
||||
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "App")
|
||||
@SceneBuilder var body: some Scene {
|
||||
MenuBarExtra(isInserted: .constant(false)) {
|
||||
EmptyView()
|
||||
} label: {
|
||||
Image(systemName: "lock")
|
||||
.task {
|
||||
await notifier.registerPersistenceHandler {
|
||||
try await authenticationHandler.persistAuthentication(secret: $0, forDuration: $1)
|
||||
}
|
||||
}
|
||||
.task {
|
||||
let socketController = SocketController(path: URL.socketPath)
|
||||
let agent = Agent(storeList: storeList, authenticationHandler: authenticationHandler, witness: notifier)
|
||||
for await session in socketController.sessions {
|
||||
Task {
|
||||
let inputParser = try await XPCAgentInputParser()
|
||||
do {
|
||||
for await message in session.messages {
|
||||
let request = try await inputParser.parse(data: message)
|
||||
let agentResponse = await agent.handle(request: request, provenance: session.provenance)
|
||||
try session.write(agentResponse)
|
||||
}
|
||||
} catch {
|
||||
try session.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// .task {
|
||||
// let socketController = SocketController(path: URL.agentHomeURL.appendingPathComponent("socket-two.ssh").path())
|
||||
// let socketController = SocketController(path: "/Users/max/Downloads/test.ssh")
|
||||
// let agent = Agent(storeList: storeList, authenticationHandler: authenticationHandler, witness: notifier)
|
||||
// for await session in socketController.sessions {
|
||||
// Task {
|
||||
// let inputParser = try await XPCAgentInputParser()
|
||||
// do {
|
||||
// for await message in session.messages {
|
||||
// let request = try await inputParser.parse(data: message)
|
||||
// let agentResponse = await agent.handle(request: request, provenance: session.provenance)
|
||||
// try session.write(agentResponse)
|
||||
// }
|
||||
// } catch {
|
||||
// try session.close()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
.task {
|
||||
for await _ in NotificationCenter.default.notifications(named: .secretStoreReloaded) {
|
||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true)
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await authenticationHandler.setBatchAuthHandler { @MainActor pending, authorize in
|
||||
self.pending = (pending, authorize)
|
||||
openWindow(id: String(describing: BatchedRequestsView.self))
|
||||
}
|
||||
|
||||
}
|
||||
.task {
|
||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true)
|
||||
notifier.prompt()
|
||||
_ = withObservationTracking {
|
||||
updater.update
|
||||
} onChange: { [updater, notifier] in
|
||||
Task {
|
||||
guard !updater.currentVersion.isTestBuild else { return }
|
||||
await notifier.notify(update: updater.update!) { release in
|
||||
await updater.ignore(release: release)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowGroup(id: String(describing: BatchedRequestsView.self)) {
|
||||
pendingView
|
||||
}
|
||||
.windowStyle(.hiddenTitleBar)
|
||||
.windowResizability(.contentSize)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var pendingView: some View {
|
||||
if let (requests, authorize) = pending {
|
||||
BatchedRequestsView(pending: requests, review: authorize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import Cocoa
|
||||
import OSLog
|
||||
import SecretKit
|
||||
import SecureEnclaveSecretKit
|
||||
import SmartCardSecretKit
|
||||
import SecretAgentKit
|
||||
import Brief
|
||||
import Observation
|
||||
import Common
|
||||
|
||||
@main
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@MainActor private let storeList: SecretStoreList = {
|
||||
let list = SecretStoreList()
|
||||
let cryptoKit = SecureEnclave.Store()
|
||||
let migrator = SecureEnclave.CryptoKitMigrator()
|
||||
try? migrator.migrate(to: cryptoKit)
|
||||
list.add(store: cryptoKit)
|
||||
list.add(store: SmartCard.Store())
|
||||
return list
|
||||
}()
|
||||
private let updater = Updater(checkOnLaunch: true)
|
||||
private let notifier = Notifier()
|
||||
private let authenticationHandler = AuthenticationHandler { pending, authorize in
|
||||
print(pending)
|
||||
print("Waiting")
|
||||
// Task {
|
||||
try await Task.sleep(for: .seconds(3))
|
||||
try await authorize(pending)
|
||||
// }
|
||||
}
|
||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(directory: URL.publicKeyDirectory)
|
||||
private lazy var agent: Agent = {
|
||||
Agent(storeList: storeList, authenticationHandler: authenticationHandler, witness: notifier)
|
||||
}()
|
||||
private lazy var socketController: SocketController = {
|
||||
let path = URL.socketPath as String
|
||||
return SocketController(path: path)
|
||||
}()
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate")
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
logger.debug("SecretAgent finished launching")
|
||||
Task {
|
||||
for await session in socketController.sessions {
|
||||
Task {
|
||||
let inputParser = try await XPCAgentInputParser()
|
||||
do {
|
||||
for await message in session.messages {
|
||||
let request = try await inputParser.parse(data: message)
|
||||
let agentResponse = await agent.handle(request: request, provenance: session.provenance)
|
||||
try session.write(agentResponse)
|
||||
}
|
||||
} catch {
|
||||
try session.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Task { [notifier, authenticationHandler] in
|
||||
await notifier.registerPersistenceHandler {
|
||||
try await authenticationHandler.persistAuthentication(secret: $0, forDuration: $1)
|
||||
}
|
||||
}
|
||||
Task {
|
||||
for await _ in NotificationCenter.default.notifications(named: .secretStoreReloaded) {
|
||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true)
|
||||
}
|
||||
}
|
||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true)
|
||||
notifier.prompt()
|
||||
_ = withObservationTracking {
|
||||
updater.update
|
||||
} onChange: { [updater, notifier] in
|
||||
Task {
|
||||
guard !updater.currentVersion.isTestBuild else { return }
|
||||
await notifier.notify(update: updater.update!) { release in
|
||||
await updater.ignore(release: release)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
58
Sources/SecretAgent/BatchedRequestsView.swift
Normal file
58
Sources/SecretAgent/BatchedRequestsView.swift
Normal file
@@ -0,0 +1,58 @@
|
||||
import SwiftUI
|
||||
import SecretKit
|
||||
import SecretAgentKit
|
||||
import SmartCardSecretKit
|
||||
|
||||
struct BatchedRequestsView: View {
|
||||
|
||||
let pending: [[SignatureRequest]]
|
||||
let review: (Set<SignatureRequest>) async throws -> Void
|
||||
|
||||
init(pending: [[SignatureRequest]], review: @escaping (Set<SignatureRequest>) async throws -> Void) {
|
||||
self.pending = pending
|
||||
self.review = review
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
// .padding()
|
||||
Form {
|
||||
// Text("Multiple authenticated requests are pending. You can approve them batches, or request they all proceed individually.")
|
||||
ForEach(Array(pending.enumerated()), id: \.offset) { group in
|
||||
Section {
|
||||
ForEach(Array(group.element.enumerated()), id: \.offset) { pending in
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
Text(pending.element.provenance.origin.displayName)
|
||||
.font(.headline)
|
||||
Text(pending.element.provenance.date.formatted())
|
||||
.font(.footnote)
|
||||
}
|
||||
Spacer()
|
||||
Button("Review") {
|
||||
Task {
|
||||
try await review([pending.element])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
HStack {
|
||||
Text("\(group.element.first!.provenance.origin.displayName) - \(group.element.first!.secret.name)")
|
||||
Spacer()
|
||||
Button("Review All") {
|
||||
Task {
|
||||
try await review(Set(group.element))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.formStyle(.grouped)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,10 +16,10 @@
|
||||
<string>1</string>
|
||||
<key>com.apple.security.hardened-process.hardened-heap</key>
|
||||
<true/>
|
||||
<key>com.apple.security.smartcard</key>
|
||||
<true/>
|
||||
<key>com.apple.security.hardened-process.platform-restrictions-string</key>
|
||||
<string>2</string>
|
||||
<key>com.apple.security.smartcard</key>
|
||||
<true/>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||
|
||||
Reference in New Issue
Block a user