WIP
This commit is contained in:
parent
56a662a9dd
commit
74a516f6fd
|
@ -5,7 +5,7 @@ import SecretKit
|
||||||
import AppKit
|
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.
|
/// 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 storeList: SecretStoreList
|
||||||
private let witness: SigningWitness?
|
private let witness: SigningWitness?
|
||||||
|
|
|
@ -2,13 +2,13 @@ import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
/// Type eraser for SecretStore.
|
/// Type eraser for SecretStore.
|
||||||
public class AnySecretStore: SecretStore {
|
@MainActor public class AnySecretStore: SecretStore {
|
||||||
|
|
||||||
let base: Any
|
let base: Any
|
||||||
private let _isAvailable: () -> Bool
|
private let _isAvailable: () -> Bool
|
||||||
private let _id: () -> UUID
|
private let _id: () -> UUID
|
||||||
private let _name: () -> String
|
private let _name: () -> String
|
||||||
private let _secrets: () -> [AnySecret]
|
private let _secrets: @MainActor () -> [AnySecret]
|
||||||
private let _sign: (Data, AnySecret, SigningRequestProvenance) throws -> Data
|
private let _sign: (Data, AnySecret, SigningRequestProvenance) throws -> Data
|
||||||
private let _verify: (Data, Data, AnySecret) throws -> Bool
|
private let _verify: (Data, Data, AnySecret) throws -> Bool
|
||||||
private let _existingPersistedAuthenticationContext: (AnySecret) -> PersistedAuthenticationContext?
|
private let _existingPersistedAuthenticationContext: (AnySecret) -> PersistedAuthenticationContext?
|
||||||
|
@ -22,7 +22,7 @@ public class AnySecretStore: SecretStore {
|
||||||
_isAvailable = { secretStore.isAvailable }
|
_isAvailable = { secretStore.isAvailable }
|
||||||
_name = { secretStore.name }
|
_name = { secretStore.name }
|
||||||
_id = { secretStore.id }
|
_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) }
|
_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) }
|
_verify = { try secretStore.verify(signature: $0, for: $1, with: $2.base as! SecretStoreType.SecretType) }
|
||||||
_existingPersistedAuthenticationContext = { secretStore.existingPersistedAuthenticationContext(secret: $0.base as! SecretStoreType.SecretType) }
|
_existingPersistedAuthenticationContext = { secretStore.existingPersistedAuthenticationContext(secret: $0.base as! SecretStoreType.SecretType) }
|
||||||
|
@ -37,8 +37,10 @@ public class AnySecretStore: SecretStore {
|
||||||
return _isAvailable()
|
return _isAvailable()
|
||||||
}
|
}
|
||||||
|
|
||||||
public var id: UUID {
|
nonisolated public var id: UUID {
|
||||||
return _id()
|
// return _id()
|
||||||
|
// FIXME: THIS
|
||||||
|
UUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
public var name: String {
|
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 _create: (String, Bool) throws -> Void
|
||||||
private let _delete: (AnySecret) throws -> Void
|
private let _delete: (AnySecret) throws -> Void
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
/// A "Store Store," which holds a list of type-erased stores.
|
/// 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.
|
/// The Stores managed by the SecretStoreList.
|
||||||
@Published public var stores: [AnySecretStore] = []
|
@Published public var stores: [AnySecretStore] = []
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
/// Manages access to Secrets, and performs signature operations on data using those Secrets.
|
/// 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
|
associatedtype SecretType: Secret
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import SecretKit
|
||||||
extension SecureEnclave {
|
extension SecureEnclave {
|
||||||
|
|
||||||
/// An implementation of Store backed by the Secure Enclave.
|
/// 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 {
|
public var isAvailable: Bool {
|
||||||
// For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false
|
// For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false
|
||||||
|
@ -29,8 +29,11 @@ extension SecureEnclave {
|
||||||
/// Initializes a Store.
|
/// Initializes a Store.
|
||||||
public init() {
|
public init() {
|
||||||
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
|
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)
|
self.reloadSecretsInternal(notifyAgent: false)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
loadSecrets()
|
loadSecrets()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,10 @@ import SmartCardSecretKit
|
||||||
import SecretAgentKit
|
import SecretAgentKit
|
||||||
import Brief
|
import Brief
|
||||||
|
|
||||||
@NSApplicationMain
|
import SwiftUI
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
|
||||||
|
@main
|
||||||
|
struct SecretiveApp: App {
|
||||||
|
|
||||||
private let storeList: SecretStoreList = {
|
private let storeList: SecretStoreList = {
|
||||||
let list = SecretStoreList()
|
let list = SecretStoreList()
|
||||||
|
@ -29,19 +31,24 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
private var updateSink: AnyCancellable?
|
private var updateSink: AnyCancellable?
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate")
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
var body: some Scene {
|
||||||
logger.debug("SecretAgent finished launching")
|
WindowGroup {
|
||||||
DispatchQueue.main.async {
|
Text("Hello")
|
||||||
self.socketController.handler = self.agent.handle(reader:writer:)
|
.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:))
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
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:))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,42 +143,42 @@ extension Notifier {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
// func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
||||||
let category = response.notification.request.content.categoryIdentifier
|
// let category = response.notification.request.content.categoryIdentifier
|
||||||
switch category {
|
// switch category {
|
||||||
case Notifier.Constants.updateCategoryIdentitifier:
|
// case Notifier.Constants.updateCategoryIdentitifier:
|
||||||
handleUpdateResponse(response: response)
|
// handleUpdateResponse(response: response)
|
||||||
case Notifier.Constants.persistAuthenticationCategoryIdentitifier:
|
// case Notifier.Constants.persistAuthenticationCategoryIdentitifier:
|
||||||
handlePersistAuthenticationResponse(response: response)
|
// handlePersistAuthenticationResponse(response: response)
|
||||||
default:
|
// default:
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
completionHandler()
|
// completionHandler()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
func handleUpdateResponse(response: UNNotificationResponse) {
|
// func handleUpdateResponse(response: UNNotificationResponse) {
|
||||||
guard let update = release else { return }
|
// guard let update = release else { return }
|
||||||
switch response.actionIdentifier {
|
// switch response.actionIdentifier {
|
||||||
case Notifier.Constants.updateActionIdentitifier, UNNotificationDefaultActionIdentifier:
|
// case Notifier.Constants.updateActionIdentitifier, UNNotificationDefaultActionIdentifier:
|
||||||
NSWorkspace.shared.open(update.html_url)
|
// NSWorkspace.shared.open(update.html_url)
|
||||||
case Notifier.Constants.ignoreActionIdentitifier:
|
// case Notifier.Constants.ignoreActionIdentitifier:
|
||||||
ignore?(update)
|
// ignore?(update)
|
||||||
default:
|
// default:
|
||||||
fatalError()
|
// fatalError()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
func handlePersistAuthenticationResponse(response: UNNotificationResponse) {
|
// func handlePersistAuthenticationResponse(response: UNNotificationResponse) {
|
||||||
guard let secretID = response.notification.request.content.userInfo[Notifier.Constants.persistSecretIDKey] as? String, let secret = pendingPersistableSecrets[secretID],
|
// 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]
|
// let storeID = response.notification.request.content.userInfo[Notifier.Constants.persistStoreIDKey] as? String, let store = pendingPersistableStores[storeID]
|
||||||
else { return }
|
// else { return }
|
||||||
pendingPersistableSecrets[secretID] = nil
|
// pendingPersistableSecrets[secretID] = nil
|
||||||
persistAuthentication?(secret, store, persistOptions[response.actionIdentifier])
|
// persistAuthentication?(secret, store, persistOptions[response.actionIdentifier])
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
// func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||||
completionHandler([.list, .banner])
|
// completionHandler([.list, .banner])
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ struct EmptyStoreView: View {
|
||||||
extension EmptyStoreView {
|
extension EmptyStoreView {
|
||||||
|
|
||||||
enum Constants {
|
enum Constants {
|
||||||
static let emptyStoreModifiableTag: AnyHashable = "emptyStoreModifiableTag"
|
static let emptyStoreModifiableTag = "emptyStoreModifiableTag"
|
||||||
static let emptyStoreTag: AnyHashable = "emptyStoreTag"
|
static let emptyStoreTag = "emptyStoreTag"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue