Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn on strict concurrency #497

Merged
merged 2 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions Sources/Packages/Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.5
// swift-tools-version:5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down Expand Up @@ -33,23 +33,28 @@ let package = Package(
targets: [
.target(
name: "SecretKit",
dependencies: []
dependencies: [],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
),
.testTarget(
name: "SecretKitTests",
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"]
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
),
.target(
name: "SecureEnclaveSecretKit",
dependencies: ["SecretKit"]
dependencies: ["SecretKit"],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
),
.target(
name: "SmartCardSecretKit",
dependencies: ["SecretKit"]
dependencies: ["SecretKit"],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
),
.target(
name: "SecretAgentKit",
dependencies: ["SecretKit", "SecretAgentKitHeaders"]
dependencies: ["SecretKit", "SecretAgentKitHeaders"],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency"), .unsafeFlags(["-warnings-as-errors"])]
),
.systemLibrary(
name: "SecretAgentKitHeaders"
Expand Down
2 changes: 1 addition & 1 deletion Sources/Packages/Sources/Brief/Updater.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation
import Combine

/// A concrete implementation of ``UpdaterProtocol`` which considers the current release and OS version.
public class Updater: ObservableObject, UpdaterProtocol {
public final class Updater: ObservableObject, UpdaterProtocol {

@Published public var update: Release?
public let testBuild: Bool
Expand Down
2 changes: 1 addition & 1 deletion Sources/Packages/Sources/SecretAgentKit/Agent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import SecretKit
import AppKit

/// The `Agent` is an implementation of an SSH agent. It manages coordination and access between a socket, traces requests, notifies witnesses and passes requests to stores.
public class Agent {
public final class Agent {

private let storeList: SecretStoreList
private let witness: SigningWitness?
Expand Down
11 changes: 11 additions & 0 deletions Sources/Packages/Sources/SecretAgentKit/Sendability.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

struct UncheckedSendable<T>: @unchecked Sendable {

let value: T

init(_ value: T) {
self.value = value
}

}
12 changes: 6 additions & 6 deletions Sources/Packages/Sources/SecretAgentKit/SocketController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import Foundation
import OSLog

/// A controller that manages socket configuration and request dispatching.
public class SocketController {
public final class SocketController {

/// The active 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.
/// False if no data could be read
public var handler: ((FileHandleReader, FileHandleWriter) async -> Bool)?
public var handler: (@Sendable (FileHandleReader, FileHandleWriter) async -> Bool)?
/// Logger.
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "SocketController")

Expand Down Expand Up @@ -69,7 +69,7 @@ public class SocketController {
@objc func handleConnectionAccept(notification: Notification) {
logger.debug("Socket controller accepted connection")
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
Task {
Task { [handler, fileHandle] in
_ = await handler?(new, new)
await new.waitForDataInBackgroundAndNotifyOnMainActor()
await fileHandle?.acceptConnectionInBackgroundAndNotifyOnMainActor()
Expand All @@ -82,12 +82,12 @@ public class SocketController {
logger.debug("Socket controller has new data available")
guard let new = notification.object as? FileHandle else { return }
logger.debug("Socket controller received new file handle")
Task {
Task { [handler, logger = UncheckedSendable(logger)] in
if((await handler?(new, new)) == true) {
logger.debug("Socket controller handled data, wait for more data")
logger.value.debug("Socket controller handled data, wait for more data")
await new.waitForDataInBackgroundAndNotifyOnMainActor()
} else {
logger.debug("Socket controller called with empty data, socked closed")
logger.value.debug("Socket controller called with empty data, socked closed")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public class AnySecretStore: SecretStore {

}

public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {
public final class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable {

private let _create: (String, Bool) throws -> Void
private let _delete: (AnySecret) throws -> Void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation
import OSLog

/// Manages storage and lookup for OpenSSH certificates.
public class OpenSSHCertificateHandler {
public final class OpenSSHCertificateHandler {

private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory())
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "OpenSSHCertificateHandler")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// Reads OpenSSH protocol data.
public class OpenSSHReader {
public final class OpenSSHReader {

var remaining: Data

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation
import OSLog

/// Controller responsible for writing public keys to disk, so that they're easily accessible by scripts.
public class PublicKeyFileStoreController {
public final class PublicKeyFileStoreController {

private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "PublicKeyFileStoreController")
private let directory: String
Expand Down
2 changes: 1 addition & 1 deletion Sources/Packages/Sources/SecretKit/SecretStoreList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation
import Combine

/// A "Store Store," which holds a list of type-erased stores.
public class SecretStoreList: ObservableObject {
public final class SecretStoreList: ObservableObject {

/// The Stores managed by the SecretStoreList.
@Published public var stores: [AnySecretStore] = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import SecretKit
extension SecureEnclave {

/// An implementation of Store backed by the Secure Enclave.
public class Store: SecretStoreModifiable {
public final class Store: SecretStoreModifiable {

public var isAvailable: Bool {
// For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false
Expand All @@ -24,8 +24,8 @@ extension SecureEnclave {

/// Initializes a Store.
public init() {
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
self.reloadSecretsInternal(notifyAgent: false)
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { [reload = reloadSecretsInternal(notifyAgent:)] _ in
reload(false)
}
loadSecrets()
}
Expand Down Expand Up @@ -211,7 +211,7 @@ 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 reloadSecretsInternal(notifyAgent: Bool = true) {
@Sendable private func reloadSecretsInternal(notifyAgent: Bool = true) {
let before = secrets
secrets.removeAll()
loadSecrets()
Expand Down
28 changes: 16 additions & 12 deletions Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import SecretKit
extension SmartCard {

/// An implementation of Store backed by a Smart Card.
public class Store: SecretStore {
public final class Store: SecretStore {

@Published public var isAvailable: Bool = false
public let id = UUID()
Expand All @@ -20,12 +20,14 @@ extension SmartCard {
/// Initializes a Store.
public init() {
tokenID = watcher.nonSecureEnclaveTokens.first
watcher.setInsertionHandler { string in
watcher.setInsertionHandler { [reload = reloadSecretsInternal] string in
guard self.tokenID == nil else { return }
guard !string.contains("setoken") else { return }

self.tokenID = string
self.reloadSecrets()
DispatchQueue.main.async {
reload()
}
self.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string)
}
if let tokenID = tokenID {
Expand Down Expand Up @@ -106,15 +108,7 @@ extension SmartCard {

/// Reloads all secrets from the store.
public func reloadSecrets() {
DispatchQueue.main.async {
self.isAvailable = self.tokenID != nil
let before = self.secrets
self.secrets.removeAll()
self.loadSecrets()
if self.secrets != before {
NotificationCenter.default.post(name: .secretStoreReloaded, object: self)
}
}
reloadSecretsInternal()
}

}
Expand All @@ -123,6 +117,16 @@ extension SmartCard {

extension SmartCard.Store {

@Sendable private func reloadSecretsInternal() {
self.isAvailable = self.tokenID != nil
let before = self.secrets
self.secrets.removeAll()
self.loadSecrets()
if self.secrets != before {
NotificationCenter.default.post(name: .secretStoreReloaded, object: self)
}
}

/// Resets the token ID and reloads secrets.
/// - Parameter tokenID: The ID of the token that was removed.
private func smartcardRemoved(for tokenID: String? = nil) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Packages/Tests/SecretAgentKitTests/StubStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ struct Stub {}

extension Stub {

public class Store: SecretStore {
public final class Store: SecretStore {

public let isAvailable = true
public let id = UUID()
Expand Down
8 changes: 7 additions & 1 deletion Sources/Secretive.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -691,6 +691,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Debug;
Expand Down Expand Up @@ -718,6 +719,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Host";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Release;
Expand Down Expand Up @@ -848,6 +850,7 @@
MARKETING_VERSION = 1;
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.Host;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Test;
Expand Down Expand Up @@ -891,6 +894,7 @@
MARKETING_VERSION = 1;
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Test;
Expand All @@ -914,6 +918,7 @@
MARKETING_VERSION = 1;
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Debug;
Expand All @@ -939,6 +944,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.maxgoedjen.Secretive.SecretAgent;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Secretive - Secret Agent";
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_VERSION = 5.0;
};
name = Release;
Expand Down
Loading