From dedc55ac24e41131bdb2e73759ba0c29a14bae4b Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Sun, 16 Jan 2022 15:21:03 +0200 Subject: [PATCH 1/6] Handle error if key was not submit to Attester --- .../SetupGenerateKeyViewController.swift | 33 +++++------ .../DataManager/DataService.swift | 5 +- .../Error Handling/ErrorHandler.swift | 1 - .../KeyServiceErrorHandler.swift | 57 ++++++++----------- .../Backup Services/BackupService.swift | 1 + .../Resources/en.lproj/Localizable.strings | 2 + 6 files changed, 49 insertions(+), 50 deletions(-) diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index e3f7b6dbe..1958dedeb 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -103,13 +103,7 @@ private actor Service { userIds: [userId] ) try await appContext.getBackupService().backupToInbox(keys: [encryptedPrv.key], for: user) - - try appContext.encryptedStorage.putKeypairs( - keyDetails: [encryptedPrv.key], - passPhrase: storageMethod == .persistent ? passPhrase: nil, - source: .generated, - for: user.email - ) + try await putKeypairs(encryptedPrv: encryptedPrv, storageMethod: storageMethod, passPhrase: passPhrase) if storageMethod == .memory { let passPhrase = PassPhrase( @@ -119,10 +113,9 @@ private actor Service { try appContext.passPhraseService.savePassPhrase(with: passPhrase, storageMethod: .memory) } - await submitKeyToAttesterAndShowAlertOnFailure( + try await submitKeyToAttester( email: userId.email, - publicKey: encryptedPrv.key.public, - viewController: viewController + publicKey: encryptedPrv.key.public ) // sending welcome email is not crucial, so we don't handle errors @@ -132,11 +125,20 @@ private actor Service { ) } - private func submitKeyToAttesterAndShowAlertOnFailure( + @MainActor + private func putKeypairs(encryptedPrv: CoreRes.GenerateKey, storageMethod: StorageMethod, passPhrase: String) throws { + try appContext.encryptedStorage.putKeypairs( + keyDetails: [encryptedPrv.key], + passPhrase: storageMethod == .persistent ? passPhrase: nil, + source: .generated, + for: user.email + ) + } + + private func submitKeyToAttester( email: String, - publicKey: String, - viewController: ViewController - ) async { + publicKey: String + ) async throws { do { _ = try await attester.update( email: email, @@ -144,8 +146,7 @@ private actor Service { token: appContext.dataService.token ) } catch { - let message = "Failed to submit Public Key" - await viewController.showAlert(error: error, message: message) + throw CreateKeyError.submitKey } } diff --git a/FlowCrypt/Functionality/DataManager/DataService.swift b/FlowCrypt/Functionality/DataManager/DataService.swift index 485ae8ee7..1239a5fec 100644 --- a/FlowCrypt/Functionality/DataManager/DataService.swift +++ b/FlowCrypt/Functionality/DataManager/DataService.swift @@ -104,7 +104,10 @@ extension DataService: DataServiceType { appDelegateGoogleSessionContainer: nil // needed only when signing in/out ).accessToken default: - return nil + guard let currentUser = currentUser else { + return nil + } + return Imap(user: currentUser).imapSess?.oAuth2Token } } diff --git a/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift b/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift index aa7cbcc17..ea0988f16 100644 --- a/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift +++ b/FlowCrypt/Functionality/Error Handling/ErrorHandler.swift @@ -35,7 +35,6 @@ private struct ComposedErrorHandler: ErrorHandler { handlers: [ KeyServiceErrorHandler(), BackupServiceErrorHandler(), - CreateKeyErrorHandler() ] ) diff --git a/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift b/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift index 059befeab..bdaccb741 100644 --- a/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift +++ b/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift @@ -10,14 +10,35 @@ import UIKit enum CreateKeyError: Error { case weakPassPhrase(_ strength: CoreRes.ZxcvbnStrengthBar) - // Missing user email + /// Missing user email case missedUserEmail - // Missing user name + /// Missing user name case missedUserName - // Pass phrases don't match + /// Pass phrases don't match case doesntMatch - // silent abort + /// Silent abort case conformingPassPhraseError + /// Failed to submit key + case submitKey +} + +extension CreateKeyError: CustomStringConvertible { + var description: String { + switch self { + case .weakPassPhrase(let strength): + return "Pass phrase strength: \(strength.word.word)\ncrack time: \(strength.time)\n\nWe recommend to use 5-6 unrelated words as your Pass Phrase." + case .missedUserEmail: + return "backupServiceError_email".localized + case .missedUserName: + return "backupServiceError_name".localized + case .doesntMatch: + return "pass_phrase_match_error".localized + case .submitKey: + return "submit_key_error".localized + case .conformingPassPhraseError: + return "" + } + } } // KeyServiceError @@ -44,31 +65,3 @@ struct KeyServiceErrorHandler: ErrorHandler { return true } } - -// CreateKeyError -struct CreateKeyErrorHandler: ErrorHandler { - func handle(error: Error, for viewController: UIViewController) -> Bool { - let errorMessage: String? - - switch error as? CreateKeyError { - case .weakPassPhrase(let strength): - errorMessage = "Pass phrase strength: \(strength.word.word)\ncrack time: \(strength.time)\n\nWe recommend to use 5-6 unrelated words as your Pass Phrase." - case .missedUserEmail: - errorMessage = "backupServiceError_email".localized - case .missedUserName: - errorMessage = "backupServiceError_name".localized - case .doesntMatch: - errorMessage = "pass_phrase_match_error".localized - case .conformingPassPhraseError: - errorMessage = nil - case .none: - errorMessage = nil - } - - guard let message = errorMessage else { return false } - - viewController.showAlert(message: message) - - return true - } -} diff --git a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift index fcb9a56ae..9f0084548 100644 --- a/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift +++ b/FlowCrypt/Functionality/Services/Backup Services/BackupService.swift @@ -37,6 +37,7 @@ extension BackupService: BackupServiceType { } } + @MainActor func backupToInbox(keys: [KeyDetails], for userId: UserId) async throws { let isFullyEncryptedKeys = keys.map(\.isFullyDecrypted).contains(false) diff --git a/FlowCrypt/Resources/en.lproj/Localizable.strings b/FlowCrypt/Resources/en.lproj/Localizable.strings index ed3964ffd..e3e53257d 100644 --- a/FlowCrypt/Resources/en.lproj/Localizable.strings +++ b/FlowCrypt/Resources/en.lproj/Localizable.strings @@ -295,3 +295,5 @@ "imap_error_no_session" = "Session isn't established"; "imap_error_provider" = "Provider error %@"; "imap_error_msg_info" = "Can't parse message without \"%@\""; + +"submit_key_error" = "Failed to submit Public Key"; From 44a6630df00a96595042cd1c7a25b4b985fb5afe Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Sun, 16 Jan 2022 16:06:47 +0200 Subject: [PATCH 2/6] Fix for weak session for IMAP --- .../Mail Provider/Message Gateway/Imap+send.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift b/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift index 0f0fe2672..6ab946d12 100644 --- a/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift +++ b/FlowCrypt/Functionality/Mail Provider/Message Gateway/Imap+send.swift @@ -6,9 +6,9 @@ import Foundation extension Imap: MessageGateway { func sendMail(input: MessageGatewayInput, progressHandler: ((Float) -> Void)?) async throws { - try await withCheckedThrowingContinuation { [weak smtpSess] (continuation: CheckedContinuation) in - guard let session = smtpSess else { - return continuation.resume(returning: ()) + try await withCheckedThrowingContinuation { [weak self] (continuation: CheckedContinuation) in + guard let session = self?.smtpSess else { + return continuation.resume(throwing: ImapError.noSession) } session.sendOperation(with: input.mime) From 0ad2638d6ace7df2a4cfd16241f393b32fd5bb88 Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Sun, 16 Jan 2022 16:09:03 +0200 Subject: [PATCH 3/6] Change order of operations during key setup --- .../Controllers/Setup/SetupGenerateKeyViewController.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index 1958dedeb..aba139289 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -102,6 +102,8 @@ private actor Service { variant: .curve25519, userIds: [userId] ) + + try await submitKeyToAttester(email: userId.email, publicKey: encryptedPrv.key.public) try await appContext.getBackupService().backupToInbox(keys: [encryptedPrv.key], for: user) try await putKeypairs(encryptedPrv: encryptedPrv, storageMethod: storageMethod, passPhrase: passPhrase) @@ -113,11 +115,6 @@ private actor Service { try appContext.passPhraseService.savePassPhrase(with: passPhrase, storageMethod: .memory) } - try await submitKeyToAttester( - email: userId.email, - publicKey: encryptedPrv.key.public - ) - // sending welcome email is not crucial, so we don't handle errors _ = try? await attester.testWelcome( email: userId.email, From ac0a45a820008db9ace81aa388ca57c7c717bd01 Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Sun, 16 Jan 2022 16:32:26 +0200 Subject: [PATCH 4/6] Specify error message for API call --- .../Setup/SetupGenerateKeyViewController.swift | 4 ++-- .../Error Handling/KeyServiceErrorHandler.swift | 6 ++++-- FlowCrypt/Functionality/Services/ApiCall.swift | 16 ++++++++++++---- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index aba139289..cbfb2358e 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -102,7 +102,7 @@ private actor Service { variant: .curve25519, userIds: [userId] ) - + try await submitKeyToAttester(email: userId.email, publicKey: encryptedPrv.key.public) try await appContext.getBackupService().backupToInbox(keys: [encryptedPrv.key], for: user) try await putKeypairs(encryptedPrv: encryptedPrv, storageMethod: storageMethod, passPhrase: passPhrase) @@ -143,7 +143,7 @@ private actor Service { token: appContext.dataService.token ) } catch { - throw CreateKeyError.submitKey + throw CreateKeyError.submitKey(error) } } diff --git a/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift b/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift index bdaccb741..7084a0460 100644 --- a/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift +++ b/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift @@ -19,7 +19,7 @@ enum CreateKeyError: Error { /// Silent abort case conformingPassPhraseError /// Failed to submit key - case submitKey + case submitKey(Error) } extension CreateKeyError: CustomStringConvertible { @@ -33,8 +33,10 @@ extension CreateKeyError: CustomStringConvertible { return "backupServiceError_name".localized case .doesntMatch: return "pass_phrase_match_error".localized - case .submitKey: + case .submitKey(let error): return "submit_key_error".localized + + "\n" + + "\(error.errorMessage)" case .conformingPassPhraseError: return "" } diff --git a/FlowCrypt/Functionality/Services/ApiCall.swift b/FlowCrypt/Functionality/Services/ApiCall.swift index aaa17455f..943ea1b82 100644 --- a/FlowCrypt/Functionality/Services/ApiCall.swift +++ b/FlowCrypt/Functionality/Services/ApiCall.swift @@ -63,15 +63,23 @@ struct ApiError: LocalizedError { extension ApiError { static func create(from httpError: HttpErr, request: ApiCall.Request) -> Self { - guard - let data = httpError.data, - let object = try? JSONDecoder().decode(HttpError.self, from: data) - else { + guard let data = httpError.data else { return ApiError( errorDescription: httpError.error?.localizedDescription ?? "", internalError: httpError.error ) } + + guard let object = try? JSONDecoder().decode(HttpError.self, from: data) else { + let errorDescription = httpError.error?.localizedDescription + ?? String(data: data, encoding: .utf8) + ?? "missed description" + + return ApiError( + errorDescription: errorDescription, + internalError: httpError.error + ) + } var message = "\(request.apiName) \(object.code) \(object.message)" message += "\n" From 3d1a9adfea3767e0ad824aee9cb23218a98ab548 Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Fri, 21 Jan 2022 15:16:16 +0200 Subject: [PATCH 5/6] Fix pr comments --- .../xcshareddata/swiftpm/Package.resolved | 8 ++++---- .../Setup/SetupGenerateKeyViewController.swift | 8 ++++---- .../Error Handling/KeyServiceErrorHandler.swift | 8 ++++---- FlowCrypt/Functionality/Services/ApiCall.swift | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/FlowCrypt.xcworkspace/xcshareddata/swiftpm/Package.resolved b/FlowCrypt.xcworkspace/xcshareddata/swiftpm/Package.resolved index 03bee17f0..6528023d1 100644 --- a/FlowCrypt.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/FlowCrypt.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -87,8 +87,8 @@ "repositoryURL": "https://github.com/realm/realm-cocoa", "state": { "branch": null, - "revision": "e83cc9f28e8bccd477b6b81cee521c02239d2773", - "version": "10.20.2" + "revision": "39177714b95bb5b1b29fffe28f1c7da77eef8e8b", + "version": "10.21.1" } }, { @@ -96,8 +96,8 @@ "repositoryURL": "https://github.com/realm/realm-core", "state": { "branch": null, - "revision": "c3c11a841642ac93c27bd1edd61f989fc0bfb809", - "version": "11.6.1" + "revision": "f1976f0d96d9b06fbe0afbd60090b1c3966b1e23", + "version": "11.8.0" } }, { diff --git a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift index cbfb2358e..2fc4394b3 100644 --- a/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift +++ b/FlowCrypt/Controllers/Setup/SetupGenerateKeyViewController.swift @@ -105,7 +105,7 @@ private actor Service { try await submitKeyToAttester(email: userId.email, publicKey: encryptedPrv.key.public) try await appContext.getBackupService().backupToInbox(keys: [encryptedPrv.key], for: user) - try await putKeypairs(encryptedPrv: encryptedPrv, storageMethod: storageMethod, passPhrase: passPhrase) + try await putKeypairsInEncryptedStorage(encryptedPrv: encryptedPrv, storageMethod: storageMethod, passPhrase: passPhrase) if storageMethod == .memory { let passPhrase = PassPhrase( @@ -123,7 +123,7 @@ private actor Service { } @MainActor - private func putKeypairs(encryptedPrv: CoreRes.GenerateKey, storageMethod: StorageMethod, passPhrase: String) throws { + private func putKeypairsInEncryptedStorage(encryptedPrv: CoreRes.GenerateKey, storageMethod: StorageMethod, passPhrase: String) throws { try appContext.encryptedStorage.putKeypairs( keyDetails: [encryptedPrv.key], passPhrase: storageMethod == .persistent ? passPhrase: nil, @@ -149,10 +149,10 @@ private actor Service { private func getUserId() throws -> UserId { guard let email = appContext.dataService.email, !email.isEmpty else { - throw CreateKeyError.missedUserEmail + throw CreateKeyError.missingUserEmail } guard let name = appContext.dataService.currentUser?.name, !name.isEmpty else { - throw CreateKeyError.missedUserName + throw CreateKeyError.missingUserName } return UserId(email: email, name: name) } diff --git a/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift b/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift index 7084a0460..3f3a8fef6 100644 --- a/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift +++ b/FlowCrypt/Functionality/Error Handling/KeyServiceErrorHandler.swift @@ -11,9 +11,9 @@ import UIKit enum CreateKeyError: Error { case weakPassPhrase(_ strength: CoreRes.ZxcvbnStrengthBar) /// Missing user email - case missedUserEmail + case missingUserEmail /// Missing user name - case missedUserName + case missingUserName /// Pass phrases don't match case doesntMatch /// Silent abort @@ -27,9 +27,9 @@ extension CreateKeyError: CustomStringConvertible { switch self { case .weakPassPhrase(let strength): return "Pass phrase strength: \(strength.word.word)\ncrack time: \(strength.time)\n\nWe recommend to use 5-6 unrelated words as your Pass Phrase." - case .missedUserEmail: + case .missingUserEmail: return "backupServiceError_email".localized - case .missedUserName: + case .missingUserName: return "backupServiceError_name".localized case .doesntMatch: return "pass_phrase_match_error".localized diff --git a/FlowCrypt/Functionality/Services/ApiCall.swift b/FlowCrypt/Functionality/Services/ApiCall.swift index 943ea1b82..83409c2bd 100644 --- a/FlowCrypt/Functionality/Services/ApiCall.swift +++ b/FlowCrypt/Functionality/Services/ApiCall.swift @@ -69,12 +69,12 @@ extension ApiError { internalError: httpError.error ) } - + guard let object = try? JSONDecoder().decode(HttpError.self, from: data) else { let errorDescription = httpError.error?.localizedDescription ?? String(data: data, encoding: .utf8) - ?? "missed description" - + ?? "missing error description" + return ApiError( errorDescription: errorDescription, internalError: httpError.error From dc4a889024abbc192d37a0709e98a2b0e178edd2 Mon Sep 17 00:00:00 2001 From: Anton Kharchevskyi Date: Mon, 24 Jan 2022 16:44:59 +0200 Subject: [PATCH 6/6] fix test_calendar_date_formatting --- FlowCryptAppTests/ExtensionTests.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/FlowCryptAppTests/ExtensionTests.swift b/FlowCryptAppTests/ExtensionTests.swift index 667482586..87d9c0f89 100644 --- a/FlowCryptAppTests/ExtensionTests.swift +++ b/FlowCryptAppTests/ExtensionTests.swift @@ -88,13 +88,20 @@ extension ExtensionTests { timeZone: .current, year: year, month: 1, - day: 24 + day: 24, + hour: 18, + minute: 34, + second: 9 ).date) // Jan 24, 2020 let otherYearDate = Date(timeIntervalSince1970: 1579883652) XCTAssertTrue(dateFormatter.date(from: DateFormatter().formatDate(sameDayDate)) != nil) - XCTAssertEqual(DateFormatter().formatDate(sameYearDate), "Jan 24") - XCTAssertEqual(DateFormatter().formatDate(otherYearDate), "Jan 24, 2020") + if Calendar.current.isDateInToday(sameYearDate) { + XCTAssertEqual(dateFormatter.formatDate(sameYearDate), "18:34") + } else { + XCTAssertEqual(dateFormatter.formatDate(sameYearDate), "Jan 24") + } + XCTAssertEqual(dateFormatter.formatDate(otherYearDate), "Jan 24, 2020") } }