XPC scaffolding
This commit is contained in:
parent
98bf285ad2
commit
d80d2ca656
|
@ -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",
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import Foundation
|
||||
|
||||
@objc public protocol AgentProtocol {
|
||||
func updatedStore(withID: UUID) async throws
|
||||
}
|
||||
|
||||
public struct AgentProtocolStoreNotFoundError: Error {
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
}
|
|
@ -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<SecretStoreType>(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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>$(TeamIdentifierPrefix)com.maxgoedjen.secretive</string>
|
||||
</array>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.smartcard</key>
|
||||
|
|
|
@ -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 = "<group>"; };
|
||||
5018F54E24064786002EB505 /* Notifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notifier.swift; sourceTree = "<group>"; };
|
||||
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; };
|
||||
50571E0424393D1500F76F6C /* LaunchAgentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAgentController.swift; sourceTree = "<group>"; };
|
||||
50571E0424393D1500F76F6C /* AgentLaunchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentLaunchController.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
|
@ -128,6 +130,7 @@
|
|||
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellConfigurationController.swift; sourceTree = "<group>"; };
|
||||
506772C62424784600034DED /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
|
||||
506772C82425BB8500034DED /* NoStoresView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoStoresView.swift; sourceTree = "<group>"; };
|
||||
50736F642782C05000A723B6 /* AgentCommunicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentCommunicationController.swift; sourceTree = "<group>"; };
|
||||
5079BA0E250F29BF00EA86F4 /* StoreListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreListView.swift; sourceTree = "<group>"; };
|
||||
508A58A9241E06B40069DC07 /* PreviewUpdater.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewUpdater.swift; sourceTree = "<group>"; };
|
||||
508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||
|
@ -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 */;
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"))!}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>$(TeamIdentifierPrefix)com.maxgoedjen.secretive</string>
|
||||
</array>
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
|
|
|
@ -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<AnySecretStoreModifiable, AgentCommunicationController>(store: modifiable, showing: $showingCreation)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import SwiftUI
|
||||
import SecretKit
|
||||
|
||||
struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
||||
struct CreateSecretView<StoreType: SecretStoreModifiable, AgentCommunicationControllerType: AgentCommunicationControllerProtocol>: View {
|
||||
|
||||
@ObservedObject var store: StoreType
|
||||
@EnvironmentObject private var agentCommunicationController: AgentCommunicationControllerType
|
||||
@Binding var showing: Bool
|
||||
|
||||
@State private var name = ""
|
||||
|
@ -52,6 +53,9 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
|||
|
||||
func save() {
|
||||
try! store.create(name: name, requiresAuthentication: requiresAuthentication)
|
||||
Task {
|
||||
try! await agentCommunicationController.agent.updatedStore(withID: store.id)
|
||||
}
|
||||
showing = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ struct SecretAgentSetupView: View {
|
|||
}
|
||||
|
||||
func install() {
|
||||
LaunchAgentController().install()
|
||||
AgentLaunchController().install()
|
||||
buttonAction()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue