From f8479fb48330287e9273a796e98e0d5a4dd721e5 Mon Sep 17 00:00:00 2001 From: Graeme Arthur <2030310+graeme@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:56:35 +0100 Subject: [PATCH 1/5] Add expire entitlement notification case --- .../Notifications/NetworkProtectionNotification.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/NetworkProtection/Notifications/NetworkProtectionNotification.swift b/Sources/NetworkProtection/Notifications/NetworkProtectionNotification.swift index 359c318d5..829296ff5 100644 --- a/Sources/NetworkProtection/Notifications/NetworkProtectionNotification.swift +++ b/Sources/NetworkProtection/Notifications/NetworkProtectionNotification.swift @@ -107,6 +107,7 @@ public enum NetworkProtectionNotification: String { case showConnectedNotification case showIssuesNotResolvedNotification case showVPNSupersededNotification + case showExpiredEntitlementNotification case showTestNotification // Server Selection From d6e896be215705f1ec772032a8772d640c9d8185 Mon Sep 17 00:00:00 2001 From: Graeme Arthur <2030310+graeme@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:56:50 +0100 Subject: [PATCH 2/5] Make entitlements monitor public --- .../Diagnostics/NetworkProtectionEntitlementMonitor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/NetworkProtection/Diagnostics/NetworkProtectionEntitlementMonitor.swift b/Sources/NetworkProtection/Diagnostics/NetworkProtectionEntitlementMonitor.swift index 1f2b43f97..7d9bd6967 100644 --- a/Sources/NetworkProtection/Diagnostics/NetworkProtectionEntitlementMonitor.swift +++ b/Sources/NetworkProtection/Diagnostics/NetworkProtectionEntitlementMonitor.swift @@ -40,7 +40,7 @@ public actor NetworkProtectionEntitlementMonitor { // MARK: - Init & deinit - init() { + public init() { os_log("[+] %{public}@", log: .networkProtectionMemoryLog, type: .debug, String(describing: self)) } From 71c950a1aae064a48638c48a4b293d4b1c257a8d Mon Sep 17 00:00:00 2001 From: Graeme Arthur <2030310+graeme@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:57:41 +0100 Subject: [PATCH 3/5] Add show privacy pro app launcher case --- Sources/NetworkProtection/AppLaunching.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/NetworkProtection/AppLaunching.swift b/Sources/NetworkProtection/AppLaunching.swift index aa46a20e7..d564a951f 100644 --- a/Sources/NetworkProtection/AppLaunching.swift +++ b/Sources/NetworkProtection/AppLaunching.swift @@ -31,6 +31,7 @@ public enum AppLaunchCommand: Codable { case stopVPN case enableOnDemand case moveAppToApplications + case showPrivacyPro } public protocol AppLaunching { From 7c26d62a1e1c7fcf04b7f8bdb5454ddf26d5715b Mon Sep 17 00:00:00 2001 From: Graeme Arthur <2030310+graeme@users.noreply.github.com> Date: Thu, 14 Mar 2024 12:50:59 +0100 Subject: [PATCH 4/5] Adapt vpn token in backend client --- .../Networking/NetworkProtectionClient.swift | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/Sources/NetworkProtection/Networking/NetworkProtectionClient.swift b/Sources/NetworkProtection/Networking/NetworkProtectionClient.swift index fadfca715..4d09dc50d 100644 --- a/Sources/NetworkProtection/Networking/NetworkProtectionClient.swift +++ b/Sources/NetworkProtection/Networking/NetworkProtectionClient.swift @@ -182,7 +182,8 @@ final class NetworkProtectionBackendClient: NetworkProtectionClient { func getLocations(authToken: String) async -> Result<[NetworkProtectionLocation], NetworkProtectionClientError> { var request = URLRequest(url: locationsURL) - request.setValue("bearer \(authToken)", forHTTPHeaderField: "Authorization") + let adaptedToken = adaptTokenIfNeeded(authToken) + request.setValue("bearer \(adaptedToken)", forHTTPHeaderField: "Authorization") let downloadedData: Data do { @@ -209,7 +210,8 @@ final class NetworkProtectionBackendClient: NetworkProtectionClient { func getServers(authToken: String) async -> Result<[NetworkProtectionServer], NetworkProtectionClientError> { var request = URLRequest(url: serversURL) - request.setValue("bearer \(authToken)", forHTTPHeaderField: "Authorization") + let adaptedToken = adaptTokenIfNeeded(authToken) + request.setValue("bearer \(adaptedToken)", forHTTPHeaderField: "Authorization") let downloadedData: Data do { @@ -245,7 +247,8 @@ final class NetworkProtectionBackendClient: NetworkProtectionClient { } var request = URLRequest(url: registerKeyURL) - request.setValue("bearer \(authToken)", forHTTPHeaderField: "Authorization") + let adaptedToken = adaptTokenIfNeeded(authToken) + request.setValue("bearer \(adaptedToken)", forHTTPHeaderField: "Authorization") request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" request.httpBody = requestBodyData @@ -342,7 +345,26 @@ final class NetworkProtectionBackendClient: NetworkProtectionClient { return .failure(NetworkProtectionClientError.failedToParseRedeemResponse(error)) } } +} + +fileprivate extension NetworkProtectionBackendClient { + private static var authTokenPrefix: String { "ddg:" } + func adaptTokenIfNeeded(_ accessToken: String) -> String { + if isSubscriptionEnabled && !Self.isSubscriptionAccessToken(accessToken) { + return Self.makeToken(from: accessToken) + } else { + return accessToken + } + } + + private static func makeToken(from subscriptionAccessToken: String) -> String { + "\(authTokenPrefix)\(subscriptionAccessToken)" + } + + private static func isSubscriptionAccessToken(_ token: String) -> Bool { + token.hasPrefix(authTokenPrefix) + } } extension URL { From 65dc4d0958a0aa8bb77823d0fd740f9d0fef6364 Mon Sep 17 00:00:00 2001 From: Graeme Arthur <2030310+graeme@users.noreply.github.com> Date: Thu, 14 Mar 2024 13:33:27 +0100 Subject: [PATCH 5/5] Adapt location list repo for subs token --- ...workProtectionLocationListRepository.swift | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Sources/NetworkProtection/Repositories/NetworkProtectionLocationListRepository.swift b/Sources/NetworkProtection/Repositories/NetworkProtectionLocationListRepository.swift index dfa6d2aa2..26e384547 100644 --- a/Sources/NetworkProtection/Repositories/NetworkProtectionLocationListRepository.swift +++ b/Sources/NetworkProtection/Repositories/NetworkProtectionLocationListRepository.swift @@ -26,7 +26,7 @@ public protocol NetworkProtectionLocationListRepository { final public class NetworkProtectionLocationListCompositeRepository: NetworkProtectionLocationListRepository { @MainActor private static var locationList: [NetworkProtectionLocation] = [] private let client: NetworkProtectionClient - private let tokenStore: NetworkProtectionTokenStore + private let fetchToken: () throws -> String? private let errorEvents: EventMapping private let isSubscriptionEnabled: Bool @@ -36,18 +36,30 @@ final public class NetworkProtectionLocationListCompositeRepository: NetworkProt isSubscriptionEnabled: Bool) { self.init( client: NetworkProtectionBackendClient(environment: environment, isSubscriptionEnabled: isSubscriptionEnabled), - tokenStore: tokenStore, + fetchToken: tokenStore.fetchToken, + errorEvents: errorEvents, + isSubscriptionEnabled: isSubscriptionEnabled + ) + } + + convenience public init(environment: VPNSettings.SelectedEnvironment, + fetchToken: @escaping () throws -> String?, + errorEvents: EventMapping, + isSubscriptionEnabled: Bool) { + self.init( + client: NetworkProtectionBackendClient(environment: environment, isSubscriptionEnabled: isSubscriptionEnabled), + fetchToken: fetchToken, errorEvents: errorEvents, isSubscriptionEnabled: isSubscriptionEnabled ) } init(client: NetworkProtectionClient, - tokenStore: NetworkProtectionTokenStore, + fetchToken: @escaping () throws -> String?, errorEvents: EventMapping, isSubscriptionEnabled: Bool) { self.client = client - self.tokenStore = tokenStore + self.fetchToken = fetchToken self.errorEvents = errorEvents self.isSubscriptionEnabled = isSubscriptionEnabled } @@ -58,7 +70,7 @@ final public class NetworkProtectionLocationListCompositeRepository: NetworkProt return Self.locationList } do { - guard let authToken = try tokenStore.fetchToken() else { + guard let authToken = try fetchToken() else { throw NetworkProtectionError.noAuthTokenFound } Self.locationList = try await client.getLocations(authToken: authToken).get()