Implement UpdateTunnelConfiguration App Intent
Signed-off-by: Alessio Nossa <alessio.nossa@gmail.com>
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||
|
||||
import AppIntents
|
||||
|
||||
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
|
||||
struct UpdateTunnelConfiguration: AppIntent {
|
||||
|
||||
static var title = LocalizedStringResource("updateTunnelConfigurationIntentName", table: "AppIntents")
|
||||
static var description = IntentDescription(
|
||||
LocalizedStringResource("updateTunnelConfigurationDescription", table: "AppIntents")
|
||||
)
|
||||
|
||||
@Parameter(
|
||||
title: LocalizedStringResource("updateTunnelConfigurationIntentTunnelParameterTitle", table: "AppIntents"),
|
||||
optionsProvider: TunnelsOptionsProvider()
|
||||
)
|
||||
var tunnelName: String
|
||||
|
||||
@Parameter(
|
||||
title: LocalizedStringResource("updateTunnelConfigurationIntentPeersParameterTitle", table: "AppIntents"),
|
||||
optionsProvider: AppIntentsPeerOptionsProvider()
|
||||
)
|
||||
var peers: [AppIntentsPeer]?
|
||||
|
||||
@Parameter(
|
||||
title: LocalizedStringResource("updateTunnelConfigurationIntentMergeParameterTitle", table: "AppIntents"),
|
||||
default: true
|
||||
)
|
||||
var mergeConfiguration: Bool
|
||||
|
||||
@Dependency
|
||||
var tunnelsManager: TunnelsManager
|
||||
|
||||
func perform() async throws -> some IntentResult {
|
||||
guard let peers else { throw AppIntentConfigurationUpdateError.missingPeerParameter }
|
||||
|
||||
guard let tunnelContainer = tunnelsManager.tunnel(named: tunnelName) else {
|
||||
throw AppIntentConfigurationUpdateError.wrongTunnel(name: tunnelName)
|
||||
}
|
||||
|
||||
guard let tunnelConfiguration = tunnelContainer.tunnelConfiguration else {
|
||||
throw AppIntentConfigurationUpdateError.missingConfiguration
|
||||
}
|
||||
|
||||
let newConfiguration = try buildNewConfiguration(from: tunnelConfiguration, peersUpdates: peers, mergeChanges: mergeConfiguration)
|
||||
|
||||
do {
|
||||
try await tunnelsManager.modify(tunnel: tunnelContainer, tunnelConfiguration: newConfiguration, onDemandOption: tunnelContainer.onDemandOption)
|
||||
} catch {
|
||||
wg_log(.error, message: error.localizedDescription)
|
||||
throw error
|
||||
}
|
||||
|
||||
wg_log(.debug, message: "Updated configuration of tunnel \(tunnelName)")
|
||||
|
||||
return .result()
|
||||
}
|
||||
|
||||
static var parameterSummary: some ParameterSummary {
|
||||
Summary("updateTunnelConfigurationIntentSummary \(\.$tunnelName)", table: "AppIntents") {
|
||||
\.$peers
|
||||
\.$mergeConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
private func buildNewConfiguration(from oldConfiguration: TunnelConfiguration, peersUpdates: [AppIntentsPeer], mergeChanges: Bool) throws -> TunnelConfiguration {
|
||||
var peers = oldConfiguration.peers
|
||||
|
||||
for peerUpdate in peersUpdates {
|
||||
let peerIndex: Array<PeerConfiguration>.Index
|
||||
if let foundIndex = peers.firstIndex(where: { $0.publicKey.base64Key == peerUpdate.publicKey }) {
|
||||
peerIndex = foundIndex
|
||||
if mergeChanges == false {
|
||||
peers[peerIndex] = PeerConfiguration(publicKey: peers[peerIndex].publicKey)
|
||||
}
|
||||
} else {
|
||||
wg_log(.debug, message: "Failed to find peer \(peerUpdate.publicKey) in tunnel with name \(tunnelName). Adding it.")
|
||||
|
||||
guard let pubKeyEncoded = PublicKey(base64Key: peerUpdate.publicKey) else {
|
||||
throw AppIntentConfigurationUpdateError.malformedPublicKey(key: peerUpdate.publicKey)
|
||||
}
|
||||
let newPeerConfig = PeerConfiguration(publicKey: pubKeyEncoded)
|
||||
peerIndex = peers.endIndex
|
||||
peers.append(newPeerConfig)
|
||||
}
|
||||
|
||||
if let endpointString = peerUpdate.endpoint {
|
||||
if let newEntpoint = Endpoint(from: endpointString) {
|
||||
peers[peerIndex].endpoint = newEntpoint
|
||||
} else {
|
||||
wg_log(.debug, message: "Failed to convert \(endpointString) to Endpoint")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let newConfiguration = TunnelConfiguration(name: oldConfiguration.name, interface: oldConfiguration.interface, peers: peers)
|
||||
return newConfiguration
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
|
||||
struct AppIntentsPeerOptionsProvider: DynamicOptionsProvider {
|
||||
|
||||
func results() async throws -> ItemCollection<AppIntentsPeer> {
|
||||
// The error thrown here is not displayed correctly to the user. A Feedback
|
||||
// has been opened (FB12098463).
|
||||
throw AppIntentConfigurationUpdateError.peerOptionsUnavailable
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *)
|
||||
enum AppIntentConfigurationUpdateError: Swift.Error, CustomLocalizedStringResourceConvertible {
|
||||
case wrongTunnel(name: String)
|
||||
case missingConfiguration
|
||||
case peerOptionsUnavailable
|
||||
case missingPeerParameter
|
||||
case malformedPublicKey(key: String)
|
||||
|
||||
var localizedStringResource: LocalizedStringResource {
|
||||
switch self {
|
||||
case .wrongTunnel(let name):
|
||||
return LocalizedStringResource("wireguardAppIntentsWrongTunnelError \(name)", table: "AppIntents")
|
||||
case .missingConfiguration:
|
||||
return LocalizedStringResource("wireguardAppIntentsMissingConfigurationError", table: "AppIntents")
|
||||
case .peerOptionsUnavailable:
|
||||
return LocalizedStringResource("updateTunnelConfigurationIntentPeerOptionsUnavailableError", table: "AppIntents")
|
||||
case .missingPeerParameter:
|
||||
return LocalizedStringResource("updateTunnelConfigurationIntentMissingPeerParameterError", table: "AppIntents")
|
||||
case .malformedPublicKey(let malformedKey):
|
||||
return LocalizedStringResource("updateTunnelConfigurationIntentMalformedPublicKeyError \(malformedKey)", table: "AppIntents")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user