summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2023-08-07 17:56:38 +0200
committerBug Magnet <marco.nikic@mullvad.net>2023-08-08 11:37:22 +0200
commit58a2ee95242f78eb7566598eded82eb0cdab5ef3 (patch)
treec284c0fd22974794e8b34cf4f6bb9cb196af2e68 /ios/MullvadVPN
parent16599f70fa38470f4de47c66bd576aab77e8d926 (diff)
downloadmullvadvpn-58a2ee95242f78eb7566598eded82eb0cdab5ef3.tar.xz
mullvadvpn-58a2ee95242f78eb7566598eded82eb0cdab5ef3.zip
Remove legacy settings
Diffstat (limited to 'ios/MullvadVPN')
-rw-r--r--ios/MullvadVPN/SettingsManager/Migrations/MigrationFromV1ToV2.swift172
-rw-r--r--ios/MullvadVPN/SettingsManager/SettingsManager.swift221
2 files changed, 5 insertions, 388 deletions
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 {