diff --git a/FlowCrypt.xcodeproj/project.pbxproj b/FlowCrypt.xcodeproj/project.pbxproj index 7188261ff..463ecd506 100644 --- a/FlowCrypt.xcodeproj/project.pbxproj +++ b/FlowCrypt.xcodeproj/project.pbxproj @@ -120,7 +120,6 @@ 9F003D6D25EA8F3200EB38C0 /* SessionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F003D6C25EA8F3200EB38C0 /* SessionService.swift */; }; 9F003DB625EA92BC00EB38C0 /* LogOutHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F003DB525EA92BC00EB38C0 /* LogOutHandler.swift */; }; 9F0C3C102316DD5B00299985 /* GoogleUserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C0F2316DD5B00299985 /* GoogleUserService.swift */; }; - 9F0C3C122316DDA500299985 /* DataService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C112316DDA500299985 /* DataService.swift */; }; 9F0C3C142316E69300299985 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C132316E69300299985 /* User.swift */; }; 9F0C3C1A231819C500299985 /* MessageKindProviderType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C19231819C500299985 /* MessageKindProviderType.swift */; }; 9F0C3C2623194E0A00299985 /* FolderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C2523194E0A00299985 /* FolderViewModel.swift */; }; @@ -237,7 +236,6 @@ 9FBD69F8277B1E66002FC602 /* MBProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = 9FBD69F7277B1E66002FC602 /* MBProgressHUD */; }; 9FBD69FA277B1E6C002FC602 /* Toast in Frameworks */ = {isa = PBXBuildFile; productRef = 9FBD69F9277B1E6C002FC602 /* Toast */; }; 9FBEAE5525D41BFF009E98D4 /* UserMailSessionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBEAE5425D41BFF009E98D4 /* UserMailSessionProvider.swift */; }; - 9FBEAF3125DFB8E1009E98D4 /* DBMigrationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBEAF3025DFB8E1009E98D4 /* DBMigrationService.swift */; }; 9FC41090268100B6004C0A69 /* CoreTypesTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D254733324C597CD00DEE698 /* CoreTypesTest.swift */; }; 9FC411212595EA12001180A8 /* MessageSearchProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC411202595EA12001180A8 /* MessageSearchProvider.swift */; }; 9FC4112E2595EA8B001180A8 /* Gmail+Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FC4112D2595EA8B001180A8 /* Gmail+Search.swift */; }; @@ -561,7 +559,6 @@ 9F003D9D25EA910B00EB38C0 /* LocalStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalStorageTests.swift; sourceTree = ""; }; 9F003DB525EA92BC00EB38C0 /* LogOutHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogOutHandler.swift; sourceTree = ""; }; 9F0C3C0F2316DD5B00299985 /* GoogleUserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleUserService.swift; sourceTree = ""; }; - 9F0C3C112316DDA500299985 /* DataService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataService.swift; sourceTree = ""; }; 9F0C3C132316E69300299985 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 9F0C3C19231819C500299985 /* MessageKindProviderType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageKindProviderType.swift; sourceTree = ""; }; 9F0C3C2523194E0A00299985 /* FolderViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderViewModel.swift; sourceTree = ""; }; @@ -685,7 +682,6 @@ 9FB22CEF25715D960026EE64 /* BackupServiceError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupServiceError.swift; sourceTree = ""; }; 9FB22CF625715DC50026EE64 /* KeyServiceErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyServiceErrorHandler.swift; sourceTree = ""; }; 9FBEAE5425D41BFF009E98D4 /* UserMailSessionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMailSessionProvider.swift; sourceTree = ""; }; - 9FBEAF3025DFB8E1009E98D4 /* DBMigrationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBMigrationService.swift; sourceTree = ""; }; 9FC411202595EA12001180A8 /* MessageSearchProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSearchProvider.swift; sourceTree = ""; }; 9FC4112D2595EA8B001180A8 /* Gmail+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Gmail+Search.swift"; sourceTree = ""; }; 9FC411342595EA94001180A8 /* Imap+Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Imap+Search.swift"; sourceTree = ""; }; @@ -1528,14 +1524,6 @@ path = "UsersMailSession Provider"; sourceTree = ""; }; - 9FBEAF3725DFB8EB009E98D4 /* Migration */ = { - isa = PBXGroup; - children = ( - 9FBEAF3025DFB8E1009E98D4 /* DBMigrationService.swift */, - ); - path = Migration; - sourceTree = ""; - }; 9FC411272595EA28001180A8 /* SearchMessage Provider */ = { isa = PBXGroup; children = ( @@ -1789,7 +1777,6 @@ 9FC4114A25961CD6001180A8 /* Mail Provider */, 9FB22CB425715C860026EE64 /* Error Handling */, 9F0C3C1F23191F2000299985 /* Services */, - 9FBEAF3725DFB8EB009E98D4 /* Migration */, ); path = Functionality; sourceTree = ""; @@ -2004,7 +1991,6 @@ D29AFFEE24092F4900C1387D /* DataManager */ = { isa = PBXGroup; children = ( - 9F0C3C112316DDA500299985 /* DataService.swift */, 9F589F1A238C85B0007FD759 /* Encrypted Storage */, 9F589F19238C82A1007FD759 /* Local Storage */, 9F003D6C25EA8F3200EB38C0 /* SessionService.swift */, @@ -2779,7 +2765,6 @@ 9FFC7E5A260C946100282FCE /* CloudContactsProvider.swift in Sources */, D2F6D1332433753100DB4065 /* IMAPConnectionParameters.swift in Sources */, 9F003DB625EA92BC00EB38C0 /* LogOutHandler.swift in Sources */, - 9F0C3C122316DDA500299985 /* DataService.swift in Sources */, D20D3C6E2520AB3900D4AA9A /* BackupViewDecorator.swift in Sources */, 9F268891237DC55600428A94 /* SetupManuallyImportKeyViewController.swift in Sources */, D2E26F7024F266F300612AF1 /* ContactDetailViewController.swift in Sources */, @@ -2800,7 +2785,6 @@ 9F88391927270A1A00669B56 /* MessagesThreadOperationsProvider.swift in Sources */, 9F53CB872555E7F300C0157A /* Imap+Other.swift in Sources */, D2F6D1352433753B00DB4065 /* SMTPSession.swift in Sources */, - 9FBEAF3125DFB8E1009E98D4 /* DBMigrationService.swift in Sources */, 9F17976D2368EEBD002BF770 /* SetupViewDecorator.swift in Sources */, 5ADEDCC023A43B0800EC495E /* KeyDetailInfoViewDecorator.swift in Sources */, D227C0E6250538780070F805 /* RemoteFoldersProvider.swift in Sources */, diff --git a/FlowCrypt/App/AppContext.swift b/FlowCrypt/App/AppContext.swift index 3d6ee84a2..1d8d885d2 100644 --- a/FlowCrypt/App/AppContext.swift +++ b/FlowCrypt/App/AppContext.swift @@ -16,7 +16,6 @@ class AppContext { let session: SessionType? // todo - session service should have maybe `.currentSession` on it, then we don't have to have `session` above? let userAccountService: SessionServiceType - let dataService: DataServiceType let keyService: KeyServiceType let passPhraseService: PassPhraseServiceType let clientConfigurationService: ClientConfigurationServiceType @@ -25,7 +24,6 @@ class AppContext { encryptedStorage: EncryptedStorageType, session: SessionType?, userAccountService: SessionServiceType, - dataService: DataServiceType, keyService: KeyServiceType, passPhraseService: PassPhraseServiceType, clientConfigurationService: ClientConfigurationServiceType, @@ -34,7 +32,6 @@ class AppContext { self.encryptedStorage = encryptedStorage self.session = session self.userAccountService = userAccountService - self.dataService = dataService self.keyService = keyService self.passPhraseService = passPhraseService self.clientConfigurationService = clientConfigurationService @@ -47,12 +44,11 @@ class AppContext { let encryptedStorage = EncryptedStorage( storageEncryptionKey: try keyChainService.getStorageEncryptionKey() ) - let dataService = DataService(encryptedStorage: encryptedStorage) let passPhraseService = PassPhraseService(encryptedStorage: encryptedStorage) let keyService = KeyService( storage: encryptedStorage, passPhraseService: passPhraseService, - currentUserEmail: { dataService.email } + currentUserEmail: { encryptedStorage.activeUser?.email } ) let clientConfigurationService = ClientConfigurationService( local: LocalClientConfiguration( @@ -64,13 +60,11 @@ class AppContext { session: nil, // will be set later. But would be nice to already set here, if available userAccountService: SessionService( encryptedStorage: encryptedStorage, - dataService: dataService, googleService: GoogleUserService( - currentUserEmail: dataService.currentUser?.email, + currentUserEmail: encryptedStorage.activeUser?.email, appDelegateGoogleSessionContainer: UIApplication.shared.delegate as? AppDelegate ) ), - dataService: dataService, keyService: keyService, passPhraseService: passPhraseService, clientConfigurationService: clientConfigurationService, @@ -83,7 +77,6 @@ class AppContext { encryptedStorage: encryptedStorage, session: session, userAccountService: userAccountService, - dataService: dataService, keyService: keyService, passPhraseService: passPhraseService, clientConfigurationService: clientConfigurationService, @@ -105,8 +98,8 @@ class AppContext { @MainActor func getOptionalMailProvider() -> MailProvider? { guard - let currentUser = dataService.currentUser, - let currentAuthType = dataService.currentAuthType + let currentUser = encryptedStorage.activeUser, + let currentAuthType = currentUser.authType else { return nil } return MailProvider( @@ -137,12 +130,12 @@ class AppContext { class AppContextWithUser: AppContext { let authType: AuthType let user: User + let userId: UserId init( encryptedStorage: EncryptedStorageType, session: SessionType?, userAccountService: SessionServiceType, - dataService: DataServiceType, keyService: KeyServiceType, passPhraseService: PassPhraseServiceType, clientConfigurationService: ClientConfigurationServiceType, @@ -152,12 +145,12 @@ class AppContextWithUser: AppContext { ) { self.authType = authType self.user = user + self.userId = UserId(email: user.email, name: user.name) super.init( encryptedStorage: encryptedStorage, session: session, userAccountService: userAccountService, - dataService: dataService, keyService: keyService, passPhraseService: passPhraseService, clientConfigurationService: clientConfigurationService, diff --git a/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewController.swift b/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewController.swift index 8f8f95211..cdfc62f81 100644 --- a/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewController.swift +++ b/FlowCrypt/Controllers/Settings/Backup/Backups Scene/BackupViewController.swift @@ -34,21 +34,18 @@ final class BackupViewController: TableNodeViewController { } } - private let appContext: AppContext + private let appContext: AppContextWithUser private let decorator: BackupViewDecorator private let service: ServiceActor - private let userId: UserId private var state: State = .idle { didSet { updateState() } } init( - appContext: AppContext, - decorator: BackupViewDecorator = BackupViewDecorator(), - userId: UserId + appContext: AppContextWithUser, + decorator: BackupViewDecorator = BackupViewDecorator() ) { self.appContext = appContext self.decorator = decorator self.service = ServiceActor(backupService: appContext.getBackupService()) - self.userId = userId super.init(node: TableNode()) } @@ -79,7 +76,7 @@ extension BackupViewController { private func fetchBackups() { Task { do { - let keys = try await service.fetchBackupsFromInbox(for: userId) + let keys = try await service.fetchBackupsFromInbox(for: appContext.userId) state = keys.isEmpty ? .noBackups : .backups(keys) @@ -135,7 +132,7 @@ extension BackupViewController: ASTableDelegate, ASTableDataSource { let optionsScreen = BackupOptionsViewController( appContext: appContext, backups: state.backups, - userId: userId + userId: appContext.userId ) navigationController?.pushViewController(optionsScreen, animated: true) } diff --git a/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift b/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift index ef8e4aa8b..1130dcc11 100644 --- a/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift +++ b/FlowCrypt/Controllers/Settings/Settings List/SettingsViewController.swift @@ -123,8 +123,7 @@ extension SettingsViewController { viewController = nil return } - let userId = UserId(email: appContext.user.email, name: appContext.user.email) - viewController = BackupViewController(appContext: appContext, userId: userId) + viewController = BackupViewController(appContext: appContext) default: viewController = nil } diff --git a/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift b/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift index 919a6007e..ef32fa784 100644 --- a/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupBackupsViewController.swift @@ -21,10 +21,9 @@ final class SetupBackupsViewController: TableNodeViewController, PassPhraseSavea } private lazy var logger = Logger.nested(in: Self.self, with: .setup) - private let appContext: AppContext + private let appContext: AppContextWithUser private let decorator: SetupViewDecorator private let keyMethods: KeyMethodsType - private let user: UserId private let fetchedEncryptedKeys: [KeyDetails] private var passPhrase: String? @@ -41,17 +40,15 @@ final class SetupBackupsViewController: TableNodeViewController, PassPhraseSavea } init( - appContext: AppContext, + appContext: AppContextWithUser, fetchedEncryptedKeys: [KeyDetails], decorator: SetupViewDecorator = SetupViewDecorator(), - keyMethods: KeyMethodsType = KeyMethods(), - user: UserId + keyMethods: KeyMethodsType = KeyMethods() ) { self.appContext = appContext self.fetchedEncryptedKeys = fetchedEncryptedKeys self.decorator = decorator self.keyMethods = keyMethods - self.user = user super.init(node: TableNode()) } @@ -141,7 +138,7 @@ extension SetupBackupsViewController { keyDetails: Array(matchingKeyBackups), passPhrase: storageMethod == .persistent ? passPhrase : nil, source: .backup, - for: user.email + for: appContext.user.email ) moveToMainFlow() } diff --git a/FlowCrypt/Controllers/Setup/SetupCreatePassphraseAbstractViewController.swift b/FlowCrypt/Controllers/Setup/SetupCreatePassphraseAbstractViewController.swift index ecf3000b2..cb8eeea20 100644 --- a/FlowCrypt/Controllers/Setup/SetupCreatePassphraseAbstractViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupCreatePassphraseAbstractViewController.swift @@ -26,9 +26,8 @@ class SetupCreatePassphraseAbstractViewController: TableNodeViewController, Pass Parts.allCases } - let appContext: AppContext + let appContext: AppContextWithUser let decorator: SetupViewDecorator - let user: UserId let fetchedKeysCount: Int var storageMethod: StorageMethod = .persistent { @@ -47,14 +46,12 @@ class SetupCreatePassphraseAbstractViewController: TableNodeViewController, Pass private lazy var logger = Logger.nested(in: Self.self, with: .setup) init( - appContext: AppContext, - user: UserId, + appContext: AppContextWithUser, fetchedKeysCount: Int = 0, router: GlobalRouterType = GlobalRouter(), decorator: SetupViewDecorator = SetupViewDecorator() ) { self.appContext = appContext - self.user = user self.fetchedKeysCount = fetchedKeysCount self.decorator = decorator super.init(node: TableNode()) diff --git a/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift index 6afb06fc5..cf4206d2c 100644 --- a/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupEKMKeyViewController.swift @@ -29,15 +29,13 @@ final class SetupEKMKeyViewController: SetupCreatePassphraseAbstractViewControll private let keys: [KeyDetails] init( - appContext: AppContext, - user: UserId, + appContext: AppContextWithUser, keys: [KeyDetails] = [], decorator: SetupViewDecorator = SetupViewDecorator() ) { self.keys = keys super.init( appContext: appContext, - user: user, fetchedKeysCount: keys.count, decorator: decorator ) @@ -93,7 +91,7 @@ extension SetupEKMKeyViewController { keyDetails: parsedKey.keyDetails, passPhrase: self.storageMethod == .persistent ? passPhrase : nil, source: .ekm, - for: self.user.email + for: self.appContext.user.email ) allFingerprintsOfAllKeys.append(contentsOf: parsedKey.keyDetails.map(\.fingerprints)) } diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index 2fc4394b3..f9f8c7a61 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -22,21 +22,18 @@ final class SetupGenerateKeyViewController: SetupCreatePassphraseAbstractViewCon private let service: Service init( - appContext: AppContext, - user: UserId, + appContext: AppContextWithUser, decorator: SetupViewDecorator = SetupViewDecorator() ) { self.attester = AttesterApi( - clientConfiguration: appContext.clientConfigurationService.getSaved(for: user.email) + clientConfiguration: appContext.clientConfigurationService.getSaved(for: appContext.user.email) ) self.service = Service( appContext: appContext, - user: user, attester: self.attester ) super.init( appContext: appContext, - user: user, decorator: decorator ) } @@ -74,17 +71,14 @@ final class SetupGenerateKeyViewController: SetupCreatePassphraseAbstractViewCon private actor Service { typealias ViewController = SetupCreatePassphraseAbstractViewController - private let appContext: AppContext - private let user: UserId + private let appContext: AppContextWithUser private let attester: AttesterApiType init( - appContext: AppContext, - user: UserId, + appContext: AppContextWithUser, attester: AttesterApiType ) { self.appContext = appContext - self.user = user self.attester = attester } @@ -93,18 +87,16 @@ private actor Service { storageMethod: StorageMethod, viewController: ViewController ) async throws { - let userId = try getUserId() - try await viewController.validateAndConfirmNewPassPhraseOrReject(passPhrase: passPhrase) let encryptedPrv = try await Core.shared.generateKey( passphrase: passPhrase, variant: .curve25519, - userIds: [userId] + userIds: [appContext.userId] ) - try await submitKeyToAttester(email: userId.email, publicKey: encryptedPrv.key.public) - try await appContext.getBackupService().backupToInbox(keys: [encryptedPrv.key], for: user) + try await submitKeyToAttester(user: appContext.user, publicKey: encryptedPrv.key.public) + try await appContext.getBackupService().backupToInbox(keys: [encryptedPrv.key], for: appContext.userId) try await putKeypairsInEncryptedStorage(encryptedPrv: encryptedPrv, storageMethod: storageMethod, passPhrase: passPhrase) if storageMethod == .memory { @@ -117,7 +109,7 @@ private actor Service { // sending welcome email is not crucial, so we don't handle errors _ = try? await attester.testWelcome( - email: userId.email, + email: appContext.user.email, pubkey: encryptedPrv.key.public ) } @@ -128,32 +120,36 @@ private actor Service { keyDetails: [encryptedPrv.key], passPhrase: storageMethod == .persistent ? passPhrase: nil, source: .generated, - for: user.email + for: appContext.user.email ) } + // todo - there is a similar method in EnterpriseServierApi + // this should be put somewhere general + private func getIdToken(for user: User) async throws -> String? { + switch user.authType { + case .oAuthGmail: + return try await GoogleUserService( + currentUserEmail: user.email, + appDelegateGoogleSessionContainer: nil // needed only when signing in/out + ).getCachedOrRefreshedIdToken() + default: + return Imap(user: user).imapSess?.oAuth2Token + } + } + private func submitKeyToAttester( - email: String, + user: User, publicKey: String ) async throws { do { - _ = try await attester.update( - email: email, + _ = try await attester.replace( + email: user.email, pubkey: publicKey, - token: appContext.dataService.token + idToken: try await getIdToken(for: user) ) } catch { throw CreateKeyError.submitKey(error) } } - - private func getUserId() throws -> UserId { - guard let email = appContext.dataService.email, !email.isEmpty else { - throw CreateKeyError.missingUserEmail - } - guard let name = appContext.dataService.currentUser?.name, !name.isEmpty else { - throw CreateKeyError.missingUserName - } - return UserId(email: email, name: name) - } } diff --git a/FlowCrypt/Controllers/Setup/SetupInitialViewController.swift b/FlowCrypt/Controllers/Setup/SetupInitialViewController.swift index 31dff95bd..c1a3f6f49 100644 --- a/FlowCrypt/Controllers/Setup/SetupInitialViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupInitialViewController.swift @@ -52,25 +52,22 @@ final class SetupInitialViewController: TableNodeViewController { } private let service: ServiceActor - private let user: UserId private let decorator: SetupViewDecorator private let clientConfiguration: ClientConfiguration private let emailKeyManagerApi: EmailKeyManagerApiType - private let appContext: AppContext + private let appContext: AppContextWithUser private lazy var logger = Logger.nested(in: Self.self, with: .setup) init( - appContext: AppContext, - user: UserId, + appContext: AppContextWithUser, decorator: SetupViewDecorator = SetupViewDecorator(), emailKeyManagerApi: EmailKeyManagerApiType? = nil ) { self.appContext = appContext - self.user = user self.service = ServiceActor(backupService: appContext.getBackupService()) self.decorator = decorator - let clientConfiguration = appContext.clientConfigurationService.getSaved(for: user.email) + let clientConfiguration = appContext.clientConfigurationService.getSaved(for: appContext.user.email) self.emailKeyManagerApi = emailKeyManagerApi ?? EmailKeyManagerApi(clientConfiguration: clientConfiguration) self.clientConfiguration = clientConfiguration super.init(node: TableNode()) @@ -123,7 +120,7 @@ extension SetupInitialViewController { Task { do { - let keys = try await service.fetchBackupsFromInbox(for: user) + let keys = try await service.fetchBackupsFromInbox(for: appContext.userId) proceedToSetupWith(keys: keys) } catch { handle(error: error) @@ -159,7 +156,7 @@ extension SetupInitialViewController { private func getIdToken() async throws -> String { let googleService = GoogleUserService( - currentUserEmail: user.email, + currentUserEmail: appContext.user.email, appDelegateGoogleSessionContainer: nil ) @@ -331,12 +328,12 @@ extension SetupInitialViewController { } private func proceedToCreatingNewKey() { - let viewController = SetupGenerateKeyViewController(appContext: appContext, user: user) + let viewController = SetupGenerateKeyViewController(appContext: appContext) navigationController?.pushViewController(viewController, animated: true) } private func proceedToSetupWithEKMKeys(keys: [KeyDetails]) { - let viewController = SetupEKMKeyViewController(appContext: appContext, user: user, keys: keys) + let viewController = SetupEKMKeyViewController(appContext: appContext, keys: keys) navigationController?.pushViewController(viewController, animated: true) } @@ -348,7 +345,7 @@ extension SetupInitialViewController { state = .noKeyBackupsInInbox } else { logger.logInfo("\(keys.count) key backups found in inbox") - let viewController = SetupBackupsViewController(appContext: appContext, fetchedEncryptedKeys: keys, user: user) + let viewController = SetupBackupsViewController(appContext: appContext, fetchedEncryptedKeys: keys) navigationController?.pushViewController(viewController, animated: true) } } diff --git a/FlowCrypt/Controllers/Setup/SetupManuallyImportKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupManuallyImportKeyViewController.swift index 279946c88..e697d9cea 100644 --- a/FlowCrypt/Controllers/Setup/SetupManuallyImportKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupManuallyImportKeyViewController.swift @@ -28,7 +28,7 @@ final class SetupManuallyImportKeyViewController: TableNodeViewController { } } - private let appContext: AppContext + private let appContext: AppContextWithUser private let decorator: SetupViewDecorator private let pasteboard: UIPasteboard @@ -37,7 +37,7 @@ final class SetupManuallyImportKeyViewController: TableNodeViewController { } init( - appContext: AppContext, + appContext: AppContextWithUser, decorator: SetupViewDecorator = SetupViewDecorator(), pasteboard: UIPasteboard = UIPasteboard.general ) { @@ -167,7 +167,7 @@ extension SetupManuallyImportKeyViewController { private func parseUserProvided(data keyData: Data) async throws { let keys = try await Core.shared.parseKeys(armoredOrBinary: keyData) let privateKey = keys.keyDetails.filter { $0.private != nil } - let user = appContext.dataService.email ?? "unknown_title".localized + let user = appContext.user.email if privateKey.isEmpty { userInfoMessage = "import_no_backups_clipboard".localized + user } else { diff --git a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift index be6711d01..16bb19df9 100644 --- a/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift +++ b/FlowCrypt/Controllers/SideMenu/Menu/MyMenuViewController.swift @@ -50,7 +50,9 @@ final class MyMenuViewController: ViewController { private var folders: [FolderViewModel] = [] private var serviceItems: [FolderViewModel] { FolderViewModel.menuItems } private var accounts: [User] { - appContext.dataService.getFinishedSetupUsers(exceptUserEmail: appContext.user.email) + appContext.encryptedStorage.getAllUsers() + .filter { $0.email != appContext.user.email } + .filter { appContext.encryptedStorage.doesAnyKeypairExist(for: $0.email) } } private let tableNode: ASTableNode diff --git a/FlowCrypt/Functionality/DataManager/DataService.swift b/FlowCrypt/Functionality/DataManager/DataService.swift deleted file mode 100644 index 1239a5fec..000000000 --- a/FlowCrypt/Functionality/DataManager/DataService.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// DataService.swift -// FlowCrypt -// -// Created by Anton Kharchevskyi on 8/28/19. -// Copyright © 2017-present FlowCrypt a. s. All rights reserved. -// - -import Foundation -import RealmSwift - -// todo DataServiceType in general is a bit of a confused class -// hopefully we can refactor it away or shrink it -protocol DataServiceType { - // data - var email: String? { get } - var currentUser: User? { get } - var isLoggedIn: Bool { get } - var isSetupFinished: Bool { get } - var currentAuthType: AuthType? { get } - var token: String? { get } - - var users: [User] { get } - - func getFinishedSetupUsers(exceptUserEmail: String) -> [User] - - func performMigrationIfNeeded() async throws -} - -enum SessionType: CustomStringConvertible { - case google(_ email: String, name: String, token: String) - case session(_ user: User) - - var description: String { - switch self { - case let .google(email, name, _): - return "Google \(email) \(name)" - case let .session(user): - return "Session \(user.email)" - } - } -} - -// MARK: - DataService -final class DataService { - - private let encryptedStorage: EncryptedStorageType - private let localStorage: LocalStorageType - private let migrationService: DBMigration - - init( - encryptedStorage: EncryptedStorageType, - localStorage: LocalStorageType = LocalStorage() - ) { - self.encryptedStorage = encryptedStorage - self.localStorage = localStorage - self.migrationService = DBMigrationService(localStorage: localStorage, encryptedStorage: encryptedStorage) - } -} - -// MARK: - DataServiceType -extension DataService: DataServiceType { - var isSetupFinished: Bool { - isLoggedIn && doesAnyKeyExistForCurrentUser - } - - private var doesAnyKeyExistForCurrentUser: Bool { - guard let currentUser = currentUser else { - return false - } - return encryptedStorage.doesAnyKeypairExist(for: currentUser.email) - } - - var isLoggedIn: Bool { - currentUser != nil - } - - var email: String? { - currentUser?.email - } - - // helper to get current user object from DB - private var activeUser: User? { - encryptedStorage.activeUser - } - - var users: [User] { - encryptedStorage.getAllUsers() - } - - var currentUser: User? { - encryptedStorage.getAllUsers().first(where: \.isActive) - } - - var currentAuthType: AuthType? { - activeUser?.authType - } - - var token: String? { - switch currentAuthType { - case .oAuthGmail: - return GoogleUserService( - currentUserEmail: currentUser?.email, - appDelegateGoogleSessionContainer: nil // needed only when signing in/out - ).accessToken - default: - guard let currentUser = currentUser else { - return nil - } - return Imap(user: currentUser).imapSess?.oAuth2Token - } - } - - func getFinishedSetupUsers(exceptUserEmail: String) -> [User] { - encryptedStorage.getAllUsers() - .filter { $0.email != exceptUserEmail } - .filter { encryptedStorage.doesAnyKeypairExist(for: $0.email) } - } -} - -// MARK: - Migration -extension DataService: DBMigration { - /// Perform all kind of migrations - func performMigrationIfNeeded() async throws { - try await migrationService.performMigrationIfNeeded() - } -} diff --git a/FlowCrypt/Functionality/DataManager/SessionService.swift b/FlowCrypt/Functionality/DataManager/SessionService.swift index 8554f4aea..fd0db82dc 100644 --- a/FlowCrypt/Functionality/DataManager/SessionService.swift +++ b/FlowCrypt/Functionality/DataManager/SessionService.swift @@ -9,6 +9,20 @@ import FlowCryptCommon import Foundation +enum SessionType: CustomStringConvertible { + case google(_ email: String, name: String, token: String) + case session(_ user: User) + + var description: String { + switch self { + case let .google(email, name, _): + return "Google \(email) \(name)" + case let .session(user): + return "Session \(user.email)" + } + } +} + protocol SessionServiceType { func startSessionFor(session: SessionType) throws func switchActiveSessionFor(user: User) throws -> SessionType? @@ -23,24 +37,21 @@ final class SessionService { private let imap: Imap private let googleService: GoogleUserService - private let dataService: DataServiceType private lazy var logger = Logger.nested(Self.self) init( encryptedStorage: EncryptedStorageType & LogOutHandler, localStorage: LocalStorageType & LogOutHandler = LocalStorage(), - dataService: DataServiceType, imap: Imap? = nil, googleService: GoogleUserService ) { self.googleService = googleService // todo - the following User.empty may be wrong - unsure, untested // maybe should instead get user - self.imap = imap ?? Imap(user: dataService.currentUser ?? User.empty) + self.imap = imap ?? Imap(user: encryptedStorage.activeUser ?? User.empty) self.encryptedStorage = encryptedStorage self.localStorage = localStorage - self.dataService = dataService } private var storages: [LogOutHandler] { @@ -66,7 +77,7 @@ extension SessionService: SessionServiceType { } func startActiveSessionForNextUser() throws -> SessionType? { - guard let currentUser = dataService.currentUser else { + guard let currentUser = encryptedStorage.activeUser else { return nil } logOut(user: currentUser) diff --git a/FlowCrypt/Functionality/Migration/DBMigrationService.swift b/FlowCrypt/Functionality/Migration/DBMigrationService.swift deleted file mode 100644 index 9c329e30a..000000000 --- a/FlowCrypt/Functionality/Migration/DBMigrationService.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// DBMigrationService.swift -// FlowCrypt -// -// Created by Anton Kharchevskyi on 19.02.2021. -// Copyright © 2017-present FlowCrypt a. s. All rights reserved. -// - -import FlowCryptCommon -import Foundation -import RealmSwift - -protocol DBMigration { - func performMigrationIfNeeded() async throws -} - -struct DBMigrationService { - - private let localStorage: LocalStorageType - private let encryptedStorage: EncryptedStorageType - - private var storage: Realm { encryptedStorage.storage } - - init(localStorage: LocalStorageType, encryptedStorage: EncryptedStorageType) { - self.localStorage = localStorage - self.encryptedStorage = encryptedStorage - } -} - -// MARK: - DBMigration -extension DBMigrationService: DBMigration { - func performMigrationIfNeeded() async throws { - } -} diff --git a/FlowCrypt/Functionality/Services/AppStartup.swift b/FlowCrypt/Functionality/Services/AppStartup.swift index ad777e25a..3e33cf2a2 100644 --- a/FlowCrypt/Functionality/Services/AppStartup.swift +++ b/FlowCrypt/Functionality/Services/AppStartup.swift @@ -32,7 +32,6 @@ struct AppStartup { do { await setupCore() - try await appContext.dataService.performMigrationIfNeeded() try await setupSession() try await getUserOrgRulesIfNeeded() chooseView(for: window) @@ -64,22 +63,31 @@ struct AppStartup { private func chooseView(for window: UIWindow) { switch entryPointForUser() { case .mainFlow: - startMainFlow(appContext: appContext, window: window) + startWithUserContext(appContext: appContext, window: window) { context in + let controller = InboxViewContainerController(appContext: context) + window.rootViewController = SideMenuNavigationController( + appContext: context, + contentViewController: controller + ) + } case .signIn: window.rootViewController = MainNavigationController( rootViewController: SignInViewController(appContext: appContext) ) - case .setupFlow(let userId): - let setupViewController = SetupInitialViewController(appContext: appContext, user: userId) - window.rootViewController = MainNavigationController(rootViewController: setupViewController) + case .setupFlow: + startWithUserContext(appContext: appContext, window: window) { context in + let controller = SetupInitialViewController(appContext: context) + window.rootViewController = MainNavigationController(rootViewController: controller) + } } } private func entryPointForUser() -> EntryPoint { - if !appContext.dataService.isLoggedIn { + guard let activeUser = appContext.encryptedStorage.activeUser else { logger.logInfo("User is not logged in -> signIn") return .signIn - } else if appContext.dataService.isSetupFinished, appContext.dataService.currentUser != nil { + } + if appContext.encryptedStorage.doesAnyKeypairExist(for: activeUser.email) { logger.logInfo("Setup finished -> mainFlow") return .mainFlow } else if let session = appContext.session, let userId = makeUserIdForSetup(session: session) { @@ -92,32 +100,30 @@ struct AppStartup { } private func getUserOrgRulesIfNeeded() async throws { - guard let currentUser = appContext.dataService.currentUser else { + guard let currentUser = appContext.encryptedStorage.activeUser else { return } - if appContext.dataService.isLoggedIn { - _ = try await appContext.clientConfigurationService.fetch(for: currentUser) - } + _ = try await appContext.clientConfigurationService.fetch(for: currentUser) } private func makeUserIdForSetup(session: SessionType) -> UserId? { - guard let currentUser = appContext.dataService.currentUser else { + guard let activeUser = appContext.encryptedStorage.activeUser else { Logger.logInfo("Can't create user id for setup") return nil } - var userId = UserId(email: currentUser.email, name: currentUser.name) + var userId = UserId(email: activeUser.email, name: activeUser.name) switch session { case let .google(email, name, _): - guard currentUser.email != email else { + guard activeUser.email != email else { logger.logInfo("UserId = current user id") return userId } logger.logInfo("UserId = google user id") userId = UserId(email: email, name: name) case let .session(userObject): - guard userObject.email != currentUser.email else { + guard userObject.email != activeUser.email else { Logger.logInfo("UserId = current user id") return userId } @@ -143,12 +149,12 @@ struct AppStartup { } @MainActor - private func startMainFlow(appContext: AppContext, window: UIWindow) { + private func startWithUserContext(appContext: AppContext, window: UIWindow, callback: (AppContextWithUser) -> Void) { let session = appContext.session guard - let authType = appContext.dataService.currentAuthType, - let user = appContext.dataService.currentUser + let user = appContext.encryptedStorage.activeUser, + let authType = user.authType else { let message = "Wrong application state. User not found for session \(session?.description ?? "nil")" logger.logError(message) @@ -166,12 +172,6 @@ struct AppStartup { return } - let appContextWithUser = appContext.withSession(session: session, authType: authType, user: user) - let contentViewController = InboxViewContainerController(appContext: appContextWithUser) - let viewController = SideMenuNavigationController( - appContext: appContextWithUser, - contentViewController: contentViewController - ) - window.rootViewController = viewController + callback(appContext.withSession(session: session, authType: authType, user: user)) } } diff --git a/FlowCrypt/Functionality/Services/GlobalRouter.swift b/FlowCrypt/Functionality/Services/GlobalRouter.swift index 49489e554..85efe4631 100644 --- a/FlowCrypt/Functionality/Services/GlobalRouter.swift +++ b/FlowCrypt/Functionality/Services/GlobalRouter.swift @@ -13,7 +13,7 @@ import UIKit protocol GlobalRouterType { func proceed() func signIn(appContext: AppContext, route: GlobalRoutingType) async - func askForContactsPermission(for route: GlobalRoutingType, appContext: AppContext) async throws + func askForContactsPermission(for route: GlobalRoutingType, appContext: AppContextWithUser) async throws func switchActive(user: User, appContext: AppContext) throws func signOut(appContext: AppContext) throws } @@ -65,7 +65,7 @@ extension GlobalRouter: GlobalRouterType { viewController.showSpinner() let googleService = GoogleUserService( - currentUserEmail: appContext.dataService.currentUser?.email, + currentUserEmail: appContext.encryptedStorage.activeUser?.email, appDelegateGoogleSessionContainer: UIApplication.shared.delegate as? AppDelegate ) let session = try await googleService.signIn( @@ -99,14 +99,14 @@ extension GlobalRouter: GlobalRouterType { } } - func askForContactsPermission(for route: GlobalRoutingType, appContext: AppContext) async throws { + func askForContactsPermission(for route: GlobalRoutingType, appContext: AppContextWithUser) async throws { logger.logInfo("Ask for contacts permission with \(route)") switch route { case .gmailLogin(let viewController): do { let googleService = GoogleUserService( - currentUserEmail: appContext.dataService.currentUser?.email, + currentUserEmail: appContext.user.email, appDelegateGoogleSessionContainer: UIApplication.shared.delegate as? AppDelegate ) let session = try await googleService.signIn( @@ -155,8 +155,8 @@ extension GlobalRouter: GlobalRouterType { private func proceed(with appContext: AppContext, session: SessionType) { logger.logInfo("proceed for session: \(session.description)") guard - let authType = appContext.dataService.currentAuthType, - let user = appContext.dataService.currentUser + let user = appContext.encryptedStorage.activeUser, + let authType = user.authType else { let message = "Wrong application state. User not found for session \(session.description)" logger.logError(message) diff --git a/FlowCrypt/Functionality/Services/Remote Pub Key Services/AttesterApi.swift b/FlowCrypt/Functionality/Services/Remote Pub Key Services/AttesterApi.swift index 1e042cd41..cc74d3eb9 100644 --- a/FlowCrypt/Functionality/Services/Remote Pub Key Services/AttesterApi.swift +++ b/FlowCrypt/Functionality/Services/Remote Pub Key Services/AttesterApi.swift @@ -7,8 +7,8 @@ import FlowCryptCommon protocol AttesterApiType { func lookup(email: String) async throws -> [KeyDetails] - func update(email: String, pubkey: String, token: String?) async throws -> String - func replace(email: String, pubkey: String) async throws -> String + func replace(email: String, pubkey: String, idToken: String?) async throws -> String + func update(email: String, pubkey: String) async throws -> String func testWelcome(email: String, pubkey: String) async throws } @@ -72,10 +72,10 @@ extension AttesterApi { } @discardableResult - func update(email: String, pubkey: String, token: String?) async throws -> String { + func replace(email: String, pubkey: String, idToken: String?) async throws -> String { let httpMethod: HTTPMethod let headers: [URLHeader] - if let value = token { + if let value = idToken { httpMethod = .post headers = [URLHeader(value: "Bearer \(value)", httpHeaderField: "Authorization")] } else { @@ -94,7 +94,7 @@ extension AttesterApi { } @discardableResult - func replace(email: String, pubkey: String) async throws -> String { + func update(email: String, pubkey: String) async throws -> String { let request = ApiCall.Request( apiName: Constants.apiName, url: pubUrl(email: email),