mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 01:22:46 +02:00
Secure backup: Add possibility to not expose recovery key when creating a secure backup.
This commit is contained in:
@@ -37,8 +37,10 @@ final class SecretsSetupRecoveryKeyCoordinator: SecretsSetupRecoveryKeyCoordinat
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(recoveryService: MXRecoveryService, passphrase: String?) {
|
||||
let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, passphrase: passphrase)
|
||||
init(recoveryService: MXRecoveryService,
|
||||
passphrase: String?,
|
||||
passphraseOnly: Bool) {
|
||||
let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly)
|
||||
let secretsSetupRecoveryKeyViewController = SecretsSetupRecoveryKeyViewController.instantiate(with: secretsSetupRecoveryKeyViewModel)
|
||||
self.secretsSetupRecoveryKeyViewModel = secretsSetupRecoveryKeyViewModel
|
||||
self.secretsSetupRecoveryKeyViewController = secretsSetupRecoveryKeyViewController
|
||||
|
||||
+50
-46
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EoE-Pl-I63">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EoE-Pl-I63">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -49,52 +47,58 @@
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="zgt-AN-9EH">
|
||||
<rect key="frame" x="0.0" y="242" width="414" height="120"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cju-9A-6Ns" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="50"/>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="Nbm-U7-u0J">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="120"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cju-9A-6Ns" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="ywR-tU-0tp"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<inset key="contentEdgeInsets" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
|
||||
<state key="normal" title="Export">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<state key="disabled">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="exportButtonAction:" destination="EoE-Pl-I63" eventType="touchUpInside" id="8Sv-N4-mhU"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="v3h-dA-xvr" userLabel="Continue" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="70" width="374" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="efg-jS-vMe"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<inset key="contentEdgeInsets" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
|
||||
<state key="normal" title="Continue">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<state key="disabled">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="doneButtonAction:" destination="EoE-Pl-I63" eventType="touchUpInside" id="NO6-2J-vWo"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="ywR-tU-0tp"/>
|
||||
<constraint firstItem="cju-9A-6Ns" firstAttribute="width" secondItem="Nbm-U7-u0J" secondAttribute="width" id="3Q0-zU-kSa"/>
|
||||
<constraint firstItem="v3h-dA-xvr" firstAttribute="width" secondItem="Nbm-U7-u0J" secondAttribute="width" id="zpJ-fc-yEu"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<inset key="contentEdgeInsets" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
|
||||
<state key="normal" title="Export">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<state key="disabled">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="exportButtonAction:" destination="EoE-Pl-I63" eventType="touchUpInside" id="8Sv-N4-mhU"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="v3h-dA-xvr" userLabel="Continue" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="70" width="374" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="efg-jS-vMe"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<inset key="contentEdgeInsets" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
|
||||
<state key="normal" title="Continue">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<state key="disabled">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="doneButtonAction:" destination="EoE-Pl-I63" eventType="touchUpInside" id="NO6-2J-vWo"/>
|
||||
</connections>
|
||||
</button>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="v3h-dA-xvr" firstAttribute="top" secondItem="cju-9A-6Ns" secondAttribute="bottom" constant="20" id="CVl-bR-ZyK"/>
|
||||
<constraint firstAttribute="bottom" secondItem="v3h-dA-xvr" secondAttribute="bottom" id="PPw-gM-JID"/>
|
||||
<constraint firstItem="v3h-dA-xvr" firstAttribute="leading" secondItem="cju-9A-6Ns" secondAttribute="leading" id="THe-E7-SvT"/>
|
||||
<constraint firstItem="v3h-dA-xvr" firstAttribute="trailing" secondItem="cju-9A-6Ns" secondAttribute="trailing" id="W5O-7M-cF3"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="cju-9A-6Ns" secondAttribute="trailing" constant="20" id="X0c-Z0-KGn"/>
|
||||
<constraint firstItem="cju-9A-6Ns" firstAttribute="top" secondItem="zgt-AN-9EH" secondAttribute="top" id="dPj-uh-WWa"/>
|
||||
<constraint firstItem="cju-9A-6Ns" firstAttribute="centerX" secondItem="zgt-AN-9EH" secondAttribute="centerX" id="iuh-Zf-BZM"/>
|
||||
<constraint firstItem="cju-9A-6Ns" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="zgt-AN-9EH" secondAttribute="leading" constant="20" id="iyq-Bv-kvf"/>
|
||||
<constraint firstItem="cju-9A-6Ns" firstAttribute="width" secondItem="zgt-AN-9EH" secondAttribute="width" priority="750" id="t3e-c6-wSf"/>
|
||||
<constraint firstItem="Nbm-U7-u0J" firstAttribute="width" secondItem="zgt-AN-9EH" secondAttribute="width" priority="750" id="IVm-Um-rVO"/>
|
||||
<constraint firstItem="Nbm-U7-u0J" firstAttribute="centerX" secondItem="zgt-AN-9EH" secondAttribute="centerX" id="Loa-hj-EGO"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Nbm-U7-u0J" secondAttribute="bottom" id="XYI-0q-4F1"/>
|
||||
<constraint firstItem="Nbm-U7-u0J" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="zgt-AN-9EH" secondAttribute="leading" constant="20" id="aNG-Pe-yCg"/>
|
||||
<constraint firstItem="Nbm-U7-u0J" firstAttribute="top" secondItem="zgt-AN-9EH" secondAttribute="top" id="pWA-9P-KBl"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Nbm-U7-u0J" secondAttribute="trailing" constant="20" id="vLC-EA-fxi"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
|
||||
@@ -33,6 +33,7 @@ final class SecretsSetupRecoveryKeyViewController: UIViewController {
|
||||
// MARK: Private
|
||||
|
||||
private var viewModel: SecretsSetupRecoveryKeyViewModelType!
|
||||
private var isPassphraseOnly: Bool = true
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
@@ -132,22 +133,56 @@ final class SecretsSetupRecoveryKeyViewController: UIViewController {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(let recoveryKey):
|
||||
self.renderLoaded(recoveryKey: recoveryKey)
|
||||
case .loaded(let passphraseOnly):
|
||||
self.renderLoaded(passphraseOnly: passphraseOnly)
|
||||
case .recoveryCreated(let recoveryKey):
|
||||
self.renderRecoveryCreated(recoveryKey: recoveryKey)
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoaded(passphraseOnly: Bool) {
|
||||
self.isPassphraseOnly = passphraseOnly
|
||||
|
||||
let title: String
|
||||
let secretsLogoImage: UIImage
|
||||
let informationText: String
|
||||
let recoveryKeyText: String?
|
||||
|
||||
if passphraseOnly {
|
||||
title = VectorL10n.secretsSetupRecoveryPassphraseSummaryTitle
|
||||
secretsLogoImage = Asset.Images.secretsSetupPassphrase.image
|
||||
informationText = VectorL10n.secretsSetupRecoveryPassphraseSummaryInformation
|
||||
recoveryKeyText = nil
|
||||
} else {
|
||||
title = VectorL10n.secretsSetupRecoveryKeyTitle
|
||||
secretsLogoImage = Asset.Images.secretsSetupKey.image
|
||||
informationText = VectorL10n.secretsSetupRecoveryKeyInformation
|
||||
recoveryKeyText = VectorL10n.secretsSetupRecoveryKeyLoading
|
||||
}
|
||||
|
||||
self.title = title
|
||||
self.secureKeyImageView.image = secretsLogoImage
|
||||
self.informationLabel.text = informationText
|
||||
self.exportButton.isHidden = passphraseOnly
|
||||
self.recoveryKeyLabel.text = recoveryKeyText
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(recoveryKey: String) {
|
||||
private func renderRecoveryCreated(recoveryKey: String) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.exportButton.isEnabled = true
|
||||
self.recoveryKey = recoveryKey
|
||||
self.recoveryKeyLabel.text = recoveryKey
|
||||
|
||||
self.exportButton.isEnabled = !self.isPassphraseOnly
|
||||
self.doneButton.isEnabled = self.isPassphraseOnly
|
||||
|
||||
if !self.isPassphraseOnly {
|
||||
self.recoveryKey = recoveryKey
|
||||
self.recoveryKeyLabel.text = recoveryKey
|
||||
}
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
@@ -208,7 +243,12 @@ final class SecretsSetupRecoveryKeyViewController: UIViewController {
|
||||
}
|
||||
|
||||
@IBAction private func doneButtonAction(_ sender: Any) {
|
||||
self.presentKeepSafeAlert()
|
||||
|
||||
if self.isPassphraseOnly {
|
||||
self.viewModel.process(viewAction: .done)
|
||||
} else {
|
||||
self.presentKeepSafeAlert()
|
||||
}
|
||||
}
|
||||
|
||||
private func cancelButtonAction() {
|
||||
|
||||
@@ -26,6 +26,7 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy
|
||||
|
||||
private let recoveryService: MXRecoveryService
|
||||
private let passphrase: String?
|
||||
private let passphraseOnly: Bool
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@@ -34,9 +35,10 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(recoveryService: MXRecoveryService, passphrase: String?) {
|
||||
init(recoveryService: MXRecoveryService, passphrase: String?, passphraseOnly: Bool) {
|
||||
self.recoveryService = recoveryService
|
||||
self.passphrase = passphrase
|
||||
self.passphraseOnly = passphraseOnly
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
@@ -44,6 +46,7 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy
|
||||
func process(viewAction: SecretsSetupRecoveryKeyViewAction) {
|
||||
switch viewAction {
|
||||
case .loadData:
|
||||
self.update(viewState: .loaded(self.passphraseOnly))
|
||||
self.createSecureKey()
|
||||
case .done:
|
||||
self.coordinatorDelegate?.secretsSetupRecoveryKeyViewModelDidComplete(self)
|
||||
@@ -60,7 +63,7 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy
|
||||
self.update(viewState: .loading)
|
||||
|
||||
self.recoveryService.createRecovery(forSecrets: nil, withPassphrase: self.passphrase, createServicesBackups: true, success: { secretStorageKeyCreationInfo in
|
||||
self.update(viewState: .loaded(secretStorageKeyCreationInfo.recoveryKey))
|
||||
self.update(viewState: .recoveryCreated(secretStorageKeyCreationInfo.recoveryKey))
|
||||
}, failure: { error in
|
||||
self.update(viewState: .error(error))
|
||||
})
|
||||
|
||||
@@ -20,7 +20,8 @@ import Foundation
|
||||
|
||||
/// SecretsSetupRecoveryKeyViewController view state
|
||||
enum SecretsSetupRecoveryKeyViewState {
|
||||
case loaded(_ passphraseOnly: Bool)
|
||||
case loading
|
||||
case loaded(_ recoveryKey: String)
|
||||
case recoveryCreated(_ recoveryKey: String)
|
||||
case error(Error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user