Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita4d3cac

Browse files
committed
chore: make helper launchdaemon approval mandatory
1 parent5affac0 commita4d3cac

File tree

9 files changed

+162
-156
lines changed

9 files changed

+162
-156
lines changed

‎Coder-Desktop/Coder-Desktop/Coder_DesktopApp.swift‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ struct DesktopApp: App {
2626
SettingsView<CoderVPNService>()
2727
.environmentObject(appDelegate.vpn)
2828
.environmentObject(appDelegate.state)
29-
.environmentObject(appDelegate.helper)
3029
.environmentObject(appDelegate.autoUpdater)
3130
}
3231
.windowResizability(.contentSize)
@@ -48,13 +47,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
4847
letfileSyncDaemon:MutagenDaemon
4948
leturlHandler:URLHandler
5049
letnotifDelegate:NotifDelegate
51-
lethelper:HelperService
5250
letautoUpdater:UpdaterService
5351

5452
overrideinit(){
5553
notifDelegate=NotifDelegate()
5654
vpn=CoderVPNService()
57-
helper=HelperService()
5855
autoUpdater=UpdaterService()
5956
letstate=AppState(onChange: vpn.configureTunnelProviderProtocol)
6057
vpn.onStart={
@@ -95,10 +92,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
9592
image:"MenuBarIcon",
9693
onAppear:{
9794
// If the VPN is enabled, it's likely the token isn't expired
98-
guardself.vpn.state!=.connected,self.state.hasSessionelse{return}
9995
Task{@MainActorin
96+
guardself.vpn.state!=.connected,self.state.hasSessionelse{return}
10097
awaitself.state.handleTokenExpiry()
10198
}
99+
// If the Helper is pending approval, we should check if it's
100+
// been approved when the tray is opened.
101+
Task{@MainActorin
102+
guardself.vpn.state==.failed(.helperError(.requiresApproval))else{return}
103+
self.vpn.refreshHelperState()
104+
}
102105
}, content:{
103106
VPNMenu<CoderVPNService,MutagenDaemon>().frame(width:256)
104107
.environmentObject(self.vpn)
@@ -119,6 +122,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
119122
ifawait !vpn.loadNetworkExtensionConfig(){
120123
state.reconfigure()
121124
}
125+
await vpn.setupHelper()
122126
}
123127
}
124128

‎Coder-Desktop/Coder-Desktop/HelperService.swift‎

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,84 @@
11
import os
22
import ServiceManagement
33

4-
// Whilst the GUI app installs the helper, the System Extension communicates
5-
// with it over XPC
6-
@MainActor
7-
classHelperService:ObservableObject{
8-
privateletlogger=Logger(subsystem:Bundle.main.bundleIdentifier!, category:"HelperService")
9-
letplistName="com.coder.Coder-Desktop.Helper.plist"
10-
@Publishedvarstate:HelperState=.uninstalled{
11-
didSet{
12-
logger.info("helper daemon state set:\(self.state.description, privacy:.public)")
13-
}
14-
}
4+
extensionCoderVPNService{
5+
varplistName:String{"com.coder.Coder-Desktop.Helper.plist"}
156

16-
init(){
17-
update()
7+
func refreshHelperState(){
8+
letdaemon=SMAppService.daemon(plistName: plistName)
9+
helperState=HelperState(status: daemon.status)
1810
}
1911

20-
func update(){
21-
letdaemon=SMAppService.daemon(plistName: plistName)
22-
state=HelperState(status: daemon.status)
12+
func setupHelper()async{
13+
refreshHelperState()
14+
switch helperState{
15+
case.uninstalled,.failed:
16+
awaitinstallHelper()
17+
case.installed:
18+
uninstallHelper()
19+
awaitinstallHelper()
20+
case.requiresApproval,.installing:
21+
break
22+
}
2323
}
2424

25-
func install(){
26-
letdaemon=SMAppService.daemon(plistName: plistName)
27-
do{
28-
try daemon.register()
29-
}catchlet error asNSError{
30-
self.state=.failed(.init(error: error))
31-
}catch{
32-
state=.failed(.unknown(error.localizedDescription))
25+
privatefunc installHelper()async{
26+
// Worst case, this setup takes a few seconds. We'll show a loading
27+
// indicator in the meantime.
28+
helperState=.installing
29+
varlastUnknownError:Error?
30+
// Registration may fail with a permissions error if it was
31+
// just unregistered, so we retry a few times.
32+
for_in0...10{
33+
letdaemon=SMAppService.daemon(plistName: plistName)
34+
do{
35+
try daemon.register()
36+
helperState=HelperState(status: daemon.status)
37+
return
38+
}catch{
39+
if daemon.status==.requiresApproval{
40+
helperState=.requiresApproval
41+
return
42+
}
43+
lethelperError=HelperError(error: errorasNSError)
44+
switch helperError{
45+
case.alreadyRegistered:
46+
helperState=.installed
47+
return
48+
case.launchDeniedByUser,.invalidSignature:
49+
// Something weird happened, we should update the UI
50+
helperState=.failed(helperError)
51+
return
52+
case.unknown:
53+
// Likely intermittent permissions error, we'll retry
54+
lastUnknownError= error
55+
logger.warning("failed to register helper:\(helperError.localizedDescription)")
56+
}
57+
58+
// Short delay before retrying
59+
try?awaitTask.sleep(for:.milliseconds(500))
60+
}
3361
}
34-
state=HelperState(status: daemon.status)
62+
// Give up, update the UI with the error
63+
helperState=.failed(.unknown(lastUnknownError?.localizedDescription??"Unknown"))
3564
}
3665

37-
funcuninstall(){
66+
privatefuncuninstallHelper(){
3867
letdaemon=SMAppService.daemon(plistName: plistName)
3968
do{
4069
try daemon.unregister()
4170
}catchlet error asNSError{
42-
self.state=.failed(.init(error: error))
71+
helperState=.failed(.init(error: error))
4372
}catch{
44-
state=.failed(.unknown(error.localizedDescription))
73+
helperState=.failed(.unknown(error.localizedDescription))
4574
}
46-
state=HelperState(status: daemon.status)
75+
helperState=HelperState(status: daemon.status)
4776
}
4877
}
4978

5079
enumHelperState:Equatable{
5180
case uninstalled
81+
case installing
5282
case installed
5383
case requiresApproval
5484
case failed(HelperError)
@@ -57,6 +87,8 @@ enum HelperState: Equatable {
5787
switchself{
5888
case.uninstalled:
5989
"Uninstalled"
90+
case.installing:
91+
"Installing"
6092
case.installed:
6193
"Installed"
6294
case.requiresApproval:

‎Coder-Desktop/Coder-Desktop/Preview Content/PreviewVPN.swift‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,7 @@ final class PreviewVPN: Coder_Desktop.VPNService {
8181
state=.connecting
8282
}
8383

84+
func updateHelperState(){}
85+
8486
var startWhenReady: Bool= false
8587
}

‎Coder-Desktop/Coder-Desktop/VPN/VPNService.swift‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ enum VPNServiceError: Error, Equatable {
3636
case internalError(String)
3737
case systemExtensionError(SystemExtensionState)
3838
case networkExtensionError(NetworkExtensionState)
39+
case helperError(HelperState)
3940

4041
vardescription:String{
4142
switchself{
@@ -45,6 +46,8 @@ enum VPNServiceError: Error, Equatable {
4546
"SystemExtensionError:\(state.description)"
4647
caselet.networkExtensionError(state):
4748
"NetworkExtensionError:\(state.description)"
49+
caselet.helperError(state):
50+
"HelperError:\(state.description)"
4851
}
4952
}
5053

@@ -67,6 +70,13 @@ final class CoderVPNService: NSObject, VPNService {
6770
@PublishedvarsysExtnState:SystemExtensionState=.uninstalled
6871
@PublishedvarneState:NetworkExtensionState=.unconfigured
6972
varstate:VPNServiceState{
73+
// The ordering here is important. The button to open the settings page
74+
// where the helper is approved is a no-op if the user has a settings
75+
// window on the page where the system extension is approved.
76+
// So, we want to ensure the helper settings button is clicked first.
77+
guard helperState==.installedelse{
78+
return.failed(.helperError(helperState))
79+
}
7080
guard sysExtnState==.installedelse{
7181
return.failed(.systemExtensionError(sysExtnState))
7282
}
@@ -80,6 +90,8 @@ final class CoderVPNService: NSObject, VPNService {
8090
return tunnelState
8191
}
8292

93+
@PublishedvarhelperState:HelperState=.uninstalled
94+
8395
@Publishedvarprogress:VPNProgress=.init(stage:.initial, downloadProgress:nil)
8496

8597
@PublishedvarmenuState:VPNMenuState=.init()
@@ -107,6 +119,14 @@ final class CoderVPNService: NSObject, VPNService {
107119
return
108120
}
109121

122+
// We have to manually fetch the helper state,
123+
// and we don't want to start the VPN
124+
// if the helper is not ready.
125+
refreshHelperState()
126+
if helperState!=.installed{
127+
return
128+
}
129+
110130
menuState.clear()
111131
awaitstartTunnel()
112132
logger.debug("network extension enabled")

‎Coder-Desktop/Coder-Desktop/Views/Settings/ExperimentalTab.swift‎

Lines changed: 0 additions & 10 deletions
This file was deleted.

‎Coder-Desktop/Coder-Desktop/Views/Settings/HelperSection.swift‎

Lines changed: 0 additions & 82 deletions
This file was deleted.

‎Coder-Desktop/Coder-Desktop/Views/Settings/Settings.swift‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ struct SettingsView<VPN: VPNService>: View {
1313
.tabItem{
1414
Label("Network", systemImage:"dot.radiowaves.left.and.right")
1515
}.tag(SettingsTab.network)
16-
ExperimentalTab()
17-
.tabItem{
18-
Label("Experimental", systemImage:"gearshape.2")
19-
}.tag(SettingsTab.experimental)
20-
2116
}.frame(width:600)
2217
.frame(maxHeight:500)
2318
.scrollContentBackground(.hidden)
@@ -28,5 +23,4 @@ struct SettingsView<VPN: VPNService>: View {
2823
enumSettingsTab:Int{
2924
case general
3025
case network
31-
case experimental
3226
}

‎Coder-Desktop/Coder-Desktop/Views/VPN/VPNMenu.swift‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,11 @@ struct VPNMenu<VPN: VPNService, FS: FileSyncDaemon>: View {
124124
// Prevent starting the VPN before the user has approved the system extension.
125125
vpn.state==.failed(.systemExtensionError(.needsUserApproval)) ||
126126
// Prevent starting the VPN without a VPN configuration.
127-
vpn.state==.failed(.networkExtensionError(.unconfigured))
127+
vpn.state==.failed(.networkExtensionError(.unconfigured)) ||
128+
// Prevent starting the VPN before the Helper is approved
129+
vpn.state==.failed(.helperError(.requiresApproval)) ||
130+
// Prevent starting the VPN before the Helper is installed
131+
vpn.state==.failed(.helperError(.installing))
128132
)
129133
}
130134
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp