Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Cryptomator Hub Authentication Flow #329

Merged
merged 17 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ on:
jobs:
build:
name: Build and test
runs-on: macos-12
runs-on: macos-13
env:
DERIVED_DATA_PATH: 'DerivedData'
DEVICE: 'iPhone 12 Pro'
DEVICE: 'iPhone 14 Pro'
if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[skip ci]')"
strategy:
matrix:
Expand Down
58 changes: 26 additions & 32 deletions Cryptomator/AddVault/Hub/HubAddVaultCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ class AddHubVaultCoordinator: Coordinator {
let vaultUID: String
let accountUID: String
let vaultItem: VaultItem
let hubAuthenticator: HubAuthenticating
let vaultManager: VaultManager
weak var parentCoordinator: Coordinator?
weak var delegate: (VaultInstalling & AnyObject)?
Expand All @@ -33,54 +32,49 @@ class AddHubVaultCoordinator: Coordinator {
vaultUID: String,
accountUID: String,
vaultItem: VaultItem,
hubAuthenticator: HubAuthenticating,
vaultManager: VaultManager = VaultDBManager.shared) {
self.navigationController = navigationController
self.downloadedVaultConfig = downloadedVaultConfig
self.vaultUID = vaultUID
self.accountUID = accountUID
self.vaultItem = vaultItem
self.hubAuthenticator = hubAuthenticator
self.vaultManager = vaultManager
}

func start() {
let viewModel = HubAuthenticationViewModel(vaultConfig: downloadedVaultConfig.vaultConfig,
hubUserAuthenticator: self,
delegate: self)
let viewController = HubAuthenticationViewController(viewModel: viewModel)
navigationController.pushViewController(viewController, animated: true)
let unlockHandler = AddHubVaultUnlockHandler(vaultUID: vaultUID,
accountUID: accountUID, vaultItem: vaultItem,
downloadedVaultConfig: downloadedVaultConfig,
vaultManager: vaultManager,
delegate: self)
let child = HubAuthenticationCoordinator(navigationController: navigationController,
vaultConfig: downloadedVaultConfig.vaultConfig,
unlockHandler: unlockHandler,
parent: self,
delegate: self)
childCoordinators.append(child)
child.start()
}
}

extension AddHubVaultCoordinator: HubAuthenticationFlowDelegate {
func didSuccessfullyRemoteUnlock(_ response: HubUnlockResponse) async {
let jwe = response.jwe
let privateKey = response.privateKey
let hubVault = ExistingHubVault(vaultUID: vaultUID,
delegateAccountUID: accountUID,
jweData: jwe.compactSerializedData,
privateKey: privateKey,
vaultItem: vaultItem,
downloadedVaultConfig: downloadedVaultConfig)
do {
try await vaultManager.addExistingHubVault(hubVault).getValue()
childDidFinish(self)
await showSuccessfullyAddedVault()
} catch {
DDLogError("Add existing Hub vault failed: \(error)")
handleError(error, for: navigationController)
}
extension AddHubVaultCoordinator: HubVaultUnlockHandlerDelegate {
func successfullyProcessedUnlockedVault() {
delegate?.showSuccessfullyAddedVault(withName: vaultItem.name, vaultUID: vaultUID)
}

@MainActor
private func showSuccessfullyAddedVault() {
delegate?.showSuccessfullyAddedVault(withName: vaultItem.name, vaultUID: vaultUID)
func failedToProcessUnlockedVault(error: Error) {
handleError(error, for: navigationController, onOKTapped: { [weak self] in
self?.parentCoordinator?.childDidFinish(self)
})
}
}

extension AddHubVaultCoordinator: HubUserLogin {
public func authenticate(with hubConfig: HubConfig) async throws -> OIDAuthState {
try await hubAuthenticator.authenticate(with: hubConfig, from: navigationController)
extension AddHubVaultCoordinator: HubAuthenticationCoordinatorDelegate {
func userDidCancelHubAuthentication() {
// do nothing as the user already sees the login screen again
}

func userDismissedHubAuthenticationErrorMessage() {
// do nothing as the user already sees the login screen again
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,7 @@ private class AuthenticatedOpenExistingVaultCoordinator: VaultInstalling, Folder
downloadedVaultConfig: downloadedVaultConfig,
vaultUID: UUID().uuidString,
accountUID: account.accountUID,
vaultItem: vaultItem,
hubAuthenticator: CryptomatorHubAuthenticator.shared)
vaultItem: vaultItem)
child.parentCoordinator = self
child.delegate = self
childCoordinators.append(child)
Expand Down
7 changes: 4 additions & 3 deletions Cryptomator/Settings/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import Combine
import CryptomatorCommonCore
import CryptomatorFileProvider
import Dependencies
import Foundation
import Promises
import StoreKit
Expand Down Expand Up @@ -90,13 +91,13 @@ class SettingsViewModel: TableViewModel<SettingsSection> {
return viewModel
}()

private let fileProviderConnector: FileProviderConnector
@Dependency(\.fileProviderConnector) private var fileProviderConnector

private var subscribers = Set<AnyCancellable>()
private lazy var showDebugModeWarningPublisher = PassthroughSubject<Void, Never>()

init(cryptomatorSettings: CryptomatorSettings = CryptomatorUserDefaults.shared, fileProviderConnector: FileProviderConnector = FileProviderXPCConnector.shared) {
init(cryptomatorSettings: CryptomatorSettings = CryptomatorUserDefaults.shared) {
self.cryptomatorSettings = cryptomatorSettings
self.fileProviderConnector = fileProviderConnector
}

func refreshCacheSize() -> Promise<Void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CocoaLumberjackSwift
import Combine
import CryptomatorCommonCore
import CryptomatorCryptoLib
import Dependencies
import FileProvider
import Foundation
import Promises
Expand Down Expand Up @@ -60,27 +61,23 @@ class ChangePasswordViewModel: TableViewModel<ChangePasswordSection>, ChangePass
return _sections
}

lazy var cells: [ChangePasswordSection: [BindableTableViewCellViewModel]] = {
return [
.oldPassword: [oldPasswordCellViewModel],
.newPassword: [newPasswordCellViewModel],
.newPasswordConfirmation: [newPasswordConfirmationCellViewModel]
]
}()

private lazy var _sections: [Section<ChangePasswordSection>] = {
return [
Section(id: .oldPassword, elements: [oldPasswordCellViewModel]),
Section(id: .newPassword, elements: [newPasswordCellViewModel]),
Section(id: .newPasswordConfirmation, elements: [newPasswordConfirmationCellViewModel])
]
}()
lazy var cells: [ChangePasswordSection: [BindableTableViewCellViewModel]] = [
.oldPassword: [oldPasswordCellViewModel],
.newPassword: [newPasswordCellViewModel],
.newPasswordConfirmation: [newPasswordConfirmationCellViewModel]
]

private lazy var _sections: [Section<ChangePasswordSection>] = [
Section(id: .oldPassword, elements: [oldPasswordCellViewModel]),
Section(id: .newPassword, elements: [newPasswordCellViewModel]),
Section(id: .newPasswordConfirmation, elements: [newPasswordConfirmationCellViewModel])
]

private static let minimumPasswordLength = 8
private let vaultAccount: VaultAccount
private let domain: NSFileProviderDomain
private let vaultManager: VaultManager
private let fileProviderConnector: FileProviderConnector
@Dependency(\.fileProviderConnector) private var fileProviderConnector

private let oldPasswordCellViewModel = TextFieldCellViewModel(type: .password, isInitialFirstResponder: true)
private let newPasswordCellViewModel = TextFieldCellViewModel(type: .password)
Expand All @@ -100,11 +97,10 @@ class ChangePasswordViewModel: TableViewModel<ChangePasswordSection>, ChangePass

private lazy var subscribers = Set<AnyCancellable>()

init(vaultAccount: VaultAccount, domain: NSFileProviderDomain, vaultManager: VaultManager = VaultDBManager.shared, fileProviderConnector: FileProviderConnector = FileProviderXPCConnector.shared) {
init(vaultAccount: VaultAccount, domain: NSFileProviderDomain, vaultManager: VaultManager = VaultDBManager.shared) {
self.vaultAccount = vaultAccount
self.domain = domain
self.vaultManager = vaultManager
self.fileProviderConnector = fileProviderConnector
super.init()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import Combine
import CryptomatorCommonCore
import Dependencies
import FileProvider
import Foundation
import Promises
Expand Down Expand Up @@ -40,19 +41,18 @@ class VaultKeepUnlockedViewModel: TableViewModel<VaultKeepUnlockedSection>, Vaul
private(set) var keepUnlockedItems = [KeepUnlockedDurationItem]()
private let vaultKeepUnlockedSettings: VaultKeepUnlockedSettings
private let masterkeyCacheManager: MasterkeyCacheManager
private let fileProviderConnector: FileProviderConnector
@Dependency(\.fileProviderConnector) private var fileProviderConnector
private let vaultInfo: VaultInfo
private let currentKeepUnlockedDuration: Bindable<KeepUnlockedDuration>
private var subscriber: AnyCancellable?
private var vaultUID: String {
return vaultInfo.vaultUID
}

init(currentKeepUnlockedDuration: Bindable<KeepUnlockedDuration>, vaultInfo: VaultInfo, vaultKeepUnlockedSettings: VaultKeepUnlockedSettings = VaultKeepUnlockedManager.shared, masterkeyCacheManager: MasterkeyCacheManager = MasterkeyCacheKeychainManager.shared, fileProviderConnector: FileProviderConnector = FileProviderXPCConnector.shared) {
init(currentKeepUnlockedDuration: Bindable<KeepUnlockedDuration>, vaultInfo: VaultInfo, vaultKeepUnlockedSettings: VaultKeepUnlockedSettings = VaultKeepUnlockedManager.shared, masterkeyCacheManager: MasterkeyCacheManager = MasterkeyCacheKeychainManager.shared) {
self.vaultInfo = vaultInfo
self.vaultKeepUnlockedSettings = vaultKeepUnlockedSettings
self.masterkeyCacheManager = masterkeyCacheManager
self.fileProviderConnector = fileProviderConnector
self.currentKeepUnlockedDuration = currentKeepUnlockedDuration

self.keepUnlockedItems = KeepUnlockedDuration.allCases.map {
Expand Down
7 changes: 3 additions & 4 deletions Cryptomator/VaultDetail/MoveVault/MoveVaultViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import CocoaLumberjackSwift
import CryptomatorCloudAccessCore
import CryptomatorCommonCore
import Dependencies
import FileProvider
import Foundation
import GRDB
Expand All @@ -28,19 +29,17 @@ class MoveVaultViewModel: ChooseFolderViewModel, MoveVaultViewModelProtocol {
private let vaultManager: VaultManager
private let vaultInfo: VaultInfo
private let domain: NSFileProviderDomain
private let fileProviderConnector: FileProviderConnector
@Dependency(\.fileProviderConnector) private var fileProviderConnector

init(provider: CloudProvider,
currentFolderChoosingCloudPath: CloudPath,
vaultInfo: VaultInfo,
domain: NSFileProviderDomain,
cloudProviderManager: CloudProviderManager = CloudProviderDBManager.shared,
vaultManager: VaultManager = VaultDBManager.shared,
fileProviderConnector: FileProviderConnector = FileProviderXPCConnector.shared) {
vaultManager: VaultManager = VaultDBManager.shared) {
self.vaultInfo = vaultInfo
self.domain = domain
self.vaultManager = vaultManager
self.fileProviderConnector = fileProviderConnector
super.init(canCreateFolder: true, cloudPath: currentFolderChoosingCloudPath, provider: provider)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import CocoaLumberjackSwift
import CryptomatorCloudAccessCore
import CryptomatorCommonCore
import Dependencies
import FileProvider
import Foundation
import GRDB
Expand All @@ -31,19 +32,18 @@ class RenameVaultViewModel: SetVaultNameViewModel, RenameVaultViewModelProtcol {
// swiftlint:disable:next weak_delegate
private let delegate: MoveVaultViewModel
private let vaultInfo: VaultInfo
@Dependency(\.fileProviderConnector) private var fileProviderConnector

init(provider: CloudProvider,
vaultInfo: VaultInfo,
domain: NSFileProviderDomain,
vaultManager: VaultManager = VaultDBManager.shared,
fileProviderConnector: FileProviderConnector = FileProviderXPCConnector.shared) {
vaultManager: VaultManager = VaultDBManager.shared) {
self.delegate = MoveVaultViewModel(
provider: provider,
currentFolderChoosingCloudPath: CloudPath("/"),
vaultInfo: vaultInfo,
domain: domain,
vaultManager: vaultManager,
fileProviderConnector: fileProviderConnector
vaultManager: vaultManager
)
self.vaultInfo = vaultInfo
}
Expand Down
18 changes: 8 additions & 10 deletions Cryptomator/VaultDetail/VaultDetailViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CocoaLumberjackSwift
import Combine
import CryptomatorCloudAccessCore
import CryptomatorCommonCore
import Dependencies
import GRDB
import LocalAuthentication
import Promises
Expand Down Expand Up @@ -73,7 +74,7 @@ class VaultDetailViewModel: VaultDetailViewModelProtocol {

private let vaultInfo: VaultInfo
private let vaultManager: VaultManager
private let fileProviderConnector: FileProviderConnector
@Dependency(\.fileProviderConnector) private var fileProviderConnector
private let context = LAContext()
private let vaultKeepUnlockedSettings: VaultKeepUnlockedSettings
private let passwordManager: VaultPasswordManager
Expand Down Expand Up @@ -136,12 +137,10 @@ class VaultDetailViewModel: VaultDetailViewModelProtocol {
}
}

private lazy var sectionFooter: [VaultDetailSection: HeaderFooterViewModel] = {
[.vaultInfoSection: VaultDetailInfoFooterViewModel(vault: vaultInfo),
.changeVaultPasswordSection: BaseHeaderFooterViewModel(title: LocalizedString.getValue("vaultDetail.changePassword.footer")),
.lockingSection: unlockSectionFooterViewModel,
.removeVaultSection: BaseHeaderFooterViewModel(title: LocalizedString.getValue("vaultDetail.removeVault.footer"))]
}()
private lazy var sectionFooter: [VaultDetailSection: HeaderFooterViewModel] = [.vaultInfoSection: VaultDetailInfoFooterViewModel(vault: vaultInfo),
.changeVaultPasswordSection: BaseHeaderFooterViewModel(title: LocalizedString.getValue("vaultDetail.changePassword.footer")),
.lockingSection: unlockSectionFooterViewModel,
.removeVaultSection: BaseHeaderFooterViewModel(title: LocalizedString.getValue("vaultDetail.removeVault.footer"))]

private lazy var unlockSectionFooterViewModel = UnlockSectionFooterViewModel(vaultUnlocked: vaultInfo.vaultIsUnlocked.value, biometricalUnlockEnabled: biometricalUnlockEnabled, biometryTypeName: context.enrolledBiometricsAuthenticationName(), keepUnlockedDuration: currentKeepUnlockedDuration.value)

Expand All @@ -156,13 +155,12 @@ class VaultDetailViewModel: VaultDetailViewModelProtocol {
private var observation: DatabaseCancellable?

convenience init(vaultInfo: VaultInfo) {
self.init(vaultInfo: vaultInfo, vaultManager: VaultDBManager.shared, fileProviderConnector: FileProviderXPCConnector.shared, passwordManager: VaultPasswordKeychainManager(), dbManager: DatabaseManager.shared, vaultKeepUnlockedSettings: VaultKeepUnlockedManager.shared)
self.init(vaultInfo: vaultInfo, vaultManager: VaultDBManager.shared, passwordManager: VaultPasswordKeychainManager(), dbManager: DatabaseManager.shared, vaultKeepUnlockedSettings: VaultKeepUnlockedManager.shared)
}

init(vaultInfo: VaultInfo, vaultManager: VaultManager, fileProviderConnector: FileProviderConnector, passwordManager: VaultPasswordManager, dbManager: DatabaseManager, vaultKeepUnlockedSettings: VaultKeepUnlockedSettings) {
init(vaultInfo: VaultInfo, vaultManager: VaultManager, passwordManager: VaultPasswordManager, dbManager: DatabaseManager, vaultKeepUnlockedSettings: VaultKeepUnlockedSettings) {
self.vaultInfo = vaultInfo
self.vaultManager = vaultManager
self.fileProviderConnector = fileProviderConnector
self.passwordManager = passwordManager
self.title = Bindable(vaultInfo.vaultName)
self.vaultKeepUnlockedSettings = vaultKeepUnlockedSettings
Expand Down
6 changes: 3 additions & 3 deletions Cryptomator/VaultList/VaultCellViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import Combine
import CryptomatorCommonCore
import Dependencies
import Promises
import UIKit

Expand All @@ -33,11 +34,10 @@ class VaultCellViewModel: TableViewCellViewModel, VaultCellViewModelProtocol {

let vault: VaultInfo
private lazy var errorPublisher = PassthroughSubject<Error, Never>()
private let fileProviderConnector: FileProviderConnector
@Dependency(\.fileProviderConnector) private var fileProviderConnector

init(vault: VaultInfo, fileProviderConnector: FileProviderConnector = FileProviderXPCConnector.shared) {
init(vault: VaultInfo) {
self.vault = vault
self.fileProviderConnector = fileProviderConnector
}

func lockVault() -> Promise<Void> {
Expand Down
8 changes: 4 additions & 4 deletions Cryptomator/VaultList/VaultListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import CocoaLumberjackSwift
import Combine
import CryptomatorCommonCore
import Dependencies
import FileProvider
import Foundation
import GRDB
Expand All @@ -27,21 +28,20 @@ class VaultListViewModel: ViewModel, VaultListViewModelProtocol {
var vaultCellViewModels: [VaultCellViewModel]
private let dbManager: DatabaseManager
private let vaultManager: VaultDBManager
private let fileProviderConnector: FileProviderConnector
@Dependency(\.fileProviderConnector) private var fileProviderConnector
private var observation: DatabaseCancellable?
private lazy var subscribers = Set<AnyCancellable>()
private lazy var errorPublisher = PassthroughSubject<Error, Never>()
private lazy var databaseChangedPublisher = CurrentValueSubject<Result<[TableViewCellViewModel], Error>, Never>(.success([]))
private var removedRow = false

convenience init() {
self.init(dbManager: DatabaseManager.shared, vaultManager: VaultDBManager.shared, fileProviderConnector: FileProviderXPCConnector.shared)
self.init(dbManager: DatabaseManager.shared, vaultManager: VaultDBManager.shared)
}

init(dbManager: DatabaseManager, vaultManager: VaultDBManager, fileProviderConnector: FileProviderConnector) {
init(dbManager: DatabaseManager, vaultManager: VaultDBManager) {
self.dbManager = dbManager
self.vaultManager = vaultManager
self.fileProviderConnector = fileProviderConnector
self.vaultCellViewModels = [VaultCellViewModel]()
}

Expand Down
Loading