-
Notifications
You must be signed in to change notification settings - Fork 354
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add API access methods UI/part of backend
- Loading branch information
1 parent
0313af5
commit 277cc9b
Showing
97 changed files
with
6,596 additions
and
256 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
69 changes: 69 additions & 0 deletions
69
ios/MullvadVPN/AccessMethodRepository/AccessMethodRepository.swift
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,69 @@ | ||
// | ||
// AccessMethodRepository.swift | ||
// MullvadVPN | ||
// | ||
// Created by pronebird on 22/11/2023. | ||
// Copyright © 2023 Mullvad VPN AB. All rights reserved. | ||
// | ||
|
||
import Combine | ||
import Foundation | ||
|
||
class AccessMethodRepository: AccessMethodRepositoryProtocol { | ||
private var memoryStore: [PersistentAccessMethod] { | ||
didSet { | ||
publisher.send(memoryStore) | ||
} | ||
} | ||
|
||
let publisher: PassthroughSubject<[PersistentAccessMethod], Never> = .init() | ||
|
||
static let shared = AccessMethodRepository() | ||
|
||
init() { | ||
memoryStore = [ | ||
PersistentAccessMethod( | ||
id: UUID(uuidString: "C9DB7457-2A55-42C3-A926-C07F82131994")!, | ||
name: "", | ||
isEnabled: true, | ||
proxyConfiguration: .direct | ||
), | ||
PersistentAccessMethod( | ||
id: UUID(uuidString: "8586E75A-CA7B-4432-B70D-EE65F3F95084")!, | ||
name: "", | ||
isEnabled: true, | ||
proxyConfiguration: .bridges | ||
), | ||
] | ||
} | ||
|
||
func add(_ method: PersistentAccessMethod) { | ||
guard !memoryStore.contains(where: { $0.id == method.id }) else { return } | ||
|
||
memoryStore.append(method) | ||
} | ||
|
||
func update(_ method: PersistentAccessMethod) { | ||
guard let index = memoryStore.firstIndex(where: { $0.id == method.id }) else { return } | ||
|
||
memoryStore[index] = method | ||
} | ||
|
||
func delete(id: UUID) { | ||
guard let index = memoryStore.firstIndex(where: { $0.id == id }) else { return } | ||
|
||
// Prevent removing methods that have static UUIDs and always present. | ||
let permanentMethod = memoryStore[index] | ||
if !permanentMethod.kind.isPermanent { | ||
memoryStore.remove(at: index) | ||
} | ||
} | ||
|
||
func fetch(by id: UUID) -> PersistentAccessMethod? { | ||
memoryStore.first { $0.id == id } | ||
} | ||
|
||
func fetchAll() -> [PersistentAccessMethod] { | ||
memoryStore | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
ios/MullvadVPN/AccessMethodRepository/AccessMethodRepositoryProtocol.swift
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,36 @@ | ||
// | ||
// AccessMethodRepositoryProtocol.swift | ||
// MullvadVPN | ||
// | ||
// Created by pronebird on 28/11/2023. | ||
// Copyright © 2023 Mullvad VPN AB. All rights reserved. | ||
// | ||
|
||
import Combine | ||
import Foundation | ||
|
||
protocol AccessMethodRepositoryProtocol { | ||
/// Publisher that propagates a snapshot of persistent store upon modifications. | ||
var publisher: PassthroughSubject<[PersistentAccessMethod], Never> { get } | ||
|
||
/// Add new access method. | ||
/// - Parameter method: persistent access method model. | ||
func add(_ method: PersistentAccessMethod) | ||
|
||
/// Persist modified access method locating existing entry by id. | ||
/// - Parameter method: persistent access method model. | ||
func update(_ method: PersistentAccessMethod) | ||
|
||
/// Delete access method by id. | ||
/// - Parameter id: an access method id. | ||
func delete(id: UUID) | ||
|
||
/// Fetch access method by id. | ||
/// - Parameter id: an access method id. | ||
/// - Returns: a persistent access method model upon success, otherwise `nil`. | ||
func fetch(by id: UUID) -> PersistentAccessMethod? | ||
|
||
/// Fetch all access method from the persistent store. | ||
/// - Returns: an array of all persistent access method. | ||
func fetchAll() -> [PersistentAccessMethod] | ||
} |
124 changes: 124 additions & 0 deletions
124
ios/MullvadVPN/AccessMethodRepository/PersistentAccessMethod.swift
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,124 @@ | ||
// | ||
// PersistentAccessMethod.swift | ||
// MullvadVPN | ||
// | ||
// Created by pronebird on 15/11/2023. | ||
// Copyright © 2023 Mullvad VPN AB. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import MullvadTypes | ||
import Network | ||
|
||
/// Persistent access method model. | ||
struct PersistentAccessMethod: Identifiable, Codable { | ||
/// The unique identifier used for referencing the access method entry in a persistent store. | ||
var id: UUID | ||
|
||
/// The user-defined name for access method. | ||
var name: String | ||
|
||
/// The flag indicating whether configuration is enabled. | ||
var isEnabled: Bool | ||
|
||
/// Proxy configuration. | ||
var proxyConfiguration: PersistentProxyConfiguration | ||
} | ||
|
||
/// Persistent proxy configuration. | ||
enum PersistentProxyConfiguration: Codable { | ||
/// Direct communication without proxy. | ||
case direct | ||
|
||
/// Communication over bridges. | ||
case bridges | ||
|
||
/// Communication over shadowsocks. | ||
case shadowsocks(ShadowsocksConfiguration) | ||
|
||
/// Communication over socks5 proxy. | ||
case socks5(SocksConfiguration) | ||
} | ||
|
||
extension PersistentProxyConfiguration { | ||
/// Socks autentication method. | ||
enum SocksAuthentication: Codable { | ||
case noAuthentication | ||
case usernamePassword(username: String, password: String) | ||
} | ||
|
||
/// Socks v5 proxy configuration. | ||
struct SocksConfiguration: Codable { | ||
/// Proxy server address. | ||
var server: AnyIPAddress | ||
|
||
/// Proxy server port. | ||
var port: UInt16 | ||
|
||
/// Authentication method. | ||
var authentication: SocksAuthentication | ||
} | ||
|
||
/// Shadowsocks configuration. | ||
struct ShadowsocksConfiguration: Codable { | ||
/// Server address. | ||
var server: AnyIPAddress | ||
|
||
/// Server port. | ||
var port: UInt16 | ||
|
||
/// Server password. | ||
var password: String | ||
|
||
/// Server cipher. | ||
var cipher: ShadowsocksCipher | ||
} | ||
} | ||
|
||
extension PersistentAccessMethod { | ||
/// A kind of access method. | ||
var kind: AccessMethodKind { | ||
switch proxyConfiguration { | ||
case .direct: | ||
.direct | ||
case .bridges: | ||
.bridges | ||
case .shadowsocks: | ||
.shadowsocks | ||
case .socks5: | ||
.socks5 | ||
} | ||
} | ||
} | ||
|
||
/// A kind of API access method. | ||
enum AccessMethodKind: Equatable, Hashable, CaseIterable { | ||
/// Direct communication. | ||
case direct | ||
|
||
/// Communication over bridges. | ||
case bridges | ||
|
||
/// Communication over shadowsocks. | ||
case shadowsocks | ||
|
||
/// Communication over socks v5 proxy. | ||
case socks5 | ||
} | ||
|
||
extension AccessMethodKind { | ||
/// Returns `true` if the method is permanent and cannot be deleted. | ||
var isPermanent: Bool { | ||
switch self { | ||
case .direct, .bridges: | ||
true | ||
case .shadowsocks, .socks5: | ||
false | ||
} | ||
} | ||
|
||
/// Returns all access method kinds that can be added by user. | ||
static var allUserDefinedKinds: [AccessMethodKind] { | ||
allCases.filter { !$0.isPermanent } | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTester.swift
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,37 @@ | ||
// | ||
// ProxyConfigurationTester.swift | ||
// MullvadVPN | ||
// | ||
// Created by pronebird on 28/11/2023. | ||
// Copyright © 2023 Mullvad VPN AB. All rights reserved. | ||
// | ||
|
||
import Combine | ||
import Foundation | ||
|
||
/// A concrete implementation of an access method proxy configuration. | ||
class ProxyConfigurationTester: ProxyConfigurationTesterProtocol { | ||
private var cancellable: Cancellable? | ||
|
||
static let shared = ProxyConfigurationTester() | ||
|
||
init() {} | ||
|
||
func start(configuration: PersistentProxyConfiguration, completion: @escaping (Error?) -> Void) { | ||
let workItem = DispatchWorkItem { | ||
let randomResult = (0 ... 255).randomElement()?.isMultiple(of: 2) ?? true | ||
|
||
completion(randomResult ? nil : URLError(.timedOut)) | ||
} | ||
|
||
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2), execute: workItem) | ||
|
||
cancellable = AnyCancellable { | ||
workItem.cancel() | ||
} | ||
} | ||
|
||
func cancel() { | ||
cancellable = nil | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTesterProtocol.swift
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,21 @@ | ||
// | ||
// ProxyConfigurationTesterProtocol.swift | ||
// MullvadVPN | ||
// | ||
// Created by pronebird on 28/11/2023. | ||
// Copyright © 2023 Mullvad VPN AB. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Type implementing access method proxy configuration testing. | ||
protocol ProxyConfigurationTesterProtocol { | ||
/// Start testing proxy configuration. | ||
/// - Parameters: | ||
/// - configuration: a proxy configuration. | ||
/// - completion: a completion handler that receives `nil` upon success, otherwise the underlying error. | ||
func start(configuration: PersistentProxyConfiguration, completion: @escaping (Error?) -> Void) | ||
|
||
/// Cancel testing proxy configuration. | ||
func cancel() | ||
} |
48 changes: 48 additions & 0 deletions
48
ios/MullvadVPN/AccessMethodRepository/ShadowsocksCipher.swift
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,48 @@ | ||
// | ||
// ShadowsocksCipher.swift | ||
// MullvadVPN | ||
// | ||
// Created by pronebird on 13/11/2023. | ||
// Copyright © 2023 Mullvad VPN AB. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Type representing a shadowsocks cipher. | ||
struct ShadowsocksCipher: RawRepresentable, CustomStringConvertible, Equatable, Hashable, Codable { | ||
let rawValue: String | ||
|
||
var description: String { | ||
rawValue | ||
} | ||
|
||
/// Default cipher. | ||
static let `default` = ShadowsocksCipher(rawValue: "chacha20") | ||
|
||
/// All supported ciphers. | ||
static let supportedCiphers = supportedCipherIdentifiers.map { ShadowsocksCipher(rawValue: $0) } | ||
} | ||
|
||
private let supportedCipherIdentifiers = [ | ||
// Stream ciphers. | ||
"aes-128-cfb", | ||
"aes-128-cfb1", | ||
"aes-128-cfb8", | ||
"aes-128-cfb128", | ||
"aes-256-cfb", | ||
"aes-256-cfb1", | ||
"aes-256-cfb8", | ||
"aes-256-cfb128", | ||
"rc4", | ||
"rc4-md5", | ||
"chacha20", | ||
"salsa20", | ||
"chacha20-ietf", | ||
// AEAD ciphers. | ||
"aes-128-gcm", | ||
"aes-256-gcm", | ||
"chacha20-ietf-poly1305", | ||
"xchacha20-ietf-poly1305", | ||
"aes-128-pmac-siv", | ||
"aes-256-pmac-siv", | ||
] |
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
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.