From d80d2ca6562fb54adea684775c78c520455a3923 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sun, 2 Jan 2022 22:15:18 -0800 Subject: [PATCH] XPC scaffolding --- Sources/Packages/Package.swift | 14 +++--- .../AgentProtocol.swift | 12 +++++ .../SecretKit/Erasers/AnySecretStore.swift | 6 +++ .../Sources/SecretKit/Types/SecretStore.swift | 3 ++ .../SecureEnclaveStore.swift | 26 ++++------- Sources/SecretAgent/AppDelegate.swift | 42 ++++++++++++++--- Sources/SecretAgent/SecretAgent.entitlements | 4 ++ Sources/Secretive.xcodeproj/project.pbxproj | 27 ++++++++--- Sources/Secretive/App.swift | 45 ++++++++++++------- .../AgentCommunicationController.swift | 36 +++++++++++++++ ...ller.swift => AgentLaunchController.swift} | 4 +- .../Controllers/JustUpdatedChecker.swift | 9 +++- Sources/Secretive/Helpers/BundleIDs.swift | 2 +- .../Preview Content/PreviewStore.swift | 5 +++ Sources/Secretive/Secretive.entitlements | 4 ++ Sources/Secretive/Views/ContentView.swift | 2 +- .../Secretive/Views/CreateSecretView.swift | 6 ++- Sources/Secretive/Views/SetupView.swift | 2 +- 18 files changed, 191 insertions(+), 58 deletions(-) create mode 100644 Sources/Packages/Sources/SecretAgentKitProtocol/AgentProtocol.swift create mode 100644 Sources/Secretive/Controllers/AgentCommunicationController.swift rename Sources/Secretive/Controllers/{LaunchAgentController.swift => AgentLaunchController.swift} (92%) diff --git a/Sources/Packages/Package.swift b/Sources/Packages/Package.swift index c5bc4c0..6fd240f 100644 --- a/Sources/Packages/Package.swift +++ b/Sources/Packages/Package.swift @@ -24,6 +24,9 @@ let package = Package( .library( name: "SecretAgentKitHeaders", targets: ["SecretAgentKitHeaders"]), + .library( + name: "SecretAgentKitProtocol", + targets: ["SecretAgentKitProtocol"]), .library( name: "Brief", targets: ["Brief"]), @@ -32,8 +35,7 @@ let package = Package( ], targets: [ .target( - name: "SecretKit", - dependencies: [] + name: "SecretKit" ), .testTarget( name: "SecretKitTests", @@ -49,18 +51,20 @@ let package = Package( ), .target( name: "SecretAgentKit", - dependencies: ["SecretKit", "SecretAgentKitHeaders"] + dependencies: ["SecretKit", "SecretAgentKitHeaders", "SecretAgentKitProtocol"] ), .systemLibrary( name: "SecretAgentKitHeaders" ), + .target( + name: "SecretAgentKitProtocol" + ), .testTarget( name: "SecretAgentKitTests", dependencies: ["SecretAgentKit"]) , .target( - name: "Brief", - dependencies: [] + name: "Brief" ), .testTarget( name: "BriefTests", diff --git a/Sources/Packages/Sources/SecretAgentKitProtocol/AgentProtocol.swift b/Sources/Packages/Sources/SecretAgentKitProtocol/AgentProtocol.swift new file mode 100644 index 0000000..1fed623 --- /dev/null +++ b/Sources/Packages/Sources/SecretAgentKitProtocol/AgentProtocol.swift @@ -0,0 +1,12 @@ +import Foundation + +@objc public protocol AgentProtocol { + func updatedStore(withID: UUID) async throws +} + +public struct AgentProtocolStoreNotFoundError: Error { + + public init() { + } + +} diff --git a/Sources/Packages/Sources/SecretKit/Erasers/AnySecretStore.swift b/Sources/Packages/Sources/SecretKit/Erasers/AnySecretStore.swift index 305ecd2..44b16f3 100644 --- a/Sources/Packages/Sources/SecretKit/Erasers/AnySecretStore.swift +++ b/Sources/Packages/Sources/SecretKit/Erasers/AnySecretStore.swift @@ -58,11 +58,13 @@ public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable { private let _create: (String, Bool) throws -> Void private let _delete: (AnySecret) throws -> Void private let _update: (AnySecret, String) throws -> Void + private let _reload: () throws -> Void public init(modifiable secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable { _create = { try secretStore.create(name: $0, requiresAuthentication: $1) } _delete = { try secretStore.delete(secret: $0.base as! SecretStoreType.SecretType) } _update = { try secretStore.update(secret: $0.base as! SecretStoreType.SecretType, name: $1) } + _reload = { try secretStore.reload() } super.init(secretStore) } @@ -78,4 +80,8 @@ public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable { try _update(secret, name) } + public func reload() throws { + try _reload() + } + } diff --git a/Sources/Packages/Sources/SecretKit/Types/SecretStore.swift b/Sources/Packages/Sources/SecretKit/Types/SecretStore.swift index 6edef38..3d34501 100644 --- a/Sources/Packages/Sources/SecretKit/Types/SecretStore.swift +++ b/Sources/Packages/Sources/SecretKit/Types/SecretStore.swift @@ -52,6 +52,9 @@ public protocol SecretStoreModifiable: SecretStore { /// - name: The new name for the Secret. func update(secret: SecretType, name: String) throws + /// Reloads the secrets from the backing store. + func reload() throws + } extension NSNotification.Name { diff --git a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift index af6b364..d51a325 100644 --- a/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift +++ b/Sources/Packages/Sources/SecureEnclaveSecretKit/SecureEnclaveStore.swift @@ -23,9 +23,6 @@ extension SecureEnclave { /// Initializes a Store. public init() { - DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in - self.reloadSecrets(notify: false) - } loadSecrets() } @@ -68,7 +65,7 @@ extension SecureEnclave { throw KeychainError(statusCode: nil) } try savePublicKey(publicKey, name: name) - reloadSecrets() + reload() } public func delete(secret: Secret) throws { @@ -80,7 +77,7 @@ extension SecureEnclave { if status != errSecSuccess { throw KeychainError(statusCode: status) } - reloadSecrets() + reload() } public func update(secret: Secret, name: String) throws { @@ -97,9 +94,14 @@ extension SecureEnclave { if status != errSecSuccess { throw KeychainError(statusCode: status) } - reloadSecrets() + reload() } - + + public func reload() { + secrets.removeAll() + loadSecrets() + } + public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData { let context: LAContext if let existing = persistedAuthenticationContexts[secret], existing.valid { @@ -170,16 +172,6 @@ extension SecureEnclave { extension SecureEnclave.Store { - /// Reloads all secrets from the store. - /// - Parameter notify: A boolean indicating whether a distributed notification should be posted, notifying other processes (ie, the SecretAgent) to reload their stores as well. - private func reloadSecrets(notify: Bool = true) { - secrets.removeAll() - loadSecrets() - if notify { - DistributedNotificationCenter.default().post(name: .secretStoreUpdated, object: nil) - } - } - /// Loads all secrets from the store. private func loadSecrets() { let attributes = [ diff --git a/Sources/SecretAgent/AppDelegate.swift b/Sources/SecretAgent/AppDelegate.swift index 00794f6..c2a155f 100644 --- a/Sources/SecretAgent/AppDelegate.swift +++ b/Sources/SecretAgent/AppDelegate.swift @@ -7,8 +7,11 @@ import SmartCardSecretKit import SecretAgentKit import Brief +import SecretKit +import SecretAgentKitProtocol + @NSApplicationMain -class AppDelegate: NSObject, NSApplicationDelegate { +class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol { private let storeList: SecretStoreList = { let list = SecretStoreList() @@ -27,9 +30,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { return SocketController(path: path) }() private var updateSink: AnyCancellable? + private let logger = Logger() + var delegate: ServiceDelegate? = nil + let listener = NSXPCListener(machServiceName: Bundle.main.bundleIdentifier!) func applicationDidFinishLaunching(_ aNotification: Notification) { - Logger().debug("SecretAgent finished launching") + logger.debug("SecretAgent finished launching") DispatchQueue.main.async { self.socketController.handler = self.agent.handle(reader:writer:) } @@ -39,13 +45,37 @@ class AppDelegate: NSObject, NSApplicationDelegate { guard let update = update else { return } self.notifier.notify(update: update, ignore: self.updater.ignore(release:)) } + connect() } - func reloadKeys() { - // TODO: This -// storeList.reloadAll() - try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true) + func connect() { + delegate = ServiceDelegate(exportedObject: self) + listener.delegate = delegate + listener.resume() + } + + func updatedStore(withID id: UUID) async throws { + logger.debug("Reloading keys for store with id: \(id)") + guard let store = storeList.modifiableStore, store.id == id else { throw AgentProtocolStoreNotFoundError() } + try store.reload() + try publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true) } } + class ServiceDelegate: NSObject, NSXPCListenerDelegate { + + let exported: AgentProtocol + + init(exportedObject: AgentProtocol) { + self.exported = exportedObject + } + + func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool { + newConnection.exportedInterface = NSXPCInterface(with: AgentProtocol.self) + newConnection.exportedObject = exported + newConnection.resume() + return true + } + + } diff --git a/Sources/SecretAgent/SecretAgent.entitlements b/Sources/SecretAgent/SecretAgent.entitlements index 895fc77..eb0c1c4 100644 --- a/Sources/SecretAgent/SecretAgent.entitlements +++ b/Sources/SecretAgent/SecretAgent.entitlements @@ -4,6 +4,10 @@ com.apple.security.app-sandbox + com.apple.security.application-groups + + $(TeamIdentifierPrefix)com.maxgoedjen.secretive + com.apple.security.network.client com.apple.security.smartcard diff --git a/Sources/Secretive.xcodeproj/project.pbxproj b/Sources/Secretive.xcodeproj/project.pbxproj index d66060b..21080dd 100644 --- a/Sources/Secretive.xcodeproj/project.pbxproj +++ b/Sources/Secretive.xcodeproj/project.pbxproj @@ -24,7 +24,7 @@ 50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; }; 5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; }; 50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; }; - 50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0424393D1500F76F6C /* LaunchAgentController.swift */; }; + 50571E0524393D1500F76F6C /* AgentLaunchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0424393D1500F76F6C /* AgentLaunchController.swift */; }; 50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; }; 50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; }; 50617D8723FCE48E0099B055 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8623FCE48E0099B055 /* Assets.xcassets */; }; @@ -36,6 +36,8 @@ 5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */; }; 506772C72424784600034DED /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 506772C62424784600034DED /* Credits.rtf */; }; 506772C92425BB8500034DED /* NoStoresView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506772C82425BB8500034DED /* NoStoresView.swift */; }; + 50736F652782C05000A723B6 /* AgentCommunicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50736F642782C05000A723B6 /* AgentCommunicationController.swift */; }; + 50736F672782C31800A723B6 /* SecretAgentKitProtocol in Frameworks */ = {isa = PBXBuildFile; productRef = 50736F662782C31800A723B6 /* SecretAgentKitProtocol */; }; 5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */; }; 508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; }; 508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; }; @@ -111,7 +113,7 @@ 50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = ""; }; 5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = ""; }; 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = ""; }; - 50571E0424393D1500F76F6C /* LaunchAgentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAgentController.swift; sourceTree = ""; }; + 50571E0424393D1500F76F6C /* AgentLaunchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentLaunchController.swift; sourceTree = ""; }; 50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; }; 50617D8223FCE48E0099B055 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; 50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -128,6 +130,7 @@ 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellConfigurationController.swift; sourceTree = ""; }; 506772C62424784600034DED /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; 506772C82425BB8500034DED /* NoStoresView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoStoresView.swift; sourceTree = ""; }; + 50736F642782C05000A723B6 /* AgentCommunicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentCommunicationController.swift; sourceTree = ""; }; 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreListView.swift; sourceTree = ""; }; 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUpdater.swift; sourceTree = ""; }; 508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; @@ -155,6 +158,7 @@ buildActionMask = 2147483647; files = ( 5003EF3B278005E800DF2006 /* SecretKit in Frameworks */, + 50736F672782C31800A723B6 /* SecretAgentKitProtocol in Frameworks */, 501421622781262300BBAA70 /* Brief in Frameworks */, 5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */, 5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */, @@ -283,9 +287,10 @@ isa = PBXGroup; children = ( 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */, + 50736F642782C05000A723B6 /* AgentCommunicationController.swift */, 5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */, 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */, - 50571E0424393D1500F76F6C /* LaunchAgentController.swift */, + 50571E0424393D1500F76F6C /* AgentLaunchController.swift */, 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */, ); path = Controllers; @@ -345,6 +350,7 @@ 5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */, 5003EF602780081600DF2006 /* SmartCardSecretKit */, 501421612781262300BBAA70 /* Brief */, + 50736F662782C31800A723B6 /* SecretAgentKitProtocol */, ); productName = Secretive; productReference = 50617D7F23FCE48E0099B055 /* Secretive.app */; @@ -485,8 +491,9 @@ 50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */, 5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */, 50153E20250AFCB200525160 /* UpdateView.swift in Sources */, - 50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */, + 50571E0524393D1500F76F6C /* AgentLaunchController.swift in Sources */, 5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */, + 50736F652782C05000A723B6 /* AgentCommunicationController.swift in Sources */, 50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */, 50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */, 50617D8323FCE48E0099B055 /* App.swift in Sources */, @@ -830,6 +837,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; @@ -874,6 +882,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\""; @@ -885,7 +894,7 @@ "@executable_path/../Frameworks", ); MARKETING_VERSION = 1; - PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent; + PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; @@ -908,7 +917,7 @@ "@executable_path/../Frameworks", ); MARKETING_VERSION = 1; - PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent; + PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; }; @@ -932,7 +941,7 @@ "@executable_path/../Frameworks", ); MARKETING_VERSION = 1; - PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent; + PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent"; SWIFT_VERSION = 5.0; @@ -1021,6 +1030,10 @@ isa = XCSwiftPackageProductDependency; productName = Brief; }; + 50736F662782C31800A723B6 /* SecretAgentKitProtocol */ = { + isa = XCSwiftPackageProductDependency; + productName = SecretAgentKitProtocol; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 50617D7723FCE48D0099B055 /* Project object */; diff --git a/Sources/Secretive/App.swift b/Sources/Secretive/App.swift index 52faacc..9f69a3d 100644 --- a/Sources/Secretive/App.swift +++ b/Sources/Secretive/App.swift @@ -15,6 +15,8 @@ struct Secretive: App { return list }() private let agentStatusChecker = AgentStatusChecker() + private let agentLaunchController = AgentLaunchController() + private let agentCommunicationController = AgentCommunicationController() private let justUpdatedChecker = JustUpdatedChecker() @AppStorage("defaultsHasRunSetup") var hasRunSetup = false @@ -27,6 +29,7 @@ struct Secretive: App { .environmentObject(storeList) .environmentObject(Updater(checkOnLaunch: hasRunSetup)) .environmentObject(agentStatusChecker) + .environmentObject(agentCommunicationController) .onAppear { if !hasRunSetup { showingSetup = true @@ -35,11 +38,17 @@ struct Secretive: App { .onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in guard hasRunSetup else { return } agentStatusChecker.check() - if agentStatusChecker.running && justUpdatedChecker.justUpdated { - // Relaunch the agent, since it'll be running from earlier update still - reinstallAgent() - } else if !agentStatusChecker.running && !agentStatusChecker.developmentBuild { - forceLaunchAgent() + if justUpdatedChecker.justUpdated || !agentStatusChecker.running { + // Two conditions in which we reinstall/attempt a force launch: + // 1: The app was just updated, and an old version of the agent is alive. Reinstall will deactivate this and activate a new one. + // 2: The agent is not running for some reason. We'll attempt to reinstall it, or relaunch directly if that fails. + reinstallAgent { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + agentCommunicationController.configure() + } + } + } else { + agentCommunicationController.configure() } } } @@ -60,6 +69,13 @@ struct Secretive: App { showingSetup = true } } + CommandGroup(after: .help) { + Button("TEST") { + Task { + try await agentCommunicationController.agent.updatedStore(withID: storeList.modifiableStore?.id as? UUID ?? UUID()) + } + } + } SidebarCommands() } } @@ -68,27 +84,24 @@ struct Secretive: App { extension Secretive { - private func reinstallAgent() { + private func reinstallAgent(completion: @escaping () -> Void) { justUpdatedChecker.check() - LaunchAgentController().install { + agentLaunchController.install { // Wait a second for launchd to kick in (next runloop isn't enough). DispatchQueue.main.asyncAfter(deadline: .now() + 1) { agentStatusChecker.check() if !agentStatusChecker.running { - forceLaunchAgent() + agentLaunchController.forceLaunch { _ in + agentStatusChecker.check() + completion() + } + } else { + completion() } } } } - private func forceLaunchAgent() { - // We've run setup, we didn't just update, launchd is just not doing it's thing. - // Force a launch directly. - LaunchAgentController().forceLaunch { _ in - agentStatusChecker.check() - } - } - } diff --git a/Sources/Secretive/Controllers/AgentCommunicationController.swift b/Sources/Secretive/Controllers/AgentCommunicationController.swift new file mode 100644 index 0000000..6d3d516 --- /dev/null +++ b/Sources/Secretive/Controllers/AgentCommunicationController.swift @@ -0,0 +1,36 @@ +import Foundation +import Combine +import AppKit +import SecretKit +import SecretAgentKitProtocol + +protocol AgentCommunicationControllerProtocol: ObservableObject { + var agent: AgentProtocol { get } +} + +class AgentCommunicationController: ObservableObject, AgentCommunicationControllerProtocol { + + let agent: AgentProtocol + private let connection: NSXPCConnection + private var running = false + + init() { + connection = NSXPCConnection(machServiceName: Bundle.main.agentBundleID) + connection.remoteObjectInterface = NSXPCInterface(with: AgentProtocol.self) + connection.invalidationHandler = { + print("INVALID") + } + agent = connection.remoteObjectProxyWithErrorHandler({ x in + print(x) + }) as! AgentProtocol + } + + func configure() { + guard !running else { return } + running = true + connection.resume() + } + +} + + diff --git a/Sources/Secretive/Controllers/LaunchAgentController.swift b/Sources/Secretive/Controllers/AgentLaunchController.swift similarity index 92% rename from Sources/Secretive/Controllers/LaunchAgentController.swift rename to Sources/Secretive/Controllers/AgentLaunchController.swift index d50299d..b82a26d 100644 --- a/Sources/Secretive/Controllers/LaunchAgentController.swift +++ b/Sources/Secretive/Controllers/AgentLaunchController.swift @@ -4,7 +4,7 @@ import AppKit import OSLog import SecretKit -struct LaunchAgentController { +struct AgentLaunchController { func install(completion: (() -> Void)? = nil) { Logger().debug("Installing agent") @@ -20,7 +20,7 @@ struct LaunchAgentController { } func forceLaunch(completion: ((Bool) -> Void)?) { - Logger().debug("Agent is not running, attempting to force launch") + Logger().debug("Agent is still not running, attempting to force launch") let url = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LoginItems/SecretAgent.app") let config = NSWorkspace.OpenConfiguration() config.activates = false diff --git a/Sources/Secretive/Controllers/JustUpdatedChecker.swift b/Sources/Secretive/Controllers/JustUpdatedChecker.swift index 4c86f68..e13f7b7 100644 --- a/Sources/Secretive/Controllers/JustUpdatedChecker.swift +++ b/Sources/Secretive/Controllers/JustUpdatedChecker.swift @@ -9,6 +9,7 @@ protocol JustUpdatedCheckerProtocol: ObservableObject { class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol { @Published var justUpdated: Bool = false + var alreadyRelaunchedForDebug = false init() { check() @@ -18,7 +19,12 @@ class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol { let lastBuild = UserDefaults.standard.object(forKey: Constants.previousVersionUserDefaultsKey) as? String ?? "None" let currentBuild = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String UserDefaults.standard.set(currentBuild, forKey: Constants.previousVersionUserDefaultsKey) - justUpdated = lastBuild != currentBuild + if currentBuild != Constants.debugVersionKey { + justUpdated = lastBuild != currentBuild + } else { + justUpdated = !alreadyRelaunchedForDebug + alreadyRelaunchedForDebug = true + } } @@ -29,6 +35,7 @@ extension JustUpdatedChecker { enum Constants { static let previousVersionUserDefaultsKey = "com.maxgoedjen.Secretive.lastBuild" + static let debugVersionKey = "GITHUB_CI_VERSION" } } diff --git a/Sources/Secretive/Helpers/BundleIDs.swift b/Sources/Secretive/Helpers/BundleIDs.swift index de4967d..e23cfbb 100644 --- a/Sources/Secretive/Helpers/BundleIDs.swift +++ b/Sources/Secretive/Helpers/BundleIDs.swift @@ -2,6 +2,6 @@ import Foundation extension Bundle { - public var agentBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "Host", with: "SecretAgent"))!} + public var agentBundleID: String { "Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent" } public var hostBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "SecretAgent", with: "Host"))!} } diff --git a/Sources/Secretive/Preview Content/PreviewStore.swift b/Sources/Secretive/Preview Content/PreviewStore.swift index 4d4fbdb..91441c8 100644 --- a/Sources/Secretive/Preview Content/PreviewStore.swift +++ b/Sources/Secretive/Preview Content/PreviewStore.swift @@ -45,6 +45,7 @@ extension Preview { } class StoreModifiable: Store, SecretStoreModifiable { + override var name: String { "Modifiable Preview Store" } func create(name: String, requiresAuthentication: Bool) throws { @@ -55,6 +56,10 @@ extension Preview { func update(secret: Preview.Secret, name: String) throws { } + + func reload() throws { + } + } } diff --git a/Sources/Secretive/Secretive.entitlements b/Sources/Secretive/Secretive.entitlements index c1bb5e0..ad1d722 100644 --- a/Sources/Secretive/Secretive.entitlements +++ b/Sources/Secretive/Secretive.entitlements @@ -4,6 +4,10 @@ com.apple.security.app-sandbox + com.apple.security.application-groups + + $(TeamIdentifierPrefix)com.maxgoedjen.secretive + com.apple.security.files.user-selected.read-write com.apple.security.network.client diff --git a/Sources/Secretive/Views/ContentView.swift b/Sources/Secretive/Views/ContentView.swift index ec9c23f..14831d3 100644 --- a/Sources/Secretive/Views/ContentView.swift +++ b/Sources/Secretive/Views/ContentView.swift @@ -87,7 +87,7 @@ extension ContentView { }) .popover(isPresented: $showingCreation, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { if let modifiable = storeList.modifiableStore { - CreateSecretView(store: modifiable, showing: $showingCreation) + CreateSecretView(store: modifiable, showing: $showingCreation) } } diff --git a/Sources/Secretive/Views/CreateSecretView.swift b/Sources/Secretive/Views/CreateSecretView.swift index 318226d..fdf5b0f 100644 --- a/Sources/Secretive/Views/CreateSecretView.swift +++ b/Sources/Secretive/Views/CreateSecretView.swift @@ -1,9 +1,10 @@ import SwiftUI import SecretKit -struct CreateSecretView: View { +struct CreateSecretView: View { @ObservedObject var store: StoreType + @EnvironmentObject private var agentCommunicationController: AgentCommunicationControllerType @Binding var showing: Bool @State private var name = "" @@ -52,6 +53,9 @@ struct CreateSecretView: View { func save() { try! store.create(name: name, requiresAuthentication: requiresAuthentication) + Task { + try! await agentCommunicationController.agent.updatedStore(withID: store.id) + } showing = false } } diff --git a/Sources/Secretive/Views/SetupView.swift b/Sources/Secretive/Views/SetupView.swift index 9329608..3a58382 100644 --- a/Sources/Secretive/Views/SetupView.swift +++ b/Sources/Secretive/Views/SetupView.swift @@ -156,7 +156,7 @@ struct SecretAgentSetupView: View { } func install() { - LaunchAgentController().install() + AgentLaunchController().install() buttonAction() }