Skip to content

Commit ee03536

Browse files
committed
Implement UpdateTunnelConfiguration App Intent
Signed-off-by: Alessio Nossa <[email protected]>
1 parent e13bc40 commit ee03536

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

Diff for: Sources/WireguardAppIntents/AppIntents.strings

+12
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@
1111
"getPeersIntentTunnelParameterTitle" = "Tunnel";
1212
"getPeersIntentSummary ${tunnelName}" = "Get peers of ${tunnelName}";
1313

14+
// Tunnel Configuration Update
15+
"updateTunnelConfigurationIntentName" = "Update Tunnel Configuration";
16+
"updateTunnelConfigurationDescription" = "Update peers configuration of the selected tunnel.";
17+
"updateTunnelConfigurationIntentTunnelParameterTitle" = "Tunnel";
18+
"updateTunnelConfigurationIntentPeersParameterTitle" = "Peers";
19+
"updateTunnelConfigurationIntentMergeParameterTitle" = "Merge configuration";
20+
"updateTunnelConfigurationIntentSummary ${tunnelName}" = "Update ${tunnelName} configuration";
21+
22+
"updateTunnelConfigurationIntentPeerOptionsUnavailableError" = "Use the output of \"Build Peer Configuration\" action to update tunnel configuration.";
23+
"updateTunnelConfigurationIntentMissingPeerParameterError" = "Peer parameter value is missing";
24+
"updateTunnelConfigurationIntentMalformedPublicKeyError %@" = "The key \"%1$@\" is not a valid Public Key encoded in Base64 format.";
25+
1426
// Build Peer Configuration
1527
"buildPeerConfigurationUpdateIntentName" = "Build Peer Configuration";
1628
"buildPeerConfigurationUpdateIntentDescription" = "";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
3+
4+
import AppIntents
5+
6+
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
7+
struct UpdateTunnelConfiguration: AppIntent {
8+
9+
static var title = LocalizedStringResource("updateTunnelConfigurationIntentName", table: "AppIntents")
10+
static var description = IntentDescription(
11+
LocalizedStringResource("updateTunnelConfigurationDescription", table: "AppIntents")
12+
)
13+
14+
@Parameter(
15+
title: LocalizedStringResource("updateTunnelConfigurationIntentTunnelParameterTitle", table: "AppIntents"),
16+
optionsProvider: TunnelsOptionsProvider()
17+
)
18+
var tunnelName: String
19+
20+
@Parameter(
21+
title: LocalizedStringResource("updateTunnelConfigurationIntentPeersParameterTitle", table: "AppIntents"),
22+
optionsProvider: AppIntentsPeerOptionsProvider()
23+
)
24+
var peers: [AppIntentsPeer]?
25+
26+
@Parameter(
27+
title: LocalizedStringResource("updateTunnelConfigurationIntentMergeParameterTitle", table: "AppIntents"),
28+
default: true
29+
)
30+
var mergeConfiguration: Bool
31+
32+
@Dependency
33+
var tunnelsManager: TunnelsManager
34+
35+
func perform() async throws -> some IntentResult {
36+
guard let peers else { throw AppIntentConfigurationUpdateError.missingPeerParameter }
37+
38+
guard let tunnelContainer = tunnelsManager.tunnel(named: tunnelName) else {
39+
throw AppIntentConfigurationUpdateError.wrongTunnel(name: tunnelName)
40+
}
41+
42+
guard let tunnelConfiguration = tunnelContainer.tunnelConfiguration else {
43+
throw AppIntentConfigurationUpdateError.missingConfiguration
44+
}
45+
46+
let newConfiguration = try buildNewConfiguration(from: tunnelConfiguration, peersUpdates: peers, mergeChanges: mergeConfiguration)
47+
48+
do {
49+
try await tunnelsManager.modify(tunnel: tunnelContainer, tunnelConfiguration: newConfiguration, onDemandOption: tunnelContainer.onDemandOption)
50+
} catch {
51+
wg_log(.error, message: error.localizedDescription)
52+
throw error
53+
}
54+
55+
wg_log(.debug, message: "Updated configuration of tunnel \(tunnelName)")
56+
57+
return .result()
58+
}
59+
60+
static var parameterSummary: some ParameterSummary {
61+
Summary("updateTunnelConfigurationIntentSummary \(\.$tunnelName)", table: "AppIntents") {
62+
\.$peers
63+
\.$mergeConfiguration
64+
}
65+
}
66+
67+
private func buildNewConfiguration(from oldConfiguration: TunnelConfiguration, peersUpdates: [AppIntentsPeer], mergeChanges: Bool) throws -> TunnelConfiguration {
68+
var peers = oldConfiguration.peers
69+
70+
for peerUpdate in peersUpdates {
71+
let peerIndex: Array<PeerConfiguration>.Index
72+
if let foundIndex = peers.firstIndex(where: { $0.publicKey.base64Key == peerUpdate.publicKey }) {
73+
peerIndex = foundIndex
74+
if mergeChanges == false {
75+
peers[peerIndex] = PeerConfiguration(publicKey: peers[peerIndex].publicKey)
76+
}
77+
} else {
78+
wg_log(.debug, message: "Failed to find peer \(peerUpdate.publicKey) in tunnel with name \(tunnelName). Adding it.")
79+
80+
guard let pubKeyEncoded = PublicKey(base64Key: peerUpdate.publicKey) else {
81+
throw AppIntentConfigurationUpdateError.malformedPublicKey(key: peerUpdate.publicKey)
82+
}
83+
let newPeerConfig = PeerConfiguration(publicKey: pubKeyEncoded)
84+
peerIndex = peers.endIndex
85+
peers.append(newPeerConfig)
86+
}
87+
88+
if let endpointString = peerUpdate.endpoint {
89+
if let newEntpoint = Endpoint(from: endpointString) {
90+
peers[peerIndex].endpoint = newEntpoint
91+
} else {
92+
wg_log(.debug, message: "Failed to convert \(endpointString) to Endpoint")
93+
}
94+
}
95+
}
96+
97+
let newConfiguration = TunnelConfiguration(name: oldConfiguration.name, interface: oldConfiguration.interface, peers: peers)
98+
return newConfiguration
99+
}
100+
}
101+
102+
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
103+
struct AppIntentsPeerOptionsProvider: DynamicOptionsProvider {
104+
105+
func results() async throws -> ItemCollection<AppIntentsPeer> {
106+
// The error thrown here is not displayed correctly to the user. A Feedback
107+
// has been opened (FB12098463).
108+
throw AppIntentConfigurationUpdateError.peerOptionsUnavailable
109+
}
110+
}
111+
112+
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
113+
enum AppIntentConfigurationUpdateError: Swift.Error, CustomLocalizedStringResourceConvertible {
114+
case wrongTunnel(name: String)
115+
case missingConfiguration
116+
case peerOptionsUnavailable
117+
case missingPeerParameter
118+
case malformedPublicKey(key: String)
119+
120+
var localizedStringResource: LocalizedStringResource {
121+
switch self {
122+
case .wrongTunnel(let name):
123+
return LocalizedStringResource("wireguardAppIntentsWrongTunnelError \(name)", table: "AppIntents")
124+
case .missingConfiguration:
125+
return LocalizedStringResource("wireguardAppIntentsMissingConfigurationError", table: "AppIntents")
126+
case .peerOptionsUnavailable:
127+
return LocalizedStringResource("updateTunnelConfigurationIntentPeerOptionsUnavailableError", table: "AppIntents")
128+
case .missingPeerParameter:
129+
return LocalizedStringResource("updateTunnelConfigurationIntentMissingPeerParameterError", table: "AppIntents")
130+
case .malformedPublicKey(let malformedKey):
131+
return LocalizedStringResource("updateTunnelConfigurationIntentMalformedPublicKeyError \(malformedKey)", table: "AppIntents")
132+
}
133+
}
134+
}

Diff for: WireGuard.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
6FFACD2021E4D8D500E9A2A5 /* ParseError+WireGuardAppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFACD1E21E4D89600E9A2A5 /* ParseError+WireGuardAppError.swift */; };
208208
A625F05529C4C627005EF23D /* GetPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A625F05029C4C627005EF23D /* GetPeers.swift */; };
209209
A6E361F829D8758500FFF234 /* AppIntents.strings in Resources */ = {isa = PBXBuildFile; fileRef = A6E361F729D8758500FFF234 /* AppIntents.strings */; };
210+
A6E361FA29D9821200FFF234 /* UpdateTunnelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E361F929D9821100FFF234 /* UpdateTunnelConfiguration.swift */; };
210211
A6E361FC29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E361FB29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift */; };
211212
A6E361FE29D9B18C00FFF234 /* TunnelsOptionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E361FD29D9B18C00FFF234 /* TunnelsOptionsProvider.swift */; };
212213
/* End PBXBuildFile section */
@@ -444,6 +445,7 @@
444445
6FFACD1E21E4D89600E9A2A5 /* ParseError+WireGuardAppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseError+WireGuardAppError.swift"; sourceTree = "<group>"; };
445446
A625F05029C4C627005EF23D /* GetPeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetPeers.swift; sourceTree = "<group>"; };
446447
A6E361F729D8758500FFF234 /* AppIntents.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = AppIntents.strings; sourceTree = "<group>"; };
448+
A6E361F929D9821100FFF234 /* UpdateTunnelConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTunnelConfiguration.swift; sourceTree = "<group>"; };
447449
A6E361FB29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildPeerConfigurationUpdate.swift; sourceTree = "<group>"; };
448450
A6E361FD29D9B18C00FFF234 /* TunnelsOptionsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelsOptionsProvider.swift; sourceTree = "<group>"; };
449451
/* End PBXFileReference section */
@@ -823,6 +825,7 @@
823825
A625F04C29C4C627005EF23D /* WireguardAppIntents */ = {
824826
isa = PBXGroup;
825827
children = (
828+
A6E361F929D9821100FFF234 /* UpdateTunnelConfiguration.swift */,
826829
A6E361FB29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift */,
827830
A625F05029C4C627005EF23D /* GetPeers.swift */,
828831
A6E361F729D8758500FFF234 /* AppIntents.strings */,
@@ -1427,6 +1430,7 @@
14271430
6FF3527221C2616C0008484E /* ringlogger.c in Sources */,
14281431
6F0F44CB222D55FD00B0FF04 /* EditableTextCell.swift in Sources */,
14291432
585B105E2577E293004F691E /* PeerConfiguration.swift in Sources */,
1433+
A6E361FA29D9821200FFF234 /* UpdateTunnelConfiguration.swift in Sources */,
14301434
6FF3527321C2616C0008484E /* Logger.swift in Sources */,
14311435
6F7774E421718281006A79B3 /* TunnelsListTableViewController.swift in Sources */,
14321436
585B108E2577E294004F691E /* x25519.c in Sources */,

0 commit comments

Comments
 (0)