From 58799a0adae62796cea6078bc6e6027a46747c97 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Mon, 24 Feb 2025 16:14:50 +1100 Subject: [PATCH] chore: clear session on launch if vpn unconfigured --- .../Coder Desktop/Coder_DesktopApp.swift | 8 ++++++ .../Coder Desktop/NetworkExtension.swift | 5 +++- Coder Desktop/Coder Desktop/VPNService.swift | 5 ++-- .../Coder Desktop/Views/LoginForm.swift | 2 ++ .../Coder Desktop/Views/VPNMenu.swift | 1 + Coder Desktop/CoderSDK/Client.swift | 2 ++ Coder Desktop/VPN/Manager.swift | 2 ++ Coder Desktop/VPN/TunnelHandle.swift | 2 ++ Coder Desktop/VPNLib/Download.swift | 8 ++++-- Coder Desktop/VPNLib/Receiver.swift | 11 +++++++- Coder Desktop/VPNLib/Speaker.swift | 25 +++++++++++++++++++ 11 files changed, 64 insertions(+), 7 deletions(-) diff --git a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift index 13f7086a..1814c118 100644 --- a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift +++ b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift @@ -49,6 +49,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { name: .NEVPNStatusDidChange, object: nil ) + Task { + // If there's no NE config, then the user needs to sign in. + // However, they might have a session from a previous install, so we + // need to clear it. + if await !vpn.loadNetworkExtensionConfig() { + state.clearSession() + } + } } // This function MUST eventually call `NSApp.reply(toApplicationShouldTerminate: true)` diff --git a/Coder Desktop/Coder Desktop/NetworkExtension.swift b/Coder Desktop/Coder Desktop/NetworkExtension.swift index effd1946..70e69b2d 100644 --- a/Coder Desktop/Coder Desktop/NetworkExtension.swift +++ b/Coder Desktop/Coder Desktop/NetworkExtension.swift @@ -24,13 +24,16 @@ enum NetworkExtensionState: Equatable { /// An actor that handles configuring, enabling, and disabling the VPN tunnel via the /// NetworkExtension APIs. extension CoderVPNService { - func loadNetworkExtensionConfig() async { + // Attempts to load the NetworkExtension configuration, returning true if successful. + func loadNetworkExtensionConfig() async -> Bool { do { let tm = try await getTunnelManager() neState = .disabled serverAddress = tm.protocolConfiguration?.serverAddress + return true } catch { neState = .unconfigured + return false } } diff --git a/Coder Desktop/Coder Desktop/VPNService.swift b/Coder Desktop/Coder Desktop/VPNService.swift index 1e29ae75..0a12ccbd 100644 --- a/Coder Desktop/Coder Desktop/VPNService.swift +++ b/Coder Desktop/Coder Desktop/VPNService.swift @@ -35,6 +35,8 @@ enum VPNServiceError: Error, Equatable { state.description } } + + var localizedDescription: String { description } } @MainActor @@ -67,9 +69,6 @@ final class CoderVPNService: NSObject, VPNService { override init() { super.init() installSystemExtension() - Task { - await loadNetworkExtensionConfig() - } } deinit { diff --git a/Coder Desktop/Coder Desktop/Views/LoginForm.swift b/Coder Desktop/Coder Desktop/Views/LoginForm.swift index 5614df58..f31ee362 100644 --- a/Coder Desktop/Coder Desktop/Views/LoginForm.swift +++ b/Coder Desktop/Coder Desktop/Views/LoginForm.swift @@ -204,6 +204,8 @@ enum LoginError: Error { "Could not authenticate with Coder deployment:\n\(err.description)" } } + + var localizedDescription: String { description } } enum LoginPage { diff --git a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift index c0a983c4..e2f6771c 100644 --- a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift +++ b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift @@ -89,6 +89,7 @@ struct VPNMenu: View { !state.hasSession || vpn.state == .connecting || vpn.state == .disconnecting || + // Prevent starting the VPN before the user has approved the system extension. vpn.state == .failed(.systemExtensionError(.needsUserApproval)) } } diff --git a/Coder Desktop/CoderSDK/Client.swift b/Coder Desktop/CoderSDK/Client.swift index 881ae99a..5f2a6a06 100644 --- a/Coder Desktop/CoderSDK/Client.swift +++ b/Coder Desktop/CoderSDK/Client.swift @@ -134,4 +134,6 @@ public enum ClientError: Error { "Failed to encode body: \(error)" } } + + public var localizedDescription: String { description } } diff --git a/Coder Desktop/VPN/Manager.swift b/Coder Desktop/VPN/Manager.swift index 95be4b23..92c06888 100644 --- a/Coder Desktop/VPN/Manager.swift +++ b/Coder Desktop/VPN/Manager.swift @@ -276,6 +276,8 @@ enum ManagerError: Error { "Failed to communicate with dylib over tunnel: \(err)" } } + + var localizedDescription: String { description } } func writeVpnLog(_ log: Vpn_Log) { diff --git a/Coder Desktop/VPN/TunnelHandle.swift b/Coder Desktop/VPN/TunnelHandle.swift index 720758ed..bebe5fa1 100644 --- a/Coder Desktop/VPN/TunnelHandle.swift +++ b/Coder Desktop/VPN/TunnelHandle.swift @@ -82,6 +82,8 @@ enum TunnelHandleError: Error { case let .close(errs): "close tunnel: \(errs.map(\.localizedDescription).joined(separator: ", "))" } } + + var localizedDescription: String { description } } enum OpenTunnelError: Int32 { diff --git a/Coder Desktop/VPNLib/Download.swift b/Coder Desktop/VPNLib/Download.swift index 4782b931..8d854a3a 100644 --- a/Coder Desktop/VPNLib/Download.swift +++ b/Coder Desktop/VPNLib/Download.swift @@ -11,7 +11,7 @@ public enum ValidationError: Error { case missingInfoPList case invalidVersion(version: String?) - public var errorDescription: String? { + public var description: String { switch self { case .fileNotFound: "The file does not exist." @@ -31,6 +31,8 @@ public enum ValidationError: Error { "Info.plist is not embedded within the dylib." } } + + public var localizedDescription: String { description } } public class SignatureValidator { @@ -156,7 +158,7 @@ public enum DownloadError: Error { case networkError(any Error) case fileOpError(any Error) - var localizedDescription: String { + public var description: String { switch self { case let .unexpectedStatusCode(code): "Unexpected HTTP status code: \(code)" @@ -168,4 +170,6 @@ public enum DownloadError: Error { "Received non-HTTP response" } } + + public var localizedDescription: String { description } } diff --git a/Coder Desktop/VPNLib/Receiver.swift b/Coder Desktop/VPNLib/Receiver.swift index 8151c3c1..699d46f3 100644 --- a/Coder Desktop/VPNLib/Receiver.swift +++ b/Coder Desktop/VPNLib/Receiver.swift @@ -75,9 +75,18 @@ actor Receiver { } } -enum ReceiveError: Error { +public enum ReceiveError: Error { case readError(String) case invalidLength + + public var description: String { + switch self { + case let .readError(err): "read error: \(err)" + case .invalidLength: "invalid message length" + } + } + + public var localizedDescription: String { description } } func deserializeLen(_ data: Data) throws -> UInt32 { diff --git a/Coder Desktop/VPNLib/Speaker.swift b/Coder Desktop/VPNLib/Speaker.swift index 27dbf2bb..b53f50a8 100644 --- a/Coder Desktop/VPNLib/Speaker.swift +++ b/Coder Desktop/VPNLib/Speaker.swift @@ -290,6 +290,19 @@ public enum HandshakeError: Error { case wrongRole(String) case invalidVersion(String) case unsupportedVersion([ProtoVersion]) + + public var description: String { + switch self { + case let .readError(err): "read error: \(err)" + case let .writeError(err): "write error: \(err)" + case let .invalidHeader(err): "invalid header: \(err)" + case let .wrongRole(err): "wrong role: \(err)" + case let .invalidVersion(err): "invalid version: \(err)" + case let .unsupportedVersion(versions): "unsupported version: \(versions)" + } + } + + public var localizedDescription: String { description } } public struct RPCRequest: Sendable { @@ -314,6 +327,18 @@ enum RPCError: Error { case notAResponse case unknownResponseID(UInt64) case shutdown + + var description: String { + switch self { + case .missingRPC: "missing RPC field" + case .notARequest: "not a request" + case .notAResponse: "not a response" + case let .unknownResponseID(id): "unknown response ID: \(id)" + case .shutdown: "RPC secretary has been shutdown" + } + } + + var localizedDescription: String { description } } /// An actor to record outgoing RPCs and route their replies to the original sender pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy