Skip to content

Commit

Permalink
Merge branch 'master' into 1850-re-enable-semaphoreci-live-test
Browse files Browse the repository at this point in the history
  • Loading branch information
ioanmo226 committed Jul 20, 2023
2 parents 951ace6 + 1a31303 commit 61cf9fe
Show file tree
Hide file tree
Showing 23 changed files with 515 additions and 74 deletions.
12 changes: 10 additions & 2 deletions FlowCrypt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@
75DCE6A02869EBC0003435F1 /* EmptyFolderCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75DCE69F2869EBC0003435F1 /* EmptyFolderCellNode.swift */; };
949ED9422303E3B400530579 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 949ED9412303E3B400530579 /* Colors.xcassets */; };
95473C1B297E61DE006C8957 /* SequenceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95473C1A297E61DE006C8957 /* SequenceExtension.swift */; };
9547EF212A5F106E00A048FF /* PassPhraseAlertNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9547EF202A5F106E00A048FF /* PassPhraseAlertNode.swift */; };
9547EF242A5FBA2B00A048FF /* MenuSeparatorCellNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9547EF232A5FBA2B00A048FF /* MenuSeparatorCellNode.swift */; };
958566B72A6126DE001C84D3 /* EncryptedStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F92EE71236F165E009BE0D7 /* EncryptedStorage.swift */; };
958566B92A612822001C84D3 /* ASButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 958566B82A612822001C84D3 /* ASButtonNode.swift */; };
9F003D6125E1B4ED00EB38C0 /* TrashFolderProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F003D6025E1B4ED00EB38C0 /* TrashFolderProvider.swift */; };
9F003D6D25EA8F3200EB38C0 /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F003D6C25EA8F3200EB38C0 /* SessionManager.swift */; };
9F0C3C102316DD5B00299985 /* GoogleAuthManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F0C3C0F2316DD5B00299985 /* GoogleAuthManager.swift */; };
Expand Down Expand Up @@ -242,7 +245,6 @@
9F8839142721EB5000669B56 /* MessageAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F8839132721EB5000669B56 /* MessageAction.swift */; };
9F883916272709E200669B56 /* MessagesThreadApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F883915272709E200669B56 /* MessagesThreadApiClient.swift */; };
9F88391927270A1A00669B56 /* MessagesThreadOperationsApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F88391827270A1A00669B56 /* MessagesThreadOperationsApiClient.swift */; };
9F92EE72236F165E009BE0D7 /* EncryptedStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F92EE71236F165E009BE0D7 /* EncryptedStorage.swift */; };
9F9361A52573CE260009912F /* MessageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9361A42573CE260009912F /* MessageProvider.swift */; };
9F9362062573D0C80009912F /* Gmail+MessagesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9362052573D0C80009912F /* Gmail+MessagesList.swift */; };
9F9362192573D10E0009912F /* Imap+Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9362182573D10E0009912F /* Imap+Message.swift */; };
Expand Down Expand Up @@ -635,7 +637,9 @@
75DCE69F2869EBC0003435F1 /* EmptyFolderCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyFolderCellNode.swift; sourceTree = "<group>"; };
949ED9412303E3B400530579 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
95473C1A297E61DE006C8957 /* SequenceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceExtension.swift; sourceTree = "<group>"; };
9547EF202A5F106E00A048FF /* PassPhraseAlertNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassPhraseAlertNode.swift; sourceTree = "<group>"; };
9547EF232A5FBA2B00A048FF /* MenuSeparatorCellNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuSeparatorCellNode.swift; sourceTree = "<group>"; };
958566B82A612822001C84D3 /* ASButtonNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASButtonNode.swift; sourceTree = "<group>"; };
9F003D6025E1B4ED00EB38C0 /* TrashFolderProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrashFolderProvider.swift; sourceTree = "<group>"; };
9F003D6C25EA8F3200EB38C0 /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = "<group>"; };
9F003D9D25EA910B00EB38C0 /* LocalStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalStorageTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2089,6 +2093,7 @@
D2FD0F682453245E00259FF0 /* Either.swift */,
750A6C3C28244A780048E1CC /* OptionalExtensions.swift */,
95473C1A297E61DE006C8957 /* SequenceExtension.swift */,
958566B82A612822001C84D3 /* ASButtonNode.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -2242,6 +2247,7 @@
50531BE32629B9A80039BAE9 /* AttachmentNode.swift */,
51DAD9BC273E7DD20076CBA7 /* BadgeNode.swift */,
51B9EE6E27567B520080B2D5 /* MessageRecipientsNode.swift */,
9547EF202A5F106E00A048FF /* PassPhraseAlertNode.swift */,
);
path = Nodes;
sourceTree = "<group>";
Expand Down Expand Up @@ -2720,6 +2726,7 @@
042B140227F596C70018BDC4 /* ComposeRecipientPopupViewController.swift in Sources */,
9FC4112E2595EA8B001180A8 /* Gmail+Search.swift in Sources */,
049E606327FDB9C70089EE2A /* ComposeViewController+Keyboard.swift in Sources */,
958566B72A6126DE001C84D3 /* EncryptedStorage.swift in Sources */,
5A948DC5239EF2F4006284D7 /* LegalViewController.swift in Sources */,
51C0C63828D1E42A003C540E /* ComposeViewController+ErrorHandling.swift in Sources */,
5133B6722716321F00C95463 /* ContactKeyDetailDecorator.swift in Sources */,
Expand Down Expand Up @@ -2824,7 +2831,6 @@
040FDF1227EDFC5C00CB936A /* IdTokenUtils.swift in Sources */,
9F953E09238310D500AEB98B /* KeyMethods.swift in Sources */,
5137CB1427F8E0A900AEF895 /* EnterpriseServerApiHelper.swift in Sources */,
9F92EE72236F165E009BE0D7 /* EncryptedStorage.swift in Sources */,
51433AC32902A6EB00E9D488 /* RSAMessage.swift in Sources */,
32DCA00224982EDA88D69C6E /* AppErr.swift in Sources */,
9F6EE17B2598F9FA0059BA51 /* Gmail+Backup.swift in Sources */,
Expand Down Expand Up @@ -2876,6 +2882,7 @@
D27177502425659F00BDA9A9 /* TitleCellNode.swift in Sources */,
50531BE42629B9A80039BAE9 /* AttachmentNode.swift in Sources */,
D2CDC3D824047066002B045F /* RecipientEmailNode.swift in Sources */,
9547EF212A5F106E00A048FF /* PassPhraseAlertNode.swift in Sources */,
D2717753242568A600BDA9A9 /* NavigationBarItemsView.swift in Sources */,
51C56BE92901867D00610D12 /* ENSideMenuNavigationController.swift in Sources */,
D211CE7023FC35AC00D1CE38 /* TableNode.swift in Sources */,
Expand Down Expand Up @@ -2927,6 +2934,7 @@
21C7DEFE26669CE100C44800 /* DateFormattingExtensions.swift in Sources */,
9FD5052927889D8200FAA82F /* UIAlertControllerExtensions.swift in Sources */,
9FBD69EC27775086002FC602 /* UIApplicationExtensions.swift in Sources */,
958566B92A612822001C84D3 /* ASButtonNode.swift in Sources */,
D2CDC3D42402D50A002B045F /* EncodableExtensions.swift in Sources */,
9FD505272785C2CD00FAA82F /* UIDeviceExtensions.swift in Sources */,
750A6C3D28244A780048E1CC /* OptionalExtensions.swift in Sources */,
Expand Down
3 changes: 2 additions & 1 deletion FlowCrypt/Controllers/Compose/ComposeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ final class ComposeViewController: TableNodeViewController {
var composedLatestDraft: ComposedDraft?

var messagePasswordAlertController: UIAlertController?
lazy var alertsFactory = AlertsFactory()
let alertsFactory: AlertsFactory

var didFinishSetup = false {
didSet {
Expand Down Expand Up @@ -132,6 +132,7 @@ final class ComposeViewController: TableNodeViewController {
)
}

self.alertsFactory = AlertsFactory(encryptedStorage: appContext.encryptedStorage)
self.filesManager = filesManager
self.photosManager = photosManager
self.pubLookup = PubLookup(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import UIKit
// MARK: - Error handling
extension ComposeViewController {
func requestMissingPassPhraseWithModal(for signingKey: Keypair, isDraft: Bool = false) {
let alert = alertsFactory.makePassPhraseAlert(
alertsFactory.makePassPhraseAlert(
viewController: self,
onCancel: { [weak self] in
self?.navigationController?.popViewController(animated: true)
},
Expand All @@ -26,8 +27,10 @@ extension ComposeViewController {
)

if matched {
self.alertsFactory.passphraseCheckSucceed()
self.handleMatchedPassphrase(isDraft: isDraft)
} else {
self.alertsFactory.passphraseCheckFailed()
self.handle(error: ComposeMessageError.passPhraseNoMatch)
}
} catch {
Expand All @@ -36,7 +39,6 @@ extension ComposeViewController {
}
}
)
present(alert, animated: true, completion: nil)
}

private func handleMatchedPassphrase(isDraft: Bool) {
Expand Down
86 changes: 49 additions & 37 deletions FlowCrypt/Controllers/Threads/Models/AlertsFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,66 +6,78 @@
// Copyright © 2017-present FlowCrypt a. s. All rights reserved.
//

import AsyncDisplayKit
import FlowCryptCommon
import FlowCryptUI
import UIKit

class AlertsFactory {
typealias PassPhraseCompletion = (String) -> Void
typealias CancelCompletion = () -> Void

let encryptedStorage: EncryptedStorageType

init(encryptedStorage: EncryptedStorageType) {
self.encryptedStorage = encryptedStorage
}

private var textFieldDelegate: UITextFieldDelegate?

func passphraseCheckFailed() {
guard var activeUser = try? encryptedStorage.activeUser else {
return
}
activeUser.failedPassPhraseAttempts = (activeUser.failedPassPhraseAttempts ?? 0) + 1
activeUser.lastUnsuccessfulPassPhraseAttempt = Date()
try? encryptedStorage.saveActiveUser(with: activeUser)
}

func passphraseCheckSucceed() {
guard var activeUser = try? encryptedStorage.activeUser else {
return
}
activeUser.failedPassPhraseAttempts = nil
activeUser.lastUnsuccessfulPassPhraseAttempt = nil
try? encryptedStorage.saveActiveUser(with: activeUser)
}

func makePassPhraseAlert(
viewController: UIViewController,
title: String = "setup_enter_pass_phrase".localized,
onCancel: @escaping CancelCompletion,
onCompletion: @escaping PassPhraseCompletion
) -> UIAlertController {
let alert = UIAlertController(
) {
guard var activeUser = try? encryptedStorage.activeUser else {
return
}
let alertNode = PassPhraseAlertNode(
failedPassPhraseAttempts: activeUser.failedPassPhraseAttempts,
lastUnsuccessfulPassPhraseAttempt: activeUser.lastUnsuccessfulPassPhraseAttempt,
title: title,
message: nil,
preferredStyle: .alert
message: nil
)

textFieldDelegate = SubmitOnPasteTextFieldDelegate(onSubmit: { passPhrase in
alert.dismiss(animated: true, completion: {
onCompletion(passPhrase)
})
})

alert.addTextField { [weak self] tf in
tf.isSecureTextEntry = true
tf.delegate = self?.textFieldDelegate
tf.accessibilityIdentifier = "aid-message-passphrase-textfield"
}
let saveAction = UIAlertAction(
title: "ok".localized,
style: .default
) { _ in
guard let textField = alert.textFields?.first,
let passPhrase = textField.text,
passPhrase.isNotEmpty
alertNode.onOkay = { passPhrase in
guard let passPhrase, passPhrase.isNotEmpty
else {
alert.dismiss(animated: true, completion: nil)
return
}

alert.dismiss(animated: true) {
viewController.dismiss(animated: true) {
onCompletion(passPhrase)
}
}
let cancelAction = UIAlertAction(
title: "cancel".localized,
style: .destructive
) { _ in
alert.dismiss(animated: true) {
onCancel()
}
alertNode.onCancel = {
viewController.dismiss(animated: true)
onCancel()
}
alertNode.resetFailedPassphraseAttempts = {
activeUser.failedPassPhraseAttempts = 0
try? self.encryptedStorage.saveActiveUser(with: activeUser)
}
let alertViewController = ASDKViewController(node: alertNode)
alertViewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
alertViewController.modalTransitionStyle = UIModalTransitionStyle.crossDissolve

alert.addAction(cancelAction)
alert.addAction(saveAction)

return alert
viewController.present(alertViewController, animated: true, completion: nil)
}
}

Expand Down
10 changes: 6 additions & 4 deletions FlowCrypt/Controllers/Threads/ThreadDetailsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import FlowCryptUI
import UIKit

final class ThreadDetailsViewController: TableNodeViewController {
private lazy var alertsFactory = AlertsFactory()
private let alertsFactory: AlertsFactory
lazy var logger = Logger.nested(Self.self)

struct Input {
Expand Down Expand Up @@ -72,6 +72,7 @@ final class ThreadDetailsViewController: TableNodeViewController {
messageProvider: try mailProvider.messageProvider,
combinedPassPhraseStorage: appContext.combinedPassPhraseStorage
)
self.alertsFactory = AlertsFactory(encryptedStorage: appContext.encryptedStorage)
self.threadOperationsApiClient = try mailProvider.threadOperationsApiClient
self.messageActionsHelper = try await MessageActionsHelper(
appContext: appContext
Expand Down Expand Up @@ -374,7 +375,8 @@ final class ThreadDetailsViewController: TableNodeViewController {
? "setup_enter_pass_phrase".localized
: "setup_wrong_pass_phrase_retry".localized

let alert = alertsFactory.makePassPhraseAlert(
alertsFactory.makePassPhraseAlert(
viewController: self,
title: title,
onCancel: { [weak self] in
self?.navigationController?.popViewController(animated: true)
Expand All @@ -383,8 +385,6 @@ final class ThreadDetailsViewController: TableNodeViewController {
self?.handlePassPhraseEntry(passPhrase, indexPath: indexPath)
}
)

present(alert, animated: true, completion: nil)
}

private func handlePassPhraseEntry(_ passPhrase: String, indexPath: IndexPath) {
Expand All @@ -409,8 +409,10 @@ final class ThreadDetailsViewController: TableNodeViewController {
isUsingKeyManager: appContext.clientConfigurationProvider.configuration.isUsingKeyManager
)

alertsFactory.passphraseCheckSucceed()
handle(processedMessage: processedMessage, at: indexPath)
} else {
alertsFactory.passphraseCheckFailed()
handleWrongPassPhrase(passPhrase, indexPath: indexPath)
}
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ final class EncryptedStorage: EncryptedStorageType {
case version12
case version13
case version14
case version15

var version: SchemaVersion {
switch self {
Expand All @@ -75,14 +76,16 @@ final class EncryptedStorage: EncryptedStorageType {
return SchemaVersion(appVersion: "1.1.1", dbSchemaVersion: 13)
case .version14:
return SchemaVersion(appVersion: "1.2.3", dbSchemaVersion: 14)
case .version15:
return SchemaVersion(appVersion: "1.2.3", dbSchemaVersion: 15)
}
}
}

private lazy var migrationLogger = Logger.nested(in: Self.self, with: .migration)
private lazy var logger = Logger.nested(Self.self)

private let currentSchema: EncryptedStorageSchema = .version14
private let currentSchema: EncryptedStorageSchema = .version15
private let supportedSchemas = EncryptedStorageSchema.allCases

private let storageEncryptionKey: Data
Expand Down
9 changes: 6 additions & 3 deletions FlowCrypt/Functionality/Services/EKMVcHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ final class EKMVcHelper: EKMVcHelperType {
private let appContext: AppContextWithUser
private let keyMethods: KeyMethodsType

private lazy var alertsFactory = AlertsFactory()
private let alertsFactory: AlertsFactory

init(appContext: AppContextWithUser) {
self.appContext = appContext
self.keyMethods = KeyMethods()
self.alertsFactory = AlertsFactory(encryptedStorage: appContext.encryptedStorage)
}

func refreshKeysFromEKMIfNeeded(in viewController: UIViewController) {
Expand Down Expand Up @@ -146,7 +147,8 @@ final class EKMVcHelper: EKMVcHelperType {
@MainActor
private func requestPassPhraseWithModal(in viewController: UIViewController) async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
let alert = alertsFactory.makePassPhraseAlert(
alertsFactory.makePassPhraseAlert(
viewController: viewController,
title: "refresh_key_alert_title".localized,
onCancel: {
return continuation.resume(returning: "")
Expand All @@ -165,8 +167,10 @@ final class EKMVcHelper: EKMVcHelperType {
passPhrase
)
if matched {
self.alertsFactory.passphraseCheckSucceed()
return continuation.resume(returning: passPhrase)
}
self.alertsFactory.passphraseCheckFailed()
// Pass phrase mismatch, display error alert and ask again
try await viewController.showAsyncAlert(message: "refresh_key_invalid_pass_phrase".localized)
let newPassPhrase = try await self.requestPassPhraseWithModal(in: viewController)
Expand All @@ -177,7 +181,6 @@ final class EKMVcHelper: EKMVcHelperType {
}
}
)
viewController.present(alert, animated: true, completion: nil)
}
}

Expand Down
5 changes: 5 additions & 0 deletions FlowCrypt/Models/Common/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ struct User: Codable, Equatable {
}
}

var lastUnsuccessfulPassPhraseAttempt: Date?
var failedPassPhraseAttempts: Int?

var imap: Session?
var smtp: Session?

Expand Down Expand Up @@ -68,6 +71,8 @@ extension User {
self.isActive = userObject.isActive
self.imap = userObject.imap.flatMap(Session.init)
self.smtp = userObject.smtp.flatMap(Session.init)
self.lastUnsuccessfulPassPhraseAttempt = userObject.lastUnsuccessfulPassPhraseAttempt
self.failedPassPhraseAttempts = userObject.failedPassPhraseAttempts
}
}

Expand Down
4 changes: 4 additions & 0 deletions FlowCrypt/Models/Realm Models/UserRealmObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ final class UserRealmObject: Object {
@Persisted var isActive: Bool
@Persisted var name: String
@Persisted var imap: SessionRealmObject?
@Persisted var lastUnsuccessfulPassPhraseAttempt: Date?
@Persisted var failedPassPhraseAttempts: Int?
@Persisted var smtp: SessionRealmObject?
}

Expand All @@ -35,5 +37,7 @@ extension UserRealmObject {
self.name = user.name
self.imap = user.imap.flatMap(SessionRealmObject.init)
self.smtp = user.smtp.flatMap(SessionRealmObject.init)
self.lastUnsuccessfulPassPhraseAttempt = user.lastUnsuccessfulPassPhraseAttempt
self.failedPassPhraseAttempts = user.failedPassPhraseAttempts
}
}
Loading

0 comments on commit 61cf9fe

Please sign in to comment.