refactor(apple): Rename VPN "Profile" to VPN "Configuration" (#7755)

Apple actually calls these `VPN Configuration`s throughout the OS, so I
thought it would be good to be consistent.

Draft because stacked.
This commit is contained in:
Jamil
2025-01-14 09:51:35 -08:00
committed by GitHub
parent c349353600
commit ed6350d34a
11 changed files with 77 additions and 77 deletions

View File

@@ -28,7 +28,7 @@ enum LogExporter {
static func export(
to archiveURL: URL,
with vpnProfileManager: VPNProfileManager
with vpnConfigurationManager: VPNConfigurationManager
) async throws {
guard let logFolderURL = SharedAccess.logFolderURL
else {
@@ -53,7 +53,7 @@ enum LogExporter {
// 3. Await tunnel log export from tunnel process
try await withCheckedThrowingContinuation { continuation in
vpnProfileManager.exportLogs(
vpnConfigurationManager.exportLogs(
appender: { chunk in
do {
// Append each chunk to the archive

View File

@@ -35,7 +35,7 @@ public enum Telemetry {
public static func start() {
SentrySDK.start { options in
options.dsn = "https://66c71f83675f01abfffa8eb977bcbbf7@o4507971108339712.ingest.us.sentry.io/4508175177023488"
options.environment = "entrypoint" // will be reconfigured in VPNProfileManager
options.environment = "entrypoint" // will be reconfigured in VPNConfigurationManager
options.releaseName = releaseName()
options.dist = distributionType()

View File

@@ -1,17 +1,17 @@
//
// VPNProfileManager.swift
// VPNConfigurationManager.swift
//
//
// Created by Jamil Bou Kheir on 4/2/24.
//
// Abstracts the nitty gritty of loading and saving to our
// VPN profile in system preferences.
// VPN configuration in system preferences.
import CryptoKit
import Foundation
import NetworkExtension
enum VPNProfileManagerError: Error {
enum VPNConfigurationManagerError: Error {
case managerNotInitialized
case cannotLoad
case decodeIPCDataFailed
@@ -31,7 +31,7 @@ enum VPNProfileManagerError: Error {
}
}
public enum VPNProfileManagerKeys {
public enum VPNConfigurationManagerKeys {
static let actorName = "actorName"
static let authBaseURL = "authBaseURL"
static let apiURL = "apiURL"
@@ -109,7 +109,7 @@ public enum TunnelMessage: Codable {
}
}
public class VPNProfileManager {
public class VPNConfigurationManager {
// Connect status updates with our listeners
private var tunnelObservingTasks: [Task<Void, Never>] = []
@@ -139,19 +139,19 @@ public class VPNProfileManager {
public static let bundleIdentifier: String = "\(Bundle.main.bundleIdentifier!).network-extension"
private let bundleDescription = "Firezone"
// Initialize and save a new VPN profile in system Preferences
// Initialize and save a new VPN configuration in system Preferences
func create() async throws {
let protocolConfiguration = NETunnelProviderProtocol()
let manager = NETunnelProviderManager()
let settings = Settings.defaultValue
protocolConfiguration.providerConfiguration = settings.toProviderConfiguration()
protocolConfiguration.providerBundleIdentifier = VPNProfileManager.bundleIdentifier
protocolConfiguration.providerBundleIdentifier = VPNConfigurationManager.bundleIdentifier
protocolConfiguration.serverAddress = settings.apiURL
manager.localizedDescription = bundleDescription
manager.protocolConfiguration = protocolConfiguration
// Save the new VPN profile to System Preferences and reload it,
// Save the new VPN configuration to System Preferences and reload it,
// which should update our status from nil -> disconnected.
// If the user denied the operation, the status will be .invalid
do {
@@ -172,7 +172,7 @@ public class VPNProfileManager {
}
func loadFromPreferences(vpnStateUpdateHandler: @escaping (NEVPNStatus, Settings?, String?) -> Void) async throws {
// loadAllFromPreferences() returns list of VPN profiles created by our main app's bundle ID.
// loadAllFromPreferences() returns list of VPN configurations created by our main app's bundle ID.
// Since our bundle ID can change (by us), find the one that's current and ignore the others.
let managers = try await NETunnelProviderManager.loadAllFromPreferences()
@@ -183,15 +183,15 @@ public class VPNProfileManager {
guard let protocolConfiguration = manager.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration as? [String: String]
else {
throw VPNProfileManagerError.cannotLoad
throw VPNConfigurationManagerError.cannotLoad
}
// Update our state
self.manager = manager
let settings = Settings.fromProviderConfiguration(providerConfiguration)
let actorName = providerConfiguration[VPNProfileManagerKeys.actorName]
if let internetResourceEnabled = providerConfiguration[VPNProfileManagerKeys.internetResourceEnabled]?.data(using: .utf8) {
let actorName = providerConfiguration[VPNConfigurationManagerKeys.actorName]
if let internetResourceEnabled = providerConfiguration[VPNConfigurationManagerKeys.internetResourceEnabled]?.data(using: .utf8) {
self.internetResourceEnabled = (try? JSONDecoder().decode(Bool.self, from: internetResourceEnabled)) ?? false
@@ -200,7 +200,7 @@ public class VPNProfileManager {
// Configure our Telemetry environment
Telemetry.setEnvironmentOrClose(settings.apiURL)
Telemetry.accountSlug = providerConfiguration[VPNProfileManagerKeys.accountSlug]
Telemetry.accountSlug = providerConfiguration[VPNConfigurationManagerKeys.accountSlug]
// Share what we found with our caller
vpnStateUpdateHandler(status, settings, actorName)
@@ -225,11 +225,11 @@ public class VPNProfileManager {
let protocolConfiguration = manager.protocolConfiguration as? NETunnelProviderProtocol,
var providerConfiguration = protocolConfiguration.providerConfiguration
else {
throw VPNProfileManagerError.managerNotInitialized
throw VPNConfigurationManagerError.managerNotInitialized
}
providerConfiguration[VPNProfileManagerKeys.actorName] = authResponse.actorName
providerConfiguration[VPNProfileManagerKeys.accountSlug] = authResponse.accountSlug
providerConfiguration[VPNConfigurationManagerKeys.actorName] = authResponse.actorName
providerConfiguration[VPNConfigurationManagerKeys.accountSlug] = authResponse.accountSlug
protocolConfiguration.providerConfiguration = providerConfiguration
manager.protocolConfiguration = protocolConfiguration
@@ -246,13 +246,13 @@ public class VPNProfileManager {
let protocolConfiguration = manager.protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration as? [String: String]
else {
throw VPNProfileManagerError.managerNotInitialized
throw VPNConfigurationManagerError.managerNotInitialized
}
var newProviderConfiguration = settings.toProviderConfiguration()
// Don't clobber existing actorName
newProviderConfiguration[VPNProfileManagerKeys.actorName] = providerConfiguration[VPNProfileManagerKeys.actorName]
newProviderConfiguration[VPNConfigurationManagerKeys.actorName] = providerConfiguration[VPNConfigurationManagerKeys.actorName]
protocolConfiguration.providerConfiguration = newProviderConfiguration
protocolConfiguration.serverAddress = settings.apiURL
manager.protocolConfiguration = protocolConfiguration
@@ -337,7 +337,7 @@ public class VPNProfileManager {
return try await withCheckedThrowingContinuation { continuation in
guard let session = session()
else {
continuation.resume(throwing: VPNProfileManagerError.managerNotInitialized)
continuation.resume(throwing: VPNConfigurationManagerError.managerNotInitialized)
return
}
@@ -356,7 +356,7 @@ public class VPNProfileManager {
return try await withCheckedThrowingContinuation { continuation in
guard let session = session()
else {
continuation.resume(throwing: VPNProfileManagerError.managerNotInitialized)
continuation.resume(throwing: VPNConfigurationManagerError.managerNotInitialized)
return
}
@@ -369,7 +369,7 @@ public class VPNProfileManager {
guard let data = data
else {
continuation
.resume(throwing: VPNProfileManagerError.decodeIPCDataFailed)
.resume(throwing: VPNConfigurationManagerError.decodeIPCDataFailed)
return
}
@@ -388,7 +388,7 @@ public class VPNProfileManager {
// in AAR format.
func exportLogs(
appender: @escaping (LogChunk) -> Void,
errorHandler: @escaping (VPNProfileManagerError) -> Void
errorHandler: @escaping (VPNConfigurationManagerError) -> Void
) {
let decoder = PropertyListDecoder()
@@ -399,7 +399,7 @@ public class VPNProfileManager {
) { data in
guard let data = data
else {
errorHandler(VPNProfileManagerError.decodeIPCDataFailed)
errorHandler(VPNConfigurationManagerError.decodeIPCDataFailed)
return
}
@@ -408,7 +408,7 @@ public class VPNProfileManager {
LogChunk.self, from: data
)
else {
errorHandler(VPNProfileManagerError.decodeIPCDataFailed)
errorHandler(VPNConfigurationManagerError.decodeIPCDataFailed)
return
}
@@ -433,7 +433,7 @@ public class VPNProfileManager {
return try await withCheckedThrowingContinuation { continuation in
guard let session = session()
else {
continuation.resume(throwing: VPNProfileManagerError.managerNotInitialized)
continuation.resume(throwing: VPNConfigurationManagerError.managerNotInitialized)
return
}
@@ -453,7 +453,7 @@ public class VPNProfileManager {
guard let reason = String(data: data, encoding: .utf8)
else {
continuation
.resume(throwing: VPNProfileManagerError.decodeIPCDataFailed)
.resume(throwing: VPNConfigurationManagerError.decodeIPCDataFailed)
return
}
@@ -487,7 +487,7 @@ public class VPNProfileManager {
) {
guard let session = notification.object as? NETunnelProviderSession
else {
Log.error(VPNProfileManagerError.invalidStatusChange)
Log.error(VPNConfigurationManagerError.invalidStatusChange)
return
}

View File

@@ -32,13 +32,13 @@ struct Settings: Equatable {
static func fromProviderConfiguration(_ providerConfiguration: [String: Any]?) -> Settings {
if let providerConfiguration = providerConfiguration as? [String: String] {
return Settings(
authBaseURL: providerConfiguration[VPNProfileManagerKeys.authBaseURL]
authBaseURL: providerConfiguration[VPNConfigurationManagerKeys.authBaseURL]
?? Settings.defaultValue.authBaseURL,
apiURL: providerConfiguration[VPNProfileManagerKeys.apiURL]
apiURL: providerConfiguration[VPNConfigurationManagerKeys.apiURL]
?? Settings.defaultValue.apiURL,
logFilter: providerConfiguration[VPNProfileManagerKeys.logFilter]
logFilter: providerConfiguration[VPNConfigurationManagerKeys.logFilter]
?? Settings.defaultValue.logFilter,
internetResourceEnabled: getInternetResourceEnabled(internetResourceEnabled: providerConfiguration[VPNProfileManagerKeys.internetResourceEnabled])
internetResourceEnabled: getInternetResourceEnabled(internetResourceEnabled: providerConfiguration[VPNConfigurationManagerKeys.internetResourceEnabled])
)
} else {
return Settings.defaultValue
@@ -54,10 +54,10 @@ struct Settings: Equatable {
// Used for initializing a new providerConfiguration from Settings
func toProviderConfiguration() -> [String: String] {
return [
VPNProfileManagerKeys.authBaseURL: authBaseURL,
VPNProfileManagerKeys.apiURL: apiURL,
VPNProfileManagerKeys.logFilter: logFilter,
VPNProfileManagerKeys.internetResourceEnabled: String(data: try! JSONEncoder().encode(internetResourceEnabled) , encoding: .utf8)!,
VPNConfigurationManagerKeys.authBaseURL: authBaseURL,
VPNConfigurationManagerKeys.apiURL: apiURL,
VPNConfigurationManagerKeys.logFilter: logFilter,
VPNConfigurationManagerKeys.internetResourceEnabled: String(data: try! JSONEncoder().encode(internetResourceEnabled) , encoding: .utf8)!,
]
}

View File

@@ -31,7 +31,7 @@ public final class Store: ObservableObject {
// we could periodically update it if we need to.
@Published private(set) var decision: UNAuthorizationStatus
let vpnProfileManager: VPNProfileManager
let vpnConfigurationManager: VPNConfigurationManager
private var sessionNotification: SessionNotification
private var cancellables: Set<AnyCancellable> = []
private var resourcesTimer: Timer?
@@ -41,13 +41,13 @@ public final class Store: ObservableObject {
self.decision = .authorized
self.settings = Settings.defaultValue
self.sessionNotification = SessionNotification()
self.vpnProfileManager = VPNProfileManager()
self.vpnConfigurationManager = VPNConfigurationManager()
initNotifications()
}
public func internetResourceEnabled() -> Bool {
self.vpnProfileManager.internetResourceEnabled
self.vpnConfigurationManager.internetResourceEnabled
}
private func initNotifications() {
@@ -65,9 +65,9 @@ public final class Store: ObservableObject {
.store(in: &cancellables)
}
func bindToVPNProfileUpdates() async throws {
// Load our existing VPN profile and set an update handler
try await self.vpnProfileManager.loadFromPreferences(
func bindToVPNConfigurationUpdates() async throws {
// Load our existing VPN configuration and set an update handler
try await self.vpnConfigurationManager.loadFromPreferences(
vpnStateUpdateHandler: { [weak self] status, settings, actorName in
guard let self else { return }
@@ -98,7 +98,7 @@ public final class Store: ObservableObject {
private func maybeShowSignedOutAlert() {
Task {
do {
if let savedValue = try await self.vpnProfileManager.consumeStopReason(),
if let savedValue = try await self.vpnConfigurationManager.consumeStopReason(),
let rawValue = Int(savedValue),
let reason = NEProviderStopReason(rawValue: rawValue),
case .authenticationCanceled = reason
@@ -121,7 +121,7 @@ public final class Store: ObservableObject {
(continuation: CheckedContinuation<Bool, Error>) in
checker.isInstalled(
identifier: VPNProfileManager.bundleIdentifier,
identifier: VPNConfigurationManager.bundleIdentifier,
continuation: continuation
)
}
@@ -138,7 +138,7 @@ public final class Store: ObservableObject {
(continuation: CheckedContinuation<Bool, Error>) in
installer.installSystemExtension(
identifier: VPNProfileManager.bundleIdentifier,
identifier: VPNConfigurationManager.bundleIdentifier,
continuation: continuation
)
}
@@ -146,11 +146,11 @@ public final class Store: ObservableObject {
}
func grantVPNPermission() async throws {
// Create a new VPN profile in system settings.
try await self.vpnProfileManager.create()
// Create a new VPN configuration in system settings.
try await self.vpnConfigurationManager.create()
// Reload our state
try await bindToVPNProfileUpdates()
try await bindToVPNConfigurationUpdates()
}
func requestNotifications() {
@@ -170,25 +170,25 @@ public final class Store: ObservableObject {
return
}
self.vpnProfileManager.start(token: token)
self.vpnConfigurationManager.start(token: token)
}
func stop(clearToken: Bool = false) {
guard [.connected, .connecting, .reasserting].contains(status)
else { return }
self.vpnProfileManager.stop(clearToken: clearToken)
self.vpnConfigurationManager.stop(clearToken: clearToken)
}
func signIn(authResponse: AuthResponse) async throws {
// Save actorName
DispatchQueue.main.async { self.actorName = authResponse.actorName }
try await self.vpnProfileManager.saveSettings(settings)
try await self.vpnProfileManager.saveAuthResponse(authResponse)
try await self.vpnConfigurationManager.saveSettings(settings)
try await self.vpnConfigurationManager.saveAuthResponse(authResponse)
// Bring the tunnel up and send it a token to start
self.vpnProfileManager.start(token: authResponse.token)
self.vpnConfigurationManager.start(token: authResponse.token)
}
func signOut() async throws {
@@ -201,12 +201,12 @@ public final class Store: ObservableObject {
func beginUpdatingResources(callback: @escaping (ResourceList) -> Void) {
Log.log("\(#function)")
self.vpnProfileManager.fetchResources(callback: callback)
self.vpnConfigurationManager.fetchResources(callback: callback)
let intervalInSeconds: TimeInterval = 1
let timer = Timer(timeInterval: intervalInSeconds, repeats: true) { [weak self] _ in
Task { @MainActor in
guard let self else { return }
self.vpnProfileManager.fetchResources(callback: callback)
self.vpnConfigurationManager.fetchResources(callback: callback)
}
}
RunLoop.main.add(timer, forMode: .common)
@@ -221,7 +221,7 @@ public final class Store: ObservableObject {
func save(_ newSettings: Settings) async throws {
Task {
do {
try await self.vpnProfileManager.saveSettings(newSettings)
try await self.vpnConfigurationManager.saveSettings(newSettings)
DispatchQueue.main.async { self.settings = newSettings }
} catch {
Log.error(error)
@@ -230,9 +230,9 @@ public final class Store: ObservableObject {
}
func toggleInternetResource(enabled: Bool) {
self.vpnProfileManager.toggleInternetResource(enabled: enabled)
self.vpnConfigurationManager.toggleInternetResource(enabled: enabled)
var newSettings = settings
newSettings.internetResourceEnabled = self.vpnProfileManager.internetResourceEnabled
newSettings.internetResourceEnabled = self.vpnConfigurationManager.internetResourceEnabled
Task {
try await save(newSettings)
}

View File

@@ -34,7 +34,7 @@ public class AppViewModel: ObservableObject {
Task {
do {
try await self.store.bindToVPNProfileUpdates()
try await self.store.bindToVPNConfigurationUpdates()
#if os(macOS)
try await self.store.checkedIfInstalled()
@@ -51,7 +51,7 @@ public class AppViewModel: ObservableObject {
if self.store.status == .disconnected {
// Try to connect on start
self.store.vpnProfileManager.start()
self.store.vpnConfigurationManager.start()
}
} catch {
Log.error(error)

View File

@@ -49,7 +49,7 @@ final class GrantVPNViewModel: ObservableObject {
#if os(macOS)
// The window has a tendency to go to the background after allowing the
// VPN profile
// VPN configuration
NSApp.activate(ignoringOtherApps: true)
#endif
} catch {
@@ -75,7 +75,7 @@ struct GrantVPNView: View {
.padding(.horizontal, 10)
Spacer()
Text(
"Firezone requires your permission to create VPN profiles. Until it has that permission, all functionality will be disabled."
"Firezone requires your permission to create VPN configurations. Until it has that permission, all functionality will be disabled."
)
.font(.body)
.multilineTextAlignment(.center)
@@ -109,7 +109,7 @@ struct GrantVPNView: View {
Spacer()
Spacer()
Text("""
Firezone needs you to enable a System Extension and allow a VPN profile in order to function.
Firezone needs you to enable a System Extension and allow a VPN configuration in order to function.
""")
.font(.title2)
.multilineTextAlignment(.center)
@@ -143,7 +143,7 @@ struct GrantVPNView: View {
}
Spacer()
VStack(alignment: .center) {
Text("Step 2: Allow the VPN profile")
Text("Step 2: Allow the VPN configuration")
.font(.title)
Text("""
1. Click the "Grant VPN Permission" button below.

View File

@@ -274,7 +274,7 @@ public final class MenuBar: NSObject, ObservableObject {
Task {
do {
// If we get here, it means either system extension got disabled or
// our VPN profile got removed. Since we don't know which, reinstall
// our VPN configuration got removed. Since we don't know which, reinstall
// the system extension here too just in case. It's a no-op if already
// installed.
try await model.store.installSystemExtension()
@@ -382,7 +382,7 @@ public final class MenuBar: NSObject, ObservableObject {
// Update "Sign In" / "Sign Out" menu items
switch status {
case nil:
signInMenuItem.title = "Loading VPN profiles from system settings…"
signInMenuItem.title = "Loading VPN configurations from system settings…"
signInMenuItem.action = nil
signOutMenuItem.isHidden = true
settingsMenuItem.target = nil

View File

@@ -108,7 +108,7 @@ struct SessionView: View {
Text("Loading Resources...")
}
case nil:
Text("Loading VPN profiles from system settings…")
Text("Loading VPN configurations from system settings…")
case .connecting:
Text("Connecting...")
case .disconnecting:

View File

@@ -35,7 +35,7 @@ public final class SettingsViewModel: ObservableObject {
}
func setupObservers() {
// Load settings from saved VPN Profile
// Load settings from saved VPN Configuration
store.$settings
.receive(on: DispatchQueue.main)
.sink { [weak self] settings in
@@ -81,7 +81,7 @@ public final class SettingsViewModel: ObservableObject {
do {
#if os(macOS)
let providerLogFolderSize = try await store.vpnProfileManager.getLogFolderSize()
let providerLogFolderSize = try await store.vpnConfigurationManager.getLogFolderSize()
let totalSize = logFolderSize + providerLogFolderSize
#else
let totalSize = logFolderSize
@@ -110,7 +110,7 @@ public final class SettingsViewModel: ObservableObject {
try Log.clear(in: SharedAccess.logFolderURL)
#if os(macOS)
try await store.vpnProfileManager.clearLogs()
try await store.vpnConfigurationManager.clearLogs()
#endif
}
}
@@ -610,7 +610,7 @@ public struct SettingsView: View {
do {
try await LogExporter.export(
to: destinationURL,
with: model.store.vpnProfileManager
with: model.store.vpnConfigurationManager
)
await MainActor.run {

View File

@@ -65,16 +65,16 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
guard
let providerConfiguration = (protocolConfiguration as? NETunnelProviderProtocol)?
.providerConfiguration as? [String: String],
let logFilter = providerConfiguration[VPNProfileManagerKeys.logFilter]
let logFilter = providerConfiguration[VPNConfigurationManagerKeys.logFilter]
else {
throw PacketTunnelProviderError
.savedProtocolConfigurationIsInvalid("providerConfiguration.logFilter")
}
// Hydrate telemetry account slug
guard let accountSlug = providerConfiguration[VPNProfileManagerKeys.accountSlug]
guard let accountSlug = providerConfiguration[VPNConfigurationManagerKeys.accountSlug]
else {
// This can happen if the user deletes the VPN profile while it's
// This can happen if the user deletes the VPN configuration while it's
// connected. The system will try to restart us with a fresh config
// once the user fixes the problem, but we'd rather not connect
// without a slug.
@@ -84,7 +84,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
Telemetry.accountSlug = accountSlug
let internetResourceEnabled: Bool = if let internetResourceEnabledJSON = providerConfiguration[VPNProfileManagerKeys.internetResourceEnabled]?.data(using: .utf8) {
let internetResourceEnabled: Bool = if let internetResourceEnabledJSON = providerConfiguration[VPNConfigurationManagerKeys.internetResourceEnabled]?.data(using: .utf8) {
(try? JSONDecoder().decode(Bool.self, from: internetResourceEnabledJSON )) ?? false
} else {
false