437 lines
16 KiB
Swift
437 lines
16 KiB
Swift
//
|
|
// Copyright 2023 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import Contacts
|
|
import Foundation
|
|
import LibSignalClient
|
|
public import SignalServiceKit
|
|
|
|
extension RegistrationCoordinatorImpl {
|
|
|
|
public enum Shims {
|
|
public typealias ContactsManager = _RegistrationCoordinator_ContactsManagerShim
|
|
public typealias ContactsStore = _RegistrationCoordinator_CNContactsStoreShim
|
|
public typealias ExperienceManager = _RegistrationCoordinator_ExperienceManagerShim
|
|
public typealias FeatureFlags = _RegistrationCoordinator_FeatureFlagsShim
|
|
public typealias MessagePipelineSupervisor = _RegistrationCoordinator_MessagePipelineSupervisorShim
|
|
public typealias MessageProcessor = _RegistrationCoordinator_MessageProcessorShim
|
|
public typealias OWS2FAManager = _RegistrationCoordinator_OWS2FAManagerShim
|
|
public typealias PreKeyManager = _RegistrationCoordinator_PreKeyManagerShim
|
|
public typealias ProfileManager = _RegistrationCoordinator_ProfileManagerShim
|
|
public typealias PushRegistrationManager = _RegistrationCoordinator_PushRegistrationManagerShim
|
|
public typealias ReceiptManager = _RegistrationCoordinator_ReceiptManagerShim
|
|
public typealias UDManager = _RegistrationCoordinator_UDManagerShim
|
|
}
|
|
public enum Wrappers {
|
|
public typealias ContactsManager = _RegistrationCoordinator_ContactsManagerWrapper
|
|
public typealias ContactsStore = _RegistrationCoordinator_CNContactsStoreWrapper
|
|
public typealias ExperienceManager = _RegistrationCoordinator_ExperienceManagerWrapper
|
|
public typealias FeatureFlags = _RegistrationCoordinator_FeatureFlagsWrapper
|
|
public typealias MessagePipelineSupervisor = _RegistrationCoordinator_MessagePipelineSupervisorWrapper
|
|
public typealias MessageProcessor = _RegistrationCoordinator_MessageProcessorWrapper
|
|
public typealias OWS2FAManager = _RegistrationCoordinator_OWS2FAManagerWrapper
|
|
public typealias PreKeyManager = _RegistrationCoordinator_PreKeyManagerWrapper
|
|
public typealias ProfileManager = _RegistrationCoordinator_ProfileManagerWrapper
|
|
public typealias PushRegistrationManager = _RegistrationCoordinator_PushRegistrationManagerWrapper
|
|
public typealias ReceiptManager = _RegistrationCoordinator_ReceiptManagerWrapper
|
|
public typealias UDManager = _RegistrationCoordinator_UDManagerWrapper
|
|
}
|
|
}
|
|
|
|
// MARK: OWSContactsManager
|
|
|
|
public protocol _RegistrationCoordinator_ContactsManagerShim {
|
|
|
|
func fetchSystemContactsOnceIfAlreadyAuthorized()
|
|
}
|
|
|
|
public class _RegistrationCoordinator_ContactsManagerWrapper: _RegistrationCoordinator_ContactsManagerShim {
|
|
|
|
private let manager: OWSContactsManager
|
|
public init(_ manager: OWSContactsManager) { self.manager = manager }
|
|
|
|
public func fetchSystemContactsOnceIfAlreadyAuthorized() {
|
|
manager.fetchSystemContactsOnceIfAlreadyAuthorized()
|
|
}
|
|
}
|
|
|
|
// MARK: CNContacts
|
|
|
|
public protocol _RegistrationCoordinator_CNContactsStoreShim {
|
|
|
|
func needsContactsAuthorization() -> Guarantee<Bool>
|
|
|
|
func requestContactsAuthorization() -> Guarantee<Void>
|
|
}
|
|
|
|
public class _RegistrationCoordinator_CNContactsStoreWrapper: _RegistrationCoordinator_CNContactsStoreShim {
|
|
|
|
public init() {}
|
|
|
|
public func needsContactsAuthorization() -> Guarantee<Bool> {
|
|
return .value(CNContactStore.authorizationStatus(for: .contacts) == .notDetermined)
|
|
}
|
|
|
|
public func requestContactsAuthorization() -> Guarantee<Void> {
|
|
let (guarantee, future) = Guarantee<Void>.pending()
|
|
CNContactStore().requestAccess(for: CNEntityType.contacts) { (granted, error) -> Void in
|
|
if granted {
|
|
Logger.info("User granted contacts permission")
|
|
} else {
|
|
// Unfortunately, we can't easily disambiguate "not granted" and
|
|
// "other error".
|
|
Logger.warn("User denied contacts permission or there was an error. Error: \(String(describing: error))")
|
|
}
|
|
future.resolve()
|
|
}
|
|
return guarantee
|
|
}
|
|
}
|
|
|
|
// MARK: - ExperienceManager
|
|
|
|
public protocol _RegistrationCoordinator_ExperienceManagerShim {
|
|
|
|
func clearIntroducingPinsExperience(_ tx: DBWriteTransaction)
|
|
|
|
func enableAllGetStartedCards(_ tx: DBWriteTransaction)
|
|
}
|
|
|
|
public class _RegistrationCoordinator_ExperienceManagerWrapper: _RegistrationCoordinator_ExperienceManagerShim {
|
|
|
|
public init() {}
|
|
|
|
public func clearIntroducingPinsExperience(_ tx: DBWriteTransaction) {
|
|
ExperienceUpgradeManager.clearExperienceUpgrade(.introducingPins, transaction: SDSDB.shimOnlyBridge(tx).unwrapGrdbWrite)
|
|
}
|
|
|
|
public func enableAllGetStartedCards(_ tx: DBWriteTransaction) {
|
|
GetStartedBannerViewController.enableAllCards(writeTx: SDSDB.shimOnlyBridge(tx))
|
|
}
|
|
}
|
|
|
|
// MARK: - FeatureFlags
|
|
|
|
public protocol _RegistrationCoordinator_FeatureFlagsShim {
|
|
|
|
var messageBackupFileAlphaRegistrationFlow: Bool { get }
|
|
}
|
|
|
|
public class _RegistrationCoordinator_FeatureFlagsWrapper: _RegistrationCoordinator_FeatureFlagsShim {
|
|
|
|
public init() {}
|
|
|
|
public var messageBackupFileAlphaRegistrationFlow: Bool { FeatureFlags.messageBackupFileAlphaRegistrationFlow }
|
|
}
|
|
|
|
// MARK: - MessagePipelineSupervisor
|
|
|
|
public protocol _RegistrationCoordinator_MessagePipelineSupervisorShim {
|
|
|
|
func suspendMessageProcessingWithoutHandle(for: MessagePipelineSupervisor.Suspension)
|
|
|
|
func unsuspendMessageProcessing(for: MessagePipelineSupervisor.Suspension)
|
|
}
|
|
|
|
public class _RegistrationCoordinator_MessagePipelineSupervisorWrapper: _RegistrationCoordinator_MessagePipelineSupervisorShim {
|
|
|
|
private let supervisor: MessagePipelineSupervisor
|
|
|
|
public init(_ supervisor: MessagePipelineSupervisor) {
|
|
self.supervisor = supervisor
|
|
}
|
|
|
|
public func suspendMessageProcessingWithoutHandle(for suspension: MessagePipelineSupervisor.Suspension) {
|
|
supervisor.suspendMessageProcessingWithoutHandle(for: suspension)
|
|
}
|
|
|
|
public func unsuspendMessageProcessing(for suspension: MessagePipelineSupervisor.Suspension) {
|
|
supervisor.unsuspendMessageProcessing(for: suspension)
|
|
}
|
|
}
|
|
|
|
// MARK: - MessageProcessor
|
|
|
|
public protocol _RegistrationCoordinator_MessageProcessorShim {
|
|
|
|
func waitForProcessingCompleteAndThenSuspend(
|
|
for suspension: MessagePipelineSupervisor.Suspension
|
|
) -> Guarantee<Void>
|
|
}
|
|
|
|
public class _RegistrationCoordinator_MessageProcessorWrapper: _RegistrationCoordinator_MessageProcessorShim {
|
|
|
|
private let processor: MessageProcessor
|
|
|
|
public init(_ processor: MessageProcessor) {
|
|
self.processor = processor
|
|
}
|
|
|
|
public func waitForProcessingCompleteAndThenSuspend(
|
|
for suspension: MessagePipelineSupervisor.Suspension
|
|
) -> Guarantee<Void> {
|
|
return processor.waitForProcessingCompleteAndThenSuspend(for: suspension)
|
|
}
|
|
}
|
|
|
|
// MARK: - OWS2FAManager
|
|
|
|
public protocol _RegistrationCoordinator_OWS2FAManagerShim {
|
|
|
|
func pinCode(_ tx: DBReadTransaction) -> String?
|
|
|
|
func clearLocalPinCode(_ tx: DBWriteTransaction)
|
|
|
|
func isReglockEnabled(_ tx: DBReadTransaction) -> Bool
|
|
|
|
func markPinEnabled(_ pin: String, _ tx: DBWriteTransaction)
|
|
|
|
func markRegistrationLockEnabled(_ tx: DBWriteTransaction)
|
|
}
|
|
|
|
public class _RegistrationCoordinator_OWS2FAManagerWrapper: _RegistrationCoordinator_OWS2FAManagerShim {
|
|
|
|
private let manager: OWS2FAManager
|
|
public init(_ manager: OWS2FAManager) { self.manager = manager }
|
|
|
|
public func pinCode(_ tx: DBReadTransaction) -> String? {
|
|
return manager.pinCode(transaction: SDSDB.shimOnlyBridge(tx))
|
|
}
|
|
|
|
public func clearLocalPinCode(_ tx: DBWriteTransaction) {
|
|
return manager.clearLocalPinCode(transaction: SDSDB.shimOnlyBridge(tx))
|
|
}
|
|
|
|
public func isReglockEnabled(_ tx: DBReadTransaction) -> Bool {
|
|
return manager.isRegistrationLockV2Enabled(transaction: SDSDB.shimOnlyBridge(tx))
|
|
}
|
|
|
|
public func markPinEnabled(_ pin: String, _ tx: DBWriteTransaction) {
|
|
manager.markEnabled(pin: pin, transaction: SDSDB.shimOnlyBridge(tx))
|
|
}
|
|
|
|
public func markRegistrationLockEnabled(_ tx: DBWriteTransaction) {
|
|
manager.markRegistrationLockV2Enabled(transaction: SDSDB.shimOnlyBridge(tx))
|
|
}
|
|
}
|
|
|
|
// MARK: - PreKeyManager
|
|
|
|
// Unfortunately this shim needs to exist because of tests; Promise.wrapAsync starts a Task,
|
|
// which is unavoidably asynchronous, which does not play nice with TestScheduler and general
|
|
// synchronous, single-threaded test setups. To avoid this, we need to push down the
|
|
// Promise.wrapAsync call into the shim, so that we can avoid the call entirely in tests.
|
|
public protocol _RegistrationCoordinator_PreKeyManagerShim {
|
|
|
|
func createPreKeysForRegistration() -> Promise<RegistrationPreKeyUploadBundles>
|
|
|
|
func finalizeRegistrationPreKeys(
|
|
_ prekeyBundles: RegistrationPreKeyUploadBundles,
|
|
uploadDidSucceed: Bool
|
|
) -> Promise<Void>
|
|
|
|
func rotateOneTimePreKeysForRegistration(auth: ChatServiceAuth) -> Promise<Void>
|
|
}
|
|
|
|
public class _RegistrationCoordinator_PreKeyManagerWrapper: _RegistrationCoordinator_PreKeyManagerShim {
|
|
|
|
private let preKeyManager: PreKeyManager
|
|
|
|
public init(_ preKeyManager: PreKeyManager) {
|
|
self.preKeyManager = preKeyManager
|
|
}
|
|
|
|
public func createPreKeysForRegistration() -> Promise<RegistrationPreKeyUploadBundles> {
|
|
let preKeyManager = self.preKeyManager
|
|
return Promise.wrapAsync {
|
|
return try await preKeyManager.createPreKeysForRegistration().value
|
|
}
|
|
}
|
|
|
|
public func finalizeRegistrationPreKeys(
|
|
_ prekeyBundles: RegistrationPreKeyUploadBundles,
|
|
uploadDidSucceed: Bool
|
|
) -> Promise<Void> {
|
|
let preKeyManager = self.preKeyManager
|
|
return Promise.wrapAsync {
|
|
return try await preKeyManager.finalizeRegistrationPreKeys(
|
|
prekeyBundles,
|
|
uploadDidSucceed: uploadDidSucceed
|
|
).value
|
|
}
|
|
}
|
|
|
|
public func rotateOneTimePreKeysForRegistration(auth: ChatServiceAuth) -> Promise<Void> {
|
|
let preKeyManager = self.preKeyManager
|
|
return Promise.wrapAsync {
|
|
return try await preKeyManager.rotateOneTimePreKeysForRegistration(auth: auth).value
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - ProfileManager
|
|
|
|
public protocol _RegistrationCoordinator_ProfileManagerShim {
|
|
|
|
var hasProfileName: Bool { get }
|
|
|
|
// NOTE: non-optional because OWSProfileManager generates a random key
|
|
// if one doesn't already exist.
|
|
var localProfileKey: Aes256Key { get }
|
|
|
|
func updateLocalProfile(
|
|
givenName: OWSUserProfile.NameComponent,
|
|
familyName: OWSUserProfile.NameComponent?,
|
|
avatarData: Data?,
|
|
authedAccount: AuthedAccount,
|
|
tx: DBWriteTransaction
|
|
) -> Promise<Void>
|
|
|
|
func scheduleReuploadLocalProfile(authedAccount: AuthedAccount)
|
|
}
|
|
|
|
public class _RegistrationCoordinator_ProfileManagerWrapper: _RegistrationCoordinator_ProfileManagerShim {
|
|
|
|
private let manager: ProfileManager
|
|
public init(_ manager: ProfileManager) { self.manager = manager }
|
|
|
|
public var hasProfileName: Bool { manager.hasProfileName }
|
|
|
|
public var localProfileKey: Aes256Key { manager.localProfileKey }
|
|
|
|
public func updateLocalProfile(
|
|
givenName: OWSUserProfile.NameComponent,
|
|
familyName: OWSUserProfile.NameComponent?,
|
|
avatarData: Data?,
|
|
authedAccount: AuthedAccount,
|
|
tx: DBWriteTransaction
|
|
) -> Promise<Void> {
|
|
return manager.updateLocalProfile(
|
|
profileGivenName: .setTo(givenName),
|
|
profileFamilyName: .setTo(familyName),
|
|
profileBio: .setTo(nil),
|
|
profileBioEmoji: .setTo(nil),
|
|
profileAvatarData: .setTo(avatarData),
|
|
visibleBadgeIds: .setTo([]),
|
|
unsavedRotatedProfileKey: nil,
|
|
userProfileWriter: .registration,
|
|
authedAccount: authedAccount,
|
|
tx: SDSDB.shimOnlyBridge(tx)
|
|
)
|
|
}
|
|
|
|
public func scheduleReuploadLocalProfile(authedAccount: AuthedAccount) {
|
|
return manager.reuploadLocalProfile(authedAccount: authedAccount)
|
|
}
|
|
}
|
|
|
|
// MARK: - PushRegistrationManager
|
|
|
|
extension Registration {
|
|
public enum RequestPushTokensResult {
|
|
case success(RegistrationRequestFactory.ApnRegistrationId)
|
|
case pushUnsupported(description: String)
|
|
case timeout
|
|
case genericError(Error)
|
|
}
|
|
|
|
public enum SyncPushTokensResult {
|
|
case success
|
|
case pushUnsupported(description: String)
|
|
case networkError
|
|
case genericError(Error)
|
|
}
|
|
}
|
|
|
|
public protocol _RegistrationCoordinator_PushRegistrationManagerShim {
|
|
|
|
func needsNotificationAuthorization() -> Guarantee<Bool>
|
|
|
|
func registerUserNotificationSettings() -> Guarantee<Void>
|
|
|
|
func requestPushToken() -> Guarantee<Registration.RequestPushTokensResult>
|
|
|
|
func receivePreAuthChallengeToken() -> Guarantee<String>
|
|
|
|
func clearPreAuthChallengeToken()
|
|
}
|
|
|
|
public class _RegistrationCoordinator_PushRegistrationManagerWrapper: _RegistrationCoordinator_PushRegistrationManagerShim {
|
|
|
|
private let manager: PushRegistrationManager
|
|
public init(_ manager: PushRegistrationManager) { self.manager = manager }
|
|
|
|
public func needsNotificationAuthorization() -> Guarantee<Bool> {
|
|
return manager.needsNotificationAuthorization()
|
|
}
|
|
|
|
public func registerUserNotificationSettings() -> Guarantee<Void> {
|
|
return Guarantee.wrapAsync { [manager] in
|
|
await manager.registerUserNotificationSettings()
|
|
}
|
|
}
|
|
|
|
public func requestPushToken() -> Guarantee<Registration.RequestPushTokensResult> {
|
|
return manager.requestPushTokens(forceRotation: false, timeOutEventually: true)
|
|
.map(on: SyncScheduler()) { .success($0) }
|
|
.recover(on: SyncScheduler()) { error in
|
|
switch error {
|
|
case PushRegistrationError.pushNotSupported(let description):
|
|
return .value(.pushUnsupported(description: description))
|
|
case PushRegistrationError.timeout:
|
|
return .value(.timeout)
|
|
default:
|
|
return .value(.genericError(error))
|
|
}
|
|
}
|
|
}
|
|
|
|
public func receivePreAuthChallengeToken() -> Guarantee<String> {
|
|
return manager.receivePreAuthChallengeToken()
|
|
}
|
|
|
|
public func clearPreAuthChallengeToken() {
|
|
manager.clearPreAuthChallengeToken()
|
|
}
|
|
}
|
|
|
|
// MARK: - ReceiptManager
|
|
|
|
public protocol _RegistrationCoordinator_ReceiptManagerShim {
|
|
|
|
func setAreReadReceiptsEnabled(_ areEnabled: Bool, _ tx: DBWriteTransaction)
|
|
func setAreStoryViewedReceiptsEnabled(_ areEnabled: Bool, _ tx: DBWriteTransaction)
|
|
}
|
|
|
|
public class _RegistrationCoordinator_ReceiptManagerWrapper: _RegistrationCoordinator_ReceiptManagerShim {
|
|
|
|
private let manager: OWSReceiptManager
|
|
public init(_ manager: OWSReceiptManager) { self.manager = manager }
|
|
|
|
public func setAreReadReceiptsEnabled(_ areEnabled: Bool, _ tx: DBWriteTransaction) {
|
|
manager.setAreReadReceiptsEnabled(areEnabled, transaction: SDSDB.shimOnlyBridge(tx))
|
|
}
|
|
|
|
public func setAreStoryViewedReceiptsEnabled(_ areEnabled: Bool, _ tx: DBWriteTransaction) {
|
|
StoryManager.setAreViewReceiptsEnabled(areEnabled, transaction: SDSDB.shimOnlyBridge(tx))
|
|
}
|
|
}
|
|
|
|
// MARK: - UDManager
|
|
|
|
public protocol _RegistrationCoordinator_UDManagerShim {
|
|
|
|
func shouldAllowUnrestrictedAccessLocal(transaction: DBReadTransaction) -> Bool
|
|
}
|
|
|
|
public class _RegistrationCoordinator_UDManagerWrapper: _RegistrationCoordinator_UDManagerShim {
|
|
|
|
private let manager: OWSUDManager
|
|
public init(_ manager: OWSUDManager) { self.manager = manager }
|
|
|
|
public func shouldAllowUnrestrictedAccessLocal(transaction: DBReadTransaction) -> Bool {
|
|
return manager.shouldAllowUnrestrictedAccessLocal(transaction: SDSDB.shimOnlyBridge(transaction))
|
|
}
|
|
}
|