diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 37b8fe137c..89bdce7194 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -1112,7 +1112,6 @@ 4B4D60D32A0C84F700BCD287 /* UserText+NetworkProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D60D22A0C84F700BCD287 /* UserText+NetworkProtection.swift */; }; 4B4D60D42A0C84F700BCD287 /* UserText+NetworkProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D60D22A0C84F700BCD287 /* UserText+NetworkProtection.swift */; }; 4B4D60DD2A0C875E00BCD287 /* NetworkProtectionOptionKeyExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D605F2A0B29FA00BCD287 /* NetworkProtectionOptionKeyExtension.swift */; }; - 4B4D60DE2A0C875E00BCD287 /* NetworkProtectionBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D605E2A0B29FA00BCD287 /* NetworkProtectionBundle.swift */; }; 4B4D60DF2A0C875F00BCD287 /* NetworkProtectionOptionKeyExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D605F2A0B29FA00BCD287 /* NetworkProtectionOptionKeyExtension.swift */; }; 4B4D60E02A0C875F00BCD287 /* NetworkProtectionBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D605E2A0B29FA00BCD287 /* NetworkProtectionBundle.swift */; }; 4B4D60E22A0C883A00BCD287 /* Main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D60E12A0C883A00BCD287 /* Main.swift */; }; @@ -1323,8 +1322,14 @@ 7B1E819E27C8874900FF0E60 /* ContentOverlayPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1E819B27C8874900FF0E60 /* ContentOverlayPopover.swift */; }; 7B1E819F27C8874900FF0E60 /* ContentOverlay.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B1E819C27C8874900FF0E60 /* ContentOverlay.storyboard */; }; 7B1E81A027C8874900FF0E60 /* ContentOverlayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1E819D27C8874900FF0E60 /* ContentOverlayViewController.swift */; }; - 7B2DDD0B2A93EA570039D884 /* NetworkProtectionAppEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2DDD0A2A93EA570039D884 /* NetworkProtectionAppEvents.swift */; }; - 7B2DDD0D2A93EB630039D884 /* NetworkProtectionAppEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2DDD0A2A93EA570039D884 /* NetworkProtectionAppEvents.swift */; }; + 7B2DDCF82A93A8BB0039D884 /* NetworkProtectionAppEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2DDCF72A93A8BB0039D884 /* NetworkProtectionAppEvents.swift */; }; + 7B2DDCFA2A93B25F0039D884 /* KeychainType+ClientDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD3AF5C2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift */; }; + 7B2DDCFB2A93B25F0039D884 /* KeychainType+ClientDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD3AF5C2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift */; }; + 7B2DDD022A93BAA60039D884 /* NetworkProtectionBouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2DDD012A93BAA60039D884 /* NetworkProtectionBouncer.swift */; }; + 7B2DDD032A93BBEC0039D884 /* NetworkProtectionBouncer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2DDD012A93BAA60039D884 /* NetworkProtectionBouncer.swift */; }; + 7B2DDD052A93BEE20039D884 /* FeatureProtectedTunnelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2DDD042A93BEE20039D884 /* FeatureProtectedTunnelController.swift */; }; + 7B2DDD072A93C17D0039D884 /* UserText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2DDD062A93C17D0039D884 /* UserText.swift */; }; + 7B2DDD092A93C2440039D884 /* AppLauncher+DefaultInitializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2DDD082A93C2440039D884 /* AppLauncher+DefaultInitializer.swift */; }; 7B2E52252A5FEC09000C6D39 /* NetworkProtectionAgentNotificationsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2E52242A5FEC09000C6D39 /* NetworkProtectionAgentNotificationsPresenter.swift */; }; 7B430EA12A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B430EA02A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift */; }; 7B430EA22A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B430EA02A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift */; }; @@ -1338,6 +1343,7 @@ 7BBD45B22A691AB500C83CA9 /* NetworkProtectionDebugUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBD45B02A691AB500C83CA9 /* NetworkProtectionDebugUtilities.swift */; }; 7BBD45B42A691C3A00C83CA9 /* NetworkProtectionLoginItemsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBD45B32A691C3A00C83CA9 /* NetworkProtectionLoginItemsManager.swift */; }; 7BBD45B52A691C3A00C83CA9 /* NetworkProtectionLoginItemsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBD45B32A691C3A00C83CA9 /* NetworkProtectionLoginItemsManager.swift */; }; + 7BD3AF5D2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD3AF5C2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift */; }; 7BE146072A6A83C700C313B8 /* NetworkProtectionDebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BE146062A6A83C700C313B8 /* NetworkProtectionDebugMenu.swift */; }; 7BE146082A6A83C700C313B8 /* NetworkProtectionDebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BE146062A6A83C700C313B8 /* NetworkProtectionDebugMenu.swift */; }; 85012B0229133F9F003D0DCC /* NavigationBarPopovers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85012B0129133F9F003D0DCC /* NavigationBarPopovers.swift */; }; @@ -2588,7 +2594,11 @@ 7B1E819B27C8874900FF0E60 /* ContentOverlayPopover.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentOverlayPopover.swift; sourceTree = ""; }; 7B1E819C27C8874900FF0E60 /* ContentOverlay.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ContentOverlay.storyboard; sourceTree = ""; }; 7B1E819D27C8874900FF0E60 /* ContentOverlayViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentOverlayViewController.swift; sourceTree = ""; }; - 7B2DDD0A2A93EA570039D884 /* NetworkProtectionAppEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionAppEvents.swift; sourceTree = ""; }; + 7B2DDCF72A93A8BB0039D884 /* NetworkProtectionAppEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionAppEvents.swift; sourceTree = ""; }; + 7B2DDD012A93BAA60039D884 /* NetworkProtectionBouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionBouncer.swift; sourceTree = ""; }; + 7B2DDD042A93BEE20039D884 /* FeatureProtectedTunnelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureProtectedTunnelController.swift; sourceTree = ""; }; + 7B2DDD062A93C17D0039D884 /* UserText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserText.swift; sourceTree = ""; }; + 7B2DDD082A93C2440039D884 /* AppLauncher+DefaultInitializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppLauncher+DefaultInitializer.swift"; sourceTree = ""; }; 7B2E52242A5FEC09000C6D39 /* NetworkProtectionAgentNotificationsPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionAgentNotificationsPresenter.swift; sourceTree = ""; }; 7B430EA02A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionSimulateFailureMenu.swift; sourceTree = ""; }; 7B4CE8DA26F02108009134B1 /* UI Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "UI Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -2602,6 +2612,7 @@ 7BB108582A43375D000AB95F /* PFMoveApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFMoveApplication.m; sourceTree = ""; }; 7BBD45B02A691AB500C83CA9 /* NetworkProtectionDebugUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionDebugUtilities.swift; sourceTree = ""; }; 7BBD45B32A691C3A00C83CA9 /* NetworkProtectionLoginItemsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionLoginItemsManager.swift; sourceTree = ""; }; + 7BD3AF5C2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeychainType+ClientDefault.swift"; sourceTree = ""; }; 7BE146062A6A83C700C313B8 /* NetworkProtectionDebugMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionDebugMenu.swift; sourceTree = ""; }; 85012B0129133F9F003D0DCC /* NavigationBarPopovers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarPopovers.swift; sourceTree = ""; }; 850E8DFA2A6FEC5E00691187 /* BookmarksBarAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksBarAppearance.swift; sourceTree = ""; }; @@ -3887,6 +3898,10 @@ isa = PBXGroup; children = ( 4B2D06512A11D19B00DE1F49 /* DuckDuckGoAgentAppDelegate.swift */, + 7B2DDD082A93C2440039D884 /* AppLauncher+DefaultInitializer.swift */, + 7B2DDD042A93BEE20039D884 /* FeatureProtectedTunnelController.swift */, + 7B2DDD012A93BAA60039D884 /* NetworkProtectionBouncer.swift */, + 7B2DDD062A93C17D0039D884 /* UserText.swift */, 4B2D06522A11D19B00DE1F49 /* Assets.xcassets */, 4B2D06442A11CFBE00DE1F49 /* DuckDuckGoAgent.entitlements */, 4B2D06532A11D19B00DE1F49 /* DuckDuckGoAgentAppStore.entitlements */, @@ -4004,10 +4019,11 @@ 4B4D60652A0B29FA00BCD287 /* NetworkProtectionNavBarButtonModel.swift */, 4B8F52402A18326600BE7131 /* NetworkProtectionTunnelController.swift */, 4B4D60692A0B29FA00BCD287 /* NetworkProtection+ConvenienceInitializers.swift */, + 7BD3AF5C2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift */, 4B4D606A2A0B29FA00BCD287 /* NetworkProtectionControllerErrorStore.swift */, 4B4D606B2A0B29FA00BCD287 /* Invite */, 4B4D60722A0B29FA00BCD287 /* EventMapping+NetworkProtectionError.swift */, - 7B2DDD0A2A93EA570039D884 /* NetworkProtectionAppEvents.swift */, + 7B2DDCF72A93A8BB0039D884 /* NetworkProtectionAppEvents.swift */, ); path = BothAppTargets; sourceTree = ""; @@ -7723,7 +7739,6 @@ 3706FB46293F65D500E42796 /* FirePopoverWrapperViewController.swift in Sources */, 3706FB47293F65D500E42796 /* NSPasteboardItemExtension.swift in Sources */, 3706FB48293F65D500E42796 /* AutofillPreferencesModel.swift in Sources */, - 4B4D60DE2A0C875E00BCD287 /* NetworkProtectionBundle.swift in Sources */, 3706FB49293F65D500E42796 /* NSException+Catch.swift in Sources */, 3706FB4A293F65D500E42796 /* PasswordManagementNoteModel.swift in Sources */, 3706FB4B293F65D500E42796 /* CookieNotificationAnimationModel.swift in Sources */, @@ -8446,13 +8461,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7B2DDD052A93BEE20039D884 /* FeatureProtectedTunnelController.swift in Sources */, B6F92BA22A691580002ABA6B /* UserDefaultsWrapper.swift in Sources */, 4B2D065B2A11D1FF00DE1F49 /* Logging.swift in Sources */, 4B2D06572A11D19B00DE1F49 /* DuckDuckGoAgentAppDelegate.swift in Sources */, + 7B2DDCFA2A93B25F0039D884 /* KeychainType+ClientDefault.swift in Sources */, + 7B2DDD072A93C17D0039D884 /* UserText.swift in Sources */, B6F92BAC2A6937B3002ABA6B /* OptionalExtension.swift in Sources */, + 7B2DDD092A93C2440039D884 /* AppLauncher+DefaultInitializer.swift in Sources */, B6F92BAA2A691A44002ABA6B /* NetworkProtectionUserDefaultsConstants.swift in Sources */, EEC589DB2A4F1CE700BCD60C /* AppLauncher.swift in Sources */, B65DA5EF2A77CC3A00CBEE8D /* Bundle+NetworkProtectionExtensions.swift in Sources */, + 7B2DDD022A93BAA60039D884 /* NetworkProtectionBouncer.swift in Sources */, B65DA5F12A77D2BC00CBEE8D /* BundleExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -8461,12 +8481,14 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 7B2DDCFB2A93B25F0039D884 /* KeychainType+ClientDefault.swift in Sources */, B6F92BA32A691583002ABA6B /* UserDefaultsWrapper.swift in Sources */, 4B2D067C2A13340900DE1F49 /* Logging.swift in Sources */, 4B2D067A2A1333EF00DE1F49 /* DuckDuckGoAgentAppDelegate.swift in Sources */, B6F92BAD2A6937B5002ABA6B /* OptionalExtension.swift in Sources */, B6F92BAB2A691A44002ABA6B /* NetworkProtectionUserDefaultsConstants.swift in Sources */, EEC589DC2A4F1CE800BCD60C /* AppLauncher.swift in Sources */, + 7B2DDD032A93BBEC0039D884 /* NetworkProtectionBouncer.swift in Sources */, B65DA5F02A77CC3C00CBEE8D /* Bundle+NetworkProtectionExtensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -8852,6 +8874,7 @@ 4BE65478271FCD41008D1D63 /* PasswordManagementNoteItemView.swift in Sources */, AA5C8F632591021700748EB7 /* NSApplicationExtension.swift in Sources */, AA9E9A5625A3AE8400D1959D /* NSWindowExtension.swift in Sources */, + 7BD3AF5D2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift in Sources */, AAC5E4C725D6A6E8007F5990 /* BookmarkPopover.swift in Sources */, 37CC53F027E8D1440028713D /* PreferencesDownloadsView.swift in Sources */, B6F7127E29F6779000594A45 /* QRSharingService.swift in Sources */, @@ -9149,6 +9172,7 @@ 856C98D52570116900A22F1F /* NSWindow+Toast.swift in Sources */, B31055C427A1BA1D001AC618 /* AutoconsentUserScript.swift in Sources */, 859E7D6B27453BF3009C2B69 /* BookmarksExporter.swift in Sources */, + 7B2DDCF82A93A8BB0039D884 /* NetworkProtectionAppEvents.swift in Sources */, 4B5FF67826B602B100D42879 /* FirefoxDataImporter.swift in Sources */, 37AFCE8B27DB69BC00471A10 /* PreferencesGeneralView.swift in Sources */, 37BF3F22286F0A7A00BD9014 /* PinnedTabsView.swift in Sources */, @@ -10241,7 +10265,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 75.0.2; + version = 75.0.3; }; }; AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c32ad6ad05..a1e77c3c7a 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "8d4c3d884762d954183a9496961b87d254b43539", - "version" : "75.0.2" + "revision" : "9bf7b16196ac4a78bf9189841d9823047b5c141b", + "version" : "75.0.3" } }, { diff --git a/DuckDuckGo/Common/Localizables/UserText.swift b/DuckDuckGo/Common/Localizables/UserText.swift index 2080344da8..deb713a3cf 100644 --- a/DuckDuckGo/Common/Localizables/UserText.swift +++ b/DuckDuckGo/Common/Localizables/UserText.swift @@ -811,9 +811,4 @@ struct UserText { static let disableEmailProtectionTitle = NSLocalizedString("disable.email.protection.title", value: "Disable Email Protection Autofill?", comment: "Title for alert shown when user disables email protection") static let disableEmailProtectionMessage = NSLocalizedString("disable.email.protection.mesage", value: "This will only disable Autofill for Duck Addresses in this browser. \n\n You can still manually enter Duck Addresses and continue to receive forwarded email.", comment: "Message for alert shown when user disables email protection") static let disable = NSLocalizedString("disable", value: "Disable", comment: "Email protection Disable button text") - - - - } - diff --git a/DuckDuckGo/DuckDuckGo.entitlements b/DuckDuckGo/DuckDuckGo.entitlements index 607a816ca4..a12f6a14bb 100644 --- a/DuckDuckGo/DuckDuckGo.entitlements +++ b/DuckDuckGo/DuckDuckGo.entitlements @@ -19,7 +19,7 @@ keychain-access-groups $(AppIdentifierPrefix)com.duckduckgo.macos.browser - $(AppIdentifierPrefix)com.duckduckgo.network-protection + $(NETP_APP_GROUP) com.apple.developer.system-extension.install diff --git a/DuckDuckGo/DuckDuckGoAppStore.entitlements b/DuckDuckGo/DuckDuckGoAppStore.entitlements index 9d0255e659..340f405420 100644 --- a/DuckDuckGo/DuckDuckGoAppStore.entitlements +++ b/DuckDuckGo/DuckDuckGoAppStore.entitlements @@ -30,6 +30,7 @@ keychain-access-groups $(AppIdentifierPrefix)com.duckduckgo.mobile.ios + $(NETP_APP_GROUP) diff --git a/DuckDuckGo/DuckDuckGoDebug.entitlements b/DuckDuckGo/DuckDuckGoDebug.entitlements index c5cb100fc2..6316638341 100644 --- a/DuckDuckGo/DuckDuckGoDebug.entitlements +++ b/DuckDuckGo/DuckDuckGoDebug.entitlements @@ -13,7 +13,7 @@ keychain-access-groups $(AppIdentifierPrefix)com.duckduckgo.macos.browser - $(AppIdentifierPrefix)com.duckduckgo.network-protection + $(NETP_APP_GROUP) com.apple.developer.usernotifications.time-sensitive diff --git a/DuckDuckGo/NetworkProtection/AppAndExtensionTargets/AppAndExtensionAndNotificationTargets/NetworkProtectionBundle.swift b/DuckDuckGo/NetworkProtection/AppAndExtensionTargets/AppAndExtensionAndNotificationTargets/NetworkProtectionBundle.swift index 3876c26209..3faab89430 100644 --- a/DuckDuckGo/NetworkProtection/AppAndExtensionTargets/AppAndExtensionAndNotificationTargets/NetworkProtectionBundle.swift +++ b/DuckDuckGo/NetworkProtection/AppAndExtensionTargets/AppAndExtensionAndNotificationTargets/NetworkProtectionBundle.swift @@ -17,6 +17,7 @@ // import Foundation +import NetworkProtection enum NetworkProtectionBundle { @@ -67,11 +68,11 @@ enum NetworkProtectionBundle { return extensionBundle } - static func usesSystemKeychain() -> Bool { + static let keychainType: KeychainType = { #if NETP_SYSTEM_EXTENSION - true + .system #else - false + .accessGroup(.named(Bundle.main.appGroupName)) #endif - } + }() } diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/KeychainType+ClientDefault.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/KeychainType+ClientDefault.swift new file mode 100644 index 0000000000..b083cc93c8 --- /dev/null +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/KeychainType+ClientDefault.swift @@ -0,0 +1,30 @@ +// +// KeychainType+ClientDefault.swift +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import NetworkProtection + +/// Implements convenience default for the client apps making use of this. +/// +/// If you add this to a new target, please make sure this default is the correct one for the target. +/// Because the default may not be right for all targets, please avoid sharing this in a framework directly. +/// This is meant to be a client-specific definition by its own nature. +/// +extension KeychainType { + static let `default`: KeychainType = .dataProtection(.named(Bundle.main.appGroupName)) +} diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtection+ConvenienceInitializers.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtection+ConvenienceInitializers.swift index 4aaa5d4ad9..b73b218a34 100644 --- a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtection+ConvenienceInitializers.swift +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtection+ConvenienceInitializers.swift @@ -40,14 +40,14 @@ extension NetworkProtectionCodeRedemptionCoordinator { extension NetworkProtectionKeychainTokenStore { convenience init() { - self.init(useSystemKeychain: false, + self.init(keychainType: .default, errorEvents: .networkProtectionAppDebugEvents) } } extension NetworkProtectionKeychainKeyStore { convenience init() { - self.init(useSystemKeychain: false, + self.init(keychainType: .default, errorEvents: .networkProtectionAppDebugEvents) } } diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionAppEvents.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionAppEvents.swift index 814bd00233..30ef263490 100644 --- a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionAppEvents.swift +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionAppEvents.swift @@ -30,6 +30,8 @@ final class NetworkProtectionAppEvents { /// Call this method when the app finishes launching, to run the startup logic for NetP. /// func applicationDidFinishLaunching() { + migrateNetworkProtectionAuthTokenToSharedKeychainIfNecessary() + let loginItemsManager = NetworkProtectionLoginItemsManager() let keychainStore = NetworkProtectionKeychainTokenStore() @@ -43,6 +45,37 @@ final class NetworkProtectionAppEvents { refreshNetworkProtectionServers() } + /// If necessary, this method migrates the auth token from an unspecified data protection keychain (our previous + /// storage location), to the new shared keychain, which is where apps in our app group will try to access the NetP + /// auth token. + /// + /// This method bails out on any error condition - the user will probably have to re-enter their auth token if we can't + /// migrate this, and that's ok. This migration only affects internal users so it's not worth pixeling, and it's not worth + /// alerting the user to an error since they'll see Network Protection disable and eventually re-enable it. + /// + private func migrateNetworkProtectionAuthTokenToSharedKeychainIfNecessary() { + let sharedKeychainStore = NetworkProtectionKeychainTokenStore() + + guard !sharedKeychainStore.isFeatureActivated else { + // We only migrate if the auth token is missing from our new shared keychain. + return + } + + let anyDataProtectionKeychainStore = NetworkProtectionKeychainTokenStore(keychainType: .dataProtection(.unspecified), errorEvents: nil) + + guard let token = try? anyDataProtectionKeychainStore.fetchToken() else { + // If fetching the token fails, we just assume we can't migrate anything and the user + // will need to re-enable NetP. + return + } + + do { + try sharedKeychainStore.store(token) + } catch { + print(String(describing: error)) + } + } + private func restartNetworkProtectionIfVersionChanged(using loginItemsManager: NetworkProtectionLoginItemsManager) { let currentVersion = AppVersion.shared.versionNumber let versionStore = NetworkProtectionLastVersionRunStore() diff --git a/DuckDuckGo/NetworkProtection/NetworkExtensionTargets/NetworkExtensionTargets/MacPacketTunnelProvider.swift b/DuckDuckGo/NetworkProtection/NetworkExtensionTargets/NetworkExtensionTargets/MacPacketTunnelProvider.swift index b2a8a7e6a3..36990e0d8c 100644 --- a/DuckDuckGo/NetworkProtection/NetworkExtensionTargets/NetworkExtensionTargets/MacPacketTunnelProvider.swift +++ b/DuckDuckGo/NetworkProtection/NetworkExtensionTargets/NetworkExtensionTargets/MacPacketTunnelProvider.swift @@ -154,7 +154,7 @@ final class MacPacketTunnelProvider: PacketTunnelProvider { super.init(notificationsPresenter: Self.makeNotificationsPresenter(), tunnelHealthStore: tunnelHealthStore, controllerErrorStore: controllerErrorStore, - useSystemKeychain: NetworkProtectionBundle.usesSystemKeychain(), + keychainType: NetworkProtectionBundle.keychainType, debugEvents: Self.networkProtectionDebugEvents(controllerErrorStore: controllerErrorStore), providerEvents: Self.packetTunnelProviderEvents) diff --git a/DuckDuckGoAgent/AppLauncher+DefaultInitializer.swift b/DuckDuckGoAgent/AppLauncher+DefaultInitializer.swift new file mode 100644 index 0000000000..08523cd486 --- /dev/null +++ b/DuckDuckGoAgent/AppLauncher+DefaultInitializer.swift @@ -0,0 +1,36 @@ +// +// AppLauncher+DefaultInitializer.swift +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// Includes a convenience default initializer for `AppLauncher` that's specific to this App target. +/// +extension AppLauncher { + convenience init() { + let appBundleURL: URL + let parentBundlePath = "../../../../" + + if #available(macOS 13, *) { + appBundleURL = URL(filePath: parentBundlePath, relativeTo: Bundle.main.bundleURL) + } else { + appBundleURL = URL(fileURLWithPath: parentBundlePath, relativeTo: Bundle.main.bundleURL) + } + + self.init(appBundleURL: appBundleURL) + } +} diff --git a/DuckDuckGoAgent/DuckDuckGoAgent.entitlements b/DuckDuckGoAgent/DuckDuckGoAgent.entitlements index a8bfad46cd..4382d0cc65 100644 --- a/DuckDuckGoAgent/DuckDuckGoAgent.entitlements +++ b/DuckDuckGoAgent/DuckDuckGoAgent.entitlements @@ -4,6 +4,10 @@ com.apple.security.app-sandbox + keychain-access-groups + + $(NETP_APP_GROUP) + com.apple.security.application-groups $(NETP_APP_GROUP) diff --git a/DuckDuckGoAgent/DuckDuckGoAgentAppDelegate.swift b/DuckDuckGoAgent/DuckDuckGoAgentAppDelegate.swift index 2e47dc4726..f315cfb046 100644 --- a/DuckDuckGoAgent/DuckDuckGoAgentAppDelegate.swift +++ b/DuckDuckGoAgent/DuckDuckGoAgentAppDelegate.swift @@ -17,10 +17,12 @@ // import Cocoa +import Combine import Common import NetworkExtension import NetworkProtection import NetworkProtectionUI +import ServiceManagement @objc(Application) final class DuckDuckGoAgentApplication: NSApplication { @@ -56,17 +58,11 @@ final class DuckDuckGoAgentAppDelegate: NSObject, NSApplicationDelegate { private var agentLaunchTime: Date private static let recentThreshold: TimeInterval = 5.0 - private lazy var appLauncher: AppLauncher = { - let appBundleURL: URL - let parentBundlePath = "../../../../" + private let appLauncher = AppLauncher() + private let bouncer = NetworkProtectionBouncer() - if #available(macOS 13, *) { - appBundleURL = URL(filePath: parentBundlePath, relativeTo: Bundle.main.bundleURL) - } else { - appBundleURL = URL(fileURLWithPath: parentBundlePath, relativeTo: Bundle.main.bundleURL) - } - - return AppLauncher(appBundleURL: appBundleURL) + private lazy var tunnelController: FeatureProtectingTunnelController = { + FeatureProtectingTunnelController(appLauncher: appLauncher, bouncer: bouncer) }() /// The status bar NetworkProtection menu @@ -82,13 +78,24 @@ final class DuckDuckGoAgentAppDelegate: NSObject, NSApplicationDelegate { let iconProvider = MenuIconProvider() #endif - return StatusBarMenu(appLauncher: appLauncher, iconProvider: iconProvider) + let menuItems = [ + StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuShareFeedback, action: { [weak self] in + await self?.appLauncher.launchApp(withCommand: .shareFeedback) + }), + StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuOpenDuckDuckGo, action: { [weak self] in + await self?.appLauncher.launchApp(withCommand: .justOpen) + }) + ] + + return StatusBarMenu(controller: tunnelController, iconProvider: iconProvider, menuItems: menuItems) }() func applicationDidFinishLaunching(_ aNotification: Notification) { os_log("DuckDuckGoAgent started", log: .networkProtectionLoginItemLog, type: .info) networkProtectionMenu.show() + bouncer.requireAuthTokenOrKillApp() + // Connect on Log In if shouldAutoConnect, // are we launched by the system? diff --git a/DuckDuckGoAgent/DuckDuckGoAgentAppStore.entitlements b/DuckDuckGoAgent/DuckDuckGoAgentAppStore.entitlements index f70dc2d7d7..7de1c91761 100644 --- a/DuckDuckGoAgent/DuckDuckGoAgentAppStore.entitlements +++ b/DuckDuckGoAgent/DuckDuckGoAgentAppStore.entitlements @@ -4,6 +4,10 @@ com.apple.security.app-sandbox + keychain-access-groups + + $(NETP_APP_GROUP) + com.apple.security.application-groups $(TeamIdentifierPrefix)com.duckduckgo.macos.browser.network-protection diff --git a/DuckDuckGoAgent/FeatureProtectedTunnelController.swift b/DuckDuckGoAgent/FeatureProtectedTunnelController.swift new file mode 100644 index 0000000000..bbff5da7a8 --- /dev/null +++ b/DuckDuckGoAgent/FeatureProtectedTunnelController.swift @@ -0,0 +1,43 @@ +// +// FeatureProtectedTunnelController.swift +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import NetworkProtection +import NetworkProtectionUI + +/// A tunnel controller that will kill the current app if Network Protection is disabled. +/// +final class FeatureProtectingTunnelController: TunnelController { + private let controller: AppLaunchingController + private let bouncer: NetworkProtectionBouncer + + init(appLauncher: AppLauncher, bouncer: NetworkProtectionBouncer) { + self.controller = AppLaunchingController(appLauncher: appLauncher) + self.bouncer = bouncer + } + + func start() async { + bouncer.requireAuthTokenOrKillApp() + await controller.start() + } + + func stop() async { + bouncer.requireAuthTokenOrKillApp() + await controller.stop() + } +} diff --git a/DuckDuckGoAgent/NetworkProtectionBouncer.swift b/DuckDuckGoAgent/NetworkProtectionBouncer.swift new file mode 100644 index 0000000000..a01834bb3e --- /dev/null +++ b/DuckDuckGoAgent/NetworkProtectionBouncer.swift @@ -0,0 +1,40 @@ +// +// NetworkProtectionBouncer.swift +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Common +import Foundation +import NetworkProtection +import ServiceManagement +import AppKit + +/// Class that implements the necessary logic to ensure Network Protection is enabled, or prevent the app from running otherwise. +/// +final class NetworkProtectionBouncer { + + /// Simply verifies that the Network Protection feature is enabled and if not, takes care of killing the + /// current app. + /// + func requireAuthTokenOrKillApp() { + let keychainStore = NetworkProtectionKeychainTokenStore(keychainType: .default, errorEvents: nil) + + guard keychainStore.isFeatureActivated else { + os_log(.error, log: .networkProtection, "🔴 Stopping: Network Protection not authorized.") + exit(EXIT_FAILURE) + } + } +} diff --git a/DuckDuckGoAgent/UserText.swift b/DuckDuckGoAgent/UserText.swift new file mode 100644 index 0000000000..227f211608 --- /dev/null +++ b/DuckDuckGoAgent/UserText.swift @@ -0,0 +1,26 @@ +// +// UserText.swift +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +final class UserText { + // MARK: - Status Menu + + static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Share Feedback...", comment: "The status menu 'Share Feedback' menu item") + static let networkProtectionStatusMenuOpenDuckDuckGo = NSLocalizedString("network.protection.status.menu.open.duckduckgo", value: "Open DuckDuckGo...", comment: "The status menu 'Open DuckDuckGo' menu item") +} diff --git a/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift b/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift index 950d9b86e9..fc0a06b67c 100644 --- a/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift +++ b/LocalPackages/BuildToolPlugins/Plugins/InputFilesChecker/InputFilesChecker.swift @@ -45,7 +45,10 @@ let extraInputFiles: [TargetName: Set] = [ .init("startVPN.app", .unknown), .init("stopVPN.app", .unknown), .init("enableOnDemand.app", .unknown), - .init("PFMoveApplication.m", .source) + .init("PFMoveApplication.m", .source), + .init("NetworkProtectionBundle.swift", .source), + .init("NetworkProtectionAppEvents.swift", .source), + .init("KeychainType+ClientDefault.swift", .source) ], "DuckDuckGo Privacy Browser App Store": [], diff --git a/LocalPackages/NetworkProtectionUI/Package.swift b/LocalPackages/NetworkProtectionUI/Package.swift index c5181eff24..fb0af3b63e 100644 --- a/LocalPackages/NetworkProtectionUI/Package.swift +++ b/LocalPackages/NetworkProtectionUI/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["NetworkProtectionUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "75.0.2"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "75.0.3"), .package(path: "../SwiftUIExtensions") ], targets: [ diff --git a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift index ae5f5e67df..b5f58e7910 100644 --- a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift +++ b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Extensions/UserText+NetworkProtectionUI.swift @@ -44,9 +44,4 @@ final class UserText { static let networkProtectionServerAddressUnknown = NSLocalizedString("network.protection.server.address.unknown", value: "Unknown", comment: "When we can't tell the user the IP of the NetP server is") static let networkProtectionServerLocationUnknown = NSLocalizedString("network.protection.server.location.unknown", value: "Unknown", comment: "When we can't tell the user the location of the NetP server") - - // MARK: - Status Menu - - static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Share Feedback...", comment: "The status menu 'Share Feedback' menu item") - static let networkProtectionStatusMenuOpenDuckDuckGo = NSLocalizedString("network.protection.status.menu.open.duckduckgo", value: "Open DuckDuckGo...", comment: "The status menu 'Open DuckDuckGo' menu item") } diff --git a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Menu/NetworkProtectionStatusBarMenu.swift b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Menu/NetworkProtectionStatusBarMenu.swift index 86ae1eede9..73acb6e040 100644 --- a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Menu/NetworkProtectionStatusBarMenu.swift +++ b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Menu/NetworkProtectionStatusBarMenu.swift @@ -25,7 +25,7 @@ import NetworkProtection /// Abstraction of the the Network Protection status bar menu with a simple interface. /// public final class StatusBarMenu { - typealias MenuItem = NetworkProtectionStatusView.Model.MenuItem + public typealias MenuItem = NetworkProtectionStatusView.Model.MenuItem private let statusItem: NSStatusItem private let popover: NetworkProtectionPopover @@ -44,8 +44,9 @@ public final class StatusBarMenu { /// public init(statusItem: NSStatusItem? = nil, statusReporter: NetworkProtectionStatusReporter? = nil, - appLauncher: AppLaunching, - iconProvider: IconProvider) { + controller: TunnelController, + iconProvider: IconProvider, + menuItems: [MenuItem]) { let statusReporter = statusReporter ?? DefaultNetworkProtectionStatusReporter( statusObserver: ConnectionStatusObserverThroughDistributedNotifications(), @@ -58,17 +59,6 @@ public final class StatusBarMenu { self.statusItem = statusItem ?? NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) self.iconPublisher = NetworkProtectionIconPublisher(statusReporter: statusReporter, iconProvider: iconProvider) - let controller = AppLaunchingController(appLauncher: appLauncher) - - let menuItems = [ - MenuItem(name: UserText.networkProtectionStatusMenuShareFeedback, action: { - await appLauncher.launchApp(withCommand: .shareFeedback) - }), - MenuItem(name: UserText.networkProtectionStatusMenuOpenDuckDuckGo, action: { - await appLauncher.launchApp(withCommand: .justOpen) - }) - ] - popover = NetworkProtectionPopover(controller: controller, statusReporter: statusReporter, menuItems: menuItems) popover.behavior = .transient diff --git a/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift b/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift index 56b20659a3..bdffe04051 100644 --- a/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift +++ b/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift @@ -26,8 +26,12 @@ import NetworkProtectionTestUtils final class StatusBarMenuTests: XCTestCase { - private final class TestAppLauncher: AppLaunching { - func launchApp(withCommand command: NetworkProtection.AppLaunchCommand) async { + private final class TestTunnelController: TunnelController { + func start() async { + // no-op + } + + func stop() async { // no-op } } @@ -37,8 +41,9 @@ final class StatusBarMenuTests: XCTestCase { let menu = StatusBarMenu( statusItem: item, statusReporter: MockNetworkProtectionStatusReporter(), - appLauncher: TestAppLauncher(), - iconProvider: MenuIconProvider()) + controller: TestTunnelController(), + iconProvider: MenuIconProvider(), + menuItems: []) menu.show() @@ -50,8 +55,9 @@ final class StatusBarMenuTests: XCTestCase { let menu = StatusBarMenu( statusItem: item, statusReporter: MockNetworkProtectionStatusReporter(), - appLauncher: TestAppLauncher(), - iconProvider: MenuIconProvider()) + controller: TestTunnelController(), + iconProvider: MenuIconProvider(), + menuItems: []) menu.hide()