summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2023-04-19 14:27:22 +0200
committerAndrej Mihajlov <and@mullvad.net>2023-04-19 14:27:22 +0200
commitd39fb86ce5c619ce62f5d4fb8d0a7d18f9ddd452 (patch)
tree0c0c586f114c32ae5dcb0a7f4d67ca2ffd5e3ecc
parent600223782395a7e1d47b7688d62bff92f2684700 (diff)
parent5dc174e47f1b623070042b69c1a116dd019f11de (diff)
downloadmullvadvpn-d39fb86ce5c619ce62f5d4fb8d0a7d18f9ddd452.tar.xz
mullvadvpn-d39fb86ce5c619ce62f5d4fb8d0a7d18f9ddd452.zip
Merge branch 'dont-keep-account-number-and-other-state-ios-72'
-rw-r--r--ios/CHANGELOG.md1
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj7
-rw-r--r--ios/MullvadVPN/AppDelegate.swift35
-rw-r--r--ios/MullvadVPN/Classes/FirstTimeLaunch.swift21
-rw-r--r--ios/MullvadVPN/SettingsManager/SettingsManager.swift84
5 files changed, 123 insertions, 25 deletions
diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md
index bcaecc10dc..582f230db5 100644
--- a/ios/CHANGELOG.md
+++ b/ios/CHANGELOG.md
@@ -25,6 +25,7 @@ Line wrap the file at 100 chars. Th
## [Unreleased]
### Added
- Add search functionality to location selection view.
+- Wipe all settings on app reinstall.
### Changed
- Changed key rotation interval from 4 to 14 days.
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index ed8b453b53..83c283dd5c 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -361,6 +361,7 @@
58FF2C03281BDE02009EF542 /* SettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF2C02281BDE02009EF542 /* SettingsManager.swift */; };
7A09C98129D99215000C2CAC /* String+FuzzyMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A09C98029D99215000C2CAC /* String+FuzzyMatch.swift */; };
7AD2DA1529DC4EB900250737 /* UISearchBar+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD2DA1429DC4EB900250737 /* UISearchBar+Appearance.swift */; };
+ 7A7AD28D29DC677800480EF1 /* FirstTimeLaunch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */; };
E1187ABC289BBB850024E748 /* OutOfTimeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */; };
E1187ABD289BBB850024E748 /* OutOfTimeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABB289BBB850024E748 /* OutOfTimeContentView.swift */; };
E158B360285381C60002F069 /* String+AccountFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = E158B35F285381C60002F069 /* String+AccountFormatting.swift */; };
@@ -945,6 +946,10 @@
58FF2C02281BDE02009EF542 /* SettingsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsManager.swift; sourceTree = "<group>"; };
7A09C98029D99215000C2CAC /* String+FuzzyMatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+FuzzyMatch.swift"; sourceTree = "<group>"; };
7AD2DA1429DC4EB900250737 /* UISearchBar+Appearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UISearchBar+Appearance.swift"; sourceTree = "<group>"; };
+ 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstTimeLaunch.swift; sourceTree = "<group>"; };
+ 7AD8490C29BA1EC500878E53 /* SettingsCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCellFactory.swift; sourceTree = "<group>"; };
+ 7AD8490E29BA26B000878E53 /* CellFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellFactoryProtocol.swift; sourceTree = "<group>"; };
+ 7AD8491029BA316500878E53 /* PreferencesCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesCellFactory.swift; sourceTree = "<group>"; };
E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeViewController.swift; sourceTree = "<group>"; };
E1187ABB289BBB850024E748 /* OutOfTimeContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeContentView.swift; sourceTree = "<group>"; };
E158B35F285381C60002F069 /* String+AccountFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+AccountFormatting.swift"; sourceTree = "<group>"; };
@@ -1473,6 +1478,7 @@
587988C628A2A01F00E3DF54 /* AccountDataThrottling.swift */,
58138E60294871C600684F0C /* DeviceDataThrottling.swift */,
589A454B28DDF5E100565204 /* Swizzle.swift */,
+ 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */,
);
path = Classes;
sourceTree = "<group>";
@@ -2601,6 +2607,7 @@
587B753B2666467500DEF7E9 /* NotificationBannerView.swift in Sources */,
58B993B12608A34500BA7811 /* LoginContentView.swift in Sources */,
5878A27529093A310096FC88 /* StorePaymentEvent.swift in Sources */,
+ 7A7AD28D29DC677800480EF1 /* FirstTimeLaunch.swift in Sources */,
58B26E2A2943545A00D5980C /* NotificationManagerDelegate.swift in Sources */,
58A1AA8C23F5584C009F7EA6 /* ConnectionPanelView.swift in Sources */,
5878A27B2909649A0096FC88 /* CustomOverlayRenderer.swift in Sources */,
diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift
index 126109663f..d5a5d987d9 100644
--- a/ios/MullvadVPN/AppDelegate.swift
+++ b/ios/MullvadVPN/AppDelegate.swift
@@ -363,6 +363,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
private func startInitialization(application: UIApplication) {
+ let wipeSettingsOperation = getWipeSettingsOperation()
+
let loadTunnelStoreOperation = AsyncBlockOperation(dispatchQueue: .main) { operation in
self.tunnelStore.loadPersistentTunnels { error in
if let error = error {
@@ -395,7 +397,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
migrationUIHandler.showMigrationError(error, completionHandler: finishHandler)
}
}
- migrateSettingsOperation.addDependency(loadTunnelStoreOperation)
+ migrateSettingsOperation.addDependencies([wipeSettingsOperation, loadTunnelStoreOperation])
let initTunnelManagerOperation = AsyncBlockOperation(dispatchQueue: .main) { operation in
self.tunnelManager.loadConfiguration { error in
@@ -428,6 +430,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
operationQueue.addOperations(
[
+ wipeSettingsOperation,
loadTunnelStoreOperation,
migrateSettingsOperation,
initTunnelManagerOperation,
@@ -437,6 +440,36 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
)
}
+ /// Returns an operation that acts on two conditions:
+ /// 1. Has the app been launched at least once after install? (`FirstTimeLaunch.hasFinished`)
+ /// 2. Has the app - at some point in time - been updated from a version compatible with wiping settings?
+ /// (`SettingsManager.getShouldWipeSettings()`)
+ /// If (1) is `false` and (2) is `true`, we know that the app has been freshly installed/reinstalled and is
+ /// compatible, thus triggering a settings wipe.
+ private func getWipeSettingsOperation() -> AsyncBlockOperation {
+ return AsyncBlockOperation {
+ if !FirstTimeLaunch.hasFinished, SettingsManager.getShouldWipeSettings() {
+ if let deviceState = try? SettingsManager.readDeviceState(),
+ let accountData = deviceState.accountData,
+ let deviceData = deviceState.deviceData
+ {
+ _ = self.devicesProxy.deleteDevice(
+ accountNumber: accountData.number,
+ identifier: deviceData.identifier,
+ retryStrategy: .noRetry
+ ) { _ in
+ // Do nothing.
+ }
+ }
+
+ SettingsManager.resetStore(completely: true)
+ }
+
+ FirstTimeLaunch.setHasFinished()
+ SettingsManager.setShouldWipeSettings()
+ }
+ }
+
// MARK: - StorePaymentManagerDelegate
func storePaymentManager(
diff --git a/ios/MullvadVPN/Classes/FirstTimeLaunch.swift b/ios/MullvadVPN/Classes/FirstTimeLaunch.swift
new file mode 100644
index 0000000000..efe2168909
--- /dev/null
+++ b/ios/MullvadVPN/Classes/FirstTimeLaunch.swift
@@ -0,0 +1,21 @@
+//
+// FirstTimeLaunch.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2023-04-04.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+enum FirstTimeLaunch {
+ private static let userDefaultsKey = "hasFinishedFirstTimeLaunch"
+
+ static var hasFinished: Bool {
+ return UserDefaults.standard.bool(forKey: userDefaultsKey)
+ }
+
+ static func setHasFinished() {
+ UserDefaults.standard.set(true, forKey: userDefaultsKey)
+ }
+}
diff --git a/ios/MullvadVPN/SettingsManager/SettingsManager.swift b/ios/MullvadVPN/SettingsManager/SettingsManager.swift
index f24dfa3935..96bed23c45 100644
--- a/ios/MullvadVPN/SettingsManager/SettingsManager.swift
+++ b/ios/MullvadVPN/SettingsManager/SettingsManager.swift
@@ -68,6 +68,23 @@ enum SettingsManager {
}
}
+ // MARK: - Should wipe settings
+
+ static func getShouldWipeSettings() -> Bool {
+ return (try? store.read(key: .shouldWipeSettings)) != nil
+ }
+
+ static func setShouldWipeSettings() {
+ do {
+ try store.write(Data(), for: .shouldWipeSettings)
+ } catch {
+ logger.error(
+ error: error,
+ message: "Failed to set should wipe settings."
+ )
+ }
+ }
+
// MARK: - Settings
static func readSettings() throws -> TunnelSettingsV2 {
@@ -153,6 +170,48 @@ enum SettingsManager {
}
}
+ /// Removes all legacy settings, device state and tunnel settings but keeps the last used
+ /// account number stored.
+ static func resetStore(completely: Bool = false) {
+ logger.debug("Reset store.")
+
+ do {
+ try store.delete(key: .deviceState)
+ } catch {
+ if (error as? KeychainError) != .itemNotFound {
+ logger.error(error: error, message: "Failed to delete device state.")
+ }
+ }
+
+ do {
+ try store.delete(key: .settings)
+ } catch {
+ if (error as? KeychainError) != .itemNotFound {
+ logger.error(error: error, message: "Failed to delete settings.")
+ }
+ }
+
+ if completely {
+ do {
+ try store.delete(key: .lastUsedAccount)
+ } catch {
+ if (error as? KeychainError) != .itemNotFound {
+ logger.error(error: error, message: "Failed to delete last used account.")
+ }
+ }
+
+ do {
+ try store.delete(key: .shouldWipeSettings)
+ } catch {
+ if (error as? KeychainError) != .itemNotFound {
+ logger.error(error: error, message: "Failed to delete should wipe settings.")
+ }
+ }
+ }
+
+ Self.deleteAllLegacySettings()
+ }
+
// MARK: - Private
private static func migrateLegacySettings(
@@ -212,30 +271,6 @@ enum SettingsManager {
throw error
}
- /// Removes all legacy settings, device state and tunnel settings but keeps the last used
- /// account number stored.
- private static func resetStore() {
- logger.debug("Reset store.")
-
- do {
- try store.delete(key: .deviceState)
- } catch {
- if (error as? KeychainError) != .itemNotFound {
- logger.error(error: error, message: "Failed to delete device state.")
- }
- }
-
- do {
- try store.delete(key: .settings)
- } catch {
- if (error as? KeychainError) != .itemNotFound {
- logger.error(error: error, message: "Failed to delete settings.")
- }
- }
-
- Self.deleteAllLegacySettings()
- }
-
// MARK: - Legacy settings
private static func readLegacySettings() -> LegacyTunnelSettings? {
@@ -409,6 +444,7 @@ enum SettingsKey: String, CaseIterable {
case settings = "Settings"
case deviceState = "DeviceState"
case lastUsedAccount = "LastUsedAccount"
+ case shouldWipeSettings = "shouldWipeSettings"
}
/// An error type describing a failure to read or parse settings version.