diff --git a/Core/NetworkProtectionNotificationIdentifier.swift b/Core/NetworkProtectionNotificationIdentifier.swift index 20e1a7631a..5ac235621c 100644 --- a/Core/NetworkProtectionNotificationIdentifier.swift +++ b/Core/NetworkProtectionNotificationIdentifier.swift @@ -23,4 +23,5 @@ public enum NetworkProtectionNotificationIdentifier: String { case connection = "network-protection.notification.connection" case superseded = "network-protection.notification.superseded" case test = "network-protection.notification.test" + case entitlement = "network-protection.notification.entitlement" } diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index ba48759ab4..878eedc2c3 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8903,7 +8903,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APP_TRACKING_PROTECTION NETWORK_PROTECTION SUBSCRIPTION"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG APP_TRACKING_PROTECTION NETWORK_PROTECTION SUBSCRIPTION ALPHA"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; VALID_ARCHS = "$(ARCHS_STANDARD_64_BIT)"; @@ -8915,7 +8915,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = "DDG-AppIcon-Alpha"; - CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; + CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CURRENT_PROJECT_VERSION = 1; @@ -9040,7 +9040,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; + CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProviderAlpha.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -9907,8 +9907,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { - kind = exactVersion; - version = 109.0.0; + branch = "anh/netp-check-entitlement-while-rekeying"; + kind = branch; }; }; C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8c6ed5092d..dffec4b993 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -12,10 +12,10 @@ { "identity" : "browserserviceskit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/BrowserServicesKit", + "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "5ecf4fe56f334be6eaecb65f6d55632a6d53921c", - "version" : "109.0.0" + "branch" : "anh/netp-check-entitlement-while-rekeying", + "revision" : "edb3fc3b8fa61f98345185e775804dbfcefdedce" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/duckduckgo-autofill.git", "state" : { - "revision" : "03d3e3a959dd75afbe8c59b5a203ea676d37555d", - "version" : "10.1.0" + "revision" : "b972bc0ab6ee1d57a0a18a197dcc31e40ae6ac57", + "version" : "10.0.3" } }, { diff --git a/DuckDuckGo/EventMapping+NetworkProtectionError.swift b/DuckDuckGo/EventMapping+NetworkProtectionError.swift index 2f465c4113..8aaa5c559a 100644 --- a/DuckDuckGo/EventMapping+NetworkProtectionError.swift +++ b/DuckDuckGo/EventMapping+NetworkProtectionError.swift @@ -70,6 +70,8 @@ extension EventMapping where Event == NetworkProtectionError { params[PixelParameters.keychainErrorCode] = String(status) case .noAuthTokenFound: pixelEvent = .networkProtectionNoAuthTokenFoundError + case .vpnAccessRevoked: + return case .noServerRegistrationInfo, .couldNotSelectClosestServer, diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index c4b19b187d..8b0ec3a6cf 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -1334,12 +1334,6 @@ class MainViewController: UIViewController { self?.onNetworkProtectionAccountSignIn(notification) } .store(in: &netpCancellables) - NotificationCenter.default.publisher(for: .accountDidSignOut) - .receive(on: DispatchQueue.main) - .sink { [weak self] notification in - self?.onNetworkProtectionAccountSignOut(notification) - } - .store(in: &netpCancellables) } @objc @@ -1351,23 +1345,14 @@ class MainViewController: UIViewController { Task { do { - try await NetworkProtectionCodeRedemptionCoordinator().exchange(accessToken: token) - print("[NetP Subscription] Exchanged access token for auth token successfully") + // todo - https://app.asana.com/0/0/1206541966681608/f + try NetworkProtectionKeychainTokenStore().store(NetworkProtectionKeychainTokenStore.makeToken(from: token)) + print("[NetP Subscription] Stored derived NetP auth token") } catch { - print("[NetP Subscription] Failed to exchange access token for auth token: \(error)") + print("[NetP Subscription] Failed to store derived NetP auth token: \(error)") } } } - - @objc - private func onNetworkProtectionAccountSignOut(_ notification: Notification) { - do { - try NetworkProtectionKeychainTokenStore().deleteToken() - print("[NetP Subscription] Deleted NetP auth token after signing out from Privacy Pro") - } catch { - print("[NetP Subscription] Failed to delete NetP auth token after signing out from Privacy Pro: \(error)") - } - } #endif @objc diff --git a/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift b/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift index 01d9287bd7..922220f2a8 100644 --- a/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift +++ b/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift @@ -58,7 +58,8 @@ extension NetworkProtectionKeychainTokenStore { convenience init() { self.init(keychainType: .dataProtection(.unspecified), serviceName: "\(Bundle.main.bundleIdentifier!).authToken", - errorEvents: .networkProtectionAppDebugEvents) + errorEvents: .networkProtectionAppDebugEvents, + isSubscriptionEnabled: AppDependencyProvider.shared.featureFlagger.isFeatureOn(.subscription)) } } @@ -69,7 +70,8 @@ extension NetworkProtectionCodeRedemptionCoordinator { environment: settings.selectedEnvironment, tokenStore: NetworkProtectionKeychainTokenStore(), isManualCodeRedemptionFlow: isManualCodeRedemptionFlow, - errorEvents: .networkProtectionAppDebugEvents + errorEvents: .networkProtectionAppDebugEvents, + isSubscriptionEnabled: AppDependencyProvider.shared.featureFlagger.isFeatureOn(.subscription) ) } } @@ -95,7 +97,8 @@ extension NetworkProtectionLocationListCompositeRepository { self.init( environment: settings.selectedEnvironment, tokenStore: NetworkProtectionKeychainTokenStore(), - errorEvents: .networkProtectionAppDebugEvents + errorEvents: .networkProtectionAppDebugEvents, + isSubscriptionEnabled: AppDependencyProvider.shared.featureFlagger.isFeatureOn(.subscription) ) } } diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 3578eb72cf..fe197f1cad 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -922,10 +922,10 @@ But if you *do* want a peek under the hood, you can find more information about static let networkProtectionNotificationPromptTitle = NSLocalizedString("network-protection.waitlist.notification-prompt-title", value: "Know the instant you're invited", comment: "Title for the alert to confirm enabling notifications") static let networkProtectionNotificationPromptDescription = NSLocalizedString("network-protection.waitlist.notification-prompt-description", value: "Get a notification when your copy of Network Protection early access is ready.", comment: "Subtitle for the alert to confirm enabling notifications") - + // MARK: Settings Screeen public static let settingsTitle = NSLocalizedString("settings.title", value: "Settings", comment: "Title for the Settings View") - + // General Section public static let settingsSetDefault = NSLocalizedString("settings.default.browser", value: "Set as Default Browser", comment: "Settings screen cell text for setting the app as default browser") public static let settingsAddToDock = NSLocalizedString("settings.add.to.dock", value: "Add App to Your Dock", comment: "Settings screen cell text for adding the app to the dock") diff --git a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift index f127d65a7d..a25daf6fe2 100644 --- a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift +++ b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift @@ -162,6 +162,8 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { params[PixelParameters.wireguardErrorCode] = String(code) case .noAuthTokenFound: pixelEvent = .networkProtectionNoAuthTokenFoundError + case .vpnAccessRevoked: + return case .unhandledError(function: let function, line: let line, error: let error): pixelEvent = .networkProtectionUnhandledError params[PixelParameters.function] = function @@ -201,24 +203,31 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { } @objc init() { +#if ALPHA + let isSubscriptionEnabled = true +#else + let isSubscriptionEnabled = false +#endif let tokenStore = NetworkProtectionKeychainTokenStore(keychainType: .dataProtection(.unspecified), - errorEvents: nil) + errorEvents: nil, + isSubscriptionEnabled: isSubscriptionEnabled) let errorStore = NetworkProtectionTunnelErrorStore() let notificationsPresenter = NetworkProtectionUNNotificationPresenter() let settings = VPNSettings(defaults: .networkProtectionGroupDefaults) - let nofificationsPresenterDecorator = NetworkProtectionNotificationsPresenterTogglableDecorator( + let notificationsPresenterDecorator = NetworkProtectionNotificationsPresenterTogglableDecorator( settings: settings, wrappee: notificationsPresenter ) notificationsPresenter.requestAuthorization() - super.init(notificationsPresenter: nofificationsPresenterDecorator, + super.init(notificationsPresenter: notificationsPresenterDecorator, tunnelHealthStore: NetworkProtectionTunnelHealthStore(), controllerErrorStore: errorStore, keychainType: .dataProtection(.unspecified), tokenStore: tokenStore, debugEvents: Self.networkProtectionDebugEvents(controllerErrorStore: errorStore), providerEvents: Self.packetTunnelProviderEvents, - settings: settings) + settings: settings, + isSubscriptionEnabled: isSubscriptionEnabled) startMonitoringMemoryPressureEvents() observeServerChanges() observeStatusChanges() @@ -268,7 +277,6 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { } .store(in: &cancellables) } - } #endif diff --git a/PacketTunnelProvider/NetworkProtection/NetworkProtectionUNNotificationPresenter.swift b/PacketTunnelProvider/NetworkProtection/NetworkProtectionUNNotificationPresenter.swift index 28358d72c8..92222d4b80 100644 --- a/PacketTunnelProvider/NetworkProtection/NetworkProtectionUNNotificationPresenter.swift +++ b/PacketTunnelProvider/NetworkProtection/NetworkProtectionUNNotificationPresenter.swift @@ -62,7 +62,7 @@ final class NetworkProtectionUNNotificationPresenter: NSObject, NetworkProtectio content.title = UserText.networkProtectionNotificationsTitle content.body = body - if #available(iOSApplicationExtension 15.0, *) { + if #available(iOS 15.0, *) { content.interruptionLevel = .timeSensitive content.relevanceScore = 0 } @@ -105,6 +105,11 @@ final class NetworkProtectionUNNotificationPresenter: NSObject, NetworkProtectio func showSupersededNotification() { } + func showExpiredEntitlementNotification() { + let content = notificationContent(body: UserText.networkProtectionEntitlementExpiredNotificationBody) + showNotification(.entitlement, content) + } + private func showNotification(_ identifier: NetworkProtectionNotificationIdentifier, _ content: UNNotificationContent) { let request = UNNotificationRequest(identifier: identifier.rawValue, content: content, trigger: .none) diff --git a/PacketTunnelProvider/UserText.swift b/PacketTunnelProvider/UserText.swift index 820a4a6ebf..7243db5e2b 100644 --- a/PacketTunnelProvider/UserText.swift +++ b/PacketTunnelProvider/UserText.swift @@ -40,5 +40,7 @@ final class UserText { static let networkProtectionConnectionInterruptedNotificationBody = NSLocalizedString("network.protection.interrupted.notification.body", value: "Network Protection was interrupted. Attempting to reconnect now...", comment: "The body of the notification shown when Network Protection's connection is interrupted") static let networkProtectionConnectionFailureNotificationBody = NSLocalizedString("network.protection.failure.notification.body", value: "Network Protection failed to connect. Please try again later.", comment: "The body of the notification shown when Network Protection fails to reconnect") + + static let networkProtectionEntitlementExpiredNotificationBody = NSLocalizedString("network.protection.entitlement.expired.notification.body", value: "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN.", comment: "The body of the notification when Privacy Pro subscription expired") } // swiftlint:enable line_length diff --git a/PacketTunnelProvider/en.lproj/Localizable.strings b/PacketTunnelProvider/en.lproj/Localizable.strings index 1a6d5fe4de..3ceec0f887 100644 --- a/PacketTunnelProvider/en.lproj/Localizable.strings +++ b/PacketTunnelProvider/en.lproj/Localizable.strings @@ -1,3 +1,6 @@ +/* The body of the notification when Privacy Pro subscription expired */ +"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; + /* The body of the notification shown when Network Protection fails to reconnect */ "network.protection.failure.notification.body" = "Network Protection failed to connect. Please try again later.";