mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-07-04 19:23:34 +00:00
Compare commits
14 Commits
projected_
...
v2.2.0
Author | SHA1 | Date | |
---|---|---|---|
067f1526b0 | |||
f43dea0d0d | |||
db8833fa25 | |||
1409e9ac31 | |||
adabe801d3 | |||
19f9494492 | |||
c50d2feaf9 | |||
03d3cc9177 | |||
141cc03b60 | |||
07559bd7ef | |||
cb206a18c2 | |||
6cb3ff80d9 | |||
05c5aca9b6 | |||
5894bbca00 |
49
.github/workflows/nightly.yml
vendored
Normal file
49
.github/workflows/nightly.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
name: Nightly
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 8 * * *"
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macos-11.0
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Setup Signing
|
||||||
|
env:
|
||||||
|
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||||
|
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||||
|
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||||
|
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||||
|
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||||
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
|
run: ./.github/scripts/signing.sh
|
||||||
|
- name: Set Environment
|
||||||
|
run: sudo xcrun xcode-select -s /Applications/Xcode_13.2.1.app
|
||||||
|
- name: Update Build Number
|
||||||
|
env:
|
||||||
|
RUN_ID: ${{ github.run_id }}
|
||||||
|
run: |
|
||||||
|
sed -i '' -e "s/GITHUB_CI_VERSION/0.0.0/g" Sources/Config/Config.xcconfig
|
||||||
|
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||||
|
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf
|
||||||
|
- name: Build
|
||||||
|
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
|
||||||
|
- name: Create ZIPs
|
||||||
|
run: |
|
||||||
|
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip
|
||||||
|
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive ./Archive.zip
|
||||||
|
- name: Notarize
|
||||||
|
env:
|
||||||
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
|
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||||
|
run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
|
||||||
|
- name: Document SHAs
|
||||||
|
run: |
|
||||||
|
shasum -a 512 Secretive.zip
|
||||||
|
shasum -a 512 Archive.zip
|
||||||
|
- name: Upload App to Artifacts
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: Secretive.zip
|
||||||
|
path: Secretive.zip
|
@ -51,6 +51,30 @@ Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
|||||||
|
|
||||||
Log out and log in again before launching Cyberduck.
|
Log out and log in again before launching Cyberduck.
|
||||||
|
|
||||||
|
## GitKraken
|
||||||
|
|
||||||
|
Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
||||||
|
|
||||||
|
```
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Label</key>
|
||||||
|
<string>link-ssh-auth-sock</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>/bin/sh</string>
|
||||||
|
<string>-c</string>
|
||||||
|
<string>/bin/ln -sf $HOME/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh $SSH_AUTH_SOCK</string>
|
||||||
|
</array>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
```
|
||||||
|
|
||||||
|
Log out and log in again before launching Gitkraken. Then enable "Use local SSH agent in GitKraken Preferences (Located under Preferences -> SSH)
|
||||||
|
|
||||||
# The app I use isn't listed here!
|
# The app I use isn't listed here!
|
||||||
|
|
||||||
|
6
FAQ.md
6
FAQ.md
@ -38,7 +38,11 @@ Awesome! Just bear in mind that because an app only has access to the keychain i
|
|||||||
|
|
||||||
### What's this network request to GitHub?
|
### What's this network request to GitHub?
|
||||||
|
|
||||||
Secretive checks in with GitHub's releases API to check if there's a new version of Secretive available. You can audit the source code for this feature [here](https://github.com/maxgoedjen/secretive/blob/main/Brief/Updater.swift).
|
Secretive checks in with GitHub's releases API to check if there's a new version of Secretive available. You can audit the source code for this feature [here](https://github.com/maxgoedjen/secretive/blob/main/Sources/Packages/Sources/Brief/Updater.swift).
|
||||||
|
|
||||||
|
### How do I uninstall Secretive?
|
||||||
|
|
||||||
|
Drag Secretive.app to the trash and remove `~/Library/Containers/com.maxgoedjen.Secretive.SecretAgent`. `SecretAgent` may continue running until you quit it or reboot.
|
||||||
|
|
||||||
### I have a security issue
|
### I have a security issue
|
||||||
|
|
||||||
|
@ -24,9 +24,6 @@ let package = Package(
|
|||||||
.library(
|
.library(
|
||||||
name: "SecretAgentKitHeaders",
|
name: "SecretAgentKitHeaders",
|
||||||
targets: ["SecretAgentKitHeaders"]),
|
targets: ["SecretAgentKitHeaders"]),
|
||||||
.library(
|
|
||||||
name: "SecretAgentKitProtocol",
|
|
||||||
targets: ["SecretAgentKitProtocol"]),
|
|
||||||
.library(
|
.library(
|
||||||
name: "Brief",
|
name: "Brief",
|
||||||
targets: ["Brief"]),
|
targets: ["Brief"]),
|
||||||
@ -35,7 +32,8 @@ let package = Package(
|
|||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
name: "SecretKit"
|
name: "SecretKit",
|
||||||
|
dependencies: []
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "SecretKitTests",
|
name: "SecretKitTests",
|
||||||
@ -51,20 +49,18 @@ let package = Package(
|
|||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "SecretAgentKit",
|
name: "SecretAgentKit",
|
||||||
dependencies: ["SecretKit", "SecretAgentKitHeaders", "SecretAgentKitProtocol"]
|
dependencies: ["SecretKit", "SecretAgentKitHeaders"]
|
||||||
),
|
),
|
||||||
.systemLibrary(
|
.systemLibrary(
|
||||||
name: "SecretAgentKitHeaders"
|
name: "SecretAgentKitHeaders"
|
||||||
),
|
),
|
||||||
.target(
|
|
||||||
name: "SecretAgentKitProtocol"
|
|
||||||
),
|
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "SecretAgentKitTests",
|
name: "SecretAgentKitTests",
|
||||||
dependencies: ["SecretAgentKit"])
|
dependencies: ["SecretAgentKit"])
|
||||||
,
|
,
|
||||||
.target(
|
.target(
|
||||||
name: "Brief"
|
name: "Brief",
|
||||||
|
dependencies: []
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "BriefTests",
|
name: "BriefTests",
|
||||||
|
@ -30,20 +30,23 @@ extension Agent {
|
|||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - reader: A ``FileHandleReader`` to read the content of the request.
|
/// - reader: A ``FileHandleReader`` to read the content of the request.
|
||||||
/// - writer: A ``FileHandleWriter`` to write the response to.
|
/// - writer: A ``FileHandleWriter`` to write the response to.
|
||||||
public func handle(reader: FileHandleReader, writer: FileHandleWriter) {
|
/// - Return value:
|
||||||
|
/// - Boolean if data could be read
|
||||||
|
public func handle(reader: FileHandleReader, writer: FileHandleWriter) -> Bool {
|
||||||
Logger().debug("Agent handling new data")
|
Logger().debug("Agent handling new data")
|
||||||
let data = Data(reader.availableData)
|
let data = Data(reader.availableData)
|
||||||
guard data.count > 4 else { return }
|
guard data.count > 4 else { return false}
|
||||||
let requestTypeInt = data[4]
|
let requestTypeInt = data[4]
|
||||||
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
||||||
writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data))
|
writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data))
|
||||||
Logger().debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
Logger().debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
Logger().debug("Agent handling request of type \(requestType.debugDescription)")
|
Logger().debug("Agent handling request of type \(requestType.debugDescription)")
|
||||||
let subData = Data(data[5...])
|
let subData = Data(data[5...])
|
||||||
let response = handle(requestType: requestType, data: subData, reader: reader)
|
let response = handle(requestType: requestType, data: subData, reader: reader)
|
||||||
writer.write(response)
|
writer.write(response)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data {
|
func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data {
|
||||||
|
@ -6,8 +6,12 @@ public class SocketController {
|
|||||||
|
|
||||||
/// The active FileHandle.
|
/// The active FileHandle.
|
||||||
private var fileHandle: FileHandle?
|
private var fileHandle: FileHandle?
|
||||||
|
/// The active SocketPort.
|
||||||
|
private var port: SocketPort?
|
||||||
/// A handler that will be notified when a new read/write handle is available.
|
/// A handler that will be notified when a new read/write handle is available.
|
||||||
public var handler: ((FileHandleReader, FileHandleWriter) -> Void)?
|
/// False if no data could be read
|
||||||
|
public var handler: ((FileHandleReader, FileHandleWriter) -> Bool)?
|
||||||
|
|
||||||
|
|
||||||
/// Initializes a socket controller with a specified path.
|
/// Initializes a socket controller with a specified path.
|
||||||
/// - Parameter path: The path to use as a socket.
|
/// - Parameter path: The path to use as a socket.
|
||||||
@ -19,6 +23,7 @@ public class SocketController {
|
|||||||
let exists = FileManager.default.fileExists(atPath: path)
|
let exists = FileManager.default.fileExists(atPath: path)
|
||||||
assert(!exists)
|
assert(!exists)
|
||||||
Logger().debug("Socket controller path is clear")
|
Logger().debug("Socket controller path is clear")
|
||||||
|
port = socketPort(at: path)
|
||||||
configureSocket(at: path)
|
configureSocket(at: path)
|
||||||
Logger().debug("Socket listening at \(path)")
|
Logger().debug("Socket listening at \(path)")
|
||||||
}
|
}
|
||||||
@ -26,7 +31,7 @@ public class SocketController {
|
|||||||
/// Configures the socket and a corresponding FileHandle.
|
/// Configures the socket and a corresponding FileHandle.
|
||||||
/// - Parameter path: The path to use as a socket.
|
/// - Parameter path: The path to use as a socket.
|
||||||
func configureSocket(at path: String) {
|
func configureSocket(at path: String) {
|
||||||
let port = socketPort(at: path)
|
guard let port = port else { return }
|
||||||
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionAccept(notification:)), name: .NSFileHandleConnectionAccepted, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionAccept(notification:)), name: .NSFileHandleConnectionAccepted, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionDataAvailable(notification:)), name: .NSFileHandleDataAvailable, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(handleConnectionDataAvailable(notification:)), name: .NSFileHandleDataAvailable, object: nil)
|
||||||
@ -62,7 +67,7 @@ public class SocketController {
|
|||||||
@objc func handleConnectionAccept(notification: Notification) {
|
@objc func handleConnectionAccept(notification: Notification) {
|
||||||
Logger().debug("Socket controller accepted connection")
|
Logger().debug("Socket controller accepted connection")
|
||||||
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
|
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
|
||||||
handler?(new, new)
|
_ = handler?(new, new)
|
||||||
new.waitForDataInBackgroundAndNotify()
|
new.waitForDataInBackgroundAndNotify()
|
||||||
fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!])
|
fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!])
|
||||||
}
|
}
|
||||||
@ -73,7 +78,12 @@ public class SocketController {
|
|||||||
Logger().debug("Socket controller has new data available")
|
Logger().debug("Socket controller has new data available")
|
||||||
guard let new = notification.object as? FileHandle else { return }
|
guard let new = notification.object as? FileHandle else { return }
|
||||||
Logger().debug("Socket controller received new file handle")
|
Logger().debug("Socket controller received new file handle")
|
||||||
handler?(new, new)
|
if((handler?(new, new)) == true) {
|
||||||
|
Logger().debug("Socket controller handled data, wait for more data")
|
||||||
|
new.waitForDataInBackgroundAndNotify()
|
||||||
|
} else {
|
||||||
|
Logger().debug("Socket controller called with empty data, socked closed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
|
|
||||||
@objc public protocol AgentProtocol {
|
|
||||||
func updatedStore(withID: UUID) async throws
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AgentProtocolStoreNotFoundError: Error {
|
|
||||||
|
|
||||||
public init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -58,13 +58,11 @@ public 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
|
||||||
private let _update: (AnySecret, String) throws -> Void
|
private let _update: (AnySecret, String) throws -> Void
|
||||||
private let _reload: () throws -> Void
|
|
||||||
|
|
||||||
public init<SecretStoreType>(modifiable secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable {
|
public init<SecretStoreType>(modifiable secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable {
|
||||||
_create = { try secretStore.create(name: $0, requiresAuthentication: $1) }
|
_create = { try secretStore.create(name: $0, requiresAuthentication: $1) }
|
||||||
_delete = { try secretStore.delete(secret: $0.base as! SecretStoreType.SecretType) }
|
_delete = { try secretStore.delete(secret: $0.base as! SecretStoreType.SecretType) }
|
||||||
_update = { try secretStore.update(secret: $0.base as! SecretStoreType.SecretType, name: $1) }
|
_update = { try secretStore.update(secret: $0.base as! SecretStoreType.SecretType, name: $1) }
|
||||||
_reload = { try secretStore.reload() }
|
|
||||||
super.init(secretStore)
|
super.init(secretStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,8 +78,4 @@ public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {
|
|||||||
try _update(secret, name)
|
try _update(secret, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reload() throws {
|
|
||||||
try _reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,12 @@ import OSLog
|
|||||||
public class PublicKeyFileStoreController {
|
public class PublicKeyFileStoreController {
|
||||||
|
|
||||||
private let logger = Logger()
|
private let logger = Logger()
|
||||||
private let directory = NSHomeDirectory().appending("/PublicKeys")
|
private let directory: String
|
||||||
|
private let keyWriter = OpenSSHKeyWriter()
|
||||||
|
|
||||||
/// Initializes a PublicKeyFileStoreController.
|
/// Initializes a PublicKeyFileStoreController.
|
||||||
public init() {
|
public init(homeDirectory: String) {
|
||||||
|
directory = homeDirectory.appending("/PublicKeys")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes out the keys specified to disk.
|
/// Writes out the keys specified to disk.
|
||||||
@ -20,7 +22,6 @@ public class PublicKeyFileStoreController {
|
|||||||
try? FileManager.default.removeItem(at: URL(fileURLWithPath: directory))
|
try? FileManager.default.removeItem(at: URL(fileURLWithPath: directory))
|
||||||
}
|
}
|
||||||
try? FileManager.default.createDirectory(at: URL(fileURLWithPath: directory), withIntermediateDirectories: false, attributes: nil)
|
try? FileManager.default.createDirectory(at: URL(fileURLWithPath: directory), withIntermediateDirectories: false, attributes: nil)
|
||||||
let keyWriter = OpenSSHKeyWriter()
|
|
||||||
for secret in secrets {
|
for secret in secrets {
|
||||||
let path = path(for: secret)
|
let path = path(for: secret)
|
||||||
guard let data = keyWriter.openSSHString(secret: secret).data(using: .utf8) else { continue }
|
guard let data = keyWriter.openSSHString(secret: secret).data(using: .utf8) else { continue }
|
||||||
@ -34,7 +35,8 @@ public class PublicKeyFileStoreController {
|
|||||||
/// - Returns: The path to the Secret's public key.
|
/// - Returns: The path to the Secret's public key.
|
||||||
/// - Warning: This method returning a path does not imply that a key has been written to disk already. This method only describes where it will be written to.
|
/// - Warning: This method returning a path does not imply that a key has been written to disk already. This method only describes where it will be written to.
|
||||||
public func path<SecretType: Secret>(for secret: SecretType) -> String {
|
public func path<SecretType: Secret>(for secret: SecretType) -> String {
|
||||||
directory.appending("/").appending("\(secret.name.replacingOccurrences(of: " ", with: "-")).pub")
|
let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
|
||||||
|
return directory.appending("/").appending("\(minimalHex).pub")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,13 +52,13 @@ public protocol SecretStoreModifiable: SecretStore {
|
|||||||
/// - name: The new name for the Secret.
|
/// - name: The new name for the Secret.
|
||||||
func update(secret: SecretType, name: String) throws
|
func update(secret: SecretType, name: String) throws
|
||||||
|
|
||||||
/// Reloads the secrets from the backing store.
|
|
||||||
func reload() throws
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NSNotification.Name {
|
extension NSNotification.Name {
|
||||||
|
|
||||||
|
// Distributed notification that keys were modified out of process (ie, that the management tool added/removed secrets)
|
||||||
public static let secretStoreUpdated = NSNotification.Name("com.maxgoedjen.Secretive.secretStore.updated")
|
public static let secretStoreUpdated = NSNotification.Name("com.maxgoedjen.Secretive.secretStore.updated")
|
||||||
|
// Internal notification that keys were reloaded from the backing store.
|
||||||
|
public static let secretStoreReloaded = NSNotification.Name("com.maxgoedjen.Secretive.secretStore.reloaded")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,9 @@ extension SecureEnclave {
|
|||||||
|
|
||||||
/// Initializes a Store.
|
/// Initializes a Store.
|
||||||
public init() {
|
public init() {
|
||||||
|
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
|
||||||
|
self.reloadSecrets(notifyAgent: false)
|
||||||
|
}
|
||||||
loadSecrets()
|
loadSecrets()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +68,7 @@ extension SecureEnclave {
|
|||||||
throw KeychainError(statusCode: nil)
|
throw KeychainError(statusCode: nil)
|
||||||
}
|
}
|
||||||
try savePublicKey(publicKey, name: name)
|
try savePublicKey(publicKey, name: name)
|
||||||
reload()
|
reloadSecrets()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func delete(secret: Secret) throws {
|
public func delete(secret: Secret) throws {
|
||||||
@ -77,7 +80,7 @@ extension SecureEnclave {
|
|||||||
if status != errSecSuccess {
|
if status != errSecSuccess {
|
||||||
throw KeychainError(statusCode: status)
|
throw KeychainError(statusCode: status)
|
||||||
}
|
}
|
||||||
reload()
|
reloadSecrets()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func update(secret: Secret, name: String) throws {
|
public func update(secret: Secret, name: String) throws {
|
||||||
@ -94,12 +97,7 @@ extension SecureEnclave {
|
|||||||
if status != errSecSuccess {
|
if status != errSecSuccess {
|
||||||
throw KeychainError(statusCode: status)
|
throw KeychainError(statusCode: status)
|
||||||
}
|
}
|
||||||
reload()
|
reloadSecrets()
|
||||||
}
|
|
||||||
|
|
||||||
public func reload() {
|
|
||||||
secrets.removeAll()
|
|
||||||
loadSecrets()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData {
|
public func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> SignedData {
|
||||||
@ -172,6 +170,17 @@ extension SecureEnclave {
|
|||||||
|
|
||||||
extension SecureEnclave.Store {
|
extension SecureEnclave.Store {
|
||||||
|
|
||||||
|
/// Reloads all secrets from the store.
|
||||||
|
/// - Parameter notifyAgent: 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(notifyAgent: Bool = true) {
|
||||||
|
secrets.removeAll()
|
||||||
|
loadSecrets()
|
||||||
|
NotificationCenter.default.post(name: .secretStoreReloaded, object: self)
|
||||||
|
if notifyAgent {
|
||||||
|
DistributedNotificationCenter.default().postNotificationName(.secretStoreUpdated, object: nil, deliverImmediately: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads all secrets from the store.
|
/// Loads all secrets from the store.
|
||||||
private func loadSecrets() {
|
private func loadSecrets() {
|
||||||
let attributes = [
|
let attributes = [
|
||||||
|
@ -7,11 +7,8 @@ import SmartCardSecretKit
|
|||||||
import SecretAgentKit
|
import SecretAgentKit
|
||||||
import Brief
|
import Brief
|
||||||
|
|
||||||
import SecretKit
|
|
||||||
import SecretAgentKitProtocol
|
|
||||||
|
|
||||||
@NSApplicationMain
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol {
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
private let storeList: SecretStoreList = {
|
private let storeList: SecretStoreList = {
|
||||||
let list = SecretStoreList()
|
let list = SecretStoreList()
|
||||||
@ -21,7 +18,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol {
|
|||||||
}()
|
}()
|
||||||
private let updater = Updater(checkOnLaunch: false)
|
private let updater = Updater(checkOnLaunch: false)
|
||||||
private let notifier = Notifier()
|
private let notifier = Notifier()
|
||||||
private let publicKeyFileStoreController = PublicKeyFileStoreController()
|
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory())
|
||||||
private lazy var agent: Agent = {
|
private lazy var agent: Agent = {
|
||||||
Agent(storeList: storeList, witness: notifier)
|
Agent(storeList: storeList, witness: notifier)
|
||||||
}()
|
}()
|
||||||
@ -30,57 +27,22 @@ class AppDelegate: NSObject, NSApplicationDelegate, AgentProtocol {
|
|||||||
return SocketController(path: path)
|
return SocketController(path: path)
|
||||||
}()
|
}()
|
||||||
private var updateSink: AnyCancellable?
|
private var updateSink: AnyCancellable?
|
||||||
private let logger = Logger()
|
|
||||||
var delegate: ServiceDelegate? = nil
|
|
||||||
let listener = NSXPCListener(machServiceName: Bundle.main.bundleIdentifier!)
|
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
logger.debug("SecretAgent finished launching")
|
Logger().debug("SecretAgent finished launching")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.socketController.handler = self.agent.handle(reader:writer:)
|
self.socketController.handler = self.agent.handle(reader:writer:)
|
||||||
}
|
}
|
||||||
|
NotificationCenter.default.addObserver(forName: .secretStoreReloaded, object: nil, queue: .main) { [self] _ in
|
||||||
|
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
|
||||||
|
}
|
||||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
|
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
|
||||||
notifier.prompt()
|
notifier.prompt()
|
||||||
updateSink = updater.$update.sink { update in
|
updateSink = updater.$update.sink { update in
|
||||||
guard let update = update else { return }
|
guard let update = update else { return }
|
||||||
self.notifier.notify(update: update, ignore: self.updater.ignore(release:))
|
self.notifier.notify(update: update, ignore: self.updater.ignore(release:))
|
||||||
}
|
}
|
||||||
// TODO: REMOVE
|
|
||||||
notifier.notify(update: Release(name: "Test", prerelease: false, html_url: URL(string: "https://example.com")!, body: ""), ignore: nil)
|
|
||||||
connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
func connect() {
|
|
||||||
delegate = ServiceDelegate(exportedObject: self)
|
|
||||||
listener.delegate = delegate
|
|
||||||
listener.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
func updatedStore(withID id: UUID) async throws {
|
|
||||||
// TODO: REMOVE
|
|
||||||
notifier.notify(update: Release(name: "UPDATESTORE", prerelease: false, html_url: URL(string: "https://example.com")!, body: ""), ignore: nil)
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: MOVE
|
|
||||||
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,10 +4,6 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.application-groups</key>
|
|
||||||
<array>
|
|
||||||
<string>$(TeamIdentifierPrefix)com.maxgoedjen.secretive</string>
|
|
||||||
</array>
|
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.smartcard</key>
|
<key>com.apple.security.smartcard</key>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; };
|
50153E22250DECA300525160 /* SecretListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50153E21250DECA300525160 /* SecretListItemView.swift */; };
|
||||||
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
|
5018F54F24064786002EB505 /* Notifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5018F54E24064786002EB505 /* Notifier.swift */; };
|
||||||
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; };
|
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */; };
|
||||||
50571E0524393D1500F76F6C /* AgentLaunchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0424393D1500F76F6C /* AgentLaunchController.swift */; };
|
50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50571E0424393D1500F76F6C /* LaunchAgentController.swift */; };
|
||||||
50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; };
|
50617D8323FCE48E0099B055 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8223FCE48E0099B055 /* App.swift */; };
|
||||||
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; };
|
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D8423FCE48E0099B055 /* ContentView.swift */; };
|
||||||
50617D8723FCE48E0099B055 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8623FCE48E0099B055 /* Assets.xcassets */; };
|
50617D8723FCE48E0099B055 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8623FCE48E0099B055 /* Assets.xcassets */; };
|
||||||
@ -36,8 +36,6 @@
|
|||||||
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */; };
|
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */; };
|
||||||
506772C72424784600034DED /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 506772C62424784600034DED /* Credits.rtf */; };
|
506772C72424784600034DED /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 506772C62424784600034DED /* Credits.rtf */; };
|
||||||
506772C92425BB8500034DED /* NoStoresView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 506772C82425BB8500034DED /* NoStoresView.swift */; };
|
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 */; };
|
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5079BA0E250F29BF00EA86F4 /* StoreListView.swift */; };
|
||||||
508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; };
|
508A58AA241E06B40069DC07 /* PreviewUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58A9241E06B40069DC07 /* PreviewUpdater.swift */; };
|
||||||
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; };
|
508A58B3241ED2180069DC07 /* AgentStatusChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */; };
|
||||||
@ -113,7 +111,7 @@
|
|||||||
50153E21250DECA300525160 /* SecretListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretListItemView.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustUpdatedChecker.swift; sourceTree = "<group>"; };
|
||||||
50571E0424393D1500F76F6C /* AgentLaunchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgentLaunchController.swift; sourceTree = "<group>"; };
|
50571E0424393D1500F76F6C /* LaunchAgentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchAgentController.swift; sourceTree = "<group>"; };
|
||||||
50617D7F23FCE48E0099B055 /* Secretive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Secretive.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
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>"; };
|
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>"; };
|
50617D8423FCE48E0099B055 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
@ -130,7 +128,6 @@
|
|||||||
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellConfigurationController.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
508A58AB241E121B0069DC07 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||||
@ -158,7 +155,6 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */,
|
5003EF3B278005E800DF2006 /* SecretKit in Frameworks */,
|
||||||
50736F672782C31800A723B6 /* SecretAgentKitProtocol in Frameworks */,
|
|
||||||
501421622781262300BBAA70 /* Brief in Frameworks */,
|
501421622781262300BBAA70 /* Brief in Frameworks */,
|
||||||
5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */,
|
5003EF5F2780081600DF2006 /* SecureEnclaveSecretKit in Frameworks */,
|
||||||
5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */,
|
5003EF612780081600DF2006 /* SmartCardSecretKit in Frameworks */,
|
||||||
@ -287,10 +283,9 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
|
508A58B2241ED2180069DC07 /* AgentStatusChecker.swift */,
|
||||||
50736F642782C05000A723B6 /* AgentCommunicationController.swift */,
|
|
||||||
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
|
5091D2BB25183B830049FD9B /* ApplicationDirectoryController.swift */,
|
||||||
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
|
50571E0224393C2600F76F6C /* JustUpdatedChecker.swift */,
|
||||||
50571E0424393D1500F76F6C /* AgentLaunchController.swift */,
|
50571E0424393D1500F76F6C /* LaunchAgentController.swift */,
|
||||||
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */,
|
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */,
|
||||||
);
|
);
|
||||||
path = Controllers;
|
path = Controllers;
|
||||||
@ -350,7 +345,6 @@
|
|||||||
5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */,
|
5003EF5E2780081600DF2006 /* SecureEnclaveSecretKit */,
|
||||||
5003EF602780081600DF2006 /* SmartCardSecretKit */,
|
5003EF602780081600DF2006 /* SmartCardSecretKit */,
|
||||||
501421612781262300BBAA70 /* Brief */,
|
501421612781262300BBAA70 /* Brief */,
|
||||||
50736F662782C31800A723B6 /* SecretAgentKitProtocol */,
|
|
||||||
);
|
);
|
||||||
productName = Secretive;
|
productName = Secretive;
|
||||||
productReference = 50617D7F23FCE48E0099B055 /* Secretive.app */;
|
productReference = 50617D7F23FCE48E0099B055 /* Secretive.app */;
|
||||||
@ -491,9 +485,8 @@
|
|||||||
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */,
|
50C385A52407A76D00AF2719 /* SecretDetailView.swift in Sources */,
|
||||||
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */,
|
5099A02423FD2AAA0062B6F2 /* CreateSecretView.swift in Sources */,
|
||||||
50153E20250AFCB200525160 /* UpdateView.swift in Sources */,
|
50153E20250AFCB200525160 /* UpdateView.swift in Sources */,
|
||||||
50571E0524393D1500F76F6C /* AgentLaunchController.swift in Sources */,
|
50571E0524393D1500F76F6C /* LaunchAgentController.swift in Sources */,
|
||||||
5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */,
|
5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */,
|
||||||
50736F652782C05000A723B6 /* AgentCommunicationController.swift in Sources */,
|
|
||||||
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
|
50B8550D24138C4F009958AC /* DeleteSecretView.swift in Sources */,
|
||||||
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
|
50BB046B2418AAAE00D6E079 /* EmptyStoreView.swift in Sources */,
|
||||||
50617D8323FCE48E0099B055 /* App.swift in Sources */,
|
50617D8323FCE48E0099B055 /* App.swift in Sources */,
|
||||||
@ -837,7 +830,6 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = Secretive/Secretive.entitlements;
|
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@ -882,7 +874,6 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = SecretAgent/SecretAgent.entitlements;
|
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"SecretAgent/Preview Content\"";
|
||||||
@ -894,7 +885,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent;
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
@ -917,7 +908,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent;
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
@ -941,7 +932,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1;
|
MARKETING_VERSION = 1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent;
|
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@ -1030,10 +1021,6 @@
|
|||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Brief;
|
productName = Brief;
|
||||||
};
|
};
|
||||||
50736F662782C31800A723B6 /* SecretAgentKitProtocol */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
productName = SecretAgentKitProtocol;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 50617D7723FCE48D0099B055 /* Project object */;
|
rootObject = 50617D7723FCE48D0099B055 /* Project object */;
|
||||||
|
@ -15,8 +15,6 @@ struct Secretive: App {
|
|||||||
return list
|
return list
|
||||||
}()
|
}()
|
||||||
private let agentStatusChecker = AgentStatusChecker()
|
private let agentStatusChecker = AgentStatusChecker()
|
||||||
private let agentLaunchController = AgentLaunchController()
|
|
||||||
private let agentCommunicationController = AgentCommunicationController()
|
|
||||||
private let justUpdatedChecker = JustUpdatedChecker()
|
private let justUpdatedChecker = JustUpdatedChecker()
|
||||||
|
|
||||||
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
@AppStorage("defaultsHasRunSetup") var hasRunSetup = false
|
||||||
@ -29,7 +27,6 @@ struct Secretive: App {
|
|||||||
.environmentObject(storeList)
|
.environmentObject(storeList)
|
||||||
.environmentObject(Updater(checkOnLaunch: hasRunSetup))
|
.environmentObject(Updater(checkOnLaunch: hasRunSetup))
|
||||||
.environmentObject(agentStatusChecker)
|
.environmentObject(agentStatusChecker)
|
||||||
.environmentObject(agentCommunicationController)
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if !hasRunSetup {
|
if !hasRunSetup {
|
||||||
showingSetup = true
|
showingSetup = true
|
||||||
@ -38,20 +35,11 @@ struct Secretive: App {
|
|||||||
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
|
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
|
||||||
guard hasRunSetup else { return }
|
guard hasRunSetup else { return }
|
||||||
agentStatusChecker.check()
|
agentStatusChecker.check()
|
||||||
if justUpdatedChecker.justUpdated || !agentStatusChecker.running {
|
if agentStatusChecker.running && justUpdatedChecker.justUpdated {
|
||||||
// Two conditions in which we reinstall/attempt a force launch:
|
// Relaunch the agent, since it'll be running from earlier update still
|
||||||
// 1: The app was just updated, and an old version of the agent is alive. Reinstall will deactivate this and activate a new one.
|
reinstallAgent()
|
||||||
// 2: The agent is not running for some reason. We'll attempt to reinstall it, or relaunch directly if that fails.
|
} else if !agentStatusChecker.running && !agentStatusChecker.developmentBuild {
|
||||||
reinstallAgent(uninstallFirst: agentStatusChecker.running) {
|
forceLaunchAgent()
|
||||||
if agentStatusChecker.noninstanceSecretAgentProcesses.isEmpty {
|
|
||||||
agentLaunchController.killNonInstanceAgents(agents: agentStatusChecker.noninstanceSecretAgentProcesses)
|
|
||||||
}
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
||||||
agentCommunicationController.configure()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
agentCommunicationController.configure()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,13 +60,6 @@ struct Secretive: App {
|
|||||||
showingSetup = true
|
showingSetup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CommandGroup(after: .help) {
|
|
||||||
Button("TEST") {
|
|
||||||
Task {
|
|
||||||
try await agentCommunicationController.agent?.updatedStore(withID: storeList.modifiableStore?.id ?? UUID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SidebarCommands()
|
SidebarCommands()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,21 +68,24 @@ struct Secretive: App {
|
|||||||
|
|
||||||
extension Secretive {
|
extension Secretive {
|
||||||
|
|
||||||
private func reinstallAgent(uninstallFirst: Bool, completion: @escaping () -> Void) {
|
private func reinstallAgent() {
|
||||||
justUpdatedChecker.check()
|
justUpdatedChecker.check()
|
||||||
agentLaunchController.install(uninstallFirst: uninstallFirst) {
|
LaunchAgentController().install {
|
||||||
// Wait a second for launchd to kick in (next runloop isn't enough).
|
// Wait a second for launchd to kick in (next runloop isn't enough).
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
agentStatusChecker.check()
|
agentStatusChecker.check()
|
||||||
if !agentStatusChecker.running {
|
if !agentStatusChecker.running {
|
||||||
agentLaunchController.forceLaunch { _ in
|
forceLaunchAgent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
agentStatusChecker.check()
|
||||||
completion()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
completion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
import Foundation
|
|
||||||
import Combine
|
|
||||||
import AppKit
|
|
||||||
import OSLog
|
|
||||||
import SecretKit
|
|
||||||
import SecretAgentKitProtocol
|
|
||||||
|
|
||||||
protocol AgentCommunicationControllerProtocol: ObservableObject {
|
|
||||||
var agent: AgentProtocol? { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
class AgentCommunicationController: ObservableObject, AgentCommunicationControllerProtocol {
|
|
||||||
|
|
||||||
private(set) var agent: AgentProtocol? = nil
|
|
||||||
private var connection: NSXPCConnection? = nil
|
|
||||||
private var running = false
|
|
||||||
|
|
||||||
init() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func configure() {
|
|
||||||
guard !running else { return }
|
|
||||||
connection = NSXPCConnection(machServiceName: Bundle.main.agentBundleID)
|
|
||||||
connection?.remoteObjectInterface = NSXPCInterface(with: AgentProtocol.self)
|
|
||||||
connection?.invalidationHandler = {
|
|
||||||
Logger().warning("XPC connection invalidated")
|
|
||||||
}
|
|
||||||
connection?.resume()
|
|
||||||
agent = connection?.remoteObjectProxyWithErrorHandler({ error in
|
|
||||||
Logger().error("\(String(describing: error))")
|
|
||||||
}) as! AgentProtocol
|
|
||||||
running = true
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -30,18 +30,13 @@ class AgentStatusChecker: ObservableObject, AgentStatusCheckerProtocol {
|
|||||||
let agents = secretAgentProcesses
|
let agents = secretAgentProcesses
|
||||||
for agent in agents {
|
for agent in agents {
|
||||||
guard let url = agent.bundleURL else { continue }
|
guard let url = agent.bundleURL else { continue }
|
||||||
if url.absoluteString.contains(Bundle.main.bundleURL.absoluteString) {
|
if url.absoluteString.hasPrefix(Bundle.main.bundleURL.absoluteString) {
|
||||||
return agent
|
return agent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// All processes, _NOT_ including one the instance agent.
|
|
||||||
var noninstanceSecretAgentProcesses: [NSRunningApplication] {
|
|
||||||
NSRunningApplication.runningApplications(withBundleIdentifier: Bundle.main.agentBundleID)
|
|
||||||
.filter({ !($0.bundleURL?.absoluteString.contains(Bundle.main.bundleURL.absoluteString) ?? false) })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whether Secretive is being run in an Xcode environment.
|
// Whether Secretive is being run in an Xcode environment.
|
||||||
var developmentBuild: Bool {
|
var developmentBuild: Bool {
|
||||||
|
@ -9,7 +9,6 @@ protocol JustUpdatedCheckerProtocol: ObservableObject {
|
|||||||
class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol {
|
class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol {
|
||||||
|
|
||||||
@Published var justUpdated: Bool = false
|
@Published var justUpdated: Bool = false
|
||||||
var alreadyRelaunchedForDebug = false
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
check()
|
check()
|
||||||
@ -19,12 +18,7 @@ class JustUpdatedChecker: ObservableObject, JustUpdatedCheckerProtocol {
|
|||||||
let lastBuild = UserDefaults.standard.object(forKey: Constants.previousVersionUserDefaultsKey) as? String ?? "None"
|
let lastBuild = UserDefaults.standard.object(forKey: Constants.previousVersionUserDefaultsKey) as? String ?? "None"
|
||||||
let currentBuild = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
|
let currentBuild = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
|
||||||
UserDefaults.standard.set(currentBuild, forKey: Constants.previousVersionUserDefaultsKey)
|
UserDefaults.standard.set(currentBuild, forKey: Constants.previousVersionUserDefaultsKey)
|
||||||
if currentBuild != Constants.debugVersionKey {
|
|
||||||
justUpdated = lastBuild != currentBuild
|
justUpdated = lastBuild != currentBuild
|
||||||
} else {
|
|
||||||
justUpdated = !alreadyRelaunchedForDebug
|
|
||||||
alreadyRelaunchedForDebug = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -35,7 +29,6 @@ extension JustUpdatedChecker {
|
|||||||
|
|
||||||
enum Constants {
|
enum Constants {
|
||||||
static let previousVersionUserDefaultsKey = "com.maxgoedjen.Secretive.lastBuild"
|
static let previousVersionUserDefaultsKey = "com.maxgoedjen.Secretive.lastBuild"
|
||||||
static let debugVersionKey = "GITHUB_CI_VERSION"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,27 +4,23 @@ import AppKit
|
|||||||
import OSLog
|
import OSLog
|
||||||
import SecretKit
|
import SecretKit
|
||||||
|
|
||||||
struct AgentLaunchController {
|
struct LaunchAgentController {
|
||||||
|
|
||||||
func install(uninstallFirst: Bool = true, completion: (() -> Void)? = nil) {
|
func install(completion: (() -> Void)? = nil) {
|
||||||
Logger().debug("Installing agent")
|
Logger().debug("Installing agent")
|
||||||
if uninstallFirst {
|
|
||||||
_ = setEnabled(false)
|
_ = setEnabled(false)
|
||||||
}
|
|
||||||
// This is definitely a bit of a "seems to work better" thing but:
|
// This is definitely a bit of a "seems to work better" thing but:
|
||||||
// Seems to more reliably hit if these are on separate runloops, otherwise it seems like it sometimes doesn't kill old
|
// Seems to more reliably hit if these are on separate runloops, otherwise it seems like it sometimes doesn't kill old
|
||||||
// and start new?
|
// and start new?
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
_ = setEnabled(true)
|
_ = setEnabled(true)
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
||||||
completion?()
|
completion?()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func forceLaunch(completion: ((Bool) -> Void)?) {
|
func forceLaunch(completion: ((Bool) -> Void)?) {
|
||||||
Logger().debug("Agent is still not running, attempting to force launch")
|
Logger().debug("Agent is not running, attempting to force launch")
|
||||||
let url = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LoginItems/SecretAgent.app")
|
let url = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LoginItems/SecretAgent.app")
|
||||||
let config = NSWorkspace.OpenConfiguration()
|
let config = NSWorkspace.OpenConfiguration()
|
||||||
config.activates = false
|
config.activates = false
|
||||||
@ -40,12 +36,6 @@ struct AgentLaunchController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func killNonInstanceAgents(agents: [NSRunningApplication]) {
|
|
||||||
for agent in agents {
|
|
||||||
agent.terminate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func setEnabled(_ enabled: Bool) -> Bool {
|
private func setEnabled(_ enabled: Bool) -> Bool {
|
||||||
SMLoginItemSetEnabled(Bundle.main.agentBundleID as CFString, enabled)
|
SMLoginItemSetEnabled(Bundle.main.agentBundleID as CFString, enabled)
|
||||||
}
|
}
|
@ -2,6 +2,6 @@ import Foundation
|
|||||||
|
|
||||||
|
|
||||||
extension Bundle {
|
extension Bundle {
|
||||||
public var agentBundleID: String { "Z72PRUAWF6.com.maxgoedjen.Secretive.SecretAgent" }
|
public var agentBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "Host", with: "SecretAgent"))!}
|
||||||
public var hostBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "SecretAgent", with: "Host"))!}
|
public var hostBundleID: String {(self.bundleIdentifier?.replacingOccurrences(of: "SecretAgent", with: "Host"))!}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,6 @@ extension Preview {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class StoreModifiable: Store, SecretStoreModifiable {
|
class StoreModifiable: Store, SecretStoreModifiable {
|
||||||
|
|
||||||
override var name: String { "Modifiable Preview Store" }
|
override var name: String { "Modifiable Preview Store" }
|
||||||
|
|
||||||
func create(name: String, requiresAuthentication: Bool) throws {
|
func create(name: String, requiresAuthentication: Bool) throws {
|
||||||
@ -56,10 +55,6 @@ extension Preview {
|
|||||||
|
|
||||||
func update(secret: Preview.Secret, name: String) throws {
|
func update(secret: Preview.Secret, name: String) throws {
|
||||||
}
|
}
|
||||||
|
|
||||||
func reload() throws {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,10 +4,6 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<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>
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
|
@ -32,6 +32,9 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
|
|||||||
appPathNotice
|
appPathNotice
|
||||||
newItem
|
newItem
|
||||||
}
|
}
|
||||||
|
.sheet(isPresented: $runningSetup) {
|
||||||
|
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -87,7 +90,7 @@ extension ContentView {
|
|||||||
})
|
})
|
||||||
.popover(isPresented: $showingCreation, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
.popover(isPresented: $showingCreation, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
||||||
if let modifiable = storeList.modifiableStore {
|
if let modifiable = storeList.modifiableStore {
|
||||||
CreateSecretView<AnySecretStoreModifiable, AgentCommunicationController>(store: modifiable, showing: $showingCreation)
|
CreateSecretView(store: modifiable, showing: $showingCreation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,9 +122,6 @@ extension ContentView {
|
|||||||
EmptyView()
|
EmptyView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $runningSetup) {
|
|
||||||
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SecretKit
|
import SecretKit
|
||||||
|
|
||||||
struct CreateSecretView<StoreType: SecretStoreModifiable, AgentCommunicationControllerType: AgentCommunicationControllerProtocol>: View {
|
struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
|
||||||
|
|
||||||
@ObservedObject var store: StoreType
|
@ObservedObject var store: StoreType
|
||||||
@EnvironmentObject private var agentCommunicationController: AgentCommunicationControllerType
|
|
||||||
@Binding var showing: Bool
|
@Binding var showing: Bool
|
||||||
|
|
||||||
@State private var name = ""
|
@State private var name = ""
|
||||||
@ -53,9 +52,6 @@ struct CreateSecretView<StoreType: SecretStoreModifiable, AgentCommunicationCont
|
|||||||
|
|
||||||
func save() {
|
func save() {
|
||||||
try! store.create(name: name, requiresAuthentication: requiresAuthentication)
|
try! store.create(name: name, requiresAuthentication: requiresAuthentication)
|
||||||
Task {
|
|
||||||
try! await agentCommunicationController.agent!.updatedStore(withID: store.id)
|
|
||||||
}
|
|
||||||
showing = false
|
showing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ struct SecretDetailView<SecretType: Secret>: View {
|
|||||||
@State var secret: SecretType
|
@State var secret: SecretType
|
||||||
|
|
||||||
private let keyWriter = OpenSSHKeyWriter()
|
private let keyWriter = OpenSSHKeyWriter()
|
||||||
private let publicKeyFileStoreController = PublicKeyFileStoreController()
|
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory().replacingOccurrences(of: Bundle.main.hostBundleID, with: Bundle.main.agentBundleID))
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@ -18,7 +18,7 @@ struct SecretDetailView<SecretType: Secret>: View {
|
|||||||
CopyableView(title: "MD5 Fingerprint", image: Image(systemName: "touchid"), text: keyWriter.openSSHMD5Fingerprint(secret: secret))
|
CopyableView(title: "MD5 Fingerprint", image: Image(systemName: "touchid"), text: keyWriter.openSSHMD5Fingerprint(secret: secret))
|
||||||
Spacer()
|
Spacer()
|
||||||
.frame(height: 20)
|
.frame(height: 20)
|
||||||
CopyableView(title: "Public Key Contents", image: Image(systemName: "key"), text: keyString)
|
CopyableView(title: "Public Key", image: Image(systemName: "key"), text: keyString)
|
||||||
Spacer()
|
Spacer()
|
||||||
.frame(height: 20)
|
.frame(height: 20)
|
||||||
CopyableView(title: "Public Key Path", image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.path(for: secret))
|
CopyableView(title: "Public Key Path", image: Image(systemName: "lock.doc"), text: publicKeyFileStoreController.path(for: secret))
|
||||||
|
@ -22,7 +22,7 @@ struct SetupView: View {
|
|||||||
}
|
}
|
||||||
.frame(width: proxy.size.width)
|
.frame(width: proxy.size.width)
|
||||||
}
|
}
|
||||||
.offset(x: -proxy.size.width * CGFloat(stepIndex), y: 0)
|
.offset(x: -proxy.size.width * Double(stepIndex), y: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ struct StepView: View {
|
|||||||
let currentStep: Int
|
let currentStep: Int
|
||||||
|
|
||||||
// Ideally we'd have a geometry reader inside this view doing this for us, but that crashes on 11.0b7
|
// Ideally we'd have a geometry reader inside this view doing this for us, but that crashes on 11.0b7
|
||||||
let width: CGFloat
|
let width: Double
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack(alignment: .leading) {
|
ZStack(alignment: .leading) {
|
||||||
@ -53,7 +53,7 @@ struct StepView: View {
|
|||||||
.frame(height: 5)
|
.frame(height: 5)
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(.green)
|
.foregroundColor(.green)
|
||||||
.frame(width: max(0, ((width - (Constants.padding * 2)) / CGFloat(numberOfSteps - 1)) * CGFloat(currentStep) - (Constants.circleWidth / 2)), height: 5)
|
.frame(width: max(0, ((width - (Constants.padding * 2)) / Double(numberOfSteps - 1)) * Double(currentStep) - (Constants.circleWidth / 2)), height: 5)
|
||||||
HStack {
|
HStack {
|
||||||
ForEach(0..<numberOfSteps) { index in
|
ForEach(0..<numberOfSteps) { index in
|
||||||
ZStack {
|
ZStack {
|
||||||
@ -92,8 +92,8 @@ extension StepView {
|
|||||||
|
|
||||||
enum Constants {
|
enum Constants {
|
||||||
|
|
||||||
static let padding: CGFloat = 15
|
static let padding: Double = 15
|
||||||
static let circleWidth: CGFloat = 30
|
static let circleWidth: Double = 30
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ struct SecretAgentSetupView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func install() {
|
func install() {
|
||||||
AgentLaunchController().install()
|
LaunchAgentController().install()
|
||||||
buttonAction()
|
buttonAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user