-
Notifications
You must be signed in to change notification settings - Fork 352
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow users to import settings by pasting JSON blobs
- Loading branch information
Jon Petersson
committed
Feb 2, 2024
1 parent
0e9adc3
commit 69f0518
Showing
17 changed files
with
796 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// | ||
// IPOverride.swift | ||
// MullvadVPN | ||
// | ||
// Created by Jon Petersson on 2024-01-16. | ||
// Copyright © 2024 Mullvad VPN AB. All rights reserved. | ||
// | ||
|
||
import Network | ||
|
||
public struct RelayOverrides: Codable { | ||
public let overrides: [IPOverride] | ||
|
||
private enum CodingKeys: String, CodingKey { | ||
case overrides = "relay_overrides" | ||
} | ||
} | ||
|
||
public struct IPOverride: Codable, Equatable { | ||
public let hostname: String | ||
public var ipv4Address: IPv4Address? | ||
public var ipv6Address: IPv6Address? | ||
|
||
private enum CodingKeys: String, CodingKey { | ||
case hostname | ||
case ipv4Address = "ipv4_addr_in" | ||
case ipv6Address = "ipv6_addr_in" | ||
} | ||
|
||
init(hostname: String, ipv4Address: IPv4Address?, ipv6Address: IPv6Address?) throws { | ||
self.hostname = hostname | ||
self.ipv4Address = ipv4Address | ||
self.ipv6Address = ipv6Address | ||
|
||
if self.ipv4Address.isNil && self.ipv6Address.isNil { | ||
throw IPOverrideFormatError(errorDescription: "ipv4Address and ipv6Address cannot both be nil.") | ||
} | ||
} | ||
|
||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.container(keyedBy: CodingKeys.self) | ||
|
||
self.hostname = try container.decode(String.self, forKey: .hostname) | ||
self.ipv4Address = try container.decodeIfPresent(IPv4Address.self, forKey: .ipv4Address) | ||
self.ipv6Address = try container.decodeIfPresent(IPv6Address.self, forKey: .ipv6Address) | ||
|
||
if self.ipv4Address.isNil && self.ipv6Address.isNil { | ||
throw IPOverrideFormatError(errorDescription: "ipv4Address and ipv6Address cannot both be nil.") | ||
} | ||
} | ||
} | ||
|
||
public struct IPOverrideFormatError: LocalizedError { | ||
public let errorDescription: String? | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// | ||
// IPOverrideRepository.swift | ||
// MullvadVPN | ||
// | ||
// Created by Jon Petersson on 2024-01-16. | ||
// Copyright © 2024 Mullvad VPN AB. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import MullvadLogging | ||
|
||
public protocol IPOverrideRepositoryProtocol { | ||
func add(_ overrides: [IPOverride]) | ||
func fetchAll() -> [IPOverride] | ||
func fetchByHostname(_ hostname: String) -> IPOverride? | ||
func deleteAll() | ||
func parse(data: Data) throws -> [IPOverride] | ||
} | ||
|
||
public class IPOverrideRepository: IPOverrideRepositoryProtocol { | ||
private let logger = Logger(label: "IPOverrideRepository") | ||
|
||
public init() {} | ||
|
||
public func add(_ overrides: [IPOverride]) { | ||
var storedOverrides = fetchAll() | ||
|
||
overrides.forEach { override in | ||
if let existingOverrideIndex = storedOverrides.firstIndex(where: { $0.hostname == override.hostname }) { | ||
var existingOverride = storedOverrides[existingOverrideIndex] | ||
|
||
if let ipv4Address = override.ipv4Address { | ||
existingOverride.ipv4Address = ipv4Address | ||
} | ||
|
||
if let ipv6Address = override.ipv6Address { | ||
existingOverride.ipv6Address = ipv6Address | ||
} | ||
|
||
storedOverrides[existingOverrideIndex] = existingOverride | ||
} else { | ||
storedOverrides.append(override) | ||
} | ||
} | ||
|
||
do { | ||
try writeIpOverrides(storedOverrides) | ||
} catch { | ||
logger.error("Could not add override(s): \(overrides) \nError: \(error)") | ||
} | ||
} | ||
|
||
public func fetchAll() -> [IPOverride] { | ||
return (try? readIpOverrides()) ?? [] | ||
} | ||
|
||
public func fetchByHostname(_ hostname: String) -> IPOverride? { | ||
return fetchAll().first { $0.hostname == hostname } | ||
} | ||
|
||
public func deleteAll() { | ||
do { | ||
try SettingsManager.store.delete(key: .ipOverrides) | ||
} catch { | ||
logger.error("Could not delete all overrides. \nError: \(error)") | ||
} | ||
} | ||
|
||
public func parse(data: Data) throws -> [IPOverride] { | ||
let decoder = JSONDecoder() | ||
let jsonData = try decoder.decode(RelayOverrides.self, from: data) | ||
|
||
return jsonData.overrides | ||
} | ||
|
||
private func readIpOverrides() throws -> [IPOverride] { | ||
let parser = makeParser() | ||
let data = try SettingsManager.store.read(key: .ipOverrides) | ||
|
||
return try parser.parseUnversionedPayload(as: [IPOverride].self, from: data) | ||
} | ||
|
||
private func writeIpOverrides(_ overrides: [IPOverride]) throws { | ||
let parser = makeParser() | ||
let data = try parser.produceUnversionedPayload(overrides) | ||
|
||
try SettingsManager.store.write(data, for: .ipOverrides) | ||
} | ||
|
||
private func makeParser() -> SettingsParser { | ||
SettingsParser(decoder: JSONDecoder(), encoder: JSONEncoder()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.