summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj14
-rw-r--r--ios/MullvadVPN/SettingsManager/Migrations/MigrationFromV1ToV2.swift172
-rw-r--r--ios/MullvadVPN/SettingsManager/SettingsManager.swift221
3 files changed, 5 insertions, 402 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 636a8f8969..cfddd66410 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -49,8 +49,6 @@
06799AF328F98E4800ACD94E /* RESTAuthenticationProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FAE67028F83CA40033DD93 /* RESTAuthenticationProxy.swift */; };
06799AF428F98E4800ACD94E /* RESTAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FAE67928F83CA50033DD93 /* RESTAuthorization.swift */; };
06799AFC28F98EE300ACD94E /* AddressCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AC114128F8413A0037AF9A /* AddressCache.swift */; };
- 068CE57029278F5300A068BB /* MigrationFromV1ToV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068CE56F29278F5300A068BB /* MigrationFromV1ToV2.swift */; };
- 068CE57229278F6D00A068BB /* MigrationFromV1ToV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068CE56F29278F5300A068BB /* MigrationFromV1ToV2.swift */; };
068CE5742927B7A400A068BB /* Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068CE5732927B7A400A068BB /* Migration.swift */; };
068CE5782927BE4800A068BB /* Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068CE5732927B7A400A068BB /* Migration.swift */; };
0697D6E728F01513007A9E99 /* TransportMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0697D6E628F01513007A9E99 /* TransportMonitor.swift */; };
@@ -850,7 +848,6 @@
06799AB428F98CE700ACD94E /* le_root_cert.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = le_root_cert.cer; sourceTree = "<group>"; };
06799ABC28F98E1D00ACD94E /* MullvadREST.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadREST.framework; sourceTree = BUILT_PRODUCTS_DIR; };
06799ABE28F98E1D00ACD94E /* MullvadREST.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadREST.h; sourceTree = "<group>"; };
- 068CE56F29278F5300A068BB /* MigrationFromV1ToV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationFromV1ToV2.swift; sourceTree = "<group>"; };
068CE5732927B7A400A068BB /* Migration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Migration.swift; sourceTree = "<group>"; };
0697D6E628F01513007A9E99 /* TransportMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransportMonitor.swift; sourceTree = "<group>"; };
06AC113628F83FD70037AF9A /* Cancellable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cancellable.swift; sourceTree = "<group>"; };
@@ -1468,18 +1465,9 @@
path = MullvadREST;
sourceTree = "<group>";
};
- 068CE57129278F5F00A068BB /* Migrations */ = {
- isa = PBXGroup;
- children = (
- 068CE56F29278F5300A068BB /* MigrationFromV1ToV2.swift */,
- );
- path = Migrations;
- sourceTree = "<group>";
- };
580F8B88281A79A7002E0998 /* SettingsManager */ = {
isa = PBXGroup;
children = (
- 068CE57129278F5F00A068BB /* Migrations */,
580F8B8528197958002E0998 /* DNSSettings.swift */,
06410DFD292CE18F00AFC18C /* KeychainSettingsStore.swift */,
068CE5732927B7A400A068BB /* Migration.swift */,
@@ -3297,7 +3285,6 @@
58ACF64D26567A5000ACE4B7 /* CustomSwitch.swift in Sources */,
58F2E14C276A61C000A79513 /* RotateKeyOperation.swift in Sources */,
5871FB96254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift in Sources */,
- 068CE57029278F5300A068BB /* MigrationFromV1ToV2.swift in Sources */,
F0E8E4C52A60499100ED26A3 /* AccountDeletionViewController.swift in Sources */,
F028A54B2A3370FA00C0CAA3 /* RedeemVoucherContentView.swift in Sources */,
58FEEB58260B662E00A621A8 /* AutomaticKeyboardResponder.swift in Sources */,
@@ -3482,7 +3469,6 @@
58C76A092A33850E00100D75 /* ApplicationTarget.swift in Sources */,
583D86482A2678DC0060D63B /* DeviceStateAccessor.swift in Sources */,
58906DE02445C7A5002F0673 /* NEProviderStopReason+Debug.swift in Sources */,
- 068CE57229278F6D00A068BB /* MigrationFromV1ToV2.swift in Sources */,
06410DFF292CF16C00AFC18C /* KeychainSettingsStore.swift in Sources */,
58E072A128814B0E008902F8 /* MullvadEndpoint+WgEndpoint.swift in Sources */,
06AC116228F94C450037AF9A /* ApplicationConfiguration.swift in Sources */,
diff --git a/ios/MullvadVPN/SettingsManager/Migrations/MigrationFromV1ToV2.swift b/ios/MullvadVPN/SettingsManager/Migrations/MigrationFromV1ToV2.swift
deleted file mode 100644
index 1cb53a9229..0000000000
--- a/ios/MullvadVPN/SettingsManager/Migrations/MigrationFromV1ToV2.swift
+++ /dev/null
@@ -1,172 +0,0 @@
-//
-// MigrationFromV1ToV2.swift
-// MullvadVPN
-//
-// Created by Sajad Vishkai on 2022-11-18.
-// Copyright © 2022 Mullvad VPN AB. All rights reserved.
-//
-
-import Foundation
-import MullvadLogging
-import MullvadREST
-import MullvadTypes
-import Operations
-
-final class MigrationFromV1ToV2: Migration {
- private let accountsProxy: REST.AccountsProxy
- private let devicesProxy: REST.DevicesProxy
-
- private var accountTask: Cancellable?
- private var deviceTask: Cancellable?
-
- private var accountCompletion: Result<Account, Error> = .failure(OperationError.cancelled)
- private var devicesCompletion: Result<[Device], Error> = .failure(OperationError.cancelled)
-
- private let legacySettings: LegacyTunnelSettings
-
- private let logger = Logger(label: "Migration.V1ToV2")
-
- init(
- restFactory: REST.ProxyFactory,
- legacySettings: LegacyTunnelSettings
- ) {
- accountsProxy = restFactory.createAccountsProxy()
- devicesProxy = restFactory.createDevicesProxy()
- self.legacySettings = legacySettings
- }
-
- func migrate(
- with store: SettingsStore,
- parser: SettingsParser,
- completion: @escaping (Error?) -> Void
- ) {
- let storedAccountNumber = legacySettings.accountNumber
-
- // Store last used account number.
- logger.debug("Store legacy account number as last used account.")
- do {
- if let accountData = storedAccountNumber.data(using: .utf8) {
- try store.write(accountData, for: .lastUsedAccount)
- } else {
- logger.error("Failed to encode account number into utf-8 data.")
- }
- } catch {
- logger.error(error: error, message: "Failed to store last used account.")
- }
-
- // Fetch remote data concurrently.
- logger.debug("Fetching account and device data...")
- let dispatchGroup = DispatchGroup()
-
- dispatchGroup.enter()
- accountTask = accountsProxy.getAccountData(
- accountNumber: storedAccountNumber,
- retryStrategy: .aggressive
- ) { completion in
- self.accountCompletion = completion
-
- dispatchGroup.leave()
- }
-
- dispatchGroup.enter()
- deviceTask = devicesProxy.getDevices(
- accountNumber: storedAccountNumber,
- retryStrategy: .aggressive
- ) { completion in
- self.devicesCompletion = completion
-
- dispatchGroup.leave()
- }
-
- dispatchGroup.notify(queue: .main) {
- let result = Result {
- try self.migrateSettings(
- store: store,
- parser: parser,
- settings: self.legacySettings,
- accountData: try self.accountCompletion.get(),
- devices: try self.devicesCompletion.get()
- )
- }
- completion(result.error)
- }
- }
-
- private func migrateSettings(
- store: SettingsStore,
- parser: SettingsParser,
- settings: LegacyTunnelSettings,
- accountData: Account,
- devices: [Device]
- ) throws {
- let tunnelSettings = settings.tunnelSettings
- let interfaceData = settings.tunnelSettings.interface
-
- // Find device that matches the public key stored in legacy settings.
- let device = devices.first { device in
- device.pubkey == interfaceData.privateKey.publicKey ||
- device.pubkey == interfaceData.nextPrivateKey?.publicKey
- }
-
- let newDeviceState: DeviceState
- if let device {
- logger.debug("Found device matching public key stored in legacy settings.")
-
- // Match private key.
- let privateKeyWithMetadata: PrivateKeyWithMetadata
- if let nextKey = interfaceData.nextPrivateKey, nextKey.publicKey == device.pubkey {
- privateKeyWithMetadata = nextKey
- } else {
- privateKeyWithMetadata = interfaceData.privateKey
- }
-
- logger.debug("Store new settings...")
-
- // Create new settings.
- newDeviceState = DeviceState.loggedIn(
- StoredAccountData(
- identifier: accountData.id,
- number: settings.accountNumber,
- expiry: accountData.expiry
- ),
- StoredDeviceData(
- creationDate: device.created,
- identifier: device.id,
- name: device.name,
- hijackDNS: device.hijackDNS,
- ipv4Address: device.ipv4Address,
- ipv6Address: device.ipv6Address,
- wgKeyData: StoredWgKeyData(
- creationDate: privateKeyWithMetadata.creationDate,
- lastRotationAttemptDate: nil,
- privateKey: privateKeyWithMetadata.privateKey
- )
- )
- )
- } else {
- logger.debug(
- """
- Failed to find a device matching public key from legacy settings. \
- Set device state to logged out.
- """
- )
-
- newDeviceState = .loggedOut
- }
-
- let newSettings = TunnelSettingsV2(
- relayConstraints: tunnelSettings.relayConstraints,
- dnsSettings: interfaceData.dnsSettings
- )
-
- // Save settings.
- let settingsData = try parser.producePayload(
- newSettings,
- version: SchemaVersion.v2.rawValue
- )
- let deviceData = try parser.produceUnversionedPayload(newDeviceState)
-
- try store.write(settingsData, for: .settings)
- try store.write(deviceData, for: .deviceState)
- }
-}
diff --git a/ios/MullvadVPN/SettingsManager/SettingsManager.swift b/ios/MullvadVPN/SettingsManager/SettingsManager.swift
index d5db37ff69..5ee563c475 100644
--- a/ios/MullvadVPN/SettingsManager/SettingsManager.swift
+++ b/ios/MullvadVPN/SettingsManager/SettingsManager.swift
@@ -152,21 +152,11 @@ enum SettingsManager {
completion(result)
}
- if let legacySettings = readLegacySettings() {
- migrateLegacySettings(
- restFactory: restFactory,
- legacySettings: legacySettings
- ) { error in
- handleCompletion(error.map { .failure($0) } ?? .success)
- }
- } else {
- do {
- try checkLatestSettingsVersion()
-
- handleCompletion(.nothing)
- } catch {
- handleCompletion(.failure(error))
- }
+ do {
+ try checkLatestSettingsVersion()
+ handleCompletion(.nothing)
+ } catch {
+ handleCompletion(.failure(error))
}
}
@@ -208,43 +198,10 @@ enum SettingsManager {
}
}
}
-
- Self.deleteAllLegacySettings()
}
// MARK: - Private
- private static func migrateLegacySettings(
- restFactory: REST.ProxyFactory,
- legacySettings: LegacyTunnelSettings,
- completion: @escaping (Error?) -> Void
- ) {
- let parser = makeParser()
-
- let migration = MigrationFromV1ToV2(
- restFactory: restFactory,
- legacySettings: legacySettings
- )
-
- migration.migrate(with: store, parser: parser) { error in
- if let error {
- let migrationError = SettingsMigrationError(
- sourceVersion: .v1,
- targetVersion: .v2,
- underlyingError: error
- )
-
- logger.error(error: migrationError)
-
- completion(migrationError)
- } else {
- Self.deleteAllLegacySettings()
-
- completion(nil)
- }
- }
- }
-
private static func checkLatestSettingsVersion() throws {
let settingsVersion: Int
do {
@@ -270,174 +227,6 @@ enum SettingsManager {
throw error
}
-
- // MARK: - Legacy settings
-
- private static func readLegacySettings() -> LegacyTunnelSettings? {
- guard let storedAccountNumber = UserDefaults.standard.string(forKey: accountTokenKey) else {
- logger.debug("Legacy account number is not found in user defaults. Nothing to migrate.")
- return nil
- }
-
- // List legacy settings stored in keychain.
- logger.debug("List legacy settings in keychain...")
-
- var storedSettings: [LegacyTunnelSettings] = []
- do {
- storedSettings = try findAllLegacySettingsInKeychain()
- } catch .itemNotFound as KeychainError {
- logger.debug("Legacy settings are not found in keychain.")
-
- return nil
- } catch {
- logger.error(
- error: error,
- message: "Failed to read legacy settings from keychain."
- )
-
- return nil
- }
-
- // Find settings matching the account number stored in user defaults.
- let matchingSettings = storedSettings.first { settings in
- settings.accountNumber == storedAccountNumber
- }
-
- guard let matchingSettings else {
- logger.debug(
- "Could not find legacy settings matching the legacy account number."
- )
-
- return nil
- }
-
- return matchingSettings
- }
-
- private static func findAllLegacySettingsInKeychain() throws -> [LegacyTunnelSettings] {
- let query: [CFString: Any] = [
- kSecClass: kSecClassGenericPassword,
- kSecAttrService: keychainServiceName,
- kSecReturnAttributes: true,
- kSecReturnData: true,
- kSecMatchLimit: kSecMatchLimitAll,
- ]
-
- var result: CFTypeRef?
- let status = SecItemCopyMatching(query as CFDictionary, &result)
-
- guard status == errSecSuccess else {
- throw KeychainError(code: status)
- }
-
- guard let items = result as? [[CFString: Any]] else {
- return []
- }
-
- return items.filter(Self.filterLegacySettings)
- .compactMap { item -> LegacyTunnelSettings? in
- guard let accountNumber = item[kSecAttrAccount] as? String,
- let data = item[kSecValueData] as? Data
- else {
- return nil
- }
- do {
- let tunnelSettings = try JSONDecoder().decode(
- TunnelSettingsV1.self,
- from: data
- )
-
- return LegacyTunnelSettings(
- accountNumber: accountNumber,
- tunnelSettings: tunnelSettings
- )
- } catch {
- logger.error(
- error: error,
- message: "Failed to decode legacy settings."
- )
- return nil
- }
- }
- }
-
- private static func deleteAllLegacySettings() {
- logger.debug("Remove legacy settings from keychain.")
- deleteLegacySettingsFromKeychain()
-
- logger.debug("Remove legacy settings from user defaults.")
- let userDefaults = UserDefaults.standard
- userDefaults.removeObject(forKey: accountTokenKey)
- userDefaults.removeObject(forKey: accountExpiryKey)
- }
-
- private static func deleteLegacySettingsFromKeychain() {
- let query: [CFString: Any] = [
- kSecClass: kSecClassGenericPassword,
- kSecAttrService: keychainServiceName,
- kSecReturnAttributes: true,
- kSecMatchLimit: kSecMatchLimitAll,
- ]
-
- var result: CFTypeRef?
- let status = SecItemCopyMatching(query as CFDictionary, &result)
-
- guard status == errSecSuccess else {
- let error = KeychainError(code: status)
-
- if error != .itemNotFound {
- logger.error(
- error: error,
- message: "Failed to list legacy settings."
- )
- }
-
- return
- }
-
- guard let items = result as? [[CFString: Any]] else {
- return
- }
-
- items.filter(Self.filterLegacySettings)
- .enumerated()
- .forEach { index, item in
- guard let account = item[kSecAttrAccount] else {
- return
- }
-
- let deleteQuery: [CFString: Any] = [
- kSecClass: kSecClassGenericPassword,
- kSecAttrService: keychainServiceName,
- kSecAttrAccount: account,
- ]
-
- let status = SecItemDelete(deleteQuery as CFDictionary)
- if status == errSecSuccess {
- logger.debug("Removed legacy settings entry \(index).")
- } else {
- let error = KeychainError(code: status)
-
- logger.error(
- error: error,
- message: "Failed to remove legacy settings entry \(index)."
- )
- }
- }
- }
-
- private static func filterLegacySettings(_ item: [CFString: Any]) -> Bool {
- guard let accountNumber = item[kSecAttrAccount] as? String else {
- return false
- }
-
- return SettingsKey(rawValue: accountNumber) == nil
- }
-}
-
-struct LegacyTunnelSettings {
- let accountNumber: String
- let tunnelSettings: TunnelSettingsV1
}
enum SettingsKey: String, CaseIterable {