mirror of
https://github.com/maxgoedjen/secretive.git
synced 2026-05-08 16:38:58 +02:00
WIP
This commit is contained in:
@@ -365,6 +365,16 @@
|
||||
},
|
||||
"shouldTranslate" : false
|
||||
},
|
||||
"%@ - %@" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "%1$@ - %2$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"about_build_log_button" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
@@ -19821,6 +19831,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Review" : {
|
||||
|
||||
},
|
||||
"Review All" : {
|
||||
|
||||
},
|
||||
"secret_detail_md5_fingerprint_label" : {
|
||||
"extractionState" : "manual",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@unsafe @preconcurrency import LocalAuthentication
|
||||
import SecretKit
|
||||
import OSLog
|
||||
|
||||
/// A context describing a persisted authentication.
|
||||
public final class AuthenticationContext: AuthenticationContextProtocol {
|
||||
@@ -63,26 +64,41 @@ public actor AuthenticationHandler {
|
||||
private var activeTask: Task<Void, any Error>?
|
||||
|
||||
private var lastBatchAuthPresentation: Set<SignatureRequest>?
|
||||
private var presentBatchAuth: ((Set<SignatureRequest>, @Sendable (Set<SignatureRequest>) async throws -> Void) async throws -> Void)?
|
||||
private var presentBatchAuth: (([[SignatureRequest]], @escaping @Sendable (Set<SignatureRequest>) async throws -> Void) async throws -> Void)?
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent")
|
||||
|
||||
public init(presentBatchAuth: ((Set<SignatureRequest>, @Sendable (Set<SignatureRequest>) async throws -> Void) async throws -> Void)?) {
|
||||
self.presentBatchAuth = presentBatchAuth
|
||||
public init() {
|
||||
}
|
||||
|
||||
public func setBatchAuthHandler(_ handler: @escaping (@Sendable ([[SignatureRequest]], @escaping @Sendable (Set<SignatureRequest>) async throws -> Void) async throws -> Void)) {
|
||||
self.presentBatchAuth = handler
|
||||
}
|
||||
|
||||
public func waitForAuthentication(for request: SignatureRequest) async throws -> any AuthenticationContextProtocol {
|
||||
if let existing = existingAuthenticationContext(for: request) { return existing }
|
||||
if let existing = existingAuthenticationContext(for: request) {
|
||||
logger.log("Short circuiting wait, existing valid context already exists.")
|
||||
return existing
|
||||
}
|
||||
holdingRequests.insert(request)
|
||||
defer { holdingRequests.remove(request) }
|
||||
logger.log("Waiting for authentication for \(request.id)")
|
||||
defer {
|
||||
logger.log("Removed hold for \(request.id)")
|
||||
holdingRequests.remove(request)
|
||||
}
|
||||
while holdingRequests.count > 1 {
|
||||
if hasBatchableRequests, holdingRequests != lastBatchAuthPresentation {
|
||||
logger.log("Batchable requests exist, cancelling existing auth prompt")
|
||||
activeTask?.cancel()
|
||||
lastBatchAuthPresentation = holdingRequests
|
||||
try await presentBatchAuth?(holdingRequests) {
|
||||
try await persistAuthentication(for: $0)
|
||||
}
|
||||
logger.log("Requesting batch auth presentation")
|
||||
try await presentBatchAuth?(batchableRequests, persistAuthentication(for:))
|
||||
logger.log("Requested batch auth presentation")
|
||||
}
|
||||
if let preauthorized = existingAuthenticationContext(for: request) {
|
||||
logger.log("Batch auth context found, proceededing with preauthorized context")
|
||||
return preauthorized
|
||||
} else {
|
||||
logger.log("Waiting for batch request handling")
|
||||
}
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
}
|
||||
@@ -92,12 +108,20 @@ public actor AuthenticationHandler {
|
||||
let context = AuthenticationContext(secret: request.secret, context: laContext, requestID: request.id)
|
||||
|
||||
activeTask = Task {
|
||||
_ = try? await laContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: laContext.localizedReason)
|
||||
logger.log("Beginning individual auth prompt")
|
||||
try await Task.sleep(for: .seconds(1000))
|
||||
// _ = try? await laContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: laContext.localizedReason)
|
||||
logger.log("Ended individual auth prompt")
|
||||
}
|
||||
_ = try await activeTask?.value
|
||||
// TODO: Check something beyond cancellation? id?
|
||||
// Is this okay? Do we always assume that a cancelled task will be the proceeded on?
|
||||
if activeTask?.isCancelled ?? false {
|
||||
logger.log("Auth prompt was cancelled, waiting for explicit auth")
|
||||
// If we explicitly cancelled the task, hang on until we auth it.
|
||||
while true {
|
||||
if let preauthorized = existingAuthenticationContext(for: request) {
|
||||
logger.log("Explicit auth context found")
|
||||
return preauthorized
|
||||
}
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
@@ -106,10 +130,17 @@ public actor AuthenticationHandler {
|
||||
return context
|
||||
}
|
||||
|
||||
private var batchableRequests: [[SignatureRequest]] {
|
||||
holdingRequests.reduce(into: [:]) { partialResult, next in
|
||||
partialResult[next.batchID, default: []].append(next)
|
||||
}
|
||||
.values
|
||||
.map { $0.sorted() }
|
||||
}
|
||||
|
||||
private var hasBatchableRequests: Bool {
|
||||
guard presentBatchAuth != nil else { return false }
|
||||
// FIXME: THIS
|
||||
return holdingRequests.count > 1
|
||||
return batchableRequests.count < holdingRequests.count
|
||||
}
|
||||
|
||||
private func existingAuthenticationContext(for request: SignatureRequest) -> (any AuthenticationContextProtocol)? {
|
||||
|
||||
@@ -13,7 +13,8 @@ public protocol AuthenticationContextProtocol: Sendable, Identifiable {
|
||||
|
||||
}
|
||||
|
||||
public struct SignatureRequest: Identifiable, Hashable, Sendable {
|
||||
public struct SignatureRequest: Identifiable, Hashable, Sendable, Comparable {
|
||||
|
||||
public let id: UUID
|
||||
public let date: Date
|
||||
public let secret: AnySecret
|
||||
@@ -25,4 +26,16 @@ public struct SignatureRequest: Identifiable, Hashable, Sendable {
|
||||
self.secret = secret
|
||||
self.provenance = provenance
|
||||
}
|
||||
|
||||
public var batchID: Int {
|
||||
var hasher = Hasher()
|
||||
provenance.batchID.hash(into: &hasher)
|
||||
secret.id.hash(into: &hasher)
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
public static func < (lhs: SignatureRequest, rhs: SignatureRequest) -> Bool {
|
||||
lhs.date < rhs.date
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,12 @@ public struct SigningRequestProvenance: Hashable, Sendable {
|
||||
self.date = date
|
||||
}
|
||||
|
||||
public var batchID: Int {
|
||||
var hasher = Hasher()
|
||||
chain.map(\.path).hash(into: &hasher)
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension SigningRequestProvenance {
|
||||
|
||||
@@ -11,7 +11,7 @@ extension ProcessInfo {
|
||||
}
|
||||
|
||||
guard let value = SecTaskCopyValueForEntitlement(task, "com.apple.developer.team-identifier" as CFString, nil) as? String else {
|
||||
assertionFailure("SecTaskCopyValueForEntitlement(com.apple.developer.team-identifier) failed")
|
||||
// assertionFailure("SecTaskCopyValueForEntitlement(com.apple.developer.team-identifier) failed")
|
||||
return fallbackTeamID
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user