Skip to content

Commit

Permalink
Merge branch 'main' into sam/startup-logging-cleanup
Browse files Browse the repository at this point in the history
# By Michal Smaga (2) and others
# Via GitHub
* main:
  BSK update for Autofill Script performance improvements (#2481)
  Add source to dbp pixelkit setup (#2482)
  Send correct platform value for App Store purchase options (#2493)
  Close subscription tabs when subscription becomes inactive (#2492)
  DBP: Changes to weekly scanning pixel parameters (#2474)
  Add tests for DBP waitlist migration (#2464)
  Ship review improvements (#2488)
  Collect extra metadata (#2476)

# Conflicts:
#	DuckDuckGo.xcodeproj/project.pbxproj
#	DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • Loading branch information
samsymons committed Mar 25, 2024
2 parents d232970 + d74d1a4 commit afeef04
Show file tree
Hide file tree
Showing 27 changed files with 444 additions and 90 deletions.
14 changes: 14 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@
317295D52AF058D3002C3206 /* MockWaitlistFeatureSetupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317295D12AF058D3002C3206 /* MockWaitlistFeatureSetupHandler.swift */; };
3184AC6D288F29D800C35E4B /* BadgeNotificationAnimationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3184AC6C288F29D800C35E4B /* BadgeNotificationAnimationModel.swift */; };
3184AC6F288F2A1100C35E4B /* CookieNotificationAnimationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3184AC6E288F2A1100C35E4B /* CookieNotificationAnimationModel.swift */; };
31A2FD172BAB41C500D0E741 /* DataBrokerProtectionVisibilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A2FD162BAB41C500D0E741 /* DataBrokerProtectionVisibilityTests.swift */; };
31A2FD182BAB43BA00D0E741 /* DataBrokerProtectionVisibilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A2FD162BAB41C500D0E741 /* DataBrokerProtectionVisibilityTests.swift */; };
31A3A4E32B0C115F0021063C /* DataBrokerProtection in Frameworks */ = {isa = PBXBuildFile; productRef = 31A3A4E22B0C115F0021063C /* DataBrokerProtection */; };
31AA6B972B960B870025014E /* DataBrokerProtectionLoginItemPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31AA6B962B960B870025014E /* DataBrokerProtectionLoginItemPixels.swift */; };
31AA6B982B960BA50025014E /* DataBrokerProtectionLoginItemPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31AA6B962B960B870025014E /* DataBrokerProtectionLoginItemPixels.swift */; };
Expand Down Expand Up @@ -3557,6 +3559,7 @@
3192EC872A4DCF21001E97A5 /* DBPHomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBPHomeViewController.swift; sourceTree = "<group>"; };
3199C6F82AF94F5B002A7BA1 /* DataBrokerProtectionFeatureDisabler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionFeatureDisabler.swift; sourceTree = "<group>"; };
3199C6FC2AF97367002A7BA1 /* DataBrokerProtectionAppEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionAppEvents.swift; sourceTree = "<group>"; };
31A2FD162BAB41C500D0E741 /* DataBrokerProtectionVisibilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionVisibilityTests.swift; sourceTree = "<group>"; };
31AA6B962B960B870025014E /* DataBrokerProtectionLoginItemPixels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionLoginItemPixels.swift; sourceTree = "<group>"; };
31B4AF522901A4F20013585E /* NSEventExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEventExtension.swift; sourceTree = "<group>"; };
31C3CE0128EDC1E70002C24A /* CustomRoundedCornersShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRoundedCornersShape.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5196,6 +5199,14 @@
path = DBP;
sourceTree = "<group>";
};
31A2FD152BAB419400D0E741 /* DBP */ = {
isa = PBXGroup;
children = (
31A2FD162BAB41C500D0E741 /* DataBrokerProtectionVisibilityTests.swift */,
);
path = DBP;
sourceTree = "<group>";
};
31E163BB293A577200963C10 /* PrivacyReferenceTests */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -7154,6 +7165,7 @@
85AC3B1525D9BBFA00C7D2AA /* Configuration */,
4B82E9B725B6A04B00656FE7 /* ContentBlocker */,
4B70BFFD27B0793D000386ED /* CrashReports */,
31A2FD152BAB419400D0E741 /* DBP */,
4B723E0226B0003E00E14D75 /* DataExport */,
4B723DFE26B0003E00E14D75 /* DataImport */,
4BBC16A327C488B900E00A38 /* DeviceAuthentication */,
Expand Down Expand Up @@ -10947,6 +10959,7 @@
3706FE44293F661700E42796 /* GeolocationServiceTests.swift in Sources */,
1DA6D1032A1FFA3B00540406 /* HTTPCookieTests.swift in Sources */,
3706FE45293F661700E42796 /* ProgressEstimationTests.swift in Sources */,
31A2FD182BAB43BA00D0E741 /* DataBrokerProtectionVisibilityTests.swift in Sources */,
B6619F072B17138D00CD9186 /* DataImportSourceViewModelTests.swift in Sources */,
3706FE46293F661700E42796 /* EncryptedValueTransformerTests.swift in Sources */,
9F3910632B68C35600CB5112 /* DownloadsTabExtensionTests.swift in Sources */,
Expand Down Expand Up @@ -12861,6 +12874,7 @@
37534C9E28104D9B002621E7 /* TabLazyLoaderTests.swift in Sources */,
B6619EF62B10DFF700CD9186 /* InstructionsFormatParserTests.swift in Sources */,
569277C429DEE09D00B633EF /* ContinueSetUpModelTests.swift in Sources */,
31A2FD172BAB41C500D0E741 /* DataBrokerProtectionVisibilityTests.swift in Sources */,
85F1B0C925EF9759004792B6 /* URLEventHandlerTests.swift in Sources */,
4B9292BD2667103100AD2C21 /* BookmarkOutlineViewDataSourceTests.swift in Sources */,
C17CA7B22B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift in Sources */,
Expand Down
1 change: 0 additions & 1 deletion DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ public struct UserDefaultsWrapper<T> {

case dataBrokerProtectionTermsAndConditionsAccepted = "data-broker-protection.waitlist-terms-and-conditions.accepted"
case shouldShowDBPWaitlistInvitedCardUI = "shouldShowDBPWaitlistInvitedCardUI"
case dataBrokerProtectionCleanedUpFromWaitlistToPrivacyPro = "data-broker-protection.cleaned-up-from-waitlist-to-privacy-pro"

// VPN

Expand Down
36 changes: 21 additions & 15 deletions DuckDuckGo/DBP/DataBrokerProtectionFeatureVisibility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,34 @@ struct DefaultDataBrokerProtectionFeatureVisibility: DataBrokerProtectionFeature
private let featureDisabler: DataBrokerProtectionFeatureDisabling
private let pixelHandler: EventMapping<DataBrokerProtectionPixels>
private let userDefaults: UserDefaults
private let waitlistStorage: WaitlistStorage
private let subscriptionAvailability: SubscriptionFeatureAvailability

@UserDefaultsWrapper(key: .dataBrokerProtectionCleanedUpFromWaitlistToPrivacyPro, defaultValue: false)
var dataBrokerProtectionCleanedUpFromWaitlistToPrivacyPro: Bool
private let dataBrokerProtectionKey = "data-broker-protection.cleaned-up-from-waitlist-to-privacy-pro"
private var dataBrokerProtectionCleanedUpFromWaitlistToPrivacyPro: Bool {
get {
return userDefaults.bool(forKey: dataBrokerProtectionKey)
}
nonmutating set {
userDefaults.set(newValue, forKey: dataBrokerProtectionKey)
}
}

/// Temporary code to use while we have both redeem flow for diary study users. Should be removed later
static var bypassWaitlist = false

init(privacyConfigurationManager: PrivacyConfigurationManaging = ContentBlocking.shared.privacyConfigurationManager,
featureDisabler: DataBrokerProtectionFeatureDisabling = DataBrokerProtectionFeatureDisabler(),
pixelHandler: EventMapping<DataBrokerProtectionPixels> = DataBrokerProtectionPixelsHandler(),
userDefaults: UserDefaults = .standard) {
userDefaults: UserDefaults = .standard,
waitlistStorage: WaitlistStorage = DataBrokerProtectionWaitlist().waitlistStorage,
subscriptionAvailability: SubscriptionFeatureAvailability = DefaultSubscriptionFeatureAvailability()) {
self.privacyConfigurationManager = privacyConfigurationManager
self.featureDisabler = featureDisabler
self.pixelHandler = pixelHandler
self.userDefaults = userDefaults
self.waitlistStorage = waitlistStorage
self.subscriptionAvailability = subscriptionAvailability
}

var waitlistIsOngoing: Bool {
Expand All @@ -70,10 +83,9 @@ struct DefaultDataBrokerProtectionFeatureVisibility: DataBrokerProtectionFeature
regionCode = "US"
}

#if DEBUG // Always assume US for debug builds
#if DEBUG // Always assume US for debug builds
regionCode = "US"
#endif

#endif
return (regionCode ?? "US") == "US"
}

Expand All @@ -90,20 +102,15 @@ struct DefaultDataBrokerProtectionFeatureVisibility: DataBrokerProtectionFeature
}

private var isWaitlistUser: Bool {
DataBrokerProtectionWaitlist().waitlistStorage.isWaitlistUser
waitlistStorage.isWaitlistUser
}

private var wasWaitlistUser: Bool {
DataBrokerProtectionWaitlist().waitlistStorage.getWaitlistInviteCode() != nil
waitlistStorage.getWaitlistInviteCode() != nil
}

func isPrivacyProEnabled() -> Bool {
#if SUBSCRIPTION
return DefaultSubscriptionFeatureAvailability().isFeatureAvailable
#else
return false
#endif

return subscriptionAvailability.isFeatureAvailable
}

func isEligibleForThankYouMessage() -> Bool {
Expand Down Expand Up @@ -153,5 +160,4 @@ struct DefaultDataBrokerProtectionFeatureVisibility: DataBrokerProtectionFeature
}
}
}

#endif
4 changes: 2 additions & 2 deletions DuckDuckGo/MainWindow/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ final class MainViewController: NSViewController {
return NetPPopoverManagerMock()
}
#endif
let vpnBundleID = Bundle.main.vpnMenuAgentBundleId
let ipcClient = TunnelControllerIPCClient(machServiceName: vpnBundleID)

let ipcClient = TunnelControllerIPCClient()
ipcClient.register()

return NetworkProtectionNavBarPopoverManager(ipcClient: ipcClient, networkProtectionFeatureDisabler: NetworkProtectionFeatureDisabler())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import Foundation
import NetworkProtection
import NetworkProtectionIPC
import Common

#if SUBSCRIPTION
Expand Down Expand Up @@ -88,4 +89,11 @@ extension NetworkProtectionLocationListCompositeRepository {
}
}

extension TunnelControllerIPCClient {

convenience init() {
self.init(machServiceName: Bundle.main.vpnMenuAgentBundleId)
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import NetworkProtection
import NetworkProtectionUI
import NetworkProtectionIPC
import NetworkExtension
import Subscription

/// Implements the sequence of steps that the VPN needs to execute when the App starts up.
///
Expand Down Expand Up @@ -81,6 +82,10 @@ final class NetworkProtectionAppEvents {
func applicationDidBecomeActive() {
Task { @MainActor in
await featureVisibility.disableIfUserHasNoAccess()

#if SUBSCRIPTION
_ = await AccountManager().hasEntitlement(for: .networkProtection, cachePolicy: .reloadIgnoringLocalCacheData)
#endif
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ final class NetworkProtectionDebugUtilities {
self.loginItemsManager = loginItemsManager
self.settings = settings

let ipcClient = TunnelControllerIPCClient(machServiceName: Bundle.main.vpnMenuAgentBundleId)
let ipcClient = TunnelControllerIPCClient()

self.ipcClient = ipcClient
self.networkProtectionFeatureDisabler = NetworkProtectionFeatureDisabler(ipcClient: ipcClient)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,52 @@

#if NETWORK_PROTECTION

import Common
import Foundation
import NetworkProtection
import NetworkProtectionIPC

final class NetworkProtectionIPCTunnelController: TunnelController {

private let featureVisibility: NetworkProtectionFeatureVisibility
private let loginItemsManager: LoginItemsManager
private let ipcClient: NetworkProtectionIPCClient

init(loginItemsManager: LoginItemsManager = LoginItemsManager(),
init(featureVisibility: NetworkProtectionFeatureVisibility = DefaultNetworkProtectionVisibility(),
loginItemsManager: LoginItemsManager = LoginItemsManager(),
ipcClient: NetworkProtectionIPCClient) {

self.featureVisibility = featureVisibility
self.loginItemsManager = loginItemsManager
self.ipcClient = ipcClient
}

@MainActor
func start() async {
enableLoginItems()
do {
guard try await enableLoginItems() else {
os_log("🔴 IPC Controller refusing to start the VPN menu app. Not authorized.", log: .networkProtection)
return
}

ipcClient.start()
ipcClient.start()
} catch {
os_log("🔴 IPC Controller found en error when starting the VPN: \(error)", log: .networkProtection)
}
}

@MainActor
func stop() async {
enableLoginItems()
do {
guard try await enableLoginItems() else {
os_log("🔴 IPC Controller refusing to start the VPN. Not authorized.", log: .networkProtection)
return
}

ipcClient.stop()
ipcClient.stop()
} catch {
os_log("🔴 IPC Controller found en error when starting the VPN: \(error)", log: .networkProtection)
}
}

/// Queries VPN to know if it's connected.
Expand All @@ -64,8 +82,14 @@ final class NetworkProtectionIPCTunnelController: TunnelController {

// MARK: - Login Items Manager

private func enableLoginItems() {
private func enableLoginItems() async throws -> Bool {
guard try await featureVisibility.isFeatureEnabled() else {
// We shouldn't enable the menu app is the VPN feature is disabled.
return false
}

loginItemsManager.enableLoginItems(LoginItemsManager.networkProtectionLoginItems, log: .networkProtection)
return true
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#if NETWORK_PROTECTION && SUBSCRIPTION

import Combine
import Foundation
import Subscription
import NetworkProtection
Expand All @@ -30,6 +31,7 @@ final class NetworkProtectionSubscriptionEventHandler {
private let networkProtectionTokenStorage: NetworkProtectionTokenStore
private let networkProtectionFeatureDisabler: NetworkProtectionFeatureDisabling
private let userDefaults: UserDefaults
private var cancellables = Set<AnyCancellable>()

init(accountManager: AccountManaging = AccountManager(),
networkProtectionRedemptionCoordinator: NetworkProtectionCodeRedeeming = NetworkProtectionCodeRedemptionCoordinator(),
Expand All @@ -41,34 +43,57 @@ final class NetworkProtectionSubscriptionEventHandler {
self.networkProtectionTokenStorage = networkProtectionTokenStorage
self.networkProtectionFeatureDisabler = networkProtectionFeatureDisabler
self.userDefaults = userDefaults

subscribeToEntitlementChanges()
}

private lazy var entitlementMonitor = NetworkProtectionEntitlementMonitor()
private func subscribeToEntitlementChanges() {
Task {
switch await AccountManager().hasEntitlement(for: .networkProtection) {
case .success(let hasEntitlements):
handleEntitlementsChange(hasEntitlements: hasEntitlements)
case .failure(let error):
break
}

NotificationCenter.default
.publisher(for: .entitlementsDidChange)
.receive(on: DispatchQueue.main)
.sink { [weak self] notification in
guard let self else {
return
}

private func setUpEntitlementMonitoring() {
guard AccountManager().isUserAuthenticated else { return }
let entitlementsCheck = {
await AccountManager().hasEntitlement(for: .networkProtection, cachePolicy: .reloadIgnoringLocalCacheData)
}
guard let entitlements = notification.userInfo?[UserDefaultsCacheKey.subscriptionEntitlements] as? [Entitlement] else {

Task {
await entitlementMonitor.start(entitlementCheck: entitlementsCheck) { result in
switch result {
case .validEntitlement:
UserDefaults.netP.networkProtectionEntitlementsExpired = false
case .invalidEntitlement:
UserDefaults.netP.networkProtectionEntitlementsExpired = true
case .error:
break
assertionFailure("Missing entitlements are truly unexpected")
return
}

let hasEntitlements = entitlements.contains { entitlement in
entitlement.product == .networkProtection
}

handleEntitlementsChange(hasEntitlements: hasEntitlements)
}
.store(in: &cancellables)
}
}

private func handleEntitlementsChange(hasEntitlements: Bool) {
if hasEntitlements {
UserDefaults.netP.networkProtectionEntitlementsExpired = false
} else {
Task {
await self.networkProtectionFeatureDisabler.disable(keepAuthToken: false, uninstallSystemExtension: false)
UserDefaults.netP.networkProtectionEntitlementsExpired = true
}
}
}

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

@objc private func handleAccountDidSignIn() {
Expand All @@ -77,12 +102,10 @@ final class NetworkProtectionSubscriptionEventHandler {
return
}
userDefaults.networkProtectionEntitlementsExpired = false
setUpEntitlementMonitoring()
}

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

Task {
await networkProtectionFeatureDisabler.disable(keepAuthToken: false, uninstallSystemExtension: false)
Expand Down
6 changes: 2 additions & 4 deletions DuckDuckGo/Preferences/Model/PreferencesSidebarModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,8 @@ final class PreferencesSidebarModel: ObservableObject {
userDefaults: UserDefaults = .netP
) {
let loadSections = {
#if SUBSCRIPTION
let includingVPN = !userDefaults.networkProtectionEntitlementsExpired && DefaultNetworkProtectionVisibility().isOnboarded
#elseif NETWORK_PROTECTION
let includingVPN = DefaultNetworkProtectionVisibility().isOnboarded
#if NETWORK_PROTECTION
let includingVPN = DefaultNetworkProtectionVisibility().isInstalled
#else
let includingVPN = false
#endif
Expand Down
Loading

0 comments on commit afeef04

Please sign in to comment.