From c970c0738006b6fadf779e4ee7422e16a4a007d2 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 15 Jun 2021 16:07:30 +0200 Subject: [PATCH 01/20] SecurityViewController: Fix crash but this code is not used in production --- Riot/Modules/Settings/Security/SecurityViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 5817a68165..94b32646ee 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -1768,7 +1768,7 @@ - (MXKTableViewCellWithButton *)settingsKeyBackupTableViewSection:(SettingsKeyBa if (indexPath) { - [self buttonCellForTableView:self.tableView atIndexPath:indexPath]; + cell = [self buttonCellForTableView:self.tableView atIndexPath:indexPath]; } return cell; From 7cc653a7c7f6666683c11c895851169f5fae0342 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 15 Jun 2021 16:09:56 +0200 Subject: [PATCH 02/20] Security settings: Display the cross-signing section like element-web. --- CHANGES.rst | 1 + Riot/Assets/en.lproj/Vector.strings | 6 ++--- Riot/Generated/Strings.swift | 6 ++--- .../Security/SecurityViewController.m | 22 +++++++++---------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 99f39da386..c94df8549c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,7 @@ Changes to be released in next version * Integrated FLEX for debug builds. * VoIP: Add dial pad for PSTN capable servers to menu on homescreen. * VoIP: Replace call bar with PiP tiles for every type of calls. + * Security settings: Display the cross-signing section (#4430). 🐛 Bugfix * StartChatViewController: Add more helpful message when trying to start DM with a user that does not exist (#224). diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index e06b7db2ef..b814f6cf9a 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -632,9 +632,9 @@ Tap the + to start adding people."; "security_settings_crosssigning_info_not_bootstrapped" = "Cross-signing is not yet set up."; "security_settings_crosssigning_info_exists" = "Your account has a cross-signing identity, but it is not yet trusted by this session. Complete security of this session."; "security_settings_crosssigning_info_trusted" = "Cross-signing is enabled. You can trust other users and your other sessions based on cross-signing but you cannot cross-sign from this session because it does not have cross-signing private keys. Complete security of this session."; -"security_settings_crosssigning_info_ok" = "Cross-signing is enabled."; -"security_settings_crosssigning_bootstrap" = "Bootstrap cross-signing"; -"security_settings_crosssigning_reset" = "Reset cross-signing"; +"security_settings_crosssigning_info_ok" = "Cross-signing is ready for use."; +"security_settings_crosssigning_bootstrap" = "Set up"; +"security_settings_crosssigning_reset" = "Reset"; "security_settings_crosssigning_complete_security" = "Complete security"; "security_settings_cryptography" = "CRYPTOGRAPHY"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 4f93cded72..cbee626fb4 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -3802,7 +3802,7 @@ internal enum VectorL10n { internal static var securitySettingsCrosssigning: String { return VectorL10n.tr("Vector", "security_settings_crosssigning") } - /// Bootstrap cross-signing + /// Set up internal static var securitySettingsCrosssigningBootstrap: String { return VectorL10n.tr("Vector", "security_settings_crosssigning_bootstrap") } @@ -3818,7 +3818,7 @@ internal enum VectorL10n { internal static var securitySettingsCrosssigningInfoNotBootstrapped: String { return VectorL10n.tr("Vector", "security_settings_crosssigning_info_not_bootstrapped") } - /// Cross-signing is enabled. + /// Cross-signing is ready for use. internal static var securitySettingsCrosssigningInfoOk: String { return VectorL10n.tr("Vector", "security_settings_crosssigning_info_ok") } @@ -3826,7 +3826,7 @@ internal enum VectorL10n { internal static var securitySettingsCrosssigningInfoTrusted: String { return VectorL10n.tr("Vector", "security_settings_crosssigning_info_trusted") } - /// Reset cross-signing + /// Reset internal static var securitySettingsCrosssigningReset: String { return VectorL10n.tr("Vector", "security_settings_crosssigning_reset") } diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 94b32646ee..5737143d70 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -36,9 +36,9 @@ SECTION_PIN_CODE, SECTION_CRYPTO_SESSIONS, SECTION_SECURE_BACKUP, + SECTION_CROSSSIGNING, SECTION_CRYPTOGRAPHY, #ifdef CROSS_SIGNING_AND_BACKUP_DEV - SECTION_CROSSSIGNING, SECTION_KEYBACKUP, #endif SECTION_ADVANCED, @@ -363,6 +363,15 @@ - (void)updateSections [sections addObject:secureBackupSection]; } + // Cross-Signing + + Section *crossSigningSection = [Section sectionWithTag:SECTION_CROSSSIGNING]; + crossSigningSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_crosssigning", @"Vector", nil); + + [crossSigningSection addRowsWithCount:[self numberOfRowsInCrossSigningSection]]; + + [sections addObject:crossSigningSection]; + // Cryptograhpy Section *cryptograhpySection = [Section sectionWithTag:SECTION_CRYPTOGRAPHY]; @@ -385,15 +394,6 @@ - (void)updateSections #ifdef CROSS_SIGNING_AND_BACKUP_DEV - // Cross-Signing - - Section *crossSigningSection = [Section sectionWithTag:SECTION_CROSSSIGNING]; - crossSigningSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_crosssigning", @"Vector", nil); - - [crossSigningSection addRowsWithCount:[self numberOfRowsInCrossSigningSection]]; - - [sections addObject:crossSigningSection]; - // Keybackup Section *keybackupSection = [Section sectionWithTag:SECTION_KEYBACKUP]; @@ -1425,6 +1425,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N { cell = [keyBackupSection cellForRowAtRow:row]; } +#endif else if (section == SECTION_CROSSSIGNING) { switch (row) @@ -1444,7 +1445,6 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N break; } } -#endif else if (section == SECTION_CRYPTOGRAPHY) { switch (row) From 8b94550c25ece704e069b0ee70b52d3d794133c1 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 15 Jun 2021 19:21:33 +0200 Subject: [PATCH 03/20] Security settings: Introduce a dynamic section for SECURE BACKUP This is a copy from SettingsKeyBackupTableViewSection. We need to get the current status of the key backup to be able to display right actions. --- Riot/Assets/en.lproj/Vector.strings | 6 +- Riot/Generated/Strings.swift | 18 +- ...SettingsSecureBackupTableViewSection.swift | 333 ++++++++++++++++++ .../SettingsSecureBackupViewAction.swift | 25 ++ .../SettingsSecureBackupViewModel.swift | 147 ++++++++ .../SettingsSecureBackupViewModelType.swift | 33 ++ .../SettingsSecureBackupViewState.swift | 41 +++ .../Security/SecurityViewController.m | 187 ++++++---- 8 files changed, 715 insertions(+), 75 deletions(-) create mode 100644 Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift create mode 100644 Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift create mode 100644 Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift create mode 100644 Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift create mode 100644 Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index b814f6cf9a..e87d3a28b7 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -622,9 +622,11 @@ Tap the + to start adding people."; "security_settings_secure_backup" = "SECURE BACKUP"; "security_settings_secure_backup_description" = "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server."; +"security_settings_secure_backup_info_checking" = "Checking…"; +"security_settings_secure_backup_info_valid" = "This session is backing up your keys."; "security_settings_secure_backup_setup" = "Set up"; -"security_settings_secure_backup_synchronise" = "Synchronise"; -"security_settings_secure_backup_delete" = "Delete"; +"security_settings_secure_backup_restore" = "Restore from Backup"; +"security_settings_secure_backup_delete" = "Delete Backup"; "security_settings_backup" = "MESSAGE BACKUP"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index cbee626fb4..afe7c36269 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -3854,7 +3854,7 @@ internal enum VectorL10n { internal static var securitySettingsSecureBackup: String { return VectorL10n.tr("Vector", "security_settings_secure_backup") } - /// Delete + /// Delete Backup internal static var securitySettingsSecureBackupDelete: String { return VectorL10n.tr("Vector", "security_settings_secure_backup_delete") } @@ -3862,14 +3862,22 @@ internal enum VectorL10n { internal static var securitySettingsSecureBackupDescription: String { return VectorL10n.tr("Vector", "security_settings_secure_backup_description") } + /// Checking… + internal static var securitySettingsSecureBackupInfoChecking: String { + return VectorL10n.tr("Vector", "security_settings_secure_backup_info_checking") + } + /// This session is backing up your keys. + internal static var securitySettingsSecureBackupInfoValid: String { + return VectorL10n.tr("Vector", "security_settings_secure_backup_info_valid") + } + /// Restore from Backup + internal static var securitySettingsSecureBackupRestore: String { + return VectorL10n.tr("Vector", "security_settings_secure_backup_restore") + } /// Set up internal static var securitySettingsSecureBackupSetup: String { return VectorL10n.tr("Vector", "security_settings_secure_backup_setup") } - /// Synchronise - internal static var securitySettingsSecureBackupSynchronise: String { - return VectorL10n.tr("Vector", "security_settings_secure_backup_synchronise") - } /// Security internal static var securitySettingsTitle: String { return VectorL10n.tr("Vector", "security_settings_title") diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift new file mode 100644 index 0000000000..a302825dc3 --- /dev/null +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -0,0 +1,333 @@ +/* + Copyright 2021 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +@objc protocol SettingsSecureBackupTableViewSectionDelegate: class { + func settingsSecureBackupTableViewSectionDidUpdate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection) + + func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, textCellForRow: Int) -> MXKTableViewCellWithTextView + func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, buttonCellForRow: Int) -> MXKTableViewCellWithButton + + + func settingsSecureBackupTableViewSectionShowKeyBackupSetup(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection) + func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) + func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) + + func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showActivityIndicator show: Bool) + func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showError error: Error) +} + +private enum BackupRows { + case info(text: String) + case setupAction + case restoreFromKeyBackupAction(keyBackupVersion: MXKeyBackupVersion, title: String) + case deleteKeyBackupAction(keyBackupVersion: MXKeyBackupVersion) +} + +@objc final class SettingsSecureBackupTableViewSection: NSObject { + + // MARK: - Properties + + @objc weak var delegate: SettingsSecureBackupTableViewSectionDelegate? + + // MARK: Private + + // This view class holds the model because the model is in pure Swift + // whereas this class can be used from objC + private var viewModel: SettingsSecureBackupViewModelType! + + // Need to know the state to make `cellForRow` deliver cells accordingly + private var viewState: SettingsSecureBackupViewState = .checkingBackup { + didSet { + self.updateBackupRows() + } + } + + private var userDevice: MXDeviceInfo + + private var backupRows: [BackupRows] = [] + + // MARK: - Public + + @objc init(withKeyBackup keyBackup: MXKeyBackup, userDevice: MXDeviceInfo) { + self.viewModel = SettingsSecureBackupViewModel(keyBackup: keyBackup) + self.userDevice = userDevice + super.init() + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .load) + } + + @objc func numberOfRows() -> Int { + return self.backupRows.count + } + + @objc func cellForRow(atRow row: Int) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell() + } + + let backupRow = self.backupRows[row] + + var cell: UITableViewCell + switch backupRow { + case .info(let infoText): + let infoCell: MXKTableViewCellWithTextView = delegate.settingsSecureBackupTableViewSection(self, textCellForRow: row) + infoCell.mxkTextView.text = infoText + cell = infoCell + case .setupAction: + cell = self.buttonCellForSetup(atRow: row) + case .restoreFromKeyBackupAction(keyBackupVersion: let keyBackupVersion, let title): + cell = self.buttonCellForRestoreFromKeyBackup(keyBackupVersion: keyBackupVersion, title: title, atRow: row) + case .deleteKeyBackupAction(keyBackupVersion: let keyBackupVersion): + cell = self.buttonCellForDeleteKeyBackup(keyBackupVersion: keyBackupVersion, atRow: row) + } + + return cell + } + + @objc func reload() { + self.viewModel.process(viewAction: .load) + } + + @objc func deleteKeyBackup(keyBackupVersion: MXKeyBackupVersion) { + self.viewModel.process(viewAction: .deleteKeyBackup(keyBackupVersion)) + } + + // MARK: - Data Computing + + private func updateBackupRows() { + + let backupRows: [BackupRows] + + switch self.viewState { + case .checkingBackup: + + let info = VectorL10n.securitySettingsSecureBackupDescription + let checking = VectorL10n.securitySettingsSecureBackupInfoChecking + let strings = [info, "", checking] + let text = strings.joined(separator: "\n") + + backupRows = [ + .info(text: text) + ] + + case .noBackup: + +// let noBackup = VectorL10n.settingsSecureBackupInfoNone +// let info = VectorL10n.settingsSecureBackupInfo +// let signoutWarning = VectorL10n.settingsSecureBackupInfoSignoutWarning +// let strings = [noBackup, "", info, "", signoutWarning] +// let backupInfoText = strings.joined(separator: "\n") + let backupInfoText = "TODO" + + backupRows = [ + .info(text: backupInfoText), + .setupAction + ] + + case .backup(let keyBackupVersion, let keyBackupVersionTrust), + .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _): + + let info = VectorL10n.securitySettingsSecureBackupDescription + let backupStatus = VectorL10n.securitySettingsSecureBackupInfoValid + let backupStrings = [info, "", backupStatus] + let backupInfoText = backupStrings.joined(separator: "\n") + +// let version = VectorL10n.settingsSecureBackupInfoVersion(keyBackupVersion.version ?? "") +// let algorithm = VectorL10n.settingsSecureBackupInfoAlgorithm(keyBackupVersion.algorithm) +// let uploadStatus = VectorL10n.settingsSecureBackupInfoProgressDone +// let additionalStrings = [version, algorithm, uploadStatus] +// let additionalInfoText = additionalStrings.joined(separator: "\n") +// +// let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust) +// let backupTrustInfoText = backupTrust.joined(separator: "\n") + + var backupViewStateRows: [BackupRows] = [ + .info(text: backupInfoText), +// .info(text: additionalInfoText), +// .info(text: backupTrustInfoText) + ] + + // TODO: Do not display restore button if all keys are stored on the device + if true { + backupViewStateRows.append(.restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore)) + } + + backupViewStateRows.append(.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion)) + + backupRows = backupViewStateRows + +// case .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, let backupProgress): +// +// let info = VectorL10n.securitySettingsSecureBackupDescription +// let backupStatus = VectorL10n.securitySettingsSecureBackupInfoValid +// let backupStrings = [info, "", backupStatus] +// let backupInfoText = backupStrings.joined(separator: "\n") +// +// let remaining = backupProgress.totalUnitCount - backupProgress.completedUnitCount +// let version = VectorL10n.settingsSecureBackupInfoVersion(keyBackupVersion.version ?? "") +// let algorithm = VectorL10n.settingsSecureBackupInfoAlgorithm(keyBackupVersion.algorithm) +// let uploadStatus = VectorL10n.settingsSecureBackupInfoProgress(String(remaining)) +// let additionalStrings = [version, algorithm, uploadStatus] +// let additionalInfoText = additionalStrings.joined(separator: "\n") +// +// let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust) +// let backupTrustInfoText = backupTrust.joined(separator: "\n") +// +// var backupAndRunningViewStateRows: [BackupRows] = [ +// .info(text: backupInfoText), +// .info(text: additionalInfoText), +// .info(text: backupTrustInfoText) +// ] +// +// // TODO: Do not display restore button if all keys are stored on the device +// if true { +// backupAndRunningViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsSecureBackupButtonRestore)) +// } +// +// backupAndRunningViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion)) +// +// backupRows = backupAndRunningViewStateRows + + case .backupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + + // TODO: What? + let info = VectorL10n.securitySettingsSecureBackupDescription + backupRows = [ + .info(text: info) + ] + +// let info = VectorL10n.securitySettingsSecureBackupDescription +// let backupStatus = VectorL10n.settingsSecureBackupInfoNotValid +// let signoutWarning = VectorL10n.settingsSecureBackupInfoSignoutWarning +// let backupStrings = [info, "", backupStatus, "", signoutWarning] +// let backupInfoText = backupStrings.joined(separator: "\n") +// +// let version = VectorL10n.settingsSecureBackupInfoVersion(keyBackupVersion.version ?? "") +// let algorithm = VectorL10n.settingsSecureBackupInfoAlgorithm(keyBackupVersion.algorithm) +// let additionalStrings = [version, algorithm] +// let additionalInfoText = additionalStrings.joined(separator: "\n") +// +// let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust) +// let backupTrustInfoText = backupTrust.joined(separator: "\n") +// +// var backupNotTrustedViewStateRows: [BackupRows] = [ +// .info(text: backupInfoText), +// .info(text: additionalInfoText), +// .info(text: backupTrustInfoText) +// ] +// +// // TODO: Do not display restore button if all keys are stored on the device +// if true { +// backupNotTrustedViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsSecureBackupButtonConnect)) +// } +// +// backupNotTrustedViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion)) +// +// backupRows = backupNotTrustedViewStateRows + } + + self.backupRows = backupRows + } + + // MARK: - Button cells + + private func buttonCellForSetup(atRow row: Int) -> UITableViewCell { + + guard let delegate = self.delegate else { + return UITableViewCell() + } + + let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row) + + let btnTitle = VectorL10n.securitySettingsSecureBackupSetup + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) + + cell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .setup) + } + + return cell + } + + private func buttonCellForRestoreFromKeyBackup(keyBackupVersion: MXKeyBackupVersion, title: String, atRow row: Int) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell() + } + + let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row) + cell.mxkButton.setTitle(title, for: .normal) + cell.mxkButton.setTitle(title, for: .highlighted) + cell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .restoreFromKeyBackup(keyBackupVersion)) + } + return cell + } + + private func buttonCellForDeleteKeyBackup(keyBackupVersion: MXKeyBackupVersion, atRow row: Int) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell() + } + + let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row) + let btnTitle = VectorL10n.securitySettingsSecureBackupDelete + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) + cell.mxkButton.tintColor = ThemeService.shared().theme.warningColor + cell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .confirmDeleteKeyBackup(keyBackupVersion)) + } + + return cell + } +} + + +// MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate +extension SettingsSecureBackupTableViewSection: SettingsSecureBackupViewModelViewDelegate { + func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateViewState viewState: SettingsSecureBackupViewState) { + self.viewState = viewState + + // The tableview datasource will call `self.cellForRow()` + self.delegate?.settingsSecureBackupTableViewSectionDidUpdate(self) + } + + func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsSecureBackupNetworkRequestViewState) { + switch networkRequestViewSate { + case .loading: + self.delegate?.settingsSecureBackupTableViewSection(self, showActivityIndicator: true) + case .loaded: + self.delegate?.settingsSecureBackupTableViewSection(self, showActivityIndicator: false) + case .error(let error): + self.delegate?.settingsSecureBackupTableViewSection(self, showError: error) + } + } + + func settingsSecureBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsSecureBackupViewModelType) { + self.delegate?.settingsSecureBackupTableViewSectionShowKeyBackupSetup(self) + } + + func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) { + self.delegate?.settingsSecureBackupTableViewSection(self, showKeyBackupRecover: keyBackupVersion) + } + + func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) { + self.delegate?.settingsSecureBackupTableViewSection(self, showKeyBackupDeleteConfirm: keyBackupVersion) + } +} diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift new file mode 100644 index 0000000000..cd83795425 --- /dev/null +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift @@ -0,0 +1,25 @@ +/* + Copyright 2021 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +enum SettingsSecureBackupViewAction { + case load + case setup + case restoreFromKeyBackup(MXKeyBackupVersion) + case confirmDeleteKeyBackup(MXKeyBackupVersion) + case deleteKeyBackup(MXKeyBackupVersion) +} diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift new file mode 100644 index 0000000000..c716d70d4a --- /dev/null +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift @@ -0,0 +1,147 @@ +/* + Copyright 2021 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { + + // MARK: - Properties + weak var viewDelegate: SettingsSecureBackupViewModelViewDelegate? + + // MARK: Private + private let keyBackup: MXKeyBackup + + init(keyBackup: MXKeyBackup) { + self.keyBackup = keyBackup + self.registerKeyBackupVersionDidChangeStateNotification() + } + + private func registerKeyBackupVersionDidChangeStateNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(keyBackupDidStateChange), name: NSNotification.Name.mxKeyBackupDidStateChange, object: self.keyBackup) + } + + @objc private func keyBackupDidStateChange() { + self.checkKeyBackupState() + } + + func process(viewAction: SettingsSecureBackupViewAction) { + guard let viewDelegate = self.viewDelegate else { + return + } + + switch viewAction { + case .load: + viewDelegate.settingsSecureBackupViewModel(self, didUpdateViewState: .checkingBackup) + self.checkKeyBackupState() + case .setup: + viewDelegate.settingsSecureBackupViewModelShowKeyBackupSetup(self) + case .restoreFromKeyBackup(let keyBackupVersion): + viewDelegate.settingsSecureBackupViewModel(self, showKeyBackupRecover: keyBackupVersion) + case .confirmDeleteKeyBackup(let keyBackupVersion): + viewDelegate.settingsSecureBackupViewModel(self, showKeyBackupDeleteConfirm: keyBackupVersion) + case .deleteKeyBackup(let keyBackupVersion): + self.deleteKeyBackupVersion(keyBackupVersion) + } + } + + // MARK: - Private + private func checkKeyBackupState() { + + // Check homeserver update in background + self.keyBackup.forceRefresh(nil, failure: nil) + + if let keyBackupVersion = self.keyBackup.keyBackupVersion { + + self.keyBackup.trust(for: keyBackupVersion, onComplete: { [weak self] (keyBackupVersionTrust) in + + guard let sself = self else { + return + } + + sself.computeState(withBackupVersionTrust: keyBackupVersionTrust) + }) + } else { + computeState() + } + } + + private func computeState(withBackupVersionTrust keyBackupVersionTrust: MXKeyBackupVersionTrust? = nil) { + + var viewState: SettingsSecureBackupViewState? + switch self.keyBackup.state { + + case MXKeyBackupStateUnknown, + MXKeyBackupStateCheckingBackUpOnHomeserver: + viewState = .checkingBackup + + case MXKeyBackupStateDisabled, MXKeyBackupStateEnabling: + viewState = .noBackup + + case MXKeyBackupStateNotTrusted: + guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { + return + } + viewState = .backupNotTrusted(keyBackupVersion, keyBackupVersionTrust) + + case MXKeyBackupStateReadyToBackUp: + guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { + return + } + viewState = .backup(keyBackupVersion, keyBackupVersionTrust) + + case MXKeyBackupStateWillBackUp, MXKeyBackupStateBackingUp: + guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { + return + } + + // Get the backup progress before updating the state + self.keyBackup.backupProgress { [weak self] (progress) in + guard let sself = self else { + return + } + + sself.viewDelegate?.settingsSecureBackupViewModel(sself, didUpdateViewState: .backupAndRunning(keyBackupVersion, keyBackupVersionTrust, progress)) + } + default: + break + } + + if let vviewState = viewState { + self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateViewState: vviewState) + } + } + + private func deleteKeyBackupVersion(_ keyBackupVersion: MXKeyBackupVersion) { + guard let keyBackupVersionVersion = keyBackupVersion.version else { + return + } + + self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateNetworkRequestViewState: .loading) + + self.keyBackup.deleteVersion(keyBackupVersionVersion, success: { [weak self] () in + guard let sself = self else { + return + } + sself.viewDelegate?.settingsSecureBackupViewModel(sself, didUpdateNetworkRequestViewState: .loaded) + + }, failure: { [weak self] error in + guard let sself = self else { + return + } + sself.viewDelegate?.settingsSecureBackupViewModel(sself, didUpdateNetworkRequestViewState: .error(error)) + }) + } +} diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift new file mode 100644 index 0000000000..eac9a14d6a --- /dev/null +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift @@ -0,0 +1,33 @@ +/* + Copyright 2021 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +protocol SettingsSecureBackupViewModelViewDelegate: class { + func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateViewState viewState: SettingsSecureBackupViewState) + func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsSecureBackupNetworkRequestViewState) + + func settingsSecureBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsSecureBackupViewModelType) + func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) + func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) +} + +protocol SettingsSecureBackupViewModelType { + + var viewDelegate: SettingsSecureBackupViewModelViewDelegate? { get set } + + func process(viewAction: SettingsSecureBackupViewAction) +} diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift new file mode 100644 index 0000000000..f76a6ca71e --- /dev/null +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift @@ -0,0 +1,41 @@ +/* + Copyright 2021 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +/// SettingsSecureBackup view state +/// +/// - checkingBackup: Load current backup on the homeserver +/// - checkError: Fail to load current backup data +/// - noBackup: There is no backup on the homeserver +/// - backup: There is a valid backup on the homeserver. All keys have been backed up to it +/// - backupAndRunning: There is a valid backup on the homeserver. Keys are being sent to it +/// - backupButNotVerified: There is a backup on the homeserver but it has not been verified yet +enum SettingsSecureBackupViewState { + case checkingBackup + case noBackup + case backup(MXKeyBackupVersion, MXKeyBackupVersionTrust) + case backupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) + case backupNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) +} + +/// State representing a network request made by the module +/// Only SettingsSecureBackupViewAction.delete generates such states +enum SettingsSecureBackupNetworkRequestViewState { + case loading + case loaded + case error(Error) +} diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 5737143d70..fe9ec95938 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -90,6 +90,7 @@ @interface SecurityViewController () < +SettingsSecureBackupTableViewSectionDelegate, #ifdef CROSS_SIGNING_AND_BACKUP_DEV SettingsKeyBackupTableViewSectionDelegate, KeyBackupSetupCoordinatorBridgePresenterDelegate, @@ -123,6 +124,8 @@ @interface SecurityViewController () < // The current pushed view controller UIViewController *pushedViewController; + + SettingsSecureBackupTableViewSection *secureBackupSection; #ifdef CROSS_SIGNING_AND_BACKUP_DEV SettingsKeyBackupTableViewSection *keyBackupSection; @@ -183,7 +186,6 @@ - (void)viewDidLoad self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.estimatedRowHeight = 50; -#ifdef CROSS_SIGNING_AND_BACKUP_DEV if (self.mainSession.crypto.backup) { MXDeviceInfo *deviceInfo = [self.mainSession.crypto.deviceList storedDevice:self.mainSession.matrixRestClient.credentials.userId @@ -191,11 +193,15 @@ - (void)viewDidLoad if (deviceInfo) { + secureBackupSection = [[SettingsSecureBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup userDevice:deviceInfo]; + secureBackupSection.delegate = self; + +#ifdef CROSS_SIGNING_AND_BACKUP_DEV keyBackupSection = [[SettingsKeyBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup userDevice:deviceInfo]; keyBackupSection.delegate = self; +#endif } } -#endif // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { @@ -355,13 +361,10 @@ - (void)updateSections Section *secureBackupSection = [Section sectionWithTag:SECTION_SECURE_BACKUP]; secureBackupSection.headerTitle = NSLocalizedStringFromTable(@"security_settings_secure_backup", @"Vector", nil); - - [secureBackupSection addRowsWithCount:[self numberOfRowsInSecureBackupSection]]; - - if (secureBackupSection.rows.count) - { - [sections addObject:secureBackupSection]; - } + + [secureBackupSection addRowsWithCount:self->secureBackupSection.numberOfRows]; + + [sections addObject:secureBackupSection]; // Cross-Signing @@ -1360,65 +1363,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } else if (section == SECTION_SECURE_BACKUP) { - switch ([self secureBackupSectionEnumForRow:row]) - { - case SECURE_BACKUP_DESCRIPTION: - { - cell = [self descriptionCellForTableView:tableView - withText:NSLocalizedStringFromTable(@"security_settings_secure_backup_description", @"Vector", nil)]; - break; - } -#ifdef CROSS_SIGNING_AND_BACKUP_DEV - case SECURE_BACKUP_INFO: - { - cell = [self descriptionCellForTableView:tableView - withText:self.secureBackupInformation]; - break; - } -#endif - case SECURE_BACKUP_SETUP: - { - MXKTableViewCellWithButton *buttonCell = [self buttonCellWithTitle:NSLocalizedStringFromTable(@"security_settings_secure_backup_setup", @"Vector", nil) - action:@selector(setupSecureBackup) - forTableView:tableView - atIndexPath:indexPath]; - - cell = buttonCell; - break; - } - case SECURE_BACKUP_RESTORE: - { - MXKTableViewCellWithButton *buttonCell = [self buttonCellWithTitle:NSLocalizedStringFromTable(@"security_settings_secure_backup_synchronise", @"Vector", nil) - action:@selector(restoreFromSecureBackup) - forTableView:tableView - atIndexPath:indexPath]; - - cell = buttonCell; - break; - } - case SECURE_BACKUP_DELETE: - { - MXKTableViewCellWithButton *buttonCell = [self buttonCellWithTitle:NSLocalizedStringFromTable(@"security_settings_secure_backup_delete", @"Vector", nil) - action:@selector(deleteSecureBackup) - forTableView:tableView - atIndexPath:indexPath]; - buttonCell.mxkButton.tintColor = ThemeService.shared.theme.warningColor; - - cell = buttonCell; - break; - } - - case SECURE_BACKUP_MANAGE_MANUALLY: - { - MXKTableViewCellWithTextView *textCell = [self textViewCellForTableView:tableView atIndexPath:indexPath]; - textCell.mxkTextView.text = @"Advanced: Manually manage keys"; // TODO - [textCell vc_setAccessoryDisclosureIndicatorWithCurrentTheme]; - - cell = textCell; - break; - } - } - + cell = [secureBackupSection cellForRowAtRow:row]; } #ifdef CROSS_SIGNING_AND_BACKUP_DEV else if (section == SECTION_KEYBACKUP) @@ -1739,6 +1684,112 @@ - (void)changePinCode:(UIButton *)sender [self.setPinCoordinatorBridgePresenter presentFrom:self animated:YES]; } + +#pragma mark - SettingsSecureBackupTableViewSectionDelegate + +- (void)settingsSecureBackupTableViewSectionDidUpdate:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection +{ + [self reloadData]; +} + +- (MXKTableViewCellWithTextView *)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection textCellForRow:(NSInteger)textCellForRow +{ + MXKTableViewCellWithTextView *cell; + + NSIndexPath *indexPath = [self.tableViewSections exactIndexPathForRowTag:textCellForRow sectionTag:SECTION_SECURE_BACKUP]; + + if (indexPath) + { + cell = [self textViewCellForTableView:self.tableView atIndexPath:indexPath]; + } + + return cell; +} + +- (MXKTableViewCellWithButton *)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection buttonCellForRow:(NSInteger)buttonCellForRow +{ + MXKTableViewCellWithButton *cell; + + NSIndexPath *indexPath = [self.tableViewSections exactIndexPathForRowTag:buttonCellForRow sectionTag:SECTION_SECURE_BACKUP]; + + if (indexPath) + { + cell = [self buttonCellForTableView:self.tableView atIndexPath:indexPath]; + } + + return cell; +} + +- (void)settingsSecureBackupTableViewSectionShowKeyBackupSetup:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection +{ + [self showKeyBackupSetupFromSignOutFlow:NO]; +} + +- (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection showKeyBackupRecover:(MXKeyBackupVersion *)keyBackupVersion +{ + self.currentkeyBackupVersion = keyBackupVersion; + + // If key backup key is stored in SSSS, ask for secrets recovery before restoring key backup. + if (!self.mainSession.crypto.backup.hasPrivateKeyInCryptoStore + && self.mainSession.crypto.recoveryService.hasRecovery + && [self.mainSession.crypto.recoveryService hasSecretWithSecretId:MXSecretId.keyBackup]) + { + [self showSecretsRecovery]; + } + else + { + [self showKeyBackupRecover:keyBackupVersion fromViewController:self]; + } +} + +- (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection showKeyBackupDeleteConfirm:(MXKeyBackupVersion *)keyBackupVersion +{ + MXWeakify(self); + [currentAlert dismissViewControllerAnimated:NO completion:nil]; + + currentAlert = + [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_title", @"Vector", nil) + message:NSLocalizedStringFromTable(@"settings_key_backup_delete_confirmation_prompt_msg", @"Vector", nil) + preferredStyle:UIAlertControllerStyleAlert]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] + style:UIAlertActionStyleCancel + handler:^(UIAlertAction * action) { + MXStrongifyAndReturnIfNil(self); + self->currentAlert = nil; + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"settings_key_backup_button_delete", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + MXStrongifyAndReturnIfNil(self); + self->currentAlert = nil; + + [self->keyBackupSection deleteWithKeyBackupVersion:keyBackupVersion]; + }]]; + + [currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCDeleteKeyBackup"]; + [self presentViewController:currentAlert animated:YES completion:nil]; +} + +- (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection showActivityIndicator:(BOOL)show +{ + if (show) + { + [self startActivityIndicator]; + } + else + { + [self stopActivityIndicator]; + } +} + +- (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection showError:(NSError *)error +{ + [[AppDelegate theDelegate] showErrorAsAlert:error]; +} + + #pragma mark - SettingsKeyBackupTableViewSectionDelegate #ifdef CROSS_SIGNING_AND_BACKUP_DEV - (void)settingsKeyBackupTableViewSectionDidUpdate:(SettingsKeyBackupTableViewSection *)settingsKeyBackupTableViewSection From 110879b4e2dbcf664e4370bfdae53a1c43483518 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 07:44:12 +0200 Subject: [PATCH 04/20] Secure backup: Allow to execute a random closure the secure backup is unlocked Only from swift. We should change the name "SecretsRecovery" classes --- Riot/Assets/en.lproj/Vector.strings | 2 + Riot/Generated/Strings.swift | 8 +++ ...SecretsRecoveryWithKeyViewController.swift | 2 + ...RecoveryWithPassphraseViewController.swift | 2 + ...cretsRecoveryWithPassphraseViewModel.swift | 62 +++++++++++++------ ...tsRecoveryCoordinatorBridgePresenter.swift | 8 +-- .../Secrets/Recover/SecretsRecoveryGoal.swift | 20 +++++- .../Security/SecurityViewController.m | 6 +- 8 files changed, 83 insertions(+), 27 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index e87d3a28b7..a0a309d474 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1455,6 +1455,8 @@ Tap the + to start adding people."; "secrets_recovery_with_key_title" = "Recovery Key"; "secrets_recovery_with_key_information_default" = "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key."; "secrets_recovery_with_key_information_verify_device" = "Use your Recovery Key to verify this device."; +"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Enter your Recovery Passphrase to continue."; +"secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Enter your Recovery Key to continue."; "secrets_recovery_with_key_recovery_key_title" = "Enter"; "secrets_recovery_with_key_recovery_key_placeholder" = "Enter Recovery Key"; "secrets_recovery_with_key_recover_action" = "Use Key"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index afe7c36269..c128b61b5a 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -3554,6 +3554,14 @@ internal enum VectorL10n { internal static var secretsRecoveryWithKeyInformationDefault: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_default") } + /// Enter your Recovery Key to continue. + internal static var secretsRecoveryWithKeyInformationUnlockSecureBackupWithKey: String { + return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_unlock_secure_backup_with_key") + } + /// Enter your Recovery Passphrase to continue. + internal static var secretsRecoveryWithKeyInformationUnlockSecureBackupWithPhrase: String { + return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_unlock_secure_backup_with_phrase") + } /// Use your Recovery Key to verify this device. internal static var secretsRecoveryWithKeyInformationVerifyDevice: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_verify_device") diff --git a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift index 306c3bcb88..a0cc96d35c 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift @@ -103,6 +103,8 @@ final class SecretsRecoveryWithKeyViewController: UIViewController { switch self.viewModel.recoveryGoal { case .default, .keyBackup, .restoreSecureBackup: informationText = VectorL10n.secretsRecoveryWithKeyInformationDefault + case .unlockSecureBackup(_): + informationText = VectorL10n.secretsRecoveryWithKeyInformationUnlockSecureBackupWithKey case .verifyDevice: informationText = VectorL10n.secretsRecoveryWithKeyInformationVerifyDevice } diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift index cf42a77a79..c1fcaaec4b 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift @@ -106,6 +106,8 @@ final class SecretsRecoveryWithPassphraseViewController: UIViewController { switch self.viewModel.recoveryGoal { case .default, .keyBackup, .restoreSecureBackup: informationText = VectorL10n.secretsRecoveryWithPassphraseInformationDefault + case .unlockSecureBackup(_): + informationText = VectorL10n.secretsRecoveryWithKeyInformationUnlockSecureBackupWithPhrase case .verifyDevice: informationText = VectorL10n.secretsRecoveryWithPassphraseInformationVerifyDevice } diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift index 77cb224895..b5e37b0825 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift @@ -73,27 +73,13 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras return } - let secretIds: [String]? - - if case SecretsRecoveryGoal.keyBackup = self.recoveryGoal { - secretIds = [MXSecretId.keyBackup.takeUnretainedValue() as String] - } else { - secretIds = nil + switch self.recoveryGoal { + case .unlockSecureBackup(let block): + self.execute(block: block, privateKey: privateKey) + default: + self.recoverSecrets(privateKey: privateKey) } - self.recoveryService.recoverSecrets(secretIds, withPrivateKey: privateKey, recoverServices: true, success: { [weak self] _ in - guard let self = self else { - return - } - self.update(viewState: .loaded) - self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self) - }, failure: { [weak self] error in - guard let self = self else { - return - } - self.update(viewState: .error(error)) - }) - }, failure: { [weak self] error in guard let self = self else { return @@ -102,6 +88,44 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras }) } + private func recoverSecrets(privateKey: Data) { + let secretIds: [String]? + + if case SecretsRecoveryGoal.keyBackup = self.recoveryGoal { + secretIds = [MXSecretId.keyBackup.takeUnretainedValue() as String] + } else { + secretIds = nil + } + + self.recoveryService.recoverSecrets(secretIds, withPrivateKey: privateKey, recoverServices: true, success: { [weak self] _ in + guard let self = self else { + return + } + self.update(viewState: .loaded) + self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self) + }, failure: { [weak self] error in + guard let self = self else { + return + } + self.update(viewState: .error(error)) + }) + } + + private func execute(block: (_ privateKey: Data, _ completion: @escaping (Result) -> Void) -> Void, privateKey: Data) { + // Run the extenal code while the view state is .loading + block(privateKey) { result in + MXLog.debug("[SecretsRecoveryWithPassphraseViewModel] execute: Block returned: \(result)") + + switch result { + case .success: + self.update(viewState: .loaded) + self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self) + case .failure(let error): + self.update(viewState: .error(error)) + } + } + } + private func update(viewState: SecretsRecoveryWithPassphraseViewState) { self.viewDelegate?.secretsRecoveryWithPassphraseViewModel(self, didUpdateViewState: viewState) } diff --git a/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorBridgePresenter.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorBridgePresenter.swift index 9ec041e61e..a5a1ea31a9 100644 --- a/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorBridgePresenter.swift @@ -32,7 +32,7 @@ final class SecretsRecoveryCoordinatorBridgePresenter: NSObject { private let session: MXSession private let recoveryMode: SecretsRecoveryMode - let recoveryGoal: SecretsRecoveryGoal + let recoveryGoal: SecretsRecoveryGoalBridge private var coordinator: SecretsRecoveryCoordinator? @@ -46,14 +46,14 @@ final class SecretsRecoveryCoordinatorBridgePresenter: NSObject { // MARK: - Setup - init(session: MXSession, recoveryMode: SecretsRecoveryMode, recoveryGoal: SecretsRecoveryGoal) { + init(session: MXSession, recoveryMode: SecretsRecoveryMode, recoveryGoal: SecretsRecoveryGoalBridge) { self.session = session self.recoveryMode = recoveryMode self.recoveryGoal = recoveryGoal super.init() } - init(session: MXSession, recoveryGoal: SecretsRecoveryGoal) { + init(session: MXSession, recoveryGoal: SecretsRecoveryGoalBridge) { self.session = session if case SecretsRecoveryAvailability.available(let secretMode) = session.crypto.recoveryService.vc_availability { @@ -74,7 +74,7 @@ final class SecretsRecoveryCoordinatorBridgePresenter: NSObject { func present(from viewController: UIViewController, animated: Bool) { - let coordinator = SecretsRecoveryCoordinator(session: self.session, recoveryMode: self.recoveryMode, recoveryGoal: self.recoveryGoal) + let coordinator = SecretsRecoveryCoordinator(session: self.session, recoveryMode: self.recoveryMode, recoveryGoal: self.recoveryGoal.goal) coordinator.delegate = self let presentable = coordinator.toPresentable() diff --git a/Riot/Modules/Secrets/Recover/SecretsRecoveryGoal.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryGoal.swift index 0017d2b75c..62b293b9f8 100644 --- a/Riot/Modules/Secrets/Recover/SecretsRecoveryGoal.swift +++ b/Riot/Modules/Secrets/Recover/SecretsRecoveryGoal.swift @@ -16,10 +16,28 @@ import Foundation +enum SecretsRecoveryGoal { + case `default` + case keyBackup + /// Unlock the secure backup (4S) to get the private key and execute a closure during the flow + case unlockSecureBackup ((_ privateKey: Data, _ completion: @escaping (Result) -> Void) -> Void) + case verifyDevice + case restoreSecureBackup +} + @objc -enum SecretsRecoveryGoal: Int { +enum SecretsRecoveryGoalBridge: Int { case `default` case keyBackup case verifyDevice case restoreSecureBackup + + var goal: SecretsRecoveryGoal { + switch self { + case .default: return .default + case .keyBackup: return .keyBackup + case .verifyDevice: return .verifyDevice + case .restoreSecureBackup: return .restoreSecureBackup + } + } } diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index fe9ec95938..ae7ac4dd17 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -1073,7 +1073,7 @@ - (void)setupSecureBackup2 - (void)restoreFromSecureBackup { - secretsRecoveryCoordinatorBridgePresenter = [[SecretsRecoveryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession recoveryGoal:SecretsRecoveryGoalRestoreSecureBackup]; + secretsRecoveryCoordinatorBridgePresenter = [[SecretsRecoveryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession recoveryGoal:SecretsRecoveryGoalBridgeRestoreSecureBackup]; [secretsRecoveryCoordinatorBridgePresenter presentFrom:self animated:true]; secretsRecoveryCoordinatorBridgePresenter.delegate = self; @@ -1955,7 +1955,7 @@ - (void)keyBackupRecoverCoordinatorBridgePresenterDidRecover:(KeyBackupRecoverCo - (void)showSecretsRecovery { - secretsRecoveryCoordinatorBridgePresenter = [[SecretsRecoveryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession recoveryGoal:SecretsRecoveryGoalKeyBackup]; + secretsRecoveryCoordinatorBridgePresenter = [[SecretsRecoveryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession recoveryGoal:SecretsRecoveryGoalBridgeKeyBackup]; [secretsRecoveryCoordinatorBridgePresenter presentFrom:self animated:true]; secretsRecoveryCoordinatorBridgePresenter.delegate = self; @@ -1971,7 +1971,7 @@ - (void)secretsRecoveryCoordinatorBridgePresenterDelegateDidComplete:(SecretsRec { UIViewController *presentedViewController = [coordinatorBridgePresenter toPresentable]; - if (coordinatorBridgePresenter.recoveryGoal == SecretsRecoveryGoalKeyBackup) + if (coordinatorBridgePresenter.recoveryGoal == SecretsRecoveryGoalBridgeKeyBackup) { // Go to the true key backup recovery screen if ([presentedViewController isKindOfClass:UINavigationController.class]) From ccf57359b30ba8dc4c467fa639bdaf8b8b1035b9 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 08:18:23 +0200 Subject: [PATCH 05/20] Key Backup setup: Create the key backup using 4S if it exists --- Riot/Assets/en.lproj/Vector.strings | 5 +- Riot/Generated/Storyboards.swift | 5 + Riot/Generated/Strings.swift | 6 +- .../Setup/KeyBackupSetupCoordinator.swift | 74 ++++++++- ...sFromSecureBackupViewController.storyboard | 140 ++++++++++++++++++ ...uccessFromSecureBackupViewController.swift | 120 +++++++++++++++ ...SettingsSecureBackupTableViewSection.swift | 29 ++-- .../SettingsSecureBackupViewAction.swift | 2 +- .../SettingsSecureBackupViewModel.swift | 4 +- .../SettingsSecureBackupViewModelType.swift | 2 +- .../Security/SecurityViewController.m | 2 +- 11 files changed, 362 insertions(+), 27 deletions(-) create mode 100644 Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.storyboard create mode 100644 Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.swift diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index a0a309d474..5beb441be2 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -567,7 +567,7 @@ Tap the + to start adding people."; "settings_key_backup_info" = "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages."; "settings_key_backup_info_checking" = "Checking…"; "settings_key_backup_info_none" = "Your keys are not being backed up from this session."; -"settings_key_backup_info_signout_warning" = "Connect this session to key backup before signing out to avoid losing any keys that may only be on this device."; +"settings_key_backup_info_signout_warning" = "Back up your keys before signing out to avoid losing them."; "settings_key_backup_info_version" = "Key Backup Version: %@"; "settings_key_backup_info_algorithm" = "Algorithm: %@"; "settings_key_backup_info_valid" = "This session is backing up your keys."; @@ -1094,6 +1094,9 @@ Tap the + to start adding people."; "key_backup_setup_success_from_recovery_key_make_copy_action" = "Make a Copy"; "key_backup_setup_success_from_recovery_key_made_copy_action" = "I've made a copy"; +// Success from secure backup +"key_backup_setup_success_from_secure_backup_info" = "Your keys are being backed up."; + // MARK: Key backup recover "key_backup_recover_title" = "Secure Messages"; diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index d592e4f762..c66d40f6a8 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -97,6 +97,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: KeyBackupSetupSuccessFromRecoveryKeyViewController.self) } + internal enum KeyBackupSetupSuccessFromSecureBackupViewController: StoryboardType { + internal static let storyboardName = "KeyBackupSetupSuccessFromSecureBackupViewController" + + internal static let initialScene = InitialSceneType(storyboard: KeyBackupSetupSuccessFromSecureBackupViewController.self) + } internal enum KeyVerificationDataLoadingViewController: StoryboardType { internal static let storyboardName = "KeyVerificationDataLoadingViewController" diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index c128b61b5a..7d6a543c2f 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1806,6 +1806,10 @@ internal enum VectorL10n { internal static var keyBackupSetupSuccessFromRecoveryKeyRecoveryKeyTitle: String { return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_recovery_key_title") } + /// Your keys are being backed up. + internal static var keyBackupSetupSuccessFromSecureBackupInfo: String { + return VectorL10n.tr("Vector", "key_backup_setup_success_from_secure_backup_info") + } /// Success! internal static var keyBackupSetupSuccessTitle: String { return VectorL10n.tr("Vector", "key_backup_setup_success_title") @@ -4262,7 +4266,7 @@ internal enum VectorL10n { internal static var settingsKeyBackupInfoProgressDone: String { return VectorL10n.tr("Vector", "settings_key_backup_info_progress_done") } - /// Connect this session to key backup before signing out to avoid losing any keys that may only be on this device. + /// Back up your keys before signing out to avoid losing them. internal static var settingsKeyBackupInfoSignoutWarning: String { return VectorL10n.tr("Vector", "settings_key_backup_info_signout_warning") } diff --git a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift index e5dd5561be..b1b96be832 100644 --- a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift +++ b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift @@ -44,11 +44,11 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType { // MARK: - Public methods func start() { - - // Set key backup setup intro as root controller - let keyBackupSetupIntroViewController = self.createSetupIntroViewController() - keyBackupSetupIntroViewController.delegate = self - self.navigationRouter.setRootModule(keyBackupSetupIntroViewController) + if self.session.crypto.recoveryService.hasRecovery() { + showUnlockSecureBackup() + } else { + showSetupIntro() + } } func toPresentable() -> UIViewController { @@ -57,6 +57,13 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType { // MARK: - Private methods + private func showSetupIntro() { + // Set key backup setup intro as root controller + let keyBackupSetupIntroViewController = self.createSetupIntroViewController() + keyBackupSetupIntroViewController.delegate = self + self.navigationRouter.setRootModule(keyBackupSetupIntroViewController) + } + private func createSetupIntroViewController() -> KeyBackupSetupIntroViewController { let backupState = self.session.crypto.backup?.state ?? MXKeyBackupStateUnknown @@ -80,6 +87,17 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType { return KeyBackupSetupIntroViewController.instantiate(isABackupAlreadyExists: isABackupAlreadyExists, encryptionKeysExportPresenter: encryptionKeysExportPresenter) } + private func showUnlockSecureBackup() { + let recoveryGoal: SecretsRecoveryGoal = .unlockSecureBackup { (privateKey, completion) in + self.createKeyBackupUsingSecureBackup(privateKey: privateKey, completion: completion) + } + + let coordinator = SecretsRecoveryCoordinator(session: self.session, recoveryMode: .passphraseOrKey, recoveryGoal: recoveryGoal, navigationRouter: self.navigationRouter) + coordinator.delegate = self + coordinator.start() + self.add(childCoordinator: coordinator) + } + private func showSetupPassphrase(animated: Bool) { let keyBackupSetupPassphraseCoordinator = KeyBackupSetupPassphraseCoordinator(session: self.session) keyBackupSetupPassphraseCoordinator.delegate = self @@ -104,6 +122,34 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType { viewController.delegate = self self.navigationRouter.push(viewController, animated: animated, popCompletion: nil) } + + private func showSetupWithSecureBackupSuccess(animated: Bool) { + + let viewController = KeyBackupSetupSuccessFromSecureBackupViewController.instantiate() + viewController.delegate = self + self.navigationRouter.push(viewController, animated: animated, popCompletion: nil) + } + + private func createKeyBackupUsingSecureBackup(privateKey: Data, completion: @escaping (Result) -> Void) { + guard let keyBackup = session.crypto.backup, let recoveryService = session.crypto.recoveryService else { + return + } + + keyBackup.prepareKeyBackupVersion(withPassword: nil, success: { megolmBackupCreationInfo in + keyBackup.createKeyBackupVersion(megolmBackupCreationInfo, success: { _ in + recoveryService.updateRecovery(forSecrets: [MXSecretId.keyBackup.takeUnretainedValue() as String], withPrivateKey: privateKey) { + completion(.success(Void())) + } failure: { error in + completion(.failure(error)) + } + + }, failure: { error in + completion(.failure(error)) + }) + }, failure: { error in + completion(.failure(error)) + }) + } } // MARK: - KeyBackupSetupIntroViewControllerDelegate @@ -133,6 +179,17 @@ extension KeyBackupSetupCoordinator: KeyBackupSetupPassphraseCoordinatorDelegate } } +// MARK: - SecretsRecoveryCoordinatorDelegate +extension KeyBackupSetupCoordinator: SecretsRecoveryCoordinatorDelegate { + func secretsRecoveryCoordinatorDidRecover(_ coordinator: SecretsRecoveryCoordinatorType) { + self.showSetupWithSecureBackupSuccess(animated: true) + } + + func secretsRecoveryCoordinatorDidCancel(_ coordinator: SecretsRecoveryCoordinatorType) { + self.delegate?.keyBackupSetupCoordinatorDidCancel(self) + } +} + // MARK: - KeyBackupSetupSuccessFromPassphraseViewControllerDelegate extension KeyBackupSetupCoordinator: KeyBackupSetupSuccessFromPassphraseViewControllerDelegate { func keyBackupSetupSuccessFromPassphraseViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromPassphraseViewController) { @@ -146,3 +203,10 @@ extension KeyBackupSetupCoordinator: KeyBackupSetupSuccessFromRecoveryKeyViewCon self.delegate?.keyBackupSetupCoordinatorDidSetupRecoveryKey(self) } } + +// MARK: - KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate +extension KeyBackupSetupCoordinator: KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate { + func keyBackupSetupSuccessFromSecureBackupViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromSecureBackupViewController) { + self.delegate?.keyBackupSetupCoordinatorDidSetupRecoveryKey(self) + } +} diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.storyboard b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.storyboard new file mode 100644 index 0000000000..1020489c28 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.storyboard @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.swift b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.swift new file mode 100644 index 0000000000..c369cb5e98 --- /dev/null +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.swift @@ -0,0 +1,120 @@ +/* + Copyright 2021 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +protocol KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate: class { + func keyBackupSetupSuccessFromSecureBackupViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromSecureBackupViewController) +} + +final class KeyBackupSetupSuccessFromSecureBackupViewController: UIViewController { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var keyBackupLogoImageView: UIImageView! + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var informationLabel: UILabel! + + @IBOutlet private weak var doneButtonBackgroundView: UIView! + @IBOutlet private weak var doneButton: UIButton! + + // MARK: Private + + private var theme: Theme! + + // MARK: Public + + weak var delegate: KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate? + + // MARK: - Setup + + class func instantiate() -> KeyBackupSetupSuccessFromSecureBackupViewController { + let viewController = StoryboardScene.KeyBackupSetupSuccessFromSecureBackupViewController.initialScene.instantiate() + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.title = VectorL10n.keyBackupSetupTitle + + self.setupViews() + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + // Hide back button + self.navigationItem.setHidesBackButton(true, animated: animated) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func setupViews() { + + let keybackupLogoImage = Asset.Images.keyBackupLogo.image.withRenderingMode(.alwaysTemplate) + self.keyBackupLogoImageView.image = keybackupLogoImage + + self.titleLabel.text = VectorL10n.keyBackupSetupSuccessTitle + self.informationLabel.text = VectorL10n.keyBackupSetupSuccessFromSecureBackupInfo + + self.doneButton.setTitle(VectorL10n.keyBackupSetupSuccessFromPassphraseDoneAction, for: .normal) + } + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + self.keyBackupLogoImageView.tintColor = theme.textPrimaryColor + self.titleLabel.textColor = theme.textPrimaryColor + self.informationLabel.textColor = theme.textPrimaryColor + + self.doneButtonBackgroundView.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.doneButton) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + // MARK: - Actions + + @IBAction private func doneButtonAction(_ sender: Any) { + self.delegate?.keyBackupSetupSuccessFromSecureBackupViewControllerDidTapDoneAction(self) + } +} diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index a302825dc3..4eb879b1c9 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -23,7 +23,7 @@ import UIKit func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, buttonCellForRow: Int) -> MXKTableViewCellWithButton - func settingsSecureBackupTableViewSectionShowKeyBackupSetup(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection) + func settingsSecureBackupTableViewSectionShowKeyBackupCreate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection) func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) @@ -33,7 +33,7 @@ import UIKit private enum BackupRows { case info(text: String) - case setupAction + case createKeyBackupAction case restoreFromKeyBackupAction(keyBackupVersion: MXKeyBackupVersion, title: String) case deleteKeyBackupAction(keyBackupVersion: MXKeyBackupVersion) } @@ -89,8 +89,8 @@ private enum BackupRows { let infoCell: MXKTableViewCellWithTextView = delegate.settingsSecureBackupTableViewSection(self, textCellForRow: row) infoCell.mxkTextView.text = infoText cell = infoCell - case .setupAction: - cell = self.buttonCellForSetup(atRow: row) + case .createKeyBackupAction: + cell = self.buttonCellForCreateKeyBackup(atRow: row) case .restoreFromKeyBackupAction(keyBackupVersion: let keyBackupVersion, let title): cell = self.buttonCellForRestoreFromKeyBackup(keyBackupVersion: keyBackupVersion, title: title, atRow: row) case .deleteKeyBackupAction(keyBackupVersion: let keyBackupVersion): @@ -128,16 +128,15 @@ private enum BackupRows { case .noBackup: -// let noBackup = VectorL10n.settingsSecureBackupInfoNone -// let info = VectorL10n.settingsSecureBackupInfo -// let signoutWarning = VectorL10n.settingsSecureBackupInfoSignoutWarning -// let strings = [noBackup, "", info, "", signoutWarning] -// let backupInfoText = strings.joined(separator: "\n") - let backupInfoText = "TODO" + let noBackup = VectorL10n.settingsKeyBackupInfoNone + let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning + let strings = [noBackup, "", signoutWarning] + let backupInfoText = strings.joined(separator: "\n") backupRows = [ + .info(text: VectorL10n.securitySettingsSecureBackupDescription), .info(text: backupInfoText), - .setupAction + .createKeyBackupAction ] case .backup(let keyBackupVersion, let keyBackupVersionTrust), @@ -247,7 +246,7 @@ private enum BackupRows { // MARK: - Button cells - private func buttonCellForSetup(atRow row: Int) -> UITableViewCell { + private func buttonCellForCreateKeyBackup(atRow row: Int) -> UITableViewCell { guard let delegate = self.delegate else { return UITableViewCell() @@ -260,7 +259,7 @@ private enum BackupRows { cell.mxkButton.setTitle(btnTitle, for: .highlighted) cell.mxkButton.vc_addAction { - self.viewModel.process(viewAction: .setup) + self.viewModel.process(viewAction: .createKeyBackup) } return cell @@ -319,8 +318,8 @@ extension SettingsSecureBackupTableViewSection: SettingsSecureBackupViewModelVie } } - func settingsSecureBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsSecureBackupViewModelType) { - self.delegate?.settingsSecureBackupTableViewSectionShowKeyBackupSetup(self) + func settingsSecureBackupViewModelShowKeyBackupCreate(_ viewModel: SettingsSecureBackupViewModelType) { + self.delegate?.settingsSecureBackupTableViewSectionShowKeyBackupCreate(self) } func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) { diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift index cd83795425..8187c7fd16 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift @@ -18,7 +18,7 @@ import UIKit enum SettingsSecureBackupViewAction { case load - case setup + case createKeyBackup case restoreFromKeyBackup(MXKeyBackupVersion) case confirmDeleteKeyBackup(MXKeyBackupVersion) case deleteKeyBackup(MXKeyBackupVersion) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift index c716d70d4a..e4151663f8 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift @@ -46,8 +46,8 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { case .load: viewDelegate.settingsSecureBackupViewModel(self, didUpdateViewState: .checkingBackup) self.checkKeyBackupState() - case .setup: - viewDelegate.settingsSecureBackupViewModelShowKeyBackupSetup(self) + case .createKeyBackup: + viewDelegate.settingsSecureBackupViewModelShowKeyBackupCreate(self) case .restoreFromKeyBackup(let keyBackupVersion): viewDelegate.settingsSecureBackupViewModel(self, showKeyBackupRecover: keyBackupVersion) case .confirmDeleteKeyBackup(let keyBackupVersion): diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift index eac9a14d6a..cb3df7ae55 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift @@ -20,7 +20,7 @@ protocol SettingsSecureBackupViewModelViewDelegate: class { func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateViewState viewState: SettingsSecureBackupViewState) func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsSecureBackupNetworkRequestViewState) - func settingsSecureBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsSecureBackupViewModelType) + func settingsSecureBackupViewModelShowKeyBackupCreate(_ viewModel: SettingsSecureBackupViewModelType) func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) } diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index ae7ac4dd17..d1271a6e21 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -1720,7 +1720,7 @@ - (MXKTableViewCellWithButton *)settingsSecureBackupTableViewSection:(SettingsSe return cell; } -- (void)settingsSecureBackupTableViewSectionShowKeyBackupSetup:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection +- (void)settingsSecureBackupTableViewSectionShowKeyBackupCreate:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection { [self showKeyBackupSetupFromSignOutFlow:NO]; } From b84ea2bc923b558e99c42789abb0a67676b1eff0 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 09:53:31 +0200 Subject: [PATCH 06/20] Security settings: Add an option to reset the secure backup It will overwrite the existing one if any --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 +++ Riot/Modules/Home/HomeViewController.m | 2 +- .../SecretsSetupRecoveryKeyCoordinator.swift | 5 +-- .../SecretsSetupRecoveryKeyViewModel.swift | 14 +++++++- .../Setup/SecureBackupSetupCoordinator.swift | 6 ++-- ...ackupSetupCoordinatorBridgePresenter.swift | 8 +++-- ...SettingsSecureBackupTableViewSection.swift | 34 +++++++++++++++++++ .../SettingsSecureBackupViewAction.swift | 1 + .../SettingsSecureBackupViewModel.swift | 2 ++ .../SettingsSecureBackupViewModelType.swift | 2 ++ .../Security/SecurityViewController.m | 7 +++- .../Modules/Settings/SettingsViewController.m | 2 +- 13 files changed, 77 insertions(+), 11 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 5beb441be2..e650a7bb5a 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -625,6 +625,7 @@ Tap the + to start adding people."; "security_settings_secure_backup_info_checking" = "Checking…"; "security_settings_secure_backup_info_valid" = "This session is backing up your keys."; "security_settings_secure_backup_setup" = "Set up"; +"security_settings_secure_backup_reset" = "Reset"; "security_settings_secure_backup_restore" = "Restore from Backup"; "security_settings_secure_backup_delete" = "Delete Backup"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 7d6a543c2f..0b65ad6a07 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -3882,6 +3882,10 @@ internal enum VectorL10n { internal static var securitySettingsSecureBackupInfoValid: String { return VectorL10n.tr("Vector", "security_settings_secure_backup_info_valid") } + /// Reset + internal static var securitySettingsSecureBackupReset: String { + return VectorL10n.tr("Vector", "security_settings_secure_backup_reset") + } /// Restore from Backup internal static var securitySettingsSecureBackupRestore: String { return VectorL10n.tr("Vector", "security_settings_secure_backup_restore") diff --git a/Riot/Modules/Home/HomeViewController.m b/Riot/Modules/Home/HomeViewController.m index 553f6e6cee..d8d5382fc9 100644 --- a/Riot/Modules/Home/HomeViewController.m +++ b/Riot/Modules/Home/HomeViewController.m @@ -167,7 +167,7 @@ - (CrossSigningSetupBannerCell *)keyVerificationSetupBannerPrototypeCell - (void)presentSecureBackupSetup { - SecureBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession]; + SecureBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession allowOverwrite:NO]; keyBackupSetupCoordinatorBridgePresenter.delegate = self; [keyBackupSetupCoordinatorBridgePresenter presentFrom:self animated:YES]; diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift index d11b3e619e..f08f88869e 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift @@ -39,8 +39,9 @@ final class SecretsSetupRecoveryKeyCoordinator: SecretsSetupRecoveryKeyCoordinat init(recoveryService: MXRecoveryService, passphrase: String?, - passphraseOnly: Bool) { - let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly) + passphraseOnly: Bool, + allowOverwrite: Bool = false) { + let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly, allowOverwrite: allowOverwrite) let secretsSetupRecoveryKeyViewController = SecretsSetupRecoveryKeyViewController.instantiate(with: secretsSetupRecoveryKeyViewModel) self.secretsSetupRecoveryKeyViewModel = secretsSetupRecoveryKeyViewModel self.secretsSetupRecoveryKeyViewController = secretsSetupRecoveryKeyViewController diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift index 8ad575dcad..af85b7eb7f 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift @@ -27,6 +27,7 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy private let recoveryService: MXRecoveryService private let passphrase: String? private let passphraseOnly: Bool + private let allowOverwrite: Bool // MARK: Public @@ -35,10 +36,11 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy // MARK: - Setup - init(recoveryService: MXRecoveryService, passphrase: String?, passphraseOnly: Bool) { + init(recoveryService: MXRecoveryService, passphrase: String?, passphraseOnly: Bool, allowOverwrite: Bool = false) { self.recoveryService = recoveryService self.passphrase = passphrase self.passphraseOnly = passphraseOnly + self.allowOverwrite = allowOverwrite } // MARK: - Public @@ -61,6 +63,16 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy private func createSecureKey() { self.update(viewState: .loading) + + if allowOverwrite && self.recoveryService.hasRecovery() { + MXLog.debug("[SecretsSetupRecoveryKeyViewModel] createSecureKey: Overwrite existing secure backup") + self.recoveryService.deleteRecovery(withDeleteServicesBackups: true) { + self.createSecureKey() + } failure: { error in + self.update(viewState: .error(error)) + } + return + } self.recoveryService.createRecovery(forSecrets: nil, withPassphrase: self.passphrase, createServicesBackups: true, success: { secretStorageKeyCreationInfo in self.update(viewState: .recoveryCreated(secretStorageKeyCreationInfo.recoveryKey)) diff --git a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift index 0ddb3416c5..5436163773 100644 --- a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift +++ b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift @@ -30,6 +30,7 @@ final class SecureBackupSetupCoordinator: SecureBackupSetupCoordinatorType { private let recoveryService: MXRecoveryService private let keyBackup: MXKeyBackup? private let checkKeyBackup: Bool + private let allowOverwrite: Bool // MARK: Public @@ -45,11 +46,12 @@ final class SecureBackupSetupCoordinator: SecureBackupSetupCoordinatorType { /// - session: The MXSession. /// - checkKeyBackup: Indicate false to ignore existing key backup. /// - navigationRouter: Use existing navigation router to plug this flow or let nil to use new one. - init(session: MXSession, checkKeyBackup: Bool = true, navigationRouter: NavigationRouterType? = nil) { + init(session: MXSession, checkKeyBackup: Bool = true, allowOverwrite: Bool = false, navigationRouter: NavigationRouterType? = nil) { self.session = session self.recoveryService = session.crypto.recoveryService self.keyBackup = session.crypto.backup self.checkKeyBackup = checkKeyBackup + self.allowOverwrite = allowOverwrite if let navigationRouter = navigationRouter { self.navigationRouter = navigationRouter @@ -85,7 +87,7 @@ final class SecureBackupSetupCoordinator: SecureBackupSetupCoordinatorType { } private func showSetupKey(passphraseOnly: Bool, passphrase: String? = nil) { - let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly) + let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly, allowOverwrite: allowOverwrite) coordinator.delegate = self coordinator.start() diff --git a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinatorBridgePresenter.swift b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinatorBridgePresenter.swift index 9748dbc519..3db379e8fc 100644 --- a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinatorBridgePresenter.swift +++ b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinatorBridgePresenter.swift @@ -33,6 +33,7 @@ final class SecureBackupSetupCoordinatorBridgePresenter: NSObject { // MARK: Private private let session: MXSession + private let allowOverwrite: Bool private var coordinator: SecureBackupSetupCoordinator? // MARK: Public @@ -40,9 +41,10 @@ final class SecureBackupSetupCoordinatorBridgePresenter: NSObject { weak var delegate: SecureBackupSetupCoordinatorBridgePresenterDelegate? // MARK: - Setup - - init(session: MXSession) { + + init(session: MXSession, allowOverwrite: Bool) { self.session = session + self.allowOverwrite = allowOverwrite super.init() } @@ -54,7 +56,7 @@ final class SecureBackupSetupCoordinatorBridgePresenter: NSObject { // } func present(from viewController: UIViewController, animated: Bool) { - let secureBackupSetupCoordinator = SecureBackupSetupCoordinator(session: self.session) + let secureBackupSetupCoordinator = SecureBackupSetupCoordinator(session: self.session, allowOverwrite: self.allowOverwrite) secureBackupSetupCoordinator.delegate = self viewController.present(secureBackupSetupCoordinator.toPresentable(), animated: animated, completion: nil) secureBackupSetupCoordinator.start() diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index 4eb879b1c9..ab5e697a3d 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -17,22 +17,28 @@ import UIKit @objc protocol SettingsSecureBackupTableViewSectionDelegate: class { + // Table view rendering func settingsSecureBackupTableViewSectionDidUpdate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection) func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, textCellForRow: Int) -> MXKTableViewCellWithTextView func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, buttonCellForRow: Int) -> MXKTableViewCellWithButton + // Secure backup + func settingsSecureBackupTableViewSectionShowSecureBackupReset(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection) + // Key backup func settingsSecureBackupTableViewSectionShowKeyBackupCreate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection) func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) + // Life cycle func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showActivityIndicator show: Bool) func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showError error: Error) } private enum BackupRows { case info(text: String) + case resetSecureBackupAction case createKeyBackupAction case restoreFromKeyBackupAction(keyBackupVersion: MXKeyBackupVersion, title: String) case deleteKeyBackupAction(keyBackupVersion: MXKeyBackupVersion) @@ -89,6 +95,8 @@ private enum BackupRows { let infoCell: MXKTableViewCellWithTextView = delegate.settingsSecureBackupTableViewSection(self, textCellForRow: row) infoCell.mxkTextView.text = infoText cell = infoCell + case .resetSecureBackupAction: + cell = self.buttonCellForResetSecureBackup(atRow: row) case .createKeyBackupAction: cell = self.buttonCellForCreateKeyBackup(atRow: row) case .restoreFromKeyBackupAction(keyBackupVersion: let keyBackupVersion, let title): @@ -168,6 +176,7 @@ private enum BackupRows { } backupViewStateRows.append(.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion)) + backupViewStateRows.append(.resetSecureBackupAction) backupRows = backupViewStateRows @@ -245,6 +254,26 @@ private enum BackupRows { } // MARK: - Button cells + + private func buttonCellForResetSecureBackup(atRow row: Int) -> UITableViewCell { + + guard let delegate = self.delegate else { + return UITableViewCell() + } + + let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row) + + let btnTitle = VectorL10n.securitySettingsSecureBackupReset + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) + cell.mxkButton.tintColor = ThemeService.shared().theme.warningColor + + cell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .resetSecureBackup) + } + + return cell + } private func buttonCellForCreateKeyBackup(atRow row: Int) -> UITableViewCell { @@ -300,6 +329,7 @@ private enum BackupRows { // MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate extension SettingsSecureBackupTableViewSection: SettingsSecureBackupViewModelViewDelegate { + func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateViewState viewState: SettingsSecureBackupViewState) { self.viewState = viewState @@ -317,6 +347,10 @@ extension SettingsSecureBackupTableViewSection: SettingsSecureBackupViewModelVie self.delegate?.settingsSecureBackupTableViewSection(self, showError: error) } } + + func settingsSecureBackupViewModelShowSecureBackupReset(_ viewModel: SettingsSecureBackupViewModelType) { + self.delegate?.settingsSecureBackupTableViewSectionShowSecureBackupReset(self) + } func settingsSecureBackupViewModelShowKeyBackupCreate(_ viewModel: SettingsSecureBackupViewModelType) { self.delegate?.settingsSecureBackupTableViewSectionShowKeyBackupCreate(self) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift index 8187c7fd16..1d05cb3933 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift @@ -18,6 +18,7 @@ import UIKit enum SettingsSecureBackupViewAction { case load + case resetSecureBackup case createKeyBackup case restoreFromKeyBackup(MXKeyBackupVersion) case confirmDeleteKeyBackup(MXKeyBackupVersion) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift index e4151663f8..75af9fc9d1 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift @@ -46,6 +46,8 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { case .load: viewDelegate.settingsSecureBackupViewModel(self, didUpdateViewState: .checkingBackup) self.checkKeyBackupState() + case .resetSecureBackup: + viewDelegate.settingsSecureBackupViewModelShowSecureBackupReset(self) case .createKeyBackup: viewDelegate.settingsSecureBackupViewModelShowKeyBackupCreate(self) case .restoreFromKeyBackup(let keyBackupVersion): diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift index cb3df7ae55..4f83b5cccb 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift @@ -19,6 +19,8 @@ import UIKit protocol SettingsSecureBackupViewModelViewDelegate: class { func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateViewState viewState: SettingsSecureBackupViewState) func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsSecureBackupNetworkRequestViewState) + + func settingsSecureBackupViewModelShowSecureBackupReset(_ viewModel: SettingsSecureBackupViewModelType) func settingsSecureBackupViewModelShowKeyBackupCreate(_ viewModel: SettingsSecureBackupViewModelType) func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index d1271a6e21..6972678cb8 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -1063,7 +1063,7 @@ - (void)setupSecureBackup - (void)setupSecureBackup2 { - SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession]; + SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession allowOverwrite:YES]; secureBackupSetupCoordinatorBridgePresenter.delegate = self; [secureBackupSetupCoordinatorBridgePresenter presentFrom:self animated:YES]; @@ -1720,6 +1720,11 @@ - (MXKTableViewCellWithButton *)settingsSecureBackupTableViewSection:(SettingsSe return cell; } +- (void)settingsSecureBackupTableViewSectionShowSecureBackupReset:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection +{ + [self setupSecureBackup]; +} + - (void)settingsSecureBackupTableViewSectionShowKeyBackupCreate:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection { [self showKeyBackupSetupFromSignOutFlow:NO]; diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 7381bfb2ac..a6dad221f6 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -4023,7 +4023,7 @@ - (void)showSecureBackupSetupFromSignOutFlow - (void)setupSecureBackup2 { - SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession]; + SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession allowOverwrite:YES]; secureBackupSetupCoordinatorBridgePresenter.delegate = self; [secureBackupSetupCoordinatorBridgePresenter presentFrom:self animated:YES]; From 82639fcca0feb23ced29733d94cfa51593302758 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 10:55:59 +0200 Subject: [PATCH 07/20] Security settings: Offer to reset the secure backup when there is no key backup --- .../SettingsSecureBackupTableViewSection.swift | 11 ++++++----- .../SettingsSecureBackupViewModel.swift | 8 ++++---- .../SettingsSecureBackupViewState.swift | 16 ++++++++-------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index ab5e697a3d..edf16ba97b 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -134,7 +134,7 @@ private enum BackupRows { .info(text: text) ] - case .noBackup: + case .noKeyBackup: let noBackup = VectorL10n.settingsKeyBackupInfoNone let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning @@ -144,11 +144,12 @@ private enum BackupRows { backupRows = [ .info(text: VectorL10n.securitySettingsSecureBackupDescription), .info(text: backupInfoText), - .createKeyBackupAction + .createKeyBackupAction, + .resetSecureBackupAction ] - case .backup(let keyBackupVersion, let keyBackupVersionTrust), - .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _): + case .keyBackup(let keyBackupVersion, let keyBackupVersionTrust), + .keyBackupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _): let info = VectorL10n.securitySettingsSecureBackupDescription let backupStatus = VectorL10n.securitySettingsSecureBackupInfoValid @@ -212,7 +213,7 @@ private enum BackupRows { // // backupRows = backupAndRunningViewStateRows - case .backupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + case .keyBackupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): // TODO: What? let info = VectorL10n.securitySettingsSecureBackupDescription diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift index 75af9fc9d1..1100641fca 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift @@ -90,19 +90,19 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { viewState = .checkingBackup case MXKeyBackupStateDisabled, MXKeyBackupStateEnabling: - viewState = .noBackup + viewState = .noKeyBackup case MXKeyBackupStateNotTrusted: guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { return } - viewState = .backupNotTrusted(keyBackupVersion, keyBackupVersionTrust) + viewState = .keyBackupNotTrusted(keyBackupVersion, keyBackupVersionTrust) case MXKeyBackupStateReadyToBackUp: guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { return } - viewState = .backup(keyBackupVersion, keyBackupVersionTrust) + viewState = .keyBackup(keyBackupVersion, keyBackupVersionTrust) case MXKeyBackupStateWillBackUp, MXKeyBackupStateBackingUp: guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { @@ -115,7 +115,7 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { return } - sself.viewDelegate?.settingsSecureBackupViewModel(sself, didUpdateViewState: .backupAndRunning(keyBackupVersion, keyBackupVersionTrust, progress)) + sself.viewDelegate?.settingsSecureBackupViewModel(sself, didUpdateViewState: .keyBackupAndRunning(keyBackupVersion, keyBackupVersionTrust, progress)) } default: break diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift index f76a6ca71e..8162faffc0 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift @@ -20,16 +20,16 @@ import UIKit /// /// - checkingBackup: Load current backup on the homeserver /// - checkError: Fail to load current backup data -/// - noBackup: There is no backup on the homeserver -/// - backup: There is a valid backup on the homeserver. All keys have been backed up to it -/// - backupAndRunning: There is a valid backup on the homeserver. Keys are being sent to it -/// - backupButNotVerified: There is a backup on the homeserver but it has not been verified yet +/// - noKeyBackup: There is no backup on the homeserver +/// - keyBackup: There is a valid backup on the homeserver. All keys have been backed up to it +/// - keyBackupAndRunning: There is a valid backup on the homeserver. Keys are being sent to it +/// - keyBackupNotTrusted: There is a backup on the homeserver but it is not trusted enum SettingsSecureBackupViewState { case checkingBackup - case noBackup - case backup(MXKeyBackupVersion, MXKeyBackupVersionTrust) - case backupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) - case backupNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) + case noKeyBackup + case keyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust) + case keyBackupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) + case keyBackupNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) } /// State representing a network request made by the module From 5d526fdb82bda78dcb69e629e9bd5235f94b9d2b Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 11:27:07 +0200 Subject: [PATCH 08/20] Security settings: Display section description at the bottom as before --- ...SettingsSecureBackupTableViewSection.swift | 122 ++++++------------ .../Security/SecurityViewController.m | 25 ++++ 2 files changed, 65 insertions(+), 82 deletions(-) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index edf16ba97b..0e0db707f1 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -21,6 +21,7 @@ import UIKit func settingsSecureBackupTableViewSectionDidUpdate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection) func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, textCellForRow: Int) -> MXKTableViewCellWithTextView + func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, descriptionCellForRow: Int) -> MXKTableViewCell func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, buttonCellForRow: Int) -> MXKTableViewCellWithButton // Secure backup @@ -38,6 +39,7 @@ import UIKit private enum BackupRows { case info(text: String) + case description(text: String) case resetSecureBackupAction case createKeyBackupAction case restoreFromKeyBackupAction(keyBackupVersion: MXKeyBackupVersion, title: String) @@ -83,18 +85,14 @@ private enum BackupRows { } @objc func cellForRow(atRow row: Int) -> UITableViewCell { - guard let delegate = self.delegate else { - return UITableViewCell() - } - let backupRow = self.backupRows[row] var cell: UITableViewCell switch backupRow { - case .info(let infoText): - let infoCell: MXKTableViewCellWithTextView = delegate.settingsSecureBackupTableViewSection(self, textCellForRow: row) - infoCell.mxkTextView.text = infoText - cell = infoCell + case .info(let text): + cell = self.textCell(atRow: row, text: text) + case .description(let text): + cell = self.descriptionCell(atRow: row, text: text) case .resetSecureBackupAction: cell = self.buttonCellForResetSecureBackup(atRow: row) case .createKeyBackupAction: @@ -124,95 +122,33 @@ private enum BackupRows { switch self.viewState { case .checkingBackup: - - let info = VectorL10n.securitySettingsSecureBackupDescription - let checking = VectorL10n.securitySettingsSecureBackupInfoChecking - let strings = [info, "", checking] - let text = strings.joined(separator: "\n") - backupRows = [ - .info(text: text) + .info(text: VectorL10n.securitySettingsSecureBackupInfoChecking), + .description(text: VectorL10n.securitySettingsSecureBackupDescription) ] case .noKeyBackup: - let noBackup = VectorL10n.settingsKeyBackupInfoNone let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning - let strings = [noBackup, "", signoutWarning] - let backupInfoText = strings.joined(separator: "\n") + let infoText = [noBackup, signoutWarning].joined(separator: "\n") backupRows = [ - .info(text: VectorL10n.securitySettingsSecureBackupDescription), - .info(text: backupInfoText), + .info(text: infoText), .createKeyBackupAction, - .resetSecureBackupAction + .resetSecureBackupAction, + .description(text: VectorL10n.securitySettingsSecureBackupDescription) ] case .keyBackup(let keyBackupVersion, let keyBackupVersionTrust), .keyBackupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _): - - let info = VectorL10n.securitySettingsSecureBackupDescription - let backupStatus = VectorL10n.securitySettingsSecureBackupInfoValid - let backupStrings = [info, "", backupStatus] - let backupInfoText = backupStrings.joined(separator: "\n") - -// let version = VectorL10n.settingsSecureBackupInfoVersion(keyBackupVersion.version ?? "") -// let algorithm = VectorL10n.settingsSecureBackupInfoAlgorithm(keyBackupVersion.algorithm) -// let uploadStatus = VectorL10n.settingsSecureBackupInfoProgressDone -// let additionalStrings = [version, algorithm, uploadStatus] -// let additionalInfoText = additionalStrings.joined(separator: "\n") -// -// let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust) -// let backupTrustInfoText = backupTrust.joined(separator: "\n") - - var backupViewStateRows: [BackupRows] = [ - .info(text: backupInfoText), -// .info(text: additionalInfoText), -// .info(text: backupTrustInfoText) + backupRows = [ + .info(text: VectorL10n.securitySettingsSecureBackupInfoValid), + .restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore), + .deleteKeyBackupAction(keyBackupVersion: keyBackupVersion), + .resetSecureBackupAction, + .description(text: VectorL10n.securitySettingsSecureBackupDescription) ] - // TODO: Do not display restore button if all keys are stored on the device - if true { - backupViewStateRows.append(.restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore)) - } - - backupViewStateRows.append(.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion)) - backupViewStateRows.append(.resetSecureBackupAction) - - backupRows = backupViewStateRows - -// case .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, let backupProgress): -// -// let info = VectorL10n.securitySettingsSecureBackupDescription -// let backupStatus = VectorL10n.securitySettingsSecureBackupInfoValid -// let backupStrings = [info, "", backupStatus] -// let backupInfoText = backupStrings.joined(separator: "\n") -// -// let remaining = backupProgress.totalUnitCount - backupProgress.completedUnitCount -// let version = VectorL10n.settingsSecureBackupInfoVersion(keyBackupVersion.version ?? "") -// let algorithm = VectorL10n.settingsSecureBackupInfoAlgorithm(keyBackupVersion.algorithm) -// let uploadStatus = VectorL10n.settingsSecureBackupInfoProgress(String(remaining)) -// let additionalStrings = [version, algorithm, uploadStatus] -// let additionalInfoText = additionalStrings.joined(separator: "\n") -// -// let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust) -// let backupTrustInfoText = backupTrust.joined(separator: "\n") -// -// var backupAndRunningViewStateRows: [BackupRows] = [ -// .info(text: backupInfoText), -// .info(text: additionalInfoText), -// .info(text: backupTrustInfoText) -// ] -// -// // TODO: Do not display restore button if all keys are stored on the device -// if true { -// backupAndRunningViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsSecureBackupButtonRestore)) -// } -// -// backupAndRunningViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion)) -// -// backupRows = backupAndRunningViewStateRows - case .keyBackupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): // TODO: What? @@ -254,6 +190,28 @@ private enum BackupRows { self.backupRows = backupRows } + // MARK: - Cells - + + private func textCell(atRow row: Int, text: String) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell() + } + + let cell = delegate.settingsSecureBackupTableViewSection(self, textCellForRow: row) + cell.mxkTextView.text = text + return cell + } + + private func descriptionCell(atRow row: Int, text: String) -> UITableViewCell { + guard let delegate = self.delegate else { + return UITableViewCell() + } + + let cell = delegate.settingsSecureBackupTableViewSection(self, descriptionCellForRow: row) + cell.textLabel?.text = text + return cell + } + // MARK: - Button cells private func buttonCellForResetSecureBackup(atRow row: Int) -> UITableViewCell { diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 6972678cb8..9f3ac8dc62 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -1231,6 +1231,17 @@ - (MXKTableViewCellWithTextView*)textViewCellForTableView:(UITableView*)tableVie return textViewCell; } +- (MXKTableViewCell*)descriptionCellForTableView:(UITableView*)tableView atIndexPath:(NSIndexPath *)indexPath +{ + MXKTableViewCell *cell = [self getDefaultTableViewCell:tableView]; + cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor; + cell.textLabel.numberOfLines = 0; + cell.contentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor; + cell.selectionStyle = UITableViewCellSelectionStyleNone; + + return cell; +} + - (MXKTableViewCellWithButton *)buttonCellForTableView:(UITableView*)tableView atIndexPath:(NSIndexPath *)indexPath { MXKTableViewCellWithButton *cell = [self.tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier] forIndexPath:indexPath]; @@ -1706,6 +1717,20 @@ - (MXKTableViewCellWithTextView *)settingsSecureBackupTableViewSection:(Settings return cell; } +- (MXKTableViewCellWithTextView *)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection descriptionCellForRow:(NSInteger)textCellForRow +{ + MXKTableViewCellWithTextView *cell; + + NSIndexPath *indexPath = [self.tableViewSections exactIndexPathForRowTag:textCellForRow sectionTag:SECTION_SECURE_BACKUP]; + + if (indexPath) + { + cell = [self descriptionCellForTableView:self.tableView atIndexPath:indexPath]; + } + + return cell; +} + - (MXKTableViewCellWithButton *)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection buttonCellForRow:(NSInteger)buttonCellForRow { MXKTableViewCellWithButton *cell; From ed7eb4edc68edcfcbd0e75634f6afa3386805268 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 11:50:50 +0200 Subject: [PATCH 09/20] Security settings: Manage the case when there is no secure backup --- ...SettingsSecureBackupTableViewSection.swift | 37 ++++++++++++++++++- .../SettingsSecureBackupViewAction.swift | 1 + .../SettingsSecureBackupViewModel.swift | 13 ++++++- .../SettingsSecureBackupViewState.swift | 1 + 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index 0e0db707f1..de8381960a 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -40,6 +40,7 @@ import UIKit private enum BackupRows { case info(text: String) case description(text: String) + case createSecureBackupAction case resetSecureBackupAction case createKeyBackupAction case restoreFromKeyBackupAction(keyBackupVersion: MXKeyBackupVersion, title: String) @@ -71,8 +72,8 @@ private enum BackupRows { // MARK: - Public - @objc init(withKeyBackup keyBackup: MXKeyBackup, userDevice: MXDeviceInfo) { - self.viewModel = SettingsSecureBackupViewModel(keyBackup: keyBackup) + @objc init(withRecoveryService recoveryService: MXRecoveryService, keyBackup: MXKeyBackup, userDevice: MXDeviceInfo) { + self.viewModel = SettingsSecureBackupViewModel(recoveryService: recoveryService, keyBackup: keyBackup) self.userDevice = userDevice super.init() self.viewModel.viewDelegate = self @@ -93,6 +94,8 @@ private enum BackupRows { cell = self.textCell(atRow: row, text: text) case .description(let text): cell = self.descriptionCell(atRow: row, text: text) + case .createSecureBackupAction: + cell = self.buttonCellForCreateSecureBackup(atRow: row) case .resetSecureBackupAction: cell = self.buttonCellForResetSecureBackup(atRow: row) case .createKeyBackupAction: @@ -127,6 +130,17 @@ private enum BackupRows { .description(text: VectorL10n.securitySettingsSecureBackupDescription) ] + case .noSecureBackup: + let noBackup = VectorL10n.settingsKeyBackupInfoNone + let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning + let infoText = [noBackup, signoutWarning].joined(separator: "\n") + + backupRows = [ + .info(text: infoText), + .createSecureBackupAction, + .description(text: VectorL10n.securitySettingsSecureBackupDescription) + ] + case .noKeyBackup: let noBackup = VectorL10n.settingsKeyBackupInfoNone let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning @@ -214,6 +228,25 @@ private enum BackupRows { // MARK: - Button cells + private func buttonCellForCreateSecureBackup(atRow row: Int) -> UITableViewCell { + + guard let delegate = self.delegate else { + return UITableViewCell() + } + + let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row) + + let btnTitle = VectorL10n.securitySettingsSecureBackupSetup + cell.mxkButton.setTitle(btnTitle, for: .normal) + cell.mxkButton.setTitle(btnTitle, for: .highlighted) + + cell.mxkButton.vc_addAction { + self.viewModel.process(viewAction: .createSecureBackup) + } + + return cell + } + private func buttonCellForResetSecureBackup(atRow row: Int) -> UITableViewCell { guard let delegate = self.delegate else { diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift index 1d05cb3933..2616d25cbc 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewAction.swift @@ -18,6 +18,7 @@ import UIKit enum SettingsSecureBackupViewAction { case load + case createSecureBackup case resetSecureBackup case createKeyBackup case restoreFromKeyBackup(MXKeyBackupVersion) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift index 1100641fca..594fd8e3c2 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift @@ -22,9 +22,11 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { weak var viewDelegate: SettingsSecureBackupViewModelViewDelegate? // MARK: Private + private let recoveryService: MXRecoveryService private let keyBackup: MXKeyBackup - init(keyBackup: MXKeyBackup) { + init(recoveryService: MXRecoveryService, keyBackup: MXKeyBackup) { + self.recoveryService = recoveryService self.keyBackup = keyBackup self.registerKeyBackupVersionDidChangeStateNotification() } @@ -46,7 +48,8 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { case .load: viewDelegate.settingsSecureBackupViewModel(self, didUpdateViewState: .checkingBackup) self.checkKeyBackupState() - case .resetSecureBackup: + case .resetSecureBackup, + .createSecureBackup: // The implement supports both viewDelegate.settingsSecureBackupViewModelShowSecureBackupReset(self) case .createKeyBackup: viewDelegate.settingsSecureBackupViewModelShowKeyBackupCreate(self) @@ -81,6 +84,12 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { } private func computeState(withBackupVersionTrust keyBackupVersionTrust: MXKeyBackupVersionTrust? = nil) { + + // We want to have a secure backup before having a key backup + if recoveryService.hasRecovery() == false { + self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateViewState: .noSecureBackup) + return + } var viewState: SettingsSecureBackupViewState? switch self.keyBackup.state { diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift index 8162faffc0..e8b1643388 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift @@ -26,6 +26,7 @@ import UIKit /// - keyBackupNotTrusted: There is a backup on the homeserver but it is not trusted enum SettingsSecureBackupViewState { case checkingBackup + case noSecureBackup case noKeyBackup case keyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust) case keyBackupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) From 13375a5913ce102786292dc906224046eb6a874a Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 11:59:41 +0200 Subject: [PATCH 10/20] Security settings: Use the same wording as element web for the secure backup description --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index e650a7bb5a..8d2018e465 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -621,7 +621,7 @@ Tap the + to start adding people."; "security_settings_crypto_sessions_description_2" = "If you don’t recognise a login, change your password and reset Secure Backup."; "security_settings_secure_backup" = "SECURE BACKUP"; -"security_settings_secure_backup_description" = "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server."; +"security_settings_secure_backup_description" = "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key."; "security_settings_secure_backup_info_checking" = "Checking…"; "security_settings_secure_backup_info_valid" = "This session is backing up your keys."; "security_settings_secure_backup_setup" = "Set up"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 0b65ad6a07..eae4760e04 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -3870,7 +3870,7 @@ internal enum VectorL10n { internal static var securitySettingsSecureBackupDelete: String { return VectorL10n.tr("Vector", "security_settings_secure_backup_delete") } - /// Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server. + /// Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key. internal static var securitySettingsSecureBackupDescription: String { return VectorL10n.tr("Vector", "security_settings_secure_backup_description") } From 115c65cae198f72b5e98348b7384c82e1708e42e Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 12:25:01 +0200 Subject: [PATCH 11/20] Security settings: Manage the case where the is a key backup but no secure backup --- ...SettingsSecureBackupTableViewSection.swift | 8 +++++++ .../SettingsSecureBackupViewModel.swift | 24 ++++++++++++------- .../SettingsSecureBackupViewState.swift | 1 + 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index de8381960a..3ae1694cb4 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -141,6 +141,14 @@ private enum BackupRows { .description(text: VectorL10n.securitySettingsSecureBackupDescription) ] + case .noSecureBackupButKeyBackup(let keyBackupVersion, let keyBackupVersionTrust): + backupRows = [ + .info(text: VectorL10n.securitySettingsSecureBackupInfoValid), + .restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore), + .deleteKeyBackupAction(keyBackupVersion: keyBackupVersion), + .description(text: VectorL10n.securitySettingsSecureBackupDescription) + ] + case .noKeyBackup: let noBackup = VectorL10n.settingsKeyBackupInfoNone let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift index 594fd8e3c2..48040ef1e2 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift @@ -85,12 +85,6 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { private func computeState(withBackupVersionTrust keyBackupVersionTrust: MXKeyBackupVersionTrust? = nil) { - // We want to have a secure backup before having a key backup - if recoveryService.hasRecovery() == false { - self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateViewState: .noSecureBackup) - return - } - var viewState: SettingsSecureBackupViewState? switch self.keyBackup.state { @@ -129,9 +123,23 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { default: break } + + // We want to have a secure backup before having a key backup + if recoveryService.hasRecovery() == false { + switch viewState { + case .checkingBackup: + break + case .keyBackup(let keyBackupVersion, let keyBackupVersionTrust), + .keyBackupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _), + .keyBackupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + viewState = .noSecureBackupButKeyBackup(keyBackupVersion, keyBackupVersionTrust) + default: + viewState = .noSecureBackup + } + } - if let vviewState = viewState { - self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateViewState: vviewState) + if let viewState = viewState { + self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateViewState: viewState) } } diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift index e8b1643388..4c4138981b 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift @@ -27,6 +27,7 @@ import UIKit enum SettingsSecureBackupViewState { case checkingBackup case noSecureBackup + case noSecureBackupButKeyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust) case noKeyBackup case keyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust) case keyBackupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) From e68c4653a37c3bdea76066777634c0f945614c78 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 13:38:23 +0200 Subject: [PATCH 12/20] Security settings: Improve the state of the secure backup section to be exhaustive --- ...SettingsSecureBackupTableViewSection.swift | 128 +++++++----------- .../SettingsSecureBackupViewModel.swift | 35 ++--- .../SettingsSecureBackupViewState.swift | 36 +++-- 3 files changed, 85 insertions(+), 114 deletions(-) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index 3ae1694cb4..48a0c39408 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -60,7 +60,7 @@ private enum BackupRows { private var viewModel: SettingsSecureBackupViewModelType! // Need to know the state to make `cellForRow` deliver cells accordingly - private var viewState: SettingsSecureBackupViewState = .checkingBackup { + private var viewState: SettingsSecureBackupViewState = .loading { didSet { self.updateBackupRows() } @@ -124,91 +124,59 @@ private enum BackupRows { let backupRows: [BackupRows] switch self.viewState { - case .checkingBackup: + case .loading: backupRows = [ .info(text: VectorL10n.securitySettingsSecureBackupInfoChecking), .description(text: VectorL10n.securitySettingsSecureBackupDescription) ] - case .noSecureBackup: - let noBackup = VectorL10n.settingsKeyBackupInfoNone - let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning - let infoText = [noBackup, signoutWarning].joined(separator: "\n") - - backupRows = [ - .info(text: infoText), - .createSecureBackupAction, - .description(text: VectorL10n.securitySettingsSecureBackupDescription) - ] - - case .noSecureBackupButKeyBackup(let keyBackupVersion, let keyBackupVersionTrust): - backupRows = [ - .info(text: VectorL10n.securitySettingsSecureBackupInfoValid), - .restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore), - .deleteKeyBackupAction(keyBackupVersion: keyBackupVersion), - .description(text: VectorL10n.securitySettingsSecureBackupDescription) - ] - - case .noKeyBackup: - let noBackup = VectorL10n.settingsKeyBackupInfoNone - let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning - let infoText = [noBackup, signoutWarning].joined(separator: "\n") - - backupRows = [ - .info(text: infoText), - .createKeyBackupAction, - .resetSecureBackupAction, - .description(text: VectorL10n.securitySettingsSecureBackupDescription) - ] - - case .keyBackup(let keyBackupVersion, let keyBackupVersionTrust), - .keyBackupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _): - backupRows = [ - .info(text: VectorL10n.securitySettingsSecureBackupInfoValid), - .restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore), - .deleteKeyBackupAction(keyBackupVersion: keyBackupVersion), - .resetSecureBackupAction, - .description(text: VectorL10n.securitySettingsSecureBackupDescription) - ] - - case .keyBackupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): - - // TODO: What? - let info = VectorL10n.securitySettingsSecureBackupDescription - backupRows = [ - .info(text: info) - ] - -// let info = VectorL10n.securitySettingsSecureBackupDescription -// let backupStatus = VectorL10n.settingsSecureBackupInfoNotValid -// let signoutWarning = VectorL10n.settingsSecureBackupInfoSignoutWarning -// let backupStrings = [info, "", backupStatus, "", signoutWarning] -// let backupInfoText = backupStrings.joined(separator: "\n") -// -// let version = VectorL10n.settingsSecureBackupInfoVersion(keyBackupVersion.version ?? "") -// let algorithm = VectorL10n.settingsSecureBackupInfoAlgorithm(keyBackupVersion.algorithm) -// let additionalStrings = [version, algorithm] -// let additionalInfoText = additionalStrings.joined(separator: "\n") -// -// let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust) -// let backupTrustInfoText = backupTrust.joined(separator: "\n") -// -// var backupNotTrustedViewStateRows: [BackupRows] = [ -// .info(text: backupInfoText), -// .info(text: additionalInfoText), -// .info(text: backupTrustInfoText) -// ] -// -// // TODO: Do not display restore button if all keys are stored on the device -// if true { -// backupNotTrustedViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsSecureBackupButtonConnect)) -// } -// -// backupNotTrustedViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion)) -// -// backupRows = backupNotTrustedViewStateRows + case .noSecureBackup(let keyBackupState): + switch keyBackupState { + case .noKeyBackup: + let noBackup = VectorL10n.settingsKeyBackupInfoNone + let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning + let infoText = [noBackup, signoutWarning].joined(separator: "\n") + + backupRows = [ + .info(text: infoText), + .createSecureBackupAction, + .description(text: VectorL10n.securitySettingsSecureBackupDescription) + ] + case .keyBackup(let keyBackupVersion, let keyBackupVersionTrust), + .keyBackupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _), + .keyBackupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + backupRows = [ + .info(text: VectorL10n.securitySettingsSecureBackupInfoValid), + .restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore), + .deleteKeyBackupAction(keyBackupVersion: keyBackupVersion), + .description(text: VectorL10n.securitySettingsSecureBackupDescription) + ] + } + case .secureBackup(let keyBackupState): + switch keyBackupState { + case .noKeyBackup: + let noBackup = VectorL10n.settingsKeyBackupInfoNone + let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning + let infoText = [noBackup, signoutWarning].joined(separator: "\n") + + backupRows = [ + .info(text: infoText), + .createKeyBackupAction, + .resetSecureBackupAction, + .description(text: VectorL10n.securitySettingsSecureBackupDescription) + ] + case .keyBackup(let keyBackupVersion, let keyBackupVersionTrust), + .keyBackupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _), + .keyBackupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + backupRows = [ + .info(text: VectorL10n.securitySettingsSecureBackupInfoValid), + .restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore), + .deleteKeyBackupAction(keyBackupVersion: keyBackupVersion), + .resetSecureBackupAction, + .description(text: VectorL10n.securitySettingsSecureBackupDescription) + ] + } } - self.backupRows = backupRows } diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift index 48040ef1e2..6f8fe37278 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift @@ -46,7 +46,7 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { switch viewAction { case .load: - viewDelegate.settingsSecureBackupViewModel(self, didUpdateViewState: .checkingBackup) + viewDelegate.settingsSecureBackupViewModel(self, didUpdateViewState: .loading) self.checkKeyBackupState() case .resetSecureBackup, .createSecureBackup: // The implement supports both @@ -86,26 +86,27 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { private func computeState(withBackupVersionTrust keyBackupVersionTrust: MXKeyBackupVersionTrust? = nil) { var viewState: SettingsSecureBackupViewState? + var keyBackupState: SettingsSecureBackupViewState.KeyBackupState? switch self.keyBackup.state { case MXKeyBackupStateUnknown, MXKeyBackupStateCheckingBackUpOnHomeserver: - viewState = .checkingBackup + viewState = .loading case MXKeyBackupStateDisabled, MXKeyBackupStateEnabling: - viewState = .noKeyBackup + keyBackupState = .noKeyBackup case MXKeyBackupStateNotTrusted: guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { return } - viewState = .keyBackupNotTrusted(keyBackupVersion, keyBackupVersionTrust) + keyBackupState = .keyBackupNotTrusted(keyBackupVersion, keyBackupVersionTrust) case MXKeyBackupStateReadyToBackUp: guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { return } - viewState = .keyBackup(keyBackupVersion, keyBackupVersionTrust) + keyBackupState = .keyBackup(keyBackupVersion, keyBackupVersionTrust) case MXKeyBackupStateWillBackUp, MXKeyBackupStateBackingUp: guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { @@ -114,28 +115,22 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { // Get the backup progress before updating the state self.keyBackup.backupProgress { [weak self] (progress) in - guard let sself = self else { + guard let self = self else { return } - - sself.viewDelegate?.settingsSecureBackupViewModel(sself, didUpdateViewState: .keyBackupAndRunning(keyBackupVersion, keyBackupVersionTrust, progress)) + + let keyBackupState: SettingsSecureBackupViewState.KeyBackupState = .keyBackupAndRunning(keyBackupVersion, keyBackupVersionTrust, progress) + let viewState: SettingsSecureBackupViewState = self.recoveryService.hasRecovery() ? .secureBackup(keyBackupState) : .noSecureBackup(keyBackupState) + + self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateViewState: viewState) } default: break } - // We want to have a secure backup before having a key backup - if recoveryService.hasRecovery() == false { - switch viewState { - case .checkingBackup: - break - case .keyBackup(let keyBackupVersion, let keyBackupVersionTrust), - .keyBackupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _), - .keyBackupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): - viewState = .noSecureBackupButKeyBackup(keyBackupVersion, keyBackupVersionTrust) - default: - viewState = .noSecureBackup - } + // Turn secure backup and key back states into view state + if viewState == nil, let keyBackupState = keyBackupState { + viewState = recoveryService.hasRecovery() ? .secureBackup(keyBackupState) : .noSecureBackup(keyBackupState) } if let viewState = viewState { diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift index 4c4138981b..872aaae813 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift @@ -16,22 +16,30 @@ import UIKit -/// SettingsSecureBackup view state +/// State of the Secure Backup section in securtiy settings. /// -/// - checkingBackup: Load current backup on the homeserver -/// - checkError: Fail to load current backup data -/// - noKeyBackup: There is no backup on the homeserver -/// - keyBackup: There is a valid backup on the homeserver. All keys have been backed up to it -/// - keyBackupAndRunning: There is a valid backup on the homeserver. Keys are being sent to it -/// - keyBackupNotTrusted: There is a backup on the homeserver but it is not trusted +/// It is a mixed of the state of the Secure Backup(4S) and the state of the Key Backup. +/// +/// - loading: Load current state +/// - noSecureBackup: The account has no secure backup +/// - secureBackup: The account has a secure backup enum SettingsSecureBackupViewState { - case checkingBackup - case noSecureBackup - case noSecureBackupButKeyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust) - case noKeyBackup - case keyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust) - case keyBackupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) - case keyBackupNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) + case loading + case noSecureBackup(KeyBackupState) + case secureBackup(KeyBackupState) + + /// Internal key backup state. It is independent from the secure backup state. + /// + /// - noKeyBackup: There is no backup on the homeserver + /// - keyBackup: There is a valid backup on the homeserver. All keys have been backed up to it + /// - keyBackupAndRunning: There is a valid backup on the homeserver. Keys are being sent to it + /// - keyBackupNotTrusted: There is a backup on the homeserver but it is not trusted + enum KeyBackupState { + case noKeyBackup + case keyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust) + case keyBackupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) + case keyBackupNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) + } } /// State representing a network request made by the module From a27dc11f5cd2e7f296edf3b56240606230afc76d Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 13:52:27 +0200 Subject: [PATCH 13/20] Security settings: Reduce the number of secure backup section states --- .../SettingsSecureBackupTableViewSection.swift | 10 ++++------ .../SettingsSecureBackupViewModel.swift | 13 +++---------- .../SettingsSecureBackupViewState.swift | 6 ++---- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index 48a0c39408..6e6139af62 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -142,9 +142,8 @@ private enum BackupRows { .createSecureBackupAction, .description(text: VectorL10n.securitySettingsSecureBackupDescription) ] - case .keyBackup(let keyBackupVersion, let keyBackupVersionTrust), - .keyBackupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _), - .keyBackupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + case .keyBackup(let keyBackupVersion, _, _), + .keyBackupNotTrusted(let keyBackupVersion, _): // Manage the key backup in the same way for the moment backupRows = [ .info(text: VectorL10n.securitySettingsSecureBackupInfoValid), .restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore), @@ -165,9 +164,8 @@ private enum BackupRows { .resetSecureBackupAction, .description(text: VectorL10n.securitySettingsSecureBackupDescription) ] - case .keyBackup(let keyBackupVersion, let keyBackupVersionTrust), - .keyBackupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, _), - .keyBackupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust): + case .keyBackup(let keyBackupVersion, _, _), + .keyBackupNotTrusted(let keyBackupVersion, _): // Manage the key backup in the same way for the moment backupRows = [ .info(text: VectorL10n.securitySettingsSecureBackupInfoValid), .restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore), diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift index 6f8fe37278..66b7208615 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModel.swift @@ -102,13 +102,7 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { } keyBackupState = .keyBackupNotTrusted(keyBackupVersion, keyBackupVersionTrust) - case MXKeyBackupStateReadyToBackUp: - guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { - return - } - keyBackupState = .keyBackup(keyBackupVersion, keyBackupVersionTrust) - - case MXKeyBackupStateWillBackUp, MXKeyBackupStateBackingUp: + case MXKeyBackupStateReadyToBackUp, MXKeyBackupStateWillBackUp, MXKeyBackupStateBackingUp: guard let keyBackupVersion = self.keyBackup.keyBackupVersion, let keyBackupVersionTrust = keyBackupVersionTrust else { return } @@ -119,9 +113,8 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { return } - let keyBackupState: SettingsSecureBackupViewState.KeyBackupState = .keyBackupAndRunning(keyBackupVersion, keyBackupVersionTrust, progress) + let keyBackupState: SettingsSecureBackupViewState.KeyBackupState = .keyBackup(keyBackupVersion, keyBackupVersionTrust, progress) let viewState: SettingsSecureBackupViewState = self.recoveryService.hasRecovery() ? .secureBackup(keyBackupState) : .noSecureBackup(keyBackupState) - self.viewDelegate?.settingsSecureBackupViewModel(self, didUpdateViewState: viewState) } default: @@ -129,7 +122,7 @@ final class SettingsSecureBackupViewModel: SettingsSecureBackupViewModelType { } // Turn secure backup and key back states into view state - if viewState == nil, let keyBackupState = keyBackupState { + if let keyBackupState = keyBackupState { viewState = recoveryService.hasRecovery() ? .secureBackup(keyBackupState) : .noSecureBackup(keyBackupState) } diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift index 872aaae813..1390f56ba7 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewState.swift @@ -31,13 +31,11 @@ enum SettingsSecureBackupViewState { /// Internal key backup state. It is independent from the secure backup state. /// /// - noKeyBackup: There is no backup on the homeserver - /// - keyBackup: There is a valid backup on the homeserver. All keys have been backed up to it - /// - keyBackupAndRunning: There is a valid backup on the homeserver. Keys are being sent to it + /// - keyBackup: There is a valid running backup on the homeserver. Keys are being sent to it /// - keyBackupNotTrusted: There is a backup on the homeserver but it is not trusted enum KeyBackupState { case noKeyBackup - case keyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust) - case keyBackupAndRunning(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) + case keyBackup(MXKeyBackupVersion, MXKeyBackupVersionTrust, Progress) case keyBackupNotTrusted(MXKeyBackupVersion, MXKeyBackupVersionTrust) } } From 83f6a01156df7f274d71dda5cb817d969995f073 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 14:40:29 +0200 Subject: [PATCH 14/20] Forgot to push this change. Thanks CI. --- Riot/Modules/Settings/Security/SecurityViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 9f3ac8dc62..69fcd5725c 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -193,7 +193,7 @@ - (void)viewDidLoad if (deviceInfo) { - secureBackupSection = [[SettingsSecureBackupTableViewSection alloc] initWithKeyBackup:self.mainSession.crypto.backup userDevice:deviceInfo]; + secureBackupSection = [[SettingsSecureBackupTableViewSection alloc] initWithRecoveryService:self.mainSession.crypto.recoveryService keyBackup:self.mainSession.crypto.backup userDevice:deviceInfo]; secureBackupSection.delegate = self; #ifdef CROSS_SIGNING_AND_BACKUP_DEV From 71ea0f82a9a9d82815148f7d57bf7a9a10b25c83 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 15:05:30 +0200 Subject: [PATCH 15/20] Fix build without compilation flag --- .../Security/SecurityViewController.m | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 69fcd5725c..b30026c239 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -126,10 +126,10 @@ @interface SecurityViewController () < UIViewController *pushedViewController; SettingsSecureBackupTableViewSection *secureBackupSection; - + KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter; + #ifdef CROSS_SIGNING_AND_BACKUP_DEV SettingsKeyBackupTableViewSection *keyBackupSection; - KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter; #endif KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter; @@ -264,11 +264,8 @@ - (void)destroy kThemeServiceDidChangeThemeNotificationObserver = nil; } -#ifdef CROSS_SIGNING_AND_BACKUP_DEV keyBackupSetupCoordinatorBridgePresenter = nil; -#endif keyBackupRecoverCoordinatorBridgePresenter = nil; - } - (void)viewWillAppear:(BOOL)animated @@ -1795,7 +1792,7 @@ - (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSecti MXStrongifyAndReturnIfNil(self); self->currentAlert = nil; - [self->keyBackupSection deleteWithKeyBackupVersion:keyBackupVersion]; + [self->secureBackupSection deleteKeyBackupWithKeyBackupVersion:keyBackupVersion]; }]]; [currentAlert mxk_setAccessibilityIdentifier: @"SettingsVCDeleteKeyBackup"]; @@ -1819,6 +1816,30 @@ - (void)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSecti [[AppDelegate theDelegate] showErrorAsAlert:error]; } +#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter + +- (void)showKeyBackupSetupFromSignOutFlow:(BOOL)showFromSignOutFlow +{ + keyBackupSetupCoordinatorBridgePresenter = [[KeyBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession]; + + [keyBackupSetupCoordinatorBridgePresenter presentFrom:self + isStartedFromSignOut:showFromSignOutFlow + animated:true]; + + keyBackupSetupCoordinatorBridgePresenter.delegate = self; +} + +- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupSetupCoordinatorBridgePresenter = nil; +} + +- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter { + [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true]; + keyBackupSetupCoordinatorBridgePresenter = nil; + + [secureBackupSection reload]; +} #pragma mark - SettingsKeyBackupTableViewSectionDelegate #ifdef CROSS_SIGNING_AND_BACKUP_DEV @@ -1924,31 +1945,6 @@ - (void)settingsKeyBackup:(SettingsKeyBackupTableViewSection *)settingsKeyBackup [[AppDelegate theDelegate] showErrorAsAlert:error]; } -#pragma mark - KeyBackupRecoverCoordinatorBridgePresenter - -- (void)showKeyBackupSetupFromSignOutFlow:(BOOL)showFromSignOutFlow -{ - keyBackupSetupCoordinatorBridgePresenter = [[KeyBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession]; - - [keyBackupSetupCoordinatorBridgePresenter presentFrom:self - isStartedFromSignOut:showFromSignOutFlow - animated:true]; - - keyBackupSetupCoordinatorBridgePresenter.delegate = self; -} - -- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidCancel:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter { - [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true]; - keyBackupSetupCoordinatorBridgePresenter = nil; -} - -- (void)keyBackupSetupCoordinatorBridgePresenterDelegateDidSetupRecoveryKey:(KeyBackupSetupCoordinatorBridgePresenter *)bridgePresenter { - [keyBackupSetupCoordinatorBridgePresenter dismissWithAnimated:true]; - keyBackupSetupCoordinatorBridgePresenter = nil; - - [keyBackupSection reload]; -} - #endif #pragma mark - KeyBackupRecoverCoordinatorBridgePresenter From 838558afb3f7d87eb302092a904a16594c55dbcf Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 15:31:39 +0200 Subject: [PATCH 16/20] Valide the 4S private key before using it --- ...cretsRecoveryWithPassphraseViewModel.swift | 30 ++++++++++++------- .../Security/SecurityViewController.m | 4 +-- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift index b5e37b0825..71e51e2700 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift @@ -111,17 +111,27 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras }) } - private func execute(block: (_ privateKey: Data, _ completion: @escaping (Result) -> Void) -> Void, privateKey: Data) { - // Run the extenal code while the view state is .loading - block(privateKey) { result in - MXLog.debug("[SecretsRecoveryWithPassphraseViewModel] execute: Block returned: \(result)") - - switch result { - case .success: - self.update(viewState: .loaded) - self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self) - case .failure(let error): + private func execute(block: @escaping (_ privateKey: Data, _ completion: @escaping (Result) -> Void) -> Void, privateKey: Data) { + // Check the private key is valid before using it + self.recoveryService.checkPrivateKey(privateKey) { match in + guard match else { + // Reuse already managed error + let error = NSError(domain: MXRecoveryServiceErrorDomain, code: Int(MXRecoveryServiceErrorCode.badRecoveryKeyErrorCode.rawValue), userInfo: nil) self.update(viewState: .error(error)) + return + } + + // Run the extenal code while the view state is .loading + block(privateKey) { result in + MXLog.debug("[SecretsRecoveryWithPassphraseViewModel] execute: Block returned: \(result)") + + switch result { + case .success: + self.update(viewState: .loaded) + self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self) + case .failure(let error): + self.update(viewState: .error(error)) + } } } } diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index b30026c239..afb4686ade 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -126,13 +126,13 @@ @interface SecurityViewController () < UIViewController *pushedViewController; SettingsSecureBackupTableViewSection *secureBackupSection; - KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter; #ifdef CROSS_SIGNING_AND_BACKUP_DEV SettingsKeyBackupTableViewSection *keyBackupSection; #endif + + KeyBackupSetupCoordinatorBridgePresenter *keyBackupSetupCoordinatorBridgePresenter; KeyBackupRecoverCoordinatorBridgePresenter *keyBackupRecoverCoordinatorBridgePresenter; - SecretsRecoveryCoordinatorBridgePresenter *secretsRecoveryCoordinatorBridgePresenter; } From 26ea4fc684c693e4d9b28d6290501f13efef1fb6 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 15:40:09 +0200 Subject: [PATCH 17/20] Wording: Replace Recovery Passphrase and Recovery Key by Security Phrase and Security Key #4268 --- CHANGES.rst | 1 + Riot/Assets/en.lproj/Vector.strings | 92 ++++++++++++++--------------- Riot/Generated/Strings.swift | 90 ++++++++++++++-------------- 3 files changed, 92 insertions(+), 91 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c94df8549c..08c02f258c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,7 @@ Changes to be released in next version * VoIP: Add dial pad for PSTN capable servers to menu on homescreen. * VoIP: Replace call bar with PiP tiles for every type of calls. * Security settings: Display the cross-signing section (#4430). + * Wording: Replace Recovery Passphrase and Recovery Key by Security Phrase and Security Key (#4268). 🐛 Bugfix * StartChatViewController: Add more helpful message when trying to start DM with a user that does not exist (#224). diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 8d2018e465..a52e2ff9f3 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -621,7 +621,7 @@ Tap the + to start adding people."; "security_settings_crypto_sessions_description_2" = "If you don’t recognise a login, change your password and reset Secure Backup."; "security_settings_secure_backup" = "SECURE BACKUP"; -"security_settings_secure_backup_description" = "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key."; +"security_settings_secure_backup_description" = "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key."; "security_settings_secure_backup_info_checking" = "Checking…"; "security_settings_secure_backup_info_valid" = "This session is backing up your keys."; "security_settings_secure_backup_setup" = "Set up"; @@ -901,7 +901,7 @@ Tap the + to start adding people."; // Key backup wrong version "e2e_key_backup_wrong_version_title" = "New Key Backup"; -"e2e_key_backup_wrong_version" = "A new secure message key backup has been detected.\n\nIf this wasn’t you, set a new passphrase in Settings."; +"e2e_key_backup_wrong_version" = "A new secure message key backup has been detected.\n\nIf this wasn’t you, set a new Security Phrase in Settings."; "e2e_key_backup_wrong_version_button_settings" = "Settings"; "e2e_key_backup_wrong_version_button_wasme" = "It was me"; @@ -1027,7 +1027,7 @@ Tap the + to start adding people."; "secure_key_backup_setup_intro_use_security_key_title" = "Use a Security Key"; "secure_key_backup_setup_intro_use_security_key_info" = "Generate a security key to store somewhere safe like a password manager or a safe."; -"secure_key_backup_setup_intro_use_security_passphrase_title" = "Use a Security Passphrase"; +"secure_key_backup_setup_intro_use_security_passphrase_title" = "Use a Security Phrase"; "secure_key_backup_setup_intro_use_security_passphrase_info" = "Enter a secret phrase only you know, and generate a key for backup."; "secure_key_backup_setup_existing_backup_error_title" = "A backup for messages already exists"; @@ -1066,32 +1066,32 @@ Tap the + to start adding people."; // Passphrase -"key_backup_setup_passphrase_title" = "Secure your backup with a Passphrase"; -"key_backup_setup_passphrase_info" = "We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.\n\nFor maximum security, this should be different from your account password."; +"key_backup_setup_passphrase_title" = "Secure your backup with a Security Phrase"; +"key_backup_setup_passphrase_info" = "We'll store an encrypted copy of your keys on our server. Protect your backup with a phrase to keep it secure.\n\nFor maximum security, this should be different from your account password."; "key_backup_setup_passphrase_passphrase_title" = "Enter"; -"key_backup_setup_passphrase_passphrase_placeholder" = "Enter passphrase"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Enter phrase"; "key_backup_setup_passphrase_passphrase_valid" = "Great!"; "key_backup_setup_passphrase_passphrase_invalid" = "Try adding a word"; "key_backup_setup_passphrase_confirm_passphrase_title" = "Confirm"; -"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Confirm passphrase"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Confirm phrase"; "key_backup_setup_passphrase_confirm_passphrase_valid" = "Great!"; -"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Passphrase doesn’t match"; -"key_backup_setup_passphrase_set_passphrase_action" = "Set Passphrase"; -"key_backup_setup_passphrase_setup_recovery_key_info" = "Or, secure your backup with a Recovery Key, saving it somewhere safe."; -"key_backup_setup_passphrase_setup_recovery_key_action" = "(Advanced) Set up with Recovery Key"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "phrase doesn’t match"; +"key_backup_setup_passphrase_set_passphrase_action" = "Set Phrase"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Or, secure your backup with a Security Key, saving it somewhere safe."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Advanced) Set up with Security Key"; // Success "key_backup_setup_success_title" = "Success!"; // Success from passphrase -"key_backup_setup_success_from_passphrase_info" = "Your keys are being backed up.\n\nYour recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your recovery key somewhere very secure, like a password manager (or a safe)."; -"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Save Recovery Key"; +"key_backup_setup_success_from_passphrase_info" = "Your keys are being backed up.\n\nYour Security Key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your Security Key somewhere very secure, like a password manager (or a safe)."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Save Security Key"; "key_backup_setup_success_from_passphrase_done_action" = "Done"; // Success from recovery key -"key_backup_setup_success_from_recovery_key_info" = "Your keys are being backed up.\n\nMake a copy of this recovery key and keep it safe."; -"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Recovery Key"; +"key_backup_setup_success_from_recovery_key_info" = "Your keys are being backed up.\n\nMake a copy of this Security Key and keep it safe."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Security Key"; "key_backup_setup_success_from_recovery_key_make_copy_action" = "Make a Copy"; "key_backup_setup_success_from_recovery_key_made_copy_action" = "I've made a copy"; @@ -1102,33 +1102,33 @@ Tap the + to start adding people."; "key_backup_recover_title" = "Secure Messages"; -"key_backup_recover_invalid_passphrase_title" = "Incorrect Recovery Passphrase"; -"key_backup_recover_invalid_passphrase" = "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase."; -"key_backup_recover_invalid_recovery_key_title" = "Recovery Key Mismatch"; -"key_backup_recover_invalid_recovery_key" = "Backup could not be decrypted with this key: please verify that you entered the correct recovery key."; +"key_backup_recover_invalid_passphrase_title" = "Incorrect Security Phrase"; +"key_backup_recover_invalid_passphrase" = "Backup could not be decrypted with this phrase: please verify that you entered the correct Security Phrase."; +"key_backup_recover_invalid_recovery_key_title" = "Security Key Mismatch"; +"key_backup_recover_invalid_recovery_key" = "Backup could not be decrypted with this key: please verify that you entered the correct Security Key."; // Recover from private key "key_backup_recover_from_private_key_info" = "Restoring backup…"; // Recover from passphrase -"key_backup_recover_from_passphrase_info" = "Use your recovery passphrase to unlock your secure message history"; +"key_backup_recover_from_passphrase_info" = "Use your Security Phrase to unlock your secure message history"; "key_backup_recover_from_passphrase_passphrase_title" = "Enter"; -"key_backup_recover_from_passphrase_passphrase_placeholder" = "Enter Passphrase"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Enter Phrase"; "key_backup_recover_from_passphrase_recover_action" = "Unlock History"; -"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Don’t know your recovery passphrase? You can "; -"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "use your recovery key"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Don’t know your Security Phrase? You can "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "use your Security Key"; "key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; // Recover from recovery key -"key_backup_recover_from_recovery_key_info" = "Use your recovery key to unlock your secure message history"; +"key_backup_recover_from_recovery_key_info" = "Use your Security Key to unlock your secure message history"; "key_backup_recover_from_recovery_key_recovery_key_title" = "Enter"; -"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Enter Recovery Key"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Enter Security Key"; "key_backup_recover_from_recovery_key_recover_action" = "Unlock History"; -"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Lost your recovery key? You can set up a new one in settings."; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Lost your Security Key You can set up a new one in settings."; // Success @@ -1205,8 +1205,8 @@ Tap the + to start adding people."; "device_verification_self_verify_wait_new_sign_in_title" = "Verify this login"; "device_verification_self_verify_wait_information" = "Verify this session from one of your other sessions, granting it access to encrypted messages.\n\nUse the latest Element on your other devices:"; "device_verification_self_verify_wait_additional_information" = "This works with Element and other cross-signing capable Matrix clients."; -"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Use Recovery Key"; -"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Use Recovery Passphrase or Key"; +"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Use Security Key"; +"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Use Security Phrase or Key"; "device_verification_self_verify_wait_recover_secrets_additional_information" = "If you can't access an existing session"; "device_verification_self_verify_wait_recover_secrets_checking_availability" = "Checking for other verification capabilities ..."; @@ -1440,40 +1440,40 @@ Tap the + to start adding people."; // Recover with passphrase -"secrets_recovery_with_passphrase_title" = "Recovery Passphrase"; -"secrets_recovery_with_passphrase_information_default" = "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase."; -"secrets_recovery_with_passphrase_information_verify_device" = "Use your Recovery Passphrase to verify this device."; +"secrets_recovery_with_passphrase_title" = "Security Phrase"; +"secrets_recovery_with_passphrase_information_default" = "Access your secure message history and your cross-signing identity for verifying other sessions by entering your Security Phrase."; +"secrets_recovery_with_passphrase_information_verify_device" = "Use your Security Phrase to verify this device."; "secrets_recovery_with_passphrase_passphrase_title" = "Enter"; -"secrets_recovery_with_passphrase_passphrase_placeholder" = "Enter Recovery Passphrase"; -"secrets_recovery_with_passphrase_recover_action" = "Use Passphrase"; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "Enter Security Phrase"; +"secrets_recovery_with_passphrase_recover_action" = "Use Phrase"; -"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Don’t know your recovery passphrase? You can "; -"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "use your recovery key"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Don’t know your Security Phrase? You can "; +"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "use your Security Key"; "secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; "secrets_recovery_with_passphrase_invalid_passphrase_title" = "Unable to access secret storage"; -"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Please verify that you entered the correct recovery passphrase."; +"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Please verify that you entered the correct Security Phrase."; // Recover with key -"secrets_recovery_with_key_title" = "Recovery Key"; -"secrets_recovery_with_key_information_default" = "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key."; -"secrets_recovery_with_key_information_verify_device" = "Use your Recovery Key to verify this device."; -"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Enter your Recovery Passphrase to continue."; -"secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Enter your Recovery Key to continue."; +"secrets_recovery_with_key_title" = "Security Key"; +"secrets_recovery_with_key_information_default" = "Access your secure message history and your cross-signing identity for verifying other sessions by entering your Security Key."; +"secrets_recovery_with_key_information_verify_device" = "Use your Security Key to verify this device."; +"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Enter your Security Phrase to continue."; +"secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Enter your Security Key to continue."; "secrets_recovery_with_key_recovery_key_title" = "Enter"; -"secrets_recovery_with_key_recovery_key_placeholder" = "Enter Recovery Key"; +"secrets_recovery_with_key_recovery_key_placeholder" = "Enter Security Key"; "secrets_recovery_with_key_recover_action" = "Use Key"; "secrets_recovery_with_key_invalid_recovery_key_title" = "Unable to access secret storage"; -"secrets_recovery_with_key_invalid_recovery_key_message" = "Please verify that you entered the correct recovery key."; +"secrets_recovery_with_key_invalid_recovery_key_message" = "Please verify that you entered the correct Security Key."; // MARK: - Secrets set up // Recovery Key "secrets_setup_recovery_key_title" = "Save your Security Key"; -"secrets_setup_recovery_key_information" = "Store your Recovery Key somewhere safe. It can be used to unlock your encrypted messages & data."; +"secrets_setup_recovery_key_information" = "Store your Security Key somewhere safe. It can be used to unlock your encrypted messages & data."; "secrets_setup_recovery_key_loading" = "Loading…"; "secrets_setup_recovery_key_export_action" = "Save"; "secrets_setup_recovery_key_done_action" = "Done"; @@ -1490,10 +1490,10 @@ Tap the + to start adding people."; "secrets_setup_recovery_passphrase_confirm_information" = "Enter your Security Phrase again to confirm it."; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Confirm"; -"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Confirm passphrase"; +"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Confirm phrase"; "key_backup_setup_passphrase_confirm_passphrase_valid" = "Great!"; -"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Passphrase doesn’t match"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Phrase doesn’t match"; "secrets_setup_recovery_passphrase_summary_title" = "Save your Security Phrase"; "secrets_setup_recovery_passphrase_summary_information" = "Remember your Security Phrase. It can be used to unlock your encrypted messages & data."; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index eae4760e04..6731c2f1df 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1034,11 +1034,11 @@ internal enum VectorL10n { internal static var deviceVerificationSelfVerifyWaitRecoverSecretsCheckingAvailability: String { return VectorL10n.tr("Vector", "device_verification_self_verify_wait_recover_secrets_checking_availability") } - /// Use Recovery Passphrase or Key + /// Use Security Phrase or Key internal static var deviceVerificationSelfVerifyWaitRecoverSecretsWithPassphrase: String { return VectorL10n.tr("Vector", "device_verification_self_verify_wait_recover_secrets_with_passphrase") } - /// Use Recovery Key + /// Use Security Key internal static var deviceVerificationSelfVerifyWaitRecoverSecretsWithoutPassphrase: String { return VectorL10n.tr("Vector", "device_verification_self_verify_wait_recover_secrets_without_passphrase") } @@ -1142,7 +1142,7 @@ internal enum VectorL10n { internal static var e2eEnablingOnAppUpdate: String { return VectorL10n.tr("Vector", "e2e_enabling_on_app_update") } - /// A new secure message key backup has been detected.\n\nIf this wasn’t you, set a new passphrase in Settings. + /// A new secure message key backup has been detected.\n\nIf this wasn’t you, set a new Security Phrase in Settings. internal static var e2eKeyBackupWrongVersion: String { return VectorL10n.tr("Vector", "e2e_key_backup_wrong_version") } @@ -1614,15 +1614,15 @@ internal enum VectorL10n { internal static var keyBackupRecoverDoneAction: String { return VectorL10n.tr("Vector", "key_backup_recover_done_action") } - /// Use your recovery passphrase to unlock your secure message history + /// Use your Security Phrase to unlock your secure message history internal static var keyBackupRecoverFromPassphraseInfo: String { return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_info") } - /// Don’t know your recovery passphrase? You can + /// Don’t know your Security Phrase? You can internal static var keyBackupRecoverFromPassphraseLostPassphraseActionPart1: String { return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_lost_passphrase_action_part1") } - /// use your recovery key + /// use your Security Key internal static var keyBackupRecoverFromPassphraseLostPassphraseActionPart2: String { return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_lost_passphrase_action_part2") } @@ -1630,7 +1630,7 @@ internal enum VectorL10n { internal static var keyBackupRecoverFromPassphraseLostPassphraseActionPart3: String { return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_lost_passphrase_action_part3") } - /// Enter Passphrase + /// Enter Phrase internal static var keyBackupRecoverFromPassphrasePassphrasePlaceholder: String { return VectorL10n.tr("Vector", "key_backup_recover_from_passphrase_passphrase_placeholder") } @@ -1646,11 +1646,11 @@ internal enum VectorL10n { internal static var keyBackupRecoverFromPrivateKeyInfo: String { return VectorL10n.tr("Vector", "key_backup_recover_from_private_key_info") } - /// Use your recovery key to unlock your secure message history + /// Use your Security Key to unlock your secure message history internal static var keyBackupRecoverFromRecoveryKeyInfo: String { return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_info") } - /// Lost your recovery key? You can set up a new one in settings. + /// Lost your Security Key You can set up a new one in settings. internal static var keyBackupRecoverFromRecoveryKeyLostRecoveryKeyAction: String { return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_lost_recovery_key_action") } @@ -1658,7 +1658,7 @@ internal enum VectorL10n { internal static var keyBackupRecoverFromRecoveryKeyRecoverAction: String { return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_recover_action") } - /// Enter Recovery Key + /// Enter Security Key internal static var keyBackupRecoverFromRecoveryKeyRecoveryKeyPlaceholder: String { return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_recovery_key_placeholder") } @@ -1666,19 +1666,19 @@ internal enum VectorL10n { internal static var keyBackupRecoverFromRecoveryKeyRecoveryKeyTitle: String { return VectorL10n.tr("Vector", "key_backup_recover_from_recovery_key_recovery_key_title") } - /// Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase. + /// Backup could not be decrypted with this phrase: please verify that you entered the correct Security Phrase. internal static var keyBackupRecoverInvalidPassphrase: String { return VectorL10n.tr("Vector", "key_backup_recover_invalid_passphrase") } - /// Incorrect Recovery Passphrase + /// Incorrect Security Phrase internal static var keyBackupRecoverInvalidPassphraseTitle: String { return VectorL10n.tr("Vector", "key_backup_recover_invalid_passphrase_title") } - /// Backup could not be decrypted with this key: please verify that you entered the correct recovery key. + /// Backup could not be decrypted with this key: please verify that you entered the correct Security Key. internal static var keyBackupRecoverInvalidRecoveryKey: String { return VectorL10n.tr("Vector", "key_backup_recover_invalid_recovery_key") } - /// Recovery Key Mismatch + /// Security Key Mismatch internal static var keyBackupRecoverInvalidRecoveryKeyTitle: String { return VectorL10n.tr("Vector", "key_backup_recover_invalid_recovery_key_title") } @@ -1714,11 +1714,11 @@ internal enum VectorL10n { internal static var keyBackupSetupIntroTitle: String { return VectorL10n.tr("Vector", "key_backup_setup_intro_title") } - /// Passphrase doesn’t match + /// Phrase doesn’t match internal static var keyBackupSetupPassphraseConfirmPassphraseInvalid: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_invalid") } - /// Confirm passphrase + /// Confirm phrase internal static var keyBackupSetupPassphraseConfirmPassphrasePlaceholder: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_placeholder") } @@ -1730,7 +1730,7 @@ internal enum VectorL10n { internal static var keyBackupSetupPassphraseConfirmPassphraseValid: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_valid") } - /// We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.\n\nFor maximum security, this should be different from your account password. + /// We'll store an encrypted copy of your keys on our server. Protect your backup with a phrase to keep it secure.\n\nFor maximum security, this should be different from your account password. internal static var keyBackupSetupPassphraseInfo: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_info") } @@ -1738,7 +1738,7 @@ internal enum VectorL10n { internal static var keyBackupSetupPassphrasePassphraseInvalid: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_invalid") } - /// Enter passphrase + /// Enter phrase internal static var keyBackupSetupPassphrasePassphrasePlaceholder: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_placeholder") } @@ -1750,19 +1750,19 @@ internal enum VectorL10n { internal static var keyBackupSetupPassphrasePassphraseValid: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_passphrase_valid") } - /// Set Passphrase + /// Set Phrase internal static var keyBackupSetupPassphraseSetPassphraseAction: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_set_passphrase_action") } - /// (Advanced) Set up with Recovery Key + /// (Advanced) Set up with Security Key internal static var keyBackupSetupPassphraseSetupRecoveryKeyAction: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_setup_recovery_key_action") } - /// Or, secure your backup with a Recovery Key, saving it somewhere safe. + /// Or, secure your backup with a Security Key, saving it somewhere safe. internal static var keyBackupSetupPassphraseSetupRecoveryKeyInfo: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_setup_recovery_key_info") } - /// Secure your backup with a Passphrase + /// Secure your backup with a Security Phrase internal static var keyBackupSetupPassphraseTitle: String { return VectorL10n.tr("Vector", "key_backup_setup_passphrase_title") } @@ -1782,15 +1782,15 @@ internal enum VectorL10n { internal static var keyBackupSetupSuccessFromPassphraseDoneAction: String { return VectorL10n.tr("Vector", "key_backup_setup_success_from_passphrase_done_action") } - /// Your keys are being backed up.\n\nYour recovery key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your recovery key somewhere very secure, like a password manager (or a safe). + /// Your keys are being backed up.\n\nYour Security Key is a safety net - you can use it to restore access to your encrypted messages if you forget your passphrase.\n\nKeep your Security Key somewhere very secure, like a password manager (or a safe). internal static var keyBackupSetupSuccessFromPassphraseInfo: String { return VectorL10n.tr("Vector", "key_backup_setup_success_from_passphrase_info") } - /// Save Recovery Key + /// Save Security Key internal static var keyBackupSetupSuccessFromPassphraseSaveRecoveryKeyAction: String { return VectorL10n.tr("Vector", "key_backup_setup_success_from_passphrase_save_recovery_key_action") } - /// Your keys are being backed up.\n\nMake a copy of this recovery key and keep it safe. + /// Your keys are being backed up.\n\nMake a copy of this Security Key and keep it safe. internal static var keyBackupSetupSuccessFromRecoveryKeyInfo: String { return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_info") } @@ -1802,7 +1802,7 @@ internal enum VectorL10n { internal static var keyBackupSetupSuccessFromRecoveryKeyMakeCopyAction: String { return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_make_copy_action") } - /// Recovery Key + /// Security Key internal static var keyBackupSetupSuccessFromRecoveryKeyRecoveryKeyTitle: String { return VectorL10n.tr("Vector", "key_backup_setup_success_from_recovery_key_recovery_key_title") } @@ -3554,23 +3554,23 @@ internal enum VectorL10n { internal static var secretsRecoveryResetActionPart2: String { return VectorL10n.tr("Vector", "secrets_recovery_reset_action_part_2") } - /// Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery key. + /// Access your secure message history and your cross-signing identity for verifying other sessions by entering your Security Key. internal static var secretsRecoveryWithKeyInformationDefault: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_default") } - /// Enter your Recovery Key to continue. + /// Enter your Security Key to continue. internal static var secretsRecoveryWithKeyInformationUnlockSecureBackupWithKey: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_unlock_secure_backup_with_key") } - /// Enter your Recovery Passphrase to continue. + /// Enter your Security Phrase to continue. internal static var secretsRecoveryWithKeyInformationUnlockSecureBackupWithPhrase: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_unlock_secure_backup_with_phrase") } - /// Use your Recovery Key to verify this device. + /// Use your Security Key to verify this device. internal static var secretsRecoveryWithKeyInformationVerifyDevice: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_information_verify_device") } - /// Please verify that you entered the correct recovery key. + /// Please verify that you entered the correct Security Key. internal static var secretsRecoveryWithKeyInvalidRecoveryKeyMessage: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_invalid_recovery_key_message") } @@ -3582,7 +3582,7 @@ internal enum VectorL10n { internal static var secretsRecoveryWithKeyRecoverAction: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_recover_action") } - /// Enter Recovery Key + /// Enter Security Key internal static var secretsRecoveryWithKeyRecoveryKeyPlaceholder: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_recovery_key_placeholder") } @@ -3590,19 +3590,19 @@ internal enum VectorL10n { internal static var secretsRecoveryWithKeyRecoveryKeyTitle: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_recovery_key_title") } - /// Recovery Key + /// Security Key internal static var secretsRecoveryWithKeyTitle: String { return VectorL10n.tr("Vector", "secrets_recovery_with_key_title") } - /// Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase. + /// Access your secure message history and your cross-signing identity for verifying other sessions by entering your Security Phrase. internal static var secretsRecoveryWithPassphraseInformationDefault: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_information_default") } - /// Use your Recovery Passphrase to verify this device. + /// Use your Security Phrase to verify this device. internal static var secretsRecoveryWithPassphraseInformationVerifyDevice: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_information_verify_device") } - /// Please verify that you entered the correct recovery passphrase. + /// Please verify that you entered the correct Security Phrase. internal static var secretsRecoveryWithPassphraseInvalidPassphraseMessage: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_invalid_passphrase_message") } @@ -3610,11 +3610,11 @@ internal enum VectorL10n { internal static var secretsRecoveryWithPassphraseInvalidPassphraseTitle: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_invalid_passphrase_title") } - /// Don’t know your recovery passphrase? You can + /// Don’t know your Security Phrase? You can internal static var secretsRecoveryWithPassphraseLostPassphraseActionPart1: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_lost_passphrase_action_part1") } - /// use your recovery key + /// use your Security Key internal static var secretsRecoveryWithPassphraseLostPassphraseActionPart2: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_lost_passphrase_action_part2") } @@ -3622,7 +3622,7 @@ internal enum VectorL10n { internal static var secretsRecoveryWithPassphraseLostPassphraseActionPart3: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_lost_passphrase_action_part3") } - /// Enter Recovery Passphrase + /// Enter Security Phrase internal static var secretsRecoveryWithPassphrasePassphrasePlaceholder: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_passphrase_placeholder") } @@ -3630,11 +3630,11 @@ internal enum VectorL10n { internal static var secretsRecoveryWithPassphrasePassphraseTitle: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_passphrase_title") } - /// Use Passphrase + /// Use Phrase internal static var secretsRecoveryWithPassphraseRecoverAction: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_recover_action") } - /// Recovery Passphrase + /// Security Phrase internal static var secretsRecoveryWithPassphraseTitle: String { return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_title") } @@ -3670,7 +3670,7 @@ internal enum VectorL10n { internal static var secretsSetupRecoveryKeyExportAction: String { return VectorL10n.tr("Vector", "secrets_setup_recovery_key_export_action") } - /// Store your Recovery Key somewhere safe. It can be used to unlock your encrypted messages & data. + /// Store your Security Key somewhere safe. It can be used to unlock your encrypted messages & data. internal static var secretsSetupRecoveryKeyInformation: String { return VectorL10n.tr("Vector", "secrets_setup_recovery_key_information") } @@ -3698,7 +3698,7 @@ internal enum VectorL10n { internal static var secretsSetupRecoveryPassphraseConfirmInformation: String { return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_confirm_information") } - /// Confirm passphrase + /// Confirm phrase internal static var secretsSetupRecoveryPassphraseConfirmPassphrasePlaceholder: String { return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_confirm_passphrase_placeholder") } @@ -3778,7 +3778,7 @@ internal enum VectorL10n { internal static var secureKeyBackupSetupIntroUseSecurityPassphraseInfo: String { return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_use_security_passphrase_info") } - /// Use a Security Passphrase + /// Use a Security Phrase internal static var secureKeyBackupSetupIntroUseSecurityPassphraseTitle: String { return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_use_security_passphrase_title") } @@ -3870,7 +3870,7 @@ internal enum VectorL10n { internal static var securitySettingsSecureBackupDelete: String { return VectorL10n.tr("Vector", "security_settings_secure_backup_delete") } - /// Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Recovery Key. + /// Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key. internal static var securitySettingsSecureBackupDescription: String { return VectorL10n.tr("Vector", "security_settings_secure_backup_description") } From 5e360b9781ba74be56946e6d78d62cf2325a219a Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 15:43:07 +0200 Subject: [PATCH 18/20] CHANGES --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 08c02f258c..a8db9759fb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,6 +11,7 @@ Changes to be released in next version * VoIP: Add dial pad for PSTN capable servers to menu on homescreen. * VoIP: Replace call bar with PiP tiles for every type of calls. * Security settings: Display the cross-signing section (#4430). + * Security settings: The Secure backup section has been updated to match element-web UX (#4430). * Wording: Replace Recovery Passphrase and Recovery Key by Security Phrase and Security Key (#4268). 🐛 Bugfix From eb06b6fa2f115db10f0bdab1eef61edd9c53c0cb Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 16:11:32 +0200 Subject: [PATCH 19/20] Cleaning --- .../Setup/KeyBackupSetupCoordinator.swift | 1 - ...SettingsSecureBackupTableViewSection.swift | 4 + .../Security/SecurityViewController.m | 100 +----------------- 3 files changed, 5 insertions(+), 100 deletions(-) diff --git a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift index b1b96be832..3fce9171db 100644 --- a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift +++ b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinator.swift @@ -124,7 +124,6 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType { } private func showSetupWithSecureBackupSuccess(animated: Bool) { - let viewController = KeyBackupSetupSuccessFromSecureBackupViewController.instantiate() viewController.delegate = self self.navigationRouter.push(viewController, animated: animated, popCompletion: nil) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index 6e6139af62..6ef387a202 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -47,6 +47,10 @@ private enum BackupRows { case deleteKeyBackupAction(keyBackupVersion: MXKeyBackupVersion) } +/// SettingsSecureBackupTableViewSection provides UITableViewCells to manage secure backup and key backup. +/// +/// All states are described in SettingsSecureBackupViewState. +/// All actions in SettingsSecureBackupViewAction. @objc final class SettingsSecureBackupTableViewSection: NSObject { // MARK: - Properties diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index afb4686ade..ced10d2529 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -51,23 +51,6 @@ CROSSSIGNING_SECOND_ACTION, // Reset }; -enum { - SECURE_BACKUP_DESCRIPTION, - // TODO: We can display the state of 4S both locally and on the server. Then, provide actions according to all combinations. - // - Does the 4S contains all the 4 keys server side? - // - Advice the user to do a recovery if there is less keys locally - // - Advice them to do a recovery if local keys are obsolete -> We cannot know now - // - Advice them to fix a secure backup if there is 4S but no key backup - // - Warm them if there is no 4S and they do not have all 3 signing keys locally. They will set up a not complete secure backup -#ifdef CROSS_SIGNING_AND_BACKUP_DEV - SECURE_BACKUP_INFO, -#endif - SECURE_BACKUP_SETUP, - SECURE_BACKUP_RESTORE, - SECURE_BACKUP_DELETE, - SECURE_BACKUP_MANAGE_MANUALLY, // TODO: What to do with that? -}; - enum { PIN_CODE_SETTING, PIN_CODE_DESCRIPTION, @@ -91,9 +74,9 @@ @interface SecurityViewController () < SettingsSecureBackupTableViewSectionDelegate, +KeyBackupSetupCoordinatorBridgePresenterDelegate, #ifdef CROSS_SIGNING_AND_BACKUP_DEV SettingsKeyBackupTableViewSectionDelegate, -KeyBackupSetupCoordinatorBridgePresenterDelegate, KeyBackupRecoverCoordinatorBridgePresenterDelegate, #endif UIDocumentInteractionControllerDelegate, @@ -108,9 +91,6 @@ @interface SecurityViewController () < // Devices NSMutableArray *devicesArray; - // SECURE_BACKUP_* rows to display - NSArray *secureBackupSectionState; - // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. id kThemeServiceDidChangeThemeNotificationObserver; @@ -582,8 +562,6 @@ - (void)loadDevices - (void)reloadData { - [self refreshSecureBackupSectionData]; - // Update table view sections and trigger a tableView reloadData [self updateSections]; } @@ -874,56 +852,6 @@ - (void)displayComingSoon #pragma mark - SSSS -- (void)refreshSecureBackupSectionData -{ - MXRecoveryService *recoveryService = self.mainSession.crypto.recoveryService; - NSMutableArray *secureBackupSectionState = [NSMutableArray new]; - if (recoveryService.hasRecovery) - { - if (RiotSettings.shared.settingsSecurityScreenShowRestoreBackup) - { - [secureBackupSectionState addObject:@(SECURE_BACKUP_RESTORE)]; - } - if (RiotSettings.shared.settingsSecurityScreenShowDeleteBackup) - { - [secureBackupSectionState addObject:@(SECURE_BACKUP_DELETE)]; - } - } - else - { - if (RiotSettings.shared.settingsSecurityScreenShowSetupBackup) - { - [secureBackupSectionState addObject:@(SECURE_BACKUP_SETUP)]; - } - } - - if (secureBackupSectionState.count) - { - [secureBackupSectionState addObject:@(SECURE_BACKUP_DESCRIPTION)]; - } - -#ifdef CROSS_SIGNING_AND_BACKUP_DEV - [secureBackupSectionState addObject:@(SECURE_BACKUP_INFO)]; -#endif - - self->secureBackupSectionState = secureBackupSectionState; -} - -- (NSUInteger)secureBackupSectionEnumForRow:(NSUInteger)row -{ - if (row < secureBackupSectionState.count) - { - return secureBackupSectionState[row].unsignedIntegerValue; - } - - return SECURE_BACKUP_DESCRIPTION; -} - -- (NSUInteger)numberOfRowsInSecureBackupSection -{ - return secureBackupSectionState.count; -} - - (NSString*)secureBackupInformation { NSString *secureBackupInformation; @@ -1068,32 +996,6 @@ - (void)setupSecureBackup2 self.secureBackupSetupCoordinatorBridgePresenter = secureBackupSetupCoordinatorBridgePresenter; } -- (void)restoreFromSecureBackup -{ - secretsRecoveryCoordinatorBridgePresenter = [[SecretsRecoveryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession recoveryGoal:SecretsRecoveryGoalBridgeRestoreSecureBackup]; - - [secretsRecoveryCoordinatorBridgePresenter presentFrom:self animated:true]; - secretsRecoveryCoordinatorBridgePresenter.delegate = self; -} - -- (void)deleteSecureBackup -{ - MXRecoveryService *recoveryService = self.mainSession.crypto.recoveryService; - if (recoveryService) - { - [self startActivityIndicator]; - [recoveryService deleteRecoveryWithDeleteServicesBackups:YES success:^{ - [self stopActivityIndicator]; - [self reloadData]; - } failure:^(NSError * _Nonnull error) { - [self stopActivityIndicator]; - [self reloadData]; - - [[AppDelegate theDelegate] showErrorAsAlert:error]; - }]; - } -} - #pragma mark - Segues From 3b180fc471bd2df3bb56037f437e13f7dfe5c295 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 16 Jun 2021 18:03:46 +0200 Subject: [PATCH 20/20] More cleaning --- Riot/Modules/Settings/Security/SecurityViewController.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index ced10d2529..b0191ae74c 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -1616,9 +1616,9 @@ - (MXKTableViewCellWithTextView *)settingsSecureBackupTableViewSection:(Settings return cell; } -- (MXKTableViewCellWithTextView *)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection descriptionCellForRow:(NSInteger)textCellForRow +- (MXKTableViewCell *)settingsSecureBackupTableViewSection:(SettingsSecureBackupTableViewSection *)settingsSecureBackupTableViewSection descriptionCellForRow:(NSInteger)textCellForRow { - MXKTableViewCellWithTextView *cell; + MXKTableViewCell *cell; NSIndexPath *indexPath = [self.tableViewSections exactIndexPathForRowTag:textCellForRow sectionTag:SECTION_SECURE_BACKUP];