diff --git a/SecretAgent/Notifier.swift b/SecretAgent/Notifier.swift index 01114be..f302d69 100644 --- a/SecretAgent/Notifier.swift +++ b/SecretAgent/Notifier.swift @@ -30,7 +30,11 @@ class Notifier { notificationCenter.requestAuthorization(options: .alert) { _, _ in } } - func notify(accessTo secret: AnySecret, by provenance: SigningRequestProvenance, promptToPersist: Bool) { + func notify(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) { + notificationDelegate.persistAuthentication = { duration in + guard let duration = duration else { return } + try? store.persistAuthentication(secret: secret, forDuration: duration) + } let notificationCenter = UNUserNotificationCenter.current() let notificationContent = UNMutableNotificationContent() notificationContent.title = "Signed Request from \(provenance.origin.displayName)" @@ -70,11 +74,11 @@ class Notifier { extension Notifier: SigningWitness { - func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws { + func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) throws { } - func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws { - notify(accessTo: secret, by: provenance, promptToPersist: false) + func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) throws { + notify(accessTo: secret, from: store, by: provenance) } } @@ -104,12 +108,27 @@ class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { fileprivate var release: Release? fileprivate var ignore: ((Release) -> Void)? + fileprivate var persistAuthentication: ((TimeInterval?) -> Void)? func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) { } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + let category = response.notification.request.content.categoryIdentifier + switch category { + case Notifier.Constants.updateCategoryIdentitifier: + handleUpdateResponse(response: response) + case Notifier.Constants.persistAuthenticationCategoryIdentitifier: + handlePersistAuthenticationResponse(response: response) + default: + fatalError() + } + + completionHandler() + } + + func handleUpdateResponse(response: UNNotificationResponse) { guard let update = release else { return } switch response.actionIdentifier { case Notifier.Constants.updateActionIdentitifier, UNNotificationDefaultActionIdentifier: @@ -119,7 +138,24 @@ class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { default: fatalError() } - completionHandler() + } + + func handlePersistAuthenticationResponse(response: UNNotificationResponse) { + switch response.actionIdentifier { + case Notifier.Constants.doNotPersistActionIdentitifier, UNNotificationDefaultActionIdentifier: + break + case Notifier.Constants.persistForOneMinuteActionIdentitifier: + // TODO: CLEANUP CONSTANTS + persistAuthentication?(60) + case Notifier.Constants.persistForFiveMinutesActionIdentitifier: + persistAuthentication?(60 * 5) + case Notifier.Constants.persistForOneHourActionIdentitifier: + persistAuthentication?(60 * 60) + case Notifier.Constants.persistForOneDayActionIdentitifier: + persistAuthentication?(60 * 60 * 24) + default: + fatalError() + } } func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { diff --git a/SecretAgentKit/Agent.swift b/SecretAgentKit/Agent.swift index b71b277..6fced2d 100644 --- a/SecretAgentKit/Agent.swift +++ b/SecretAgentKit/Agent.swift @@ -89,7 +89,7 @@ extension Agent { } if let witness = witness { - try witness.speakNowOrForeverHoldYourPeace(forAccessTo: secret, by: provenance) + try witness.speakNowOrForeverHoldYourPeace(forAccessTo: secret, from: store, by: provenance) } let dataToSign = reader.readNextChunk() @@ -134,7 +134,7 @@ extension Agent { signedData.append(writer.lengthAndData(of: sub)) if let witness = witness { - try witness.witness(accessTo: secret, by: provenance) + try witness.witness(accessTo: secret, from: store, by: provenance) } Logger().debug("Agent signed request") diff --git a/SecretAgentKit/SigningWitness.swift b/SecretAgentKit/SigningWitness.swift index d85467c..c1b57df 100644 --- a/SecretAgentKit/SigningWitness.swift +++ b/SecretAgentKit/SigningWitness.swift @@ -3,7 +3,7 @@ import SecretKit public protocol SigningWitness { - func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws - func witness(accessTo secret: AnySecret, by provenance: SigningRequestProvenance) throws + func speakNowOrForeverHoldYourPeace(forAccessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) throws + func witness(accessTo secret: AnySecret, from store: AnySecretStore, by provenance: SigningRequestProvenance) throws } diff --git a/SecretKit/Common/Erasers/AnySecretStore.swift b/SecretKit/Common/Erasers/AnySecretStore.swift index 8f35e4d..ecf6601 100644 --- a/SecretKit/Common/Erasers/AnySecretStore.swift +++ b/SecretKit/Common/Erasers/AnySecretStore.swift @@ -9,6 +9,8 @@ public class AnySecretStore: SecretStore { private let _name: () -> String private let _secrets: () -> [AnySecret] private let _sign: (Data, AnySecret, SigningRequestProvenance) throws -> Data + private let _persistAuthentication: (AnySecret, TimeInterval) throws -> Void + private var sink: AnyCancellable? public init(_ secretStore: SecretStoreType) where SecretStoreType: SecretStore { @@ -18,6 +20,7 @@ public class AnySecretStore: SecretStore { _id = { secretStore.id } _secrets = { secretStore.secrets.map { AnySecret($0) } } _sign = { try secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2) } + _persistAuthentication = { try secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) } sink = secretStore.objectWillChange.sink { _ in self.objectWillChange.send() } @@ -43,6 +46,10 @@ public class AnySecretStore: SecretStore { try _sign(data, secret, provenance) } + public func persistAuthentication(secret: AnySecret, forDuration duration: TimeInterval) throws { + try _persistAuthentication(secret, duration) + } + } public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable { @@ -69,4 +76,5 @@ public class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable { public func update(secret: AnySecret, name: String) throws { try _update(secret, name) } + } diff --git a/SecretKit/Common/Types/SecretStore.swift b/SecretKit/Common/Types/SecretStore.swift index 1f245a9..7274b19 100644 --- a/SecretKit/Common/Types/SecretStore.swift +++ b/SecretKit/Common/Types/SecretStore.swift @@ -11,6 +11,9 @@ public protocol SecretStore: ObservableObject, Identifiable { func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) throws -> Data + // TODO: MOVE TO SEPARATE PROTOCOL? + func persistAuthentication(secret: SecretType, forDuration: TimeInterval) throws + } public protocol SecretStoreModifiable: SecretStore { @@ -21,12 +24,6 @@ public protocol SecretStoreModifiable: SecretStore { } -public protocol SecretStoreAuthenticationPersistable: SecretStore { - - func persistAuthentication(secret: SecretType, forDuration: TimeInterval) throws - -} - extension NSNotification.Name { static let secretStoreUpdated = NSNotification.Name("com.maxgoedjen.Secretive.secretStore.updated") diff --git a/SecretKit/SecureEnclave/SecureEnclaveStore.swift b/SecretKit/SecureEnclave/SecureEnclaveStore.swift index 71f87eb..90762b1 100644 --- a/SecretKit/SecureEnclave/SecureEnclaveStore.swift +++ b/SecretKit/SecureEnclave/SecureEnclaveStore.swift @@ -5,7 +5,7 @@ import LocalAuthentication extension SecureEnclave { - public class Store: SecretStoreModifiable, SecretStoreAuthenticationPersistable { + public class Store: SecretStoreModifiable { public var isAvailable: Bool { // For some reason, as of build time, CryptoKit.SecureEnclave.isAvailable always returns false diff --git a/SecretKit/SmartCard/SmartCardStore.swift b/SecretKit/SmartCard/SmartCardStore.swift index 236ec0c..1be657d 100644 --- a/SecretKit/SmartCard/SmartCardStore.swift +++ b/SecretKit/SmartCard/SmartCardStore.swift @@ -82,6 +82,9 @@ extension SmartCard { return signature as Data } + public func persistAuthentication(secret: SmartCard.Secret, forDuration: TimeInterval) throws { + } + } }