From 74a516f6fd76ff8918f28cc8fae2c667939d410c Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Mon, 26 Aug 2024 13:56:49 -0700 Subject: [PATCH] WIP --- .../Sources/SecretAgentKit/Agent.swift | 2 +- .../SecretKit/Erasers/AnySecretStore.swift | 14 ++-- .../Sources/SecretKit/SecretStoreList.swift | 2 +- .../Sources/SecretKit/Types/SecretStore.swift | 2 +- .../SecureEnclaveStore.swift | 9 ++- Sources/SecretAgent/AppDelegate.swift | 37 ++++++---- Sources/SecretAgent/Notifier.swift | 74 +++++++++---------- Sources/Secretive/Views/EmptyStoreView.swift | 4 +- 8 files changed, 78 insertions(+), 66 deletions(-) diff --git a/Sources/Packages/Sources/SecretAgentKit/Agent.swift b/Sources/Packages/Sources/SecretAgentKit/Agent.swift index c27c94b..59476fb 100644 --- a/Sources/Packages/Sources/SecretAgentKit/Agent.swift +++ b/Sources/Packages/Sources/SecretAgentKit/Agent.swift @@ -5,7 +5,7 @@ import SecretKit import AppKit /// The `Agent` is an implementation of an SSH agent. It manages coordination and access between a socket, traces requests, notifies witnesses and passes requests to stores. -public actor Agent { +@MainActor public final class Agent { private let storeList: SecretStoreList private let witness: SigningWitness? diff --git a/Sources/Packages/Sources/SecretKit/Erasers/AnySecretStore.swift b/Sources/Packages/Sources/SecretKit/Erasers/AnySecretStore.swift index bf5a74d..68b023d 100644 --- a/Sources/Packages/Sources/SecretKit/Erasers/AnySecretStore.swift +++ b/Sources/Packages/Sources/SecretKit/Erasers/AnySecretStore.swift @@ -2,13 +2,13 @@ import Foundation import Combine /// Type eraser for SecretStore. -public class AnySecretStore: SecretStore { +@MainActor public class AnySecretStore: SecretStore { let base: Any private let _isAvailable: () -> Bool private let _id: () -> UUID private let _name: () -> String - private let _secrets: () -> [AnySecret] + private let _secrets: @MainActor () -> [AnySecret] private let _sign: (Data, AnySecret, SigningRequestProvenance) throws -> Data private let _verify: (Data, Data, AnySecret) throws -> Bool private let _existingPersistedAuthenticationContext: (AnySecret) -> PersistedAuthenticationContext? @@ -22,7 +22,7 @@ public class AnySecretStore: SecretStore { _isAvailable = { secretStore.isAvailable } _name = { secretStore.name } _id = { secretStore.id } - _secrets = { secretStore.secrets.map { AnySecret($0) } } + _secrets = { @MainActor in secretStore.secrets.map { AnySecret($0) } } _sign = { try secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2) } _verify = { try secretStore.verify(signature: $0, for: $1, with: $2.base as! SecretStoreType.SecretType) } _existingPersistedAuthenticationContext = { secretStore.existingPersistedAuthenticationContext(secret: $0.base as! SecretStoreType.SecretType) } @@ -37,8 +37,10 @@ public class AnySecretStore: SecretStore { return _isAvailable() } - public var id: UUID { - return _id() + nonisolated public var id: UUID { +// return _id() + // FIXME: THIS + UUID() } public var name: String { @@ -71,7 +73,7 @@ public class AnySecretStore: SecretStore { } -public final class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable { +@MainActor public final class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable { private let _create: (String, Bool) throws -> Void private let _delete: (AnySecret) throws -> Void diff --git a/Sources/Packages/Sources/SecretKit/SecretStoreList.swift b/Sources/Packages/Sources/SecretKit/SecretStoreList.swift index eb8456f..43685ac 100644 --- a/Sources/Packages/Sources/SecretKit/SecretStoreList.swift +++ b/Sources/Packages/Sources/SecretKit/SecretStoreList.swift @@ -2,7 +2,7 @@ import Foundation import Combine /// A "Store Store," which holds a list of type-erased stores. -public final class SecretStoreList: ObservableObject { +@MainActor public final class SecretStoreList: ObservableObject { /// The Stores managed by the SecretStoreList. @Published public var stores: [AnySecretStore] = [] diff --git a/Sources/Packages/Sources/SecretKit/Types/SecretStore.swift b/Sources/Packages/Sources/SecretKit/Types/SecretStore.swift index f780201..ac35e4c 100644 --- a/Sources/Packages/Sources/SecretKit/Types/SecretStore.swift +++ b/Sources/Packages/Sources/SecretKit/Types/SecretStore.swift @@ -2,7 +2,7 @@ import Foundation import Combine /// Manages access to Secrets, and performs signature operations on data using those Secrets. -public protocol SecretStore: ObservableObject, Identifiable { +@MainActor public protocol SecretStore: ObservableObject, Identifiable, Sendable { associatedtype SecretType: Secret diff --git a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift index b5f8fa3..85d3ff1 100644 --- a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift +++ b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift @@ -8,7 +8,7 @@ import SecretKit extension SecureEnclave { /// An implementation of Store backed by the Secure Enclave. - public final class Store: SecretStoreModifiable, Sendable { + @MainActor public final class Store: SecretStoreModifiable, Sendable { public var isAvailable: Bool { // For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false @@ -28,8 +28,11 @@ extension SecureEnclave { /// Initializes a Store. public init() { - DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in - self.reloadSecretsInternal(notifyAgent: false) + DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in + // We've explicitly specified main queue as an argument. + MainActor.assumeIsolated { + self.reloadSecretsInternal(notifyAgent: false) + } } loadSecrets() } diff --git a/Sources/SecretAgent/AppDelegate.swift b/Sources/SecretAgent/AppDelegate.swift index ab6a3cd..384f708 100644 --- a/Sources/SecretAgent/AppDelegate.swift +++ b/Sources/SecretAgent/AppDelegate.swift @@ -7,8 +7,10 @@ import SmartCardSecretKit import SecretAgentKit import Brief -@NSApplicationMain -class AppDelegate: NSObject, NSApplicationDelegate { +import SwiftUI + +@main +struct SecretiveApp: App { private let storeList: SecretStoreList = { let list = SecretStoreList() @@ -29,19 +31,24 @@ class AppDelegate: NSObject, NSApplicationDelegate { private var updateSink: AnyCancellable? private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate") - func applicationDidFinishLaunching(_ aNotification: Notification) { - logger.debug("SecretAgent finished launching") - DispatchQueue.main.async { - self.socketController.handler = self.agent.handle(reader:writer:) - } - NotificationCenter.default.addObserver(forName: .secretStoreReloaded, object: nil, queue: .main) { [self] _ in - try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true) - } - try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true) - notifier.prompt() - updateSink = updater.$update.sink { update in - guard let update = update else { return } - self.notifier.notify(update: update, ignore: self.updater.ignore(release:)) + var body: some Scene { + WindowGroup { + Text("Hello") + .onAppear { +// logger.debug("SecretAgent finished launching") +// DispatchQueue.main.async { +// self.socketController.handler = self.agent.handle(reader:writer:) +// } +// NotificationCenter.default.addObserver(forName: .secretStoreReloaded, object: nil, queue: .main) { [self] _ in +// try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true) +// } +// try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true) +// notifier.prompt() +// updateSink = updater.$update.sink { update in +// guard let update = update else { return } +// self.notifier.notify(update: update, ignore: self.updater.ignore(release:)) +// } + } } } diff --git a/Sources/SecretAgent/Notifier.swift b/Sources/SecretAgent/Notifier.swift index d442623..ebb67b1 100644 --- a/Sources/SecretAgent/Notifier.swift +++ b/Sources/SecretAgent/Notifier.swift @@ -143,42 +143,42 @@ extension Notifier { } - func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { - let category = response.notification.request.content.categoryIdentifier - switch category { - case Notifier.Constants.updateCategoryIdentitifier: - handleUpdateResponse(response: response) - case Notifier.Constants.persistAuthenticationCategoryIdentitifier: - handlePersistAuthenticationResponse(response: response) - default: - break - } - - completionHandler() - } - - func handleUpdateResponse(response: UNNotificationResponse) { - guard let update = release else { return } - switch response.actionIdentifier { - case Notifier.Constants.updateActionIdentitifier, UNNotificationDefaultActionIdentifier: - NSWorkspace.shared.open(update.html_url) - case Notifier.Constants.ignoreActionIdentitifier: - ignore?(update) - default: - fatalError() - } - } - - func handlePersistAuthenticationResponse(response: UNNotificationResponse) { - guard let secretID = response.notification.request.content.userInfo[Notifier.Constants.persistSecretIDKey] as? String, let secret = pendingPersistableSecrets[secretID], - let storeID = response.notification.request.content.userInfo[Notifier.Constants.persistStoreIDKey] as? String, let store = pendingPersistableStores[storeID] - else { return } - pendingPersistableSecrets[secretID] = nil - persistAuthentication?(secret, store, persistOptions[response.actionIdentifier]) - } - - func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { - completionHandler([.list, .banner]) - } +// func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { +// let category = response.notification.request.content.categoryIdentifier +// switch category { +// case Notifier.Constants.updateCategoryIdentitifier: +// handleUpdateResponse(response: response) +// case Notifier.Constants.persistAuthenticationCategoryIdentitifier: +// handlePersistAuthenticationResponse(response: response) +// default: +// break +// } +// +// completionHandler() +// } +// +// func handleUpdateResponse(response: UNNotificationResponse) { +// guard let update = release else { return } +// switch response.actionIdentifier { +// case Notifier.Constants.updateActionIdentitifier, UNNotificationDefaultActionIdentifier: +// NSWorkspace.shared.open(update.html_url) +// case Notifier.Constants.ignoreActionIdentitifier: +// ignore?(update) +// default: +// fatalError() +// } +// } +// +// func handlePersistAuthenticationResponse(response: UNNotificationResponse) { +// guard let secretID = response.notification.request.content.userInfo[Notifier.Constants.persistSecretIDKey] as? String, let secret = pendingPersistableSecrets[secretID], +// let storeID = response.notification.request.content.userInfo[Notifier.Constants.persistStoreIDKey] as? String, let store = pendingPersistableStores[storeID] +// else { return } +// pendingPersistableSecrets[secretID] = nil +// persistAuthentication?(secret, store, persistOptions[response.actionIdentifier]) +// } +// +// func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { +// completionHandler([.list, .banner]) +// } } diff --git a/Sources/Secretive/Views/EmptyStoreView.swift b/Sources/Secretive/Views/EmptyStoreView.swift index 6a88c2b..86646e8 100644 --- a/Sources/Secretive/Views/EmptyStoreView.swift +++ b/Sources/Secretive/Views/EmptyStoreView.swift @@ -22,8 +22,8 @@ struct EmptyStoreView: View { extension EmptyStoreView { enum Constants { - static let emptyStoreModifiableTag: AnyHashable = "emptyStoreModifiableTag" - static let emptyStoreTag: AnyHashable = "emptyStoreTag" + static let emptyStoreModifiableTag = "emptyStoreModifiableTag" + static let emptyStoreTag = "emptyStoreTag" } }