diff options
| author | Bug Magnet <marco.nikic@mullvad.net> | 2023-08-07 17:56:38 +0200 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2023-08-08 11:37:22 +0200 |
| commit | 58a2ee95242f78eb7566598eded82eb0cdab5ef3 (patch) | |
| tree | c284c0fd22974794e8b34cf4f6bb9cb196af2e68 /ios | |
| parent | 16599f70fa38470f4de47c66bd576aab77e8d926 (diff) | |
| download | mullvadvpn-58a2ee95242f78eb7566598eded82eb0cdab5ef3.tar.xz mullvadvpn-58a2ee95242f78eb7566598eded82eb0cdab5ef3.zip | |
Remove legacy settings
Diffstat (limited to 'ios')
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 14 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsManager/Migrations/MigrationFromV1ToV2.swift | 172 | ||||
| -rw-r--r-- | ios/MullvadVPN/SettingsManager/SettingsManager.swift | 221 |
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 { |
