Skip to content

Commit

Permalink
Merge branch 'main' into fcappelli/subscription_oauth_api_v2
Browse files Browse the repository at this point in the history
# Conflicts:
#	DuckDuckGo.xcodeproj/project.pbxproj
#	DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
#	DuckDuckGo/Menus/MainMenuActions.swift
#	DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift
#	DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift
#	DuckDuckGo/Preferences/Model/PreferencesSection.swift
#	DuckDuckGo/Subscription/SubscriptionManager+StandardConfiguration.swift
#	DuckDuckGo/Tab/UserScripts/UserScripts.swift
#	DuckDuckGo/Waitlist/VPNFeatureGatekeeper.swift
#	DuckDuckGoDBPBackgroundAgent/DuckDuckGoDBPBackgroundAgentAppDelegate.swift
#	UnitTests/Subscription/SubscriptionPagesUseSubscriptionFeatureForStripeTests.swift
#	UnitTests/Subscription/SubscriptionPagesUseSubscriptionFeatureTests.swift
  • Loading branch information
federicocappelli committed Jan 9, 2025
2 parents a0d5a7a + 7e20083 commit a1bfa12
Show file tree
Hide file tree
Showing 56 changed files with 253 additions and 248 deletions.
2 changes: 1 addition & 1 deletion Configuration/BuildNumber.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1 @@
CURRENT_PROJECT_VERSION = 337
CURRENT_PROJECT_VERSION = 338
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,25 @@
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"branch" : "fcappelli/subscription_oauth_api_v2",
"revision" : "d1998cae838ac33584b24c99662dd898e2eca0f0"
"revision" : "351dc80d2364cce863cc10c1ed753a55bbcca7a2"
}
},
{
"identity" : "content-scope-scripts",
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/content-scope-scripts",
"state" : {
"revision" : "bc808eb735d9eb72d5c54cf2452b104b6a370e25",
"version" : "6.43.0"
"revision" : "a539758027d9fd37d9d26213399ac156ca9fb81c",
"version" : "7.1.0"
}
},
{
"identity" : "duckduckgo-autofill",
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/duckduckgo-autofill.git",
"state" : {
"revision" : "88982a3802ac504e2f1a118a73bfdf2d8f4a7735",
"version" : "16.0.0"
"revision" : "47c26dc32b94cdbcef3e6157497147917678c25c",
"version" : "16.1.0"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,6 @@
<Test
Identifier = "HTTPSUpgradeIntegrationTests">
</Test>
<Test
Identifier = "MaliciousSiteProtectionIntegrationTests">
</Test>
<Test
Identifier = "NavigationProtectionIntegrationTests/testAMPLinks()">
</Test>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,6 @@
<Test
Identifier = "HTTPSUpgradeIntegrationTests">
</Test>
<Test
Identifier = "MaliciousSiteProtectionIntegrationTests">
</Test>
<Test
Identifier = "NavigationProtectionIntegrationTests/testAMPLinks()">
</Test>
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/DBP/DBPHomeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ final class DBPHomeViewController: NSViewController {
passwordGeneration: false,
inlineIconCredentials: false,
thirdPartyCredentialsProvider: false,
unknownUsernameCategorization: false)
unknownUsernameCategorization: false,
partialFormSaves: false)

let isGPCEnabled = WebTrackingProtectionPreferences.shared.isGPCEnabled
let sessionKey = UUID().uuidString
Expand Down
6 changes: 3 additions & 3 deletions DuckDuckGo/DBP/DataBrokerProtectionDebugMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,21 @@ final class DataBrokerProtectionDebugMenu: NSMenu {
}

@objc private func startScheduledOperations(_ sender: NSMenuItem) {
Logger.dataBrokerProtection.debug("Running queued operations...")
Logger.dataBrokerProtection.log("Running queued operations...")
let showWebView = sender.representedObject as? Bool ?? false

DataBrokerProtectionManager.shared.loginItemInterface.startScheduledOperations(showWebView: showWebView)
}

@objc private func runScanOperations(_ sender: NSMenuItem) {
Logger.dataBrokerProtection.debug("Running scan operations...")
Logger.dataBrokerProtection.log("Running scan operations...")
let showWebView = sender.representedObject as? Bool ?? false

DataBrokerProtectionManager.shared.loginItemInterface.startImmediateOperations(showWebView: showWebView)
}

@objc private func runOptoutOperations(_ sender: NSMenuItem) {
Logger.dataBrokerProtection.debug("Running Optout operations...")
Logger.dataBrokerProtection.log("Running Optout operations...")
let showWebView = sender.representedObject as? Bool ?? false

DataBrokerProtectionManager.shared.loginItemInterface.runAllOptOuts(showWebView: showWebView)
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/DBP/DataBrokerProtectionFeatureGatekeeper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ struct DefaultDataBrokerProtectionFeatureGatekeeper: DataBrokerProtectionFeature
func disableAndDeleteForAllUsers() {
featureDisabler.disableAndDelete()

Logger.dataBrokerProtection.debug("Disabling and removing DBP for all users")
Logger.dataBrokerProtection.log("Disabling and removing DBP for all users")
}

/// Checks DBP prerequisites
Expand Down
17 changes: 0 additions & 17 deletions DuckDuckGo/Menus/MainMenuActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -909,23 +909,6 @@ extension MainViewController {
guard let internalUserDecider = NSApp.delegateTyped.internalUserDecider as? DefaultInternalUserDecider else { return }
let state = internalUserDecider.isInternalUser
internalUserDecider.debugSetInternalUserState(!state)

if !DefaultSubscriptionFeatureAvailability().isFeatureAvailable {
// We only clear PPro state when it's not available, as otherwise
// there should be no state to clear. Clearing PPro state can
// trigger notifications which we want to avoid unless
// necessary.
clearPrivacyProState()
}
}

/// Clears the PrivacyPro state to make testing easier.
///
private func clearPrivacyProState() {
Task {
await Application.appDelegate.subscriptionManager.signOut()
UserDefaults.netP.networkProtectionEntitlementsExpired = false
}
}

@objc func resetDailyPixels(_ sender: Any?) {
Expand Down
15 changes: 7 additions & 8 deletions DuckDuckGo/NavigationBar/View/AddressBarTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -421,14 +421,13 @@ final class AddressBarTextField: NSTextField {
}
#endif

if DefaultSubscriptionFeatureAvailability().isFeatureAvailable {
let baseURL = Application.appDelegate.subscriptionManager.url(for: .baseURL)
let identityTheftRestorationURL = Application.appDelegate.subscriptionManager.url(for: .identityTheftRestoration)
if providedUrl.isChild(of: baseURL) || providedUrl.isChild(of: identityTheftRestorationURL) {
self.updateValue(selectedTabViewModel: nil, addressBarString: nil) // reset
self.window?.makeFirstResponder(nil)
return
}
// Prevent typing in subscription URLs directly in the address bar
let baseURL = Application.appDelegate.subscriptionManager.url(for: .baseURL)
let identityTheftRestorationURL = Application.appDelegate.subscriptionManager.url(for: .identityTheftRestoration)
if providedUrl.isChild(of: baseURL) || providedUrl.isChild(of: identityTheftRestorationURL) {
self.updateValue(selectedTabViewModel: nil, addressBarString: nil) // reset
self.window?.makeFirstResponder(nil)
return
}

self.window?.makeFirstResponder(nil)
Expand Down
4 changes: 1 addition & 3 deletions DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,6 @@ final class MoreOptionsMenu: NSMenu, NSMenuDelegate {

@MainActor
private func addSubscriptionItems() {
guard subscriptionFeatureAvailability.isFeatureAvailable else { return }

func shouldHideDueToNoProduct() -> Bool {
let platform = subscriptionManager.currentEnvironment.purchasePlatform
return platform == .appStore && subscriptionManager.canPurchase == false
Expand Down Expand Up @@ -968,7 +966,7 @@ final class SubscriptionSubMenu: NSMenu, NSMenuDelegate {
}

private func refreshAvailabilityBasedOnEntitlements() async {
// guard subscriptionFeatureAvailability.isFeatureAvailable, subscriptionManager.isUserAuthenticated else { return }
guard subscriptionManager.isUserAuthenticated else { return }
let features = await subscriptionManager.currentSubscriptionFeatures(forceRefresh: false)
let vpnFeature = features.first { $0.entitlement == .networkProtection }
let dbpFeature = features.first { $0.entitlement == .dataBrokerProtection }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,11 +344,9 @@ final class NavigationBarViewController: NSViewController {
}

private func toggleNetworkProtectionPopover() {
guard DefaultSubscriptionFeatureAvailability().isFeatureAvailable,
subscriptionManager.isUserAuthenticated else {
guard subscriptionManager.isUserAuthenticated else {
return
}

popovers.toggleNetworkProtectionPopover(from: networkProtectionButton, withDelegate: networkProtectionButtonModel)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,6 @@ final class MacPacketTunnelProvider: PacketTunnelProvider {
let subscriptionManager = DefaultSubscriptionManager(oAuthClient: authClient,
subscriptionEndpointService: subscriptionEndpointService,
subscriptionEnvironment: subscriptionEnvironment,
subscriptionFeatureFlagger: nil,
pixelHandler: pixelHandler)

// MARK: -
Expand Down
26 changes: 12 additions & 14 deletions DuckDuckGo/Preferences/Model/PreferencesSection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,22 @@ struct PreferencesSection: Hashable, Identifiable {
.init(id: .about, panes: otherPanes)
]

if DefaultSubscriptionFeatureAvailability().isFeatureAvailable {
let subscriptionManager = Application.appDelegate.subscriptionManager
let platform = subscriptionManager.currentEnvironment.purchasePlatform
var shouldHidePrivacyProDueToNoProducts = platform == .appStore && subscriptionManager.canPurchase == false
let subscriptionManager = Application.appDelegate.subscriptionManager
let platform = subscriptionManager.currentEnvironment.purchasePlatform
var shouldHidePrivacyProDueToNoProducts = platform == .appStore && subscriptionManager.canPurchase == false

if subscriptionManager.isUserAuthenticated {
shouldHidePrivacyProDueToNoProducts = false
}

if !shouldHidePrivacyProDueToNoProducts {
var subscriptionPanes: [PreferencePaneIdentifier] = [.subscription]
if subscriptionManager.isUserAuthenticated {
shouldHidePrivacyProDueToNoProducts = false
}

if includingVPN {
subscriptionPanes.append(.vpn)
}
if !shouldHidePrivacyProDueToNoProducts {
var subscriptionPanes: [PreferencePaneIdentifier] = [.subscription]

sections.insert(.init(id: .privacyPro, panes: subscriptionPanes), at: 1)
if includingVPN {
subscriptionPanes.append(.vpn)
}

sections.insert(.init(id: .privacyPro, panes: subscriptionPanes), at: 1)
}

return sections
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ extension DefaultSubscriptionManager {
}

switch feature {
case .isLaunchedROW:
return featureFlagger.isFeatureOn(.isPrivacyProLaunchedROW)
case .isLaunchedROWOverride:
return featureFlagger.isFeatureOn(.isPrivacyProLaunchedROWOverride)
case .usePrivacyProUSARegionOverride:
return (featureFlagger.internalUserDecider.isInternalUser &&
environment.serviceEnvironment == .staging &&
Expand All @@ -98,13 +94,11 @@ extension DefaultSubscriptionManager {
oAuthClient: authClient,
subscriptionEndpointService: subscriptionEndpointService,
subscriptionEnvironment: environment,
subscriptionFeatureFlagger: subscriptionFeatureFlagger,
pixelHandler: pixelHandler)
} else {
self.init(oAuthClient: authClient,
subscriptionEndpointService: subscriptionEndpointService,
subscriptionEnvironment: environment,
subscriptionFeatureFlagger: subscriptionFeatureFlagger,
pixelHandler: pixelHandler)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// SubscriptionManager+StandardConfiguration.swift
//
// Copyright © 2024 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 Subscription
import Common
import PixelKit
import BrowserServicesKit
import FeatureFlags

extension DefaultSubscriptionManager {

// Init the SubscriptionManager using the standard dependencies and configuration, to be used only in the dependencies tree root
public convenience init(featureFlagger: FeatureFlagger? = nil) {
// MARK: - Configure Subscription
let subscriptionAppGroup = Bundle.main.appGroup(bundle: .subs)
let subscriptionUserDefaults = UserDefaults(suiteName: subscriptionAppGroup)!
let subscriptionEnvironment = DefaultSubscriptionManager.getSavedOrDefaultEnvironment(userDefaults: subscriptionUserDefaults)

let entitlementsCache = UserDefaultsCache<[Entitlement]>(userDefaults: subscriptionUserDefaults,
key: UserDefaultsCacheKey.subscriptionEntitlements,
settings: UserDefaultsCacheSettings(defaultExpirationInterval: .minutes(20)))
let accessTokenStorage = SubscriptionTokenKeychainStorage(keychainType: .dataProtection(.named(subscriptionAppGroup)))
let subscriptionEndpointService = DefaultSubscriptionEndpointService(currentServiceEnvironment: subscriptionEnvironment.serviceEnvironment)
let authEndpointService = DefaultAuthEndpointService(currentServiceEnvironment: subscriptionEnvironment.serviceEnvironment)
let subscriptionFeatureMappingCache = DefaultSubscriptionFeatureMappingCache(subscriptionEndpointService: subscriptionEndpointService,
userDefaults: subscriptionUserDefaults)

let accountManager = DefaultAccountManager(accessTokenStorage: accessTokenStorage,
entitlementsCache: entitlementsCache,
subscriptionEndpointService: subscriptionEndpointService,
authEndpointService: authEndpointService)

let subscriptionFeatureFlagger: FeatureFlaggerMapping<SubscriptionFeatureFlags> = FeatureFlaggerMapping { feature in
guard let featureFlagger else {
// With no featureFlagger provided there is no gating of features
return feature.defaultState
}

switch feature {
case .usePrivacyProUSARegionOverride:
return (featureFlagger.internalUserDecider.isInternalUser &&
subscriptionEnvironment.serviceEnvironment == .staging &&
subscriptionUserDefaults.storefrontRegionOverride == .usa)
case .usePrivacyProROWRegionOverride:
return (featureFlagger.internalUserDecider.isInternalUser &&
subscriptionEnvironment.serviceEnvironment == .staging &&
subscriptionUserDefaults.storefrontRegionOverride == .restOfWorld)
}
}

if #available(macOS 12.0, *) {
let storePurchaseManager = DefaultStorePurchaseManager(subscriptionFeatureMappingCache: subscriptionFeatureMappingCache,
subscriptionFeatureFlagger: subscriptionFeatureFlagger)
self.init(storePurchaseManager: storePurchaseManager,
accountManager: accountManager,
subscriptionEndpointService: subscriptionEndpointService,
authEndpointService: authEndpointService,
subscriptionFeatureMappingCache: subscriptionFeatureMappingCache,
subscriptionEnvironment: subscriptionEnvironment)
} else {
self.init(accountManager: accountManager,
subscriptionEndpointService: subscriptionEndpointService,
authEndpointService: authEndpointService,
subscriptionFeatureMappingCache: subscriptionFeatureMappingCache,
subscriptionEnvironment: subscriptionEnvironment)
}

accountManager.delegate = self
}
}

extension DefaultSubscriptionManager: AccountManagerKeychainAccessDelegate {

public func accountManagerKeychainAccessFailed(accessType: AccountKeychainAccessType, error: AccountKeychainAccessError) {
PixelKit.fire(PrivacyProErrorPixel.privacyProKeychainAccessError(accessType: accessType, accessError: error),
frequency: .legacyDailyAndCount)
}
}
8 changes: 2 additions & 6 deletions DuckDuckGo/Subscription/SubscriptionRedirectManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,13 @@ protocol SubscriptionRedirectManager: AnyObject {

final class PrivacyProSubscriptionRedirectManager: SubscriptionRedirectManager {

private let featureAvailabiltyProvider: () -> Bool
private let subscriptionEnvironment: SubscriptionEnvironment
private let canPurchase: () -> Bool
private let baseURL: URL

init(featureAvailabiltyProvider: @escaping @autoclosure () -> Bool = DefaultSubscriptionFeatureAvailability().isFeatureAvailable,
subscriptionEnvironment: SubscriptionEnvironment,
init(subscriptionEnvironment: SubscriptionEnvironment,
baseURL: URL,
canPurchase: @escaping () -> Bool) {
self.featureAvailabiltyProvider = featureAvailabiltyProvider
self.subscriptionEnvironment = subscriptionEnvironment
self.canPurchase = canPurchase
self.baseURL = baseURL
Expand All @@ -45,9 +42,8 @@ final class PrivacyProSubscriptionRedirectManager: SubscriptionRedirectManager {
guard url.isPart(ofDomain: "duckduckgo.com") else { return nil }

if url.pathComponents == URL.privacyPro.pathComponents {
let isFeatureAvailable = featureAvailabiltyProvider()
let shouldHidePrivacyProDueToNoProducts = subscriptionEnvironment.purchasePlatform == .appStore && canPurchase() == false
let isPurchasePageRedirectActive = isFeatureAvailable && !shouldHidePrivacyProDueToNoProducts
let isPurchasePageRedirectActive = !shouldHidePrivacyProDueToNoProducts
// Redirect the `/pro` URL to `/subscriptions` URL. If there are any query items in the original URL it appends to the `/subscriptions` URL.
return isPurchasePageRedirectActive ? baseURL.addingQueryItems(from: url) : nil
}
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/Tab/Model/ContentScopeFeatureFlagging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extension ContentScopeFeatureToggles {
passwordGeneration: autofillPrefs.askToSaveUsernamesAndPasswords,
inlineIconCredentials: autofillPrefs.askToSaveUsernamesAndPasswords,
thirdPartyCredentialsProvider: true,
unknownUsernameCategorization: privacyConfig.isSubfeatureEnabled(AutofillSubfeature.unknownUsernameCategorization))
unknownUsernameCategorization: privacyConfig.isSubfeatureEnabled(AutofillSubfeature.unknownUsernameCategorization),
partialFormSaves: privacyConfig.isSubfeatureEnabled(AutofillSubfeature.partialFormSaves))
}
}
Loading

0 comments on commit a1bfa12

Please sign in to comment.