From 9690365dd430000059780d8b40a82783ccffe1d3 Mon Sep 17 00:00:00 2001 From: Roopesh Chander Date: Wed, 3 Apr 2019 16:32:12 +0530 Subject: [PATCH] macOS: Better handling of tunnels created by another user Previously, the tunnels just got deleted. Signed-off-by: Roopesh Chander --- .../NETunnelProviderProtocol+Extension.swift | 6 +- WireGuard/WireGuard.xcodeproj/project.pbxproj | 4 ++ .../WireGuard/Base.lproj/Localizable.strings | 6 ++ .../WireGuard/Tunnel/TunnelsManager.swift | 17 ++++- .../ManageTunnelsRootViewController.swift | 20 +++++- .../UnusableTunnelDetailViewController.swift | 71 +++++++++++++++++++ 6 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 WireGuard/WireGuard/UI/macOS/ViewController/UnusableTunnelDetailViewController.swift diff --git a/WireGuard/Shared/Model/NETunnelProviderProtocol+Extension.swift b/WireGuard/Shared/Model/NETunnelProviderProtocol+Extension.swift index e3da61a..856df1f 100644 --- a/WireGuard/Shared/Model/NETunnelProviderProtocol+Extension.swift +++ b/WireGuard/Shared/Model/NETunnelProviderProtocol+Extension.swift @@ -49,9 +49,9 @@ extension NETunnelProviderProtocol { Keychain.deleteReference(called: ref) } - func verifyConfigurationReference() -> Data? { - guard let ref = passwordReference else { return nil } - return Keychain.verifyReference(called: ref) ? ref : nil + func verifyConfigurationReference() -> Bool { + guard let ref = passwordReference else { return false } + return Keychain.verifyReference(called: ref) } @discardableResult diff --git a/WireGuard/WireGuard.xcodeproj/project.pbxproj b/WireGuard/WireGuard.xcodeproj/project.pbxproj index 9ae6f0f..a7ceac9 100644 --- a/WireGuard/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard/WireGuard.xcodeproj/project.pbxproj @@ -93,6 +93,7 @@ 6F919EDB218C65C50023B400 /* wireguard_doc_logo_64x64.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED7218C65C50023B400 /* wireguard_doc_logo_64x64.png */; }; 6F919EDC218C65C50023B400 /* wireguard_doc_logo_320x320.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED8218C65C50023B400 /* wireguard_doc_logo_320x320.png */; }; 6F9B8A8E223398610041B9C4 /* SSIDOptionDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F9B8A8D223398610041B9C4 /* SSIDOptionDetailTableViewController.swift */; }; + 6FADE96C2254B8C300B838A4 /* UnusableTunnelDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FADE96A2254A6C200B838A4 /* UnusableTunnelDetailViewController.swift */; }; 6FB1017921C57DE600766195 /* MockTunnels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB1017821C57DE600766195 /* MockTunnels.swift */; }; 6FB17946222FD5960018AE71 /* OnDemandWiFiControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB17945222FD5960018AE71 /* OnDemandWiFiControls.swift */; }; 6FB1BD6021D2607A00A991BF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */; }; @@ -342,6 +343,7 @@ 6F919ED7218C65C50023B400 /* wireguard_doc_logo_64x64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_64x64.png; sourceTree = ""; }; 6F919ED8218C65C50023B400 /* wireguard_doc_logo_320x320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_320x320.png; sourceTree = ""; }; 6F9B8A8D223398610041B9C4 /* SSIDOptionDetailTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSIDOptionDetailTableViewController.swift; sourceTree = ""; }; + 6FADE96A2254A6C200B838A4 /* UnusableTunnelDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnusableTunnelDetailViewController.swift; sourceTree = ""; }; 6FB1017821C57DE600766195 /* MockTunnels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnels.swift; sourceTree = ""; }; 6FB17945222FD5960018AE71 /* OnDemandWiFiControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnDemandWiFiControls.swift; sourceTree = ""; }; 6FB1BD5D21D2607A00A991BF /* WireGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WireGuard.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -646,6 +648,7 @@ 6FCD99A821E0E0C700BA4C82 /* ButtonedDetailViewController.swift */, 6FCD99B021E0EDA900BA4C82 /* TunnelEditViewController.swift */, 6FDB6D12224A15BE00EE4BC3 /* LogViewController.swift */, + 6FADE96A2254A6C200B838A4 /* UnusableTunnelDetailViewController.swift */, ); path = ViewController; sourceTree = ""; @@ -1281,6 +1284,7 @@ 5F52D0BF21E3788900283CEA /* NSColor+Hex.swift in Sources */, 6FB1BDBE21D50F0200A991BF /* Logger.swift in Sources */, 6FB1BDBF21D50F0200A991BF /* TunnelConfiguration+WgQuickConfig.swift in Sources */, + 6FADE96C2254B8C300B838A4 /* UnusableTunnelDetailViewController.swift in Sources */, 6FFACD2021E4D8D500E9A2A5 /* ParseError+WireGuardAppError.swift in Sources */, 6FB1BDC021D50F0200A991BF /* NETunnelProviderProtocol+Extension.swift in Sources */, 6FBA101821D656000051C35F /* StatusMenu.swift in Sources */, diff --git a/WireGuard/WireGuard/Base.lproj/Localizable.strings b/WireGuard/WireGuard/Base.lproj/Localizable.strings index c4e9cb1..5e9b6d5 100644 --- a/WireGuard/WireGuard/Base.lproj/Localizable.strings +++ b/WireGuard/WireGuard/Base.lproj/Localizable.strings @@ -398,3 +398,9 @@ "macLogColumnTitleLogMessage" = "Log message"; "macLogButtonTitleClose" = "Close"; "macLogButtonTitleSave" = "Save…"; + +// Mac unusable tunnel view + +"macUnusableTunnelMessage" = "The configuration for this tunnel cannot be found in the keychain."; +"macUnusableTunnelInfo" = "In case this tunnel was created by another user, only that user can view, edit, or activate this tunnel."; +"macUnusableTunnelButtonTitleDeleteTunnel" = "Delete tunnel"; diff --git a/WireGuard/WireGuard/Tunnel/TunnelsManager.swift b/WireGuard/WireGuard/Tunnel/TunnelsManager.swift index 8f4c87c..3b976f4 100644 --- a/WireGuard/WireGuard/Tunnel/TunnelsManager.swift +++ b/WireGuard/WireGuard/Tunnel/TunnelsManager.swift @@ -47,11 +47,18 @@ class TunnelsManager { var tunnelManagers = managers ?? [] var refs: Set = [] for (index, tunnelManager) in tunnelManagers.enumerated().reversed() { - let proto = tunnelManager.protocolConfiguration as? NETunnelProviderProtocol - if proto?.migrateConfigurationIfNeeded(called: tunnelManager.localizedDescription ?? "unknown") ?? false { + guard let proto = tunnelManager.protocolConfiguration as? NETunnelProviderProtocol else { continue } + if proto.migrateConfigurationIfNeeded(called: tunnelManager.localizedDescription ?? "unknown") { tunnelManager.saveToPreferences { _ in } } - if let ref = proto?.verifyConfigurationReference() { + #if os(iOS) + let passwordRef = proto.verifyConfigurationReference() ? proto.passwordReference : nil + #elseif os(macOS) + let passwordRef = proto.passwordReference // To handle multiple users in macOS, we skip verifying + #else + #error("Unimplemented") + #endif + if let ref = passwordRef { refs.insert(ref) } else { tunnelManager.removeFromPreferences { _ in } @@ -455,6 +462,10 @@ class TunnelContainer: NSObject { return tunnelProvider.tunnelConfiguration } + var isTunnelConfigurationAvailableInKeychain: Bool { + return (tunnelProvider.protocolConfiguration as? NETunnelProviderProtocol)?.verifyConfigurationReference() ?? false + } + var onDemandOption: ActivateOnDemandOption { return ActivateOnDemandOption(from: tunnelProvider) } diff --git a/WireGuard/WireGuard/UI/macOS/ViewController/ManageTunnelsRootViewController.swift b/WireGuard/WireGuard/UI/macOS/ViewController/ManageTunnelsRootViewController.swift index 18b8fcb..de41963 100644 --- a/WireGuard/WireGuard/UI/macOS/ViewController/ManageTunnelsRootViewController.swift +++ b/WireGuard/WireGuard/UI/macOS/ViewController/ManageTunnelsRootViewController.swift @@ -81,9 +81,23 @@ extension ManageTunnelsRootViewController: TunnelsListTableViewControllerDelegat assert(!tunnelIndices.isEmpty) if tunnelIndices.count == 1 { let tunnel = tunnelsManager.tunnel(at: tunnelIndices.first!) - let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel) - setTunnelDetailContentVC(tunnelDetailVC) - self.tunnelDetailVC = tunnelDetailVC + if tunnel.isTunnelConfigurationAvailableInKeychain { + let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel) + setTunnelDetailContentVC(tunnelDetailVC) + self.tunnelDetailVC = tunnelDetailVC + } else { + let unusableTunnelDetailVC: UnusableTunnelDetailViewController + if let unusableTunnelContentVC = tunnelDetailContentVC as? UnusableTunnelDetailViewController { + unusableTunnelDetailVC = unusableTunnelContentVC + } else { + unusableTunnelDetailVC = UnusableTunnelDetailViewController() + } + unusableTunnelDetailVC.onButtonClicked = { [weak tunnelsListVC] in + tunnelsListVC?.handleRemoveTunnelAction() + } + setTunnelDetailContentVC(unusableTunnelDetailVC) + self.tunnelDetailVC = nil + } } else if tunnelIndices.count > 1 { let multiSelectionVC: ButtonedDetailViewController if let buttonedDetailVC = tunnelDetailContentVC as? ButtonedDetailViewController { diff --git a/WireGuard/WireGuard/UI/macOS/ViewController/UnusableTunnelDetailViewController.swift b/WireGuard/WireGuard/UI/macOS/ViewController/UnusableTunnelDetailViewController.swift new file mode 100644 index 0000000..612e8c1 --- /dev/null +++ b/WireGuard/WireGuard/UI/macOS/ViewController/UnusableTunnelDetailViewController.swift @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved. + +import Cocoa + +class UnusableTunnelDetailViewController: NSViewController { + + var onButtonClicked: (() -> Void)? + + let messageLabel: NSTextField = { + let text = tr("macUnusableTunnelMessage") + let boldFont = NSFont.boldSystemFont(ofSize: NSFont.systemFontSize) + let boldText = NSAttributedString(string: text, attributes: [.font: boldFont]) + let label = NSTextField(labelWithAttributedString: boldText) + return label + }() + + let infoLabel: NSTextField = { + let label = NSTextField(wrappingLabelWithString: tr("macUnusableTunnelInfo")) + return label + }() + + let button: NSButton = { + let button = NSButton() + button.title = tr("macUnusableTunnelButtonTitleDeleteTunnel") + button.setButtonType(.momentaryPushIn) + button.bezelStyle = .rounded + return button + }() + + init() { + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + + button.target = self + button.action = #selector(buttonClicked) + + let margin: CGFloat = 20 + let internalSpacing: CGFloat = 20 + let buttonSpacing: CGFloat = 30 + let stackView = NSStackView(views: [messageLabel, infoLabel, button]) + stackView.orientation = .vertical + stackView.edgeInsets = NSEdgeInsets(top: margin, left: margin, bottom: margin, right: margin) + stackView.spacing = internalSpacing + stackView.setCustomSpacing(buttonSpacing, after: infoLabel) + + let view = NSView() + view.addSubview(stackView) + stackView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + stackView.widthAnchor.constraint(equalToConstant: 360), + stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + view.widthAnchor.constraint(greaterThanOrEqualToConstant: 420), + view.heightAnchor.constraint(greaterThanOrEqualToConstant: 240) + ]) + + self.view = view + } + + @objc func buttonClicked() { + onButtonClicked?() + } +}