Skip to content

Commit

Permalink
Add NetP subscription integration (#1710)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/0/1205645389501251/f
Tech Design URL:
CC:

Description:

This PR integrates NetP with the subscription library.
  • Loading branch information
samsymons authored Dec 25, 2023
1 parent 6925a02 commit c9b0316
Show file tree
Hide file tree
Showing 22 changed files with 130 additions and 29 deletions.
8 changes: 7 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,8 @@
4B2D067C2A13340900DE1F49 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4BEC322A11B509001D9AC5 /* Logging.swift */; };
4B2D067F2A1334D700DE1F49 /* NetworkProtectionUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4B2D067E2A1334D700DE1F49 /* NetworkProtectionUI */; };
4B2E7D6326FF9D6500D2DB17 /* PrintingUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E7D6226FF9D6500D2DB17 /* PrintingUserScript.swift */; };
4B2F565C2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2F565B2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift */; };
4B2F565D2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2F565B2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift */; };
4B379C1527BD91E3008A968E /* QuartzIdleStateProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B379C1427BD91E3008A968E /* QuartzIdleStateProvider.swift */; };
4B379C1E27BDB7FF008A968E /* DeviceAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B379C1D27BDB7FF008A968E /* DeviceAuthenticator.swift */; };
4B379C2227BDBA29008A968E /* LocalAuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B379C2127BDBA29008A968E /* LocalAuthenticationService.swift */; };
Expand Down Expand Up @@ -3393,6 +3395,7 @@
4B2D06642A132F3A00DE1F49 /* NetworkProtectionAppExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NetworkProtectionAppExtension.entitlements; sourceTree = "<group>"; };
4B2D06692A13318400DE1F49 /* DuckDuckGo VPN App Store.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DuckDuckGo VPN App Store.app"; sourceTree = BUILT_PRODUCTS_DIR; };
4B2E7D6226FF9D6500D2DB17 /* PrintingUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrintingUserScript.swift; sourceTree = "<group>"; };
4B2F565B2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionSubscriptionEventHandler.swift; sourceTree = "<group>"; };
4B379C1427BD91E3008A968E /* QuartzIdleStateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuartzIdleStateProvider.swift; sourceTree = "<group>"; };
4B379C1D27BDB7FF008A968E /* DeviceAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceAuthenticator.swift; sourceTree = "<group>"; };
4B379C2127BDBA29008A968E /* LocalAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5213,6 +5216,7 @@
children = (
4BCF15D52ABB83D70083F6DF /* NetworkProtectionRemoteMessaging */,
7BA7CC4D2AD11F6F0042E5CE /* NetworkProtectionIPCTunnelController.swift */,
4B2F565B2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift */,
);
path = DeveloperIDTarget;
sourceTree = "<group>";
Expand Down Expand Up @@ -10830,6 +10834,7 @@
4B957B052AC7AE700062CA31 /* FindInPageModel.swift in Sources */,
4B957B062AC7AE700062CA31 /* PseudoFolder.swift in Sources */,
4B957B072AC7AE700062CA31 /* Visit.swift in Sources */,
4B2F565D2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift in Sources */,
4B957B082AC7AE700062CA31 /* PixelDataStore.swift in Sources */,
4B957B092AC7AE700062CA31 /* WaitlistStorage.swift in Sources */,
4B957B0A2AC7AE700062CA31 /* Pixel.swift in Sources */,
Expand Down Expand Up @@ -11243,6 +11248,7 @@
1DFAB51D2A8982A600A0F7F6 /* SetExtension.swift in Sources */,
315AA07028CA5CC800200030 /* YoutubePlayerNavigationHandler.swift in Sources */,
37AFCE9227DB8CAD00471A10 /* PreferencesAboutView.swift in Sources */,
4B2F565C2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift in Sources */,
9826B0A02747DF3D0092F683 /* ContentBlocking.swift in Sources */,
4B379C2227BDBA29008A968E /* LocalAuthenticationService.swift in Sources */,
37CEFCA92A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */,
Expand Down Expand Up @@ -13044,7 +13050,7 @@
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 99.0.2;
version = 100.0.0;
};
};
AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "18043cbc24e5bb79aa1f44e01a367e0bccd2fa6e",
"version" : "99.0.2"
"revision" : "c3a482a4ca22d706207d08a68db8f23f0c262040",
"version" : "100.0.0"
}
},
{
Expand Down
8 changes: 8 additions & 0 deletions DuckDuckGo/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel
private var emailCancellables = Set<AnyCancellable>()
let bookmarksManager = LocalBookmarkManager.shared

#if NETWORK_PROTECTION && SUBSCRIPTION
private let networkProtectionSubscriptionEventHandler = NetworkProtectionSubscriptionEventHandler()
#endif

#if DBP && SUBSCRIPTION
private let dataBrokerProtectionSubscriptionEventHandler = DataBrokerProtectionSubscriptionEventHandler()
#endif
Expand Down Expand Up @@ -247,6 +251,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel

UserDefaultsWrapper<Any>.clearRemovedKeys()

#if NETWORK_PROTECTION && SUBSCRIPTION
networkProtectionSubscriptionEventHandler.registerForSubscriptionAccountManagerEvents()
#endif

#if NETWORK_PROTECTION
NetworkProtectionAppEvents().applicationDidFinishLaunching()
UNUserNotificationCenter.current().delegate = self
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/Bookmarks/Services/BookmarkStoreMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public final class BookmarkStoreMock: BookmarkStore {
}

var moveObjectUUIDCalled = false
func move(objectUUIDs: [String], toIndex: Int?, withinParentFolder: DuckDuckGo_Privacy_Browser.ParentFolderType, completion: @escaping (Error?) -> Void) {
func move(objectUUIDs: [String], toIndex: Int?, withinParentFolder: ParentFolderType, completion: @escaping (Error?) -> Void) {
moveObjectUUIDCalled = true
}

Expand Down
4 changes: 4 additions & 0 deletions DuckDuckGo/MainWindow/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import Carbon.HIToolbox
import Combine
import Common

#if NETWORK_PROTECTION
import NetworkProtection
#endif

final class MainViewController: NSViewController {

private let tabBarContainerView = NSView()
Expand Down
12 changes: 0 additions & 12 deletions DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -356,18 +356,6 @@ final class MoreOptionsMenu: NSMenu {

#endif // DBP

#if SUBSCRIPTION
let item1 = NSMenuItem(title: "Placeholder A", action: #selector(openPreferences(_:)), keyEquivalent: "")
.targetting(self)
.withImage(.image(for: .vpnIcon))
items.append(item1)

let item2 = NSMenuItem(title: "Placeholder B", action: #selector(openPreferences(_:)), keyEquivalent: "")
.targetting(self)
.withImage(.image(for: .vpnIcon))
items.append(item2)
#endif

return items
}

Expand Down
11 changes: 11 additions & 0 deletions DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import NetworkProtectionUI
#endif

#if SUBSCRIPTION
import Subscription
import SubscriptionUI
#endif

Expand Down Expand Up @@ -331,6 +332,16 @@ final class NavigationBarViewController: NSViewController {
return
}

#if SUBSCRIPTION
let accountManager = AccountManager()
let networkProtectionTokenStorage = NetworkProtectionKeychainTokenStore()

if accountManager.accessToken != nil && (try? networkProtectionTokenStorage.fetchToken()) == nil {
print("[NetP Subscription] Got access token but not auth token, meaning token exchange failed")
return
}
#endif

// 1. If the user is on the waitlist but hasn't been invited or accepted terms and conditions, show the waitlist screen.
// 2. If the user has no waitlist state but has an auth token, show the NetP popover.
// 3. If the user has no state of any kind, show the waitlist screen.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ extension EventMapping where Event == NetworkProtectionError {
.wireGuardDnsResolution,
.wireGuardSetNetworkSettings,
.startWireGuardBackend,
// Needs Privacy triage for macOS Geoswitching pixels
.failedToRetrieveAuthToken,
.failedToFetchLocationList,
.failedToParseLocationListResponse:
domainEvent = .networkProtectionUnhandledError(function: #function, line: #line, error: event)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// NetworkProtectionSubscriptionEventHandler.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.
//

#if NETWORK_PROTECTION && SUBSCRIPTION

import Foundation
import Subscription
import NetworkProtection

final class NetworkProtectionSubscriptionEventHandler {

private let accountManager: AccountManaging
private let networkProtectionRedemptionCoordinator: NetworkProtectionCodeRedeeming
private let networkProtectionTokenStorage: NetworkProtectionTokenStore
private let networkProtectionFeatureDisabler: NetworkProtectionFeatureDisabling

init(accountManager: AccountManaging = AccountManager(),
networkProtectionRedemptionCoordinator: NetworkProtectionCodeRedeeming = NetworkProtectionCodeRedemptionCoordinator(),
networkProtectionTokenStorage: NetworkProtectionTokenStore = NetworkProtectionKeychainTokenStore(),
networkProtectionFeatureDisabler: NetworkProtectionFeatureDisabling = NetworkProtectionFeatureDisabler()) {
self.accountManager = accountManager
self.networkProtectionRedemptionCoordinator = networkProtectionRedemptionCoordinator
self.networkProtectionTokenStorage = networkProtectionTokenStorage
self.networkProtectionFeatureDisabler = networkProtectionFeatureDisabler
}

func registerForSubscriptionAccountManagerEvents() {
NotificationCenter.default.addObserver(self, selector: #selector(handleAccountDidSignIn), name: .accountDidSignIn, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleAccountDidSignOut), name: .accountDidSignOut, object: nil)
}

@objc private func handleAccountDidSignIn() {
guard let token = accountManager.accessToken else {
assertionFailure("[NetP Subscription] AccountManager signed in but token could not be retrieved")
return
}

Task {
do {
try await networkProtectionRedemptionCoordinator.exchange(accessToken: token)
print("[NetP Subscription] Exchanged access token for auth token successfully")
} catch {
print("[NetP Subscription] Failed to exchange access token for auth token: \(error)")
}
}
}

@objc private func handleAccountDidSignOut() {
print("[NetP Subscription] Deleted NetP auth token after signing out from Privacy Pro")

Task {
await networkProtectionFeatureDisabler.disable(keepAuthToken: false, uninstallSystemExtension: false)
}
}

}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ final class MacPacketTunnelProvider: PacketTunnelProvider {
domainEvent = .networkProtectionNoAuthTokenFoundError
case .unhandledError(function: let function, line: let line, error: let error):
domainEvent = .networkProtectionUnhandledError(function: function, line: line, error: error)
case .failedToFetchLocationList,
case .failedToRetrieveAuthToken,
.failedToFetchLocationList,
.failedToParseLocationListResponse:
// Needs Privacy triage for macOS Geoswitching pixels
return
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/Preferences/View/PreferencesAboutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ extension Preferences {
Text(UserText.privacySimplified).font(.privacySimplified)

Text(UserText.versionLabel(version: model.appVersion.versionNumber, build: model.appVersion.buildNumber)).onTapGesture(count: 12) {
#if NETWORK_PROTECTION
#if NETWORK_PROTECTION && !SUBSCRIPTION
model.displayNetPInvite()
#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ let nonSandboxedExtraInputFiles: Set<InputFile> = [
.init("NetworkProtectionAppEvents.swift", .source),
.init("NetworkProtectionIPCTunnelController.swift", .source),
.init("NetworkProtectionNavBarPopoverManager.swift", .source),
.init("NetworkProtectionSubscriptionEventHandler.swift", .source),
.init("KeychainType+ClientDefault.swift", .source),
.init("DBPHomeViewController.swift", .source),
.init("DataBrokerProtectionManager.swift", .source),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/DataBrokerProtection/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ let package = Package(
targets: ["DataBrokerProtection"])
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
.package(path: "../PixelKit"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../XPCHelper")
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/LoginItems/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/NetworkProtectionMac/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let package = Package(
.library(name: "NetworkProtectionUI", targets: ["NetworkProtectionUI"])
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
.package(path: "../XPCHelper"),
.package(path: "../SwiftUIExtensions")
],
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/PixelKit/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/Subscription/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let package = Package(
targets: ["Subscription"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SwiftUIExtensions/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SyncUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
],
dependencies: [
.package(path: "../SwiftUIExtensions"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SystemExtensionManager/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/XPCHelper/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let package = Package(
.library(name: "XPCHelper", targets: ["XPCHelper"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
11 changes: 10 additions & 1 deletion UnitTests/Waitlist/Mocks/MockNetworkProtectionCodeRedeemer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ final class MockNetworkProtectionCodeRedeemer: NetworkProtectionCodeRedeeming {
case error
}

var redeemedCode: String?
var throwError: Bool = false

var redeemedCode: String?
func redeem(_ code: String) async throws {
if throwError {
throw MockNetworkProtectionCodeRedeemerError.error
Expand All @@ -38,6 +38,15 @@ final class MockNetworkProtectionCodeRedeemer: NetworkProtectionCodeRedeeming {
}
}

var redeemedAccessToken: String?
func exchange(accessToken: String) async throws {
if throwError {
throw MockNetworkProtectionCodeRedeemerError.error
} else {
redeemedAccessToken = accessToken
}
}

}

#endif

0 comments on commit c9b0316

Please sign in to comment.