From 799b044d6e4efde2c57dacbf8199bf2851574e23 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 14 Aug 2024 10:37:16 -0500 Subject: [PATCH 01/34] Convert app to use default configuration manager --- DuckDuckGo.xcodeproj/project.pbxproj | 10 ++ .../xcshareddata/swiftpm/Package.resolved | 9 -- .../xcschemes/sandbox-test-tool.xcscheme | 2 +- .../Configuration/ConfigurationManager.swift | 87 +------------ .../Configuration/ConfigurationManager.swift | 41 ++++++ .../Configuration/ConfigurationStore.swift | 120 ++++++++++++++++++ 6 files changed, 177 insertions(+), 92 deletions(-) create mode 100644 LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift create mode 100644 LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 6633cc4470..bf884fdc8c 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 020807B22C6CFF95006F94C4 /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 020807B12C6CFF95006F94C4 /* Configuration */; }; 021EA0802BD2A9D500772C9A /* TabsPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021EA07F2BD2A9D500772C9A /* TabsPreferences.swift */; }; 021EA0812BD2A9D500772C9A /* TabsPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021EA07F2BD2A9D500772C9A /* TabsPreferences.swift */; }; 021EA0842BD6E01A00772C9A /* TabsPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021EA0822BD6DF1B00772C9A /* TabsPreferencesTests.swift */; }; @@ -2948,6 +2949,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 020807B52C6D00A8006F94C4 /* BrowserServicesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = BrowserServicesKit; path = ../BrowserServicesKit; sourceTree = ""; }; 021EA07F2BD2A9D500772C9A /* TabsPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsPreferences.swift; sourceTree = ""; }; 021EA0822BD6DF1B00772C9A /* TabsPreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsPreferencesTests.swift; sourceTree = ""; }; 0230C0A2272080090018F728 /* KeyedCodingExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedCodingExtension.swift; sourceTree = ""; }; @@ -4523,6 +4525,7 @@ buildActionMask = 2147483647; files = ( 9DEF97E12B06C4EE00764F03 /* Networking in Frameworks */, + 020807B22C6CFF95006F94C4 /* Configuration in Frameworks */, F1D0428E2BFB9F9C00A31506 /* Subscription in Frameworks */, 9D9AE8F92AAA3AD00026E7DC /* DataBrokerProtection in Frameworks */, ); @@ -6836,6 +6839,7 @@ AA585D75248FD31100E9A3E2 = { isa = PBXGroup; children = ( + 020807B52C6D00A8006F94C4 /* BrowserServicesKit */, 378B5886295CF2A4002C0CC0 /* Configuration */, 378E279C2970217400FCADA2 /* LocalPackages */, 7BB108552A43375D000AB95F /* LocalThirdParty */, @@ -9009,6 +9013,7 @@ 9D9AE8F82AAA3AD00026E7DC /* DataBrokerProtection */, 9DEF97E02B06C4EE00764F03 /* Networking */, F1D0428D2BFB9F9C00A31506 /* Subscription */, + 020807B12C6CFF95006F94C4 /* Configuration */, ); productName = DuckDuckGoAgent; productReference = 9D9AE8D12AAA39A70026E7DC /* DuckDuckGo Personal Information Removal.app */; @@ -13535,6 +13540,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 020807B12C6CFF95006F94C4 /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Configuration; + }; 08D4923DC968236E22E373E2 /* Crashes */ = { isa = XCSwiftPackageProductDependency; package = FAE06B199CA1F209B55B34E9 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index dfc932907d..e71fc310d6 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -27,15 +27,6 @@ "version" : "3.0.0" } }, - { - "identity" : "browserserviceskit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/BrowserServicesKit", - "state" : { - "revision" : "c6ce430371032930d770b0388cbe44a2d40ad729", - "version" : "184.0.0" - } - }, { "identity" : "content-scope-scripts", "kind" : "remoteSourceControl", diff --git a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/sandbox-test-tool.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/sandbox-test-tool.xcscheme index eb7e5e26bb..41730d7069 100644 --- a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/sandbox-test-tool.xcscheme +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/sandbox-test-tool.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> Swift.Error { - let nsError = self as NSError - return NSError(domain: nsError.domain, code: nsError.code, userInfo: [NSUnderlyingErrorKey: underlyingError]) - } - - } - - enum Constants { - - static let downloadTimeoutSeconds = 60.0 * 5 -#if DEBUG - static let refreshPeriodSeconds = 60.0 * 2 // 2 minutes -#else - static let refreshPeriodSeconds = 60.0 * 30 // 30 minutes -#endif - static let retryDelaySeconds = 60.0 * 60 * 1 // 1 hour delay before checking again if something went wrong last time - static let refreshCheckIntervalSeconds = 60.0 // check if we need a refresh every minute - - } - - static let shared = ConfigurationManager() - static let queue: DispatchQueue = DispatchQueue(label: "Configuration Manager") + static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, + log: .config, + eventMapping: configurationDebugEvents)) @UserDefaultsWrapper(key: .configLastUpdated, defaultValue: .distantPast) private(set) var lastUpdateTime: Date @@ -64,13 +36,6 @@ final class ConfigurationManager { @UserDefaultsWrapper(key: .configLastInstalled, defaultValue: nil) private(set) var lastConfigurationInstallDate: Date? - private var timerCancellable: AnyCancellable? - private var lastRefreshCheckTime: Date = Date() - - private lazy var fetcher = ConfigurationFetcher(store: ConfigurationStore.shared, - log: .config, - eventMapping: Self.configurationDebugEvents) - static let configurationDebugEvents = EventMapping { event, error, _, _ in let domainEvent: GeneralPixel switch event { @@ -81,26 +46,12 @@ final class ConfigurationManager { PixelKit.fire(DebugEvent(domainEvent, error: error)) } - func start() { - os_log("Starting configuration refresh timer", log: .config, type: .debug) - timerCancellable = Timer.publish(every: Constants.refreshCheckIntervalSeconds, on: .main, in: .default) - .autoconnect() - .receive(on: Self.queue) - .sink(receiveValue: { _ in - self.lastRefreshCheckTime = Date() - self.refreshIfNeeded() - }) - Task { - await refreshNow() - } - } - func log() { os_log("last update %{public}s", log: .config, type: .default, String(describing: lastUpdateTime)) os_log("last refresh check %{public}s", log: .config, type: .default, String(describing: lastRefreshCheckTime)) } - private func refreshNow(isDebug: Bool = false) async { + override public func refreshNow(isDebug: Bool = false) async { let updateTrackerBlockingDependenciesTask = Task { let didFetchAnyTrackerBlockingDependencies = await fetchTrackerBlockingDependencies(isDebug: isDebug) if didFetchAnyTrackerBlockingDependencies { @@ -174,34 +125,6 @@ final class ConfigurationManager { tryAgainSoon() } - @discardableResult - public func refreshIfNeeded() -> Task? { - guard isReadyToRefresh else { - os_log("Configuration refresh is not needed at this time", log: .config, type: .debug) - return nil - } - return Task { - await refreshNow() - } - } - - private var isReadyToRefresh: Bool { Date().timeIntervalSince(lastUpdateTime) > Constants.refreshPeriodSeconds } - - public func forceRefresh(isDebug: Bool = false) { - Task { - await refreshNow(isDebug: isDebug) - } - } - - private func tryAgainLater() { - lastUpdateTime = Date() - } - - private func tryAgainSoon() { - // Set the last update time to in the past so it triggers again sooner - lastUpdateTime = Date(timeIntervalSinceNow: Constants.refreshPeriodSeconds - Constants.retryDelaySeconds) - } - private func updateTrackerBlockingDependencies() { lastConfigurationInstallDate = Date() ContentBlocking.shared.trackerDataManager.reload(etag: ConfigurationStore.shared.loadEtag(for: .trackerDataSet), diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift new file mode 100644 index 0000000000..72639b53fd --- /dev/null +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -0,0 +1,41 @@ +// +// ConfigurationManager.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 BrowserServicesKit +import Configuration + +final class ConfigurationManager { + + enum Constants { + + static let downloadTimeoutSeconds = 60.0 * 5 +#if DEBUG + static let refreshPeriodSeconds = 60.0 * 2 // 2 minutes +#else + static let refreshPeriodSeconds = 60.0 * 30 // 30 minutes +#endif + static let retryDelaySeconds = 60.0 * 60 * 1 // 1 hour delay before checking again if something went wrong last time + static let refreshCheckIntervalSeconds = 60.0 // check if we need a refresh every minute + + } + + static let shared = ConfigurationManager() + static let queue: DispatchQueue = DispatchQueue(label: "Configuration Manager") + +} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift new file mode 100644 index 0000000000..1f2fd220cb --- /dev/null +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift @@ -0,0 +1,120 @@ +// +// ConfigurationStore.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 BrowserServicesKit +import Configuration + +final class ConfigurationStore: ConfigurationStoring { + + private static let fileLocations: [Configuration: String] = [ + .privacyConfiguration: "macos-config.json", + ] + + enum Error: Swift.Error { + + case unsupportedConfig + + func withUnderlyingError(_ underlyingError: Swift.Error) -> Swift.Error { + let nsError = self as NSError + return NSError(domain: nsError.domain, code: nsError.code, userInfo: [NSUnderlyingErrorKey: underlyingError]) + } + + } + + private var privacyConfigurationEtagKey: String { + return "configurationPrivacyConfigurationEtag" + } + + static let shared = ConfigurationStore() + let defaults = UserDefaults.dbp + + var privacyConfigurationEtag: String? { + get { + defaults.string(forKey: privacyConfigurationEtagKey) + } + set { + defaults.setValue(newValue, forKey: privacyConfigurationEtagKey) + } + } + + func loadData(for configuration: Configuration) -> Data? { + guard configuration == .privacyConfiguration else { return nil } + + let file = fileUrl(for: configuration) + do { + return try Data(contentsOf: file) + } catch { + // TODO: Fire error pixel + + return nil + } + } + + func loadEtag(for configuration: Configuration) -> String? { + guard configuration == .privacyConfiguration else { return nil } + + return privacyConfigurationEtag + } + + func loadEmbeddedEtag(for configuration: Configuration) -> String? { + guard configuration == .privacyConfiguration else { return nil } + + // TODO: Handle embedded config + return privacyConfigurationEtag + } + + func saveData(_ data: Data, for configuration: Configuration) throws { + guard configuration == .privacyConfiguration else { throw Error.unsupportedConfig } + + let file = fileUrl(for: configuration) + try data.write(to: file, options: .atomic) + } + + func saveEtag(_ etag: String, for configuration: Configuration) throws { + guard configuration == .privacyConfiguration else { throw Error.unsupportedConfig } + + privacyConfigurationEtag = etag + } + + func fileUrl(for config: Configuration) -> URL { + let fm = FileManager.default + + guard let dir = fm.containerURL(forSecurityApplicationGroupIdentifier: Bundle.main.dbpAppGroup) else { // TODO: Change to Configuration group + fatalError("Failed to get application group URL") + } + let subDir = dir.appendingPathComponent("Configuration") + + var isDir: ObjCBool = false + if !fm.fileExists(atPath: subDir.path, isDirectory: &isDir) { + do { + try fm.createDirectory(at: subDir, withIntermediateDirectories: true, attributes: nil) + isDir = true + } catch { + fatalError("Failed to create directory at \(subDir.path)") + } + } + + if !isDir.boolValue { + fatalError("Configuration folder at \(subDir.path) is not a directory") + } + + return subDir.appendingPathComponent(Self.fileLocations[config]!) + } + +} From 2d36d740bf3cd1f562d3dcc549f2738bdabbff68 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Thu, 15 Aug 2024 10:17:08 -0500 Subject: [PATCH 02/34] Setup configurationmanager in DBP agent --- .../Configuration/ConfigurationManager.swift | 50 ++++++++++++++----- .../DataBrokerProtectionAgentManager.swift | 1 + 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift index 72639b53fd..08160b1017 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -19,23 +19,49 @@ import Foundation import BrowserServicesKit import Configuration +import Common +import PixelKit -final class ConfigurationManager { +final class ConfigurationManager: DefaultConfigurationManager { - enum Constants { + static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, + log: .default, + eventMapping: nil)) + // TODO: EventMapping for error pixels - static let downloadTimeoutSeconds = 60.0 * 5 -#if DEBUG - static let refreshPeriodSeconds = 60.0 * 2 // 2 minutes -#else - static let refreshPeriodSeconds = 60.0 * 30 // 30 minutes -#endif - static let retryDelaySeconds = 60.0 * 60 * 1 // 1 hour delay before checking again if something went wrong last time - static let refreshCheckIntervalSeconds = 60.0 // check if we need a refresh every minute + override public func refreshNow(isDebug: Bool = false) async { + let updateConfigDependenciesTask = Task { + let didFetchConfig = await fetchConfigDependencies(isDebug: isDebug) + if didFetchConfig { + updateConfigDependencies() + tryAgainLater() + } + } + await updateConfigDependenciesTask.value + +// ConfigurationStore.shared.log() +// log() } - static let shared = ConfigurationManager() - static let queue: DispatchQueue = DispatchQueue(label: "Configuration Manager") + func fetchConfigDependencies(isDebug: Bool) async -> Bool { + do { + try await fetcher.fetch(.privacyConfiguration, isDebug: isDebug) + return true + } catch { + os_log("Failed to complete configuration update to %@: %@", + log: .default, + type: .error, + Configuration.privacyConfiguration.rawValue, + error.localizedDescription) + tryAgainSoon() + } + + return false + } + func updateConfigDependencies() { + // TODO: Update lastConfigurationInstallDate + // TODO: Provide config to dependency manager + } } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift index 81dbcaed47..f39b398e5e 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift @@ -31,6 +31,7 @@ public class DataBrokerProtectionAgentManagerProvider { let activityScheduler = DefaultDataBrokerProtectionBackgroundActivityScheduler(config: executionConfig) let notificationService = DefaultDataBrokerProtectionUserNotificationService(pixelHandler: pixelHandler) + ConfigurationManager.shared.start() let privacyConfigurationManager = PrivacyConfigurationManagingMock() // Forgive me, for I have sinned let ipcServer = DefaultDataBrokerProtectionIPCServer(machServiceName: Bundle.main.bundleIdentifier!) From d7e1d5c6414e236140fe62371557f1aac6d8103d Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Mon, 19 Aug 2024 13:50:18 -0500 Subject: [PATCH 03/34] Migrate DBP to real privacy config --- .../Configuration/ConfigurationManager.swift | 3 - .../DataBrokerProtectionPixelsHandler.swift | 3 +- .../Configuration/ConfigurationManager.swift | 28 ++++- .../Configuration/ConfigurationStore.swift | 5 + .../DBPPrivacyConfigurationManager.swift | 115 ++++++++++++++++++ .../DataBrokerRunCustomJSONViewModel.swift | 2 +- .../Pixels/DataBrokerProtectionPixels.swift | 13 +- .../Scheduler/DBPMocks.swift | 59 --------- .../DataBrokerProtectionAgentManager.swift | 2 +- 9 files changed, 158 insertions(+), 72 deletions(-) create mode 100644 LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift diff --git a/DuckDuckGo/Configuration/ConfigurationManager.swift b/DuckDuckGo/Configuration/ConfigurationManager.swift index fd5ecd058a..9703726b07 100644 --- a/DuckDuckGo/Configuration/ConfigurationManager.swift +++ b/DuckDuckGo/Configuration/ConfigurationManager.swift @@ -30,9 +30,6 @@ final class ConfigurationManager: DefaultConfigurationManager { log: .config, eventMapping: configurationDebugEvents)) - @UserDefaultsWrapper(key: .configLastUpdated, defaultValue: .distantPast) - private(set) var lastUpdateTime: Date - @UserDefaultsWrapper(key: .configLastInstalled, defaultValue: nil) private(set) var lastConfigurationInstallDate: Date? diff --git a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift index de4b54b3ee..de97f53d17 100644 --- a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift +++ b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift @@ -98,7 +98,8 @@ public class DataBrokerProtectionPixelsHandler: EventMapping { event, error, _, _ in + let domainEvent: DataBrokerProtectionPixels + switch event { + case .invalidPayload(let configuration): + domainEvent = .invalidPayload(configuration) + } + + PixelKit.fire(DebugEvent(domainEvent, error: error)) + } + + func log() { + os_log("last update %{public}s", log: .default, type: .default, String(describing: lastUpdateTime)) + os_log("last refresh check %{public}s", log: .default, type: .default, String(describing: lastRefreshCheckTime)) + } override public func refreshNow(isDebug: Bool = false) async { let updateConfigDependenciesTask = Task { @@ -40,8 +55,8 @@ final class ConfigurationManager: DefaultConfigurationManager { await updateConfigDependenciesTask.value -// ConfigurationStore.shared.log() -// log() + ConfigurationStore.shared.log() + log() } func fetchConfigDependencies(isDebug: Bool) async -> Bool { @@ -62,6 +77,9 @@ final class ConfigurationManager: DefaultConfigurationManager { func updateConfigDependencies() { // TODO: Update lastConfigurationInstallDate - // TODO: Provide config to dependency manager + DBBPPrivacyConfigurationManager.shared.reload( + etag: ConfigurationStore.shared.loadEtag(for: .privacyConfiguration), + data: ConfigurationStore.shared.loadData(for: .privacyConfiguration) + ) } } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift index 1f2fd220cb..46d369a09f 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift @@ -18,6 +18,7 @@ import Foundation import BrowserServicesKit +import Common import Configuration final class ConfigurationStore: ConfigurationStoring { @@ -53,6 +54,10 @@ final class ConfigurationStore: ConfigurationStoring { } } + func log() { + os_log("privacyConfigurationEtag %{public}s", log: .default, type: .default, privacyConfigurationEtag ?? "") + } + func loadData(for configuration: Configuration) -> Data? { guard configuration == .privacyConfiguration else { return nil } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift new file mode 100644 index 0000000000..96cd9354a5 --- /dev/null +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift @@ -0,0 +1,115 @@ +// +// DBPPrivacyConfigurationManager.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. +// + +import Foundation +import BrowserServicesKit +import Combine +import Common + +public final class DBBPPrivacyConfigurationManager: PrivacyConfigurationManaging { + + static let shared = DBBPPrivacyConfigurationManager() + + private let lock = NSLock() + + var embeddedConfigData: Data { + let configString = """ + { + "readme": "https://github.com/duckduckgo/privacy-configuration", + "version": 1693838894358, + "features": { + "brokerProtection": { + "state": "enabled", + "exceptions": [], + "settings": {} + } + }, + "unprotectedTemporary": [] + } + """ + let data = configString.data(using: .utf8) + return data! + } + + private var _fetchedConfigData: PrivacyConfigurationManager.ConfigurationData? + private(set) public var fetchedConfigData: PrivacyConfigurationManager.ConfigurationData? { + get { + lock.lock() + let data = _fetchedConfigData + lock.unlock() + return data + } + set { + lock.lock() + _fetchedConfigData = newValue + lock.unlock() + } + } + + public var currentConfig: Data { + if let fetchedData = fetchedConfigData { + return fetchedData.rawData + } + return embeddedConfigData + } + + public var updatesPublisher: AnyPublisher = .init(Just(())) + + public var privacyConfig: BrowserServicesKit.PrivacyConfiguration { + guard let privacyConfigurationData = try? PrivacyConfigurationData(data: currentConfig) else { + fatalError("Could not retrieve privacy configuration data") + } + let privacyConfig = privacyConfiguration(withData: privacyConfigurationData, + internalUserDecider: internalUserDecider) + return privacyConfig + } + + public var internalUserDecider: InternalUserDecider = DefaultInternalUserDecider(store: InternalUserDeciderStoreMock()) + + @discardableResult + public func reload(etag: String?, data: Data?) -> PrivacyConfigurationManager.ReloadResult { + let result: PrivacyConfigurationManager.ReloadResult + + if let etag = etag, let data = data { + result = .downloaded + + do { + let configData = try PrivacyConfigurationData(data: data) + fetchedConfigData = (data, configData, etag) + } catch { + // TODO: Fire failed to parse pixel + fetchedConfigData = nil + return .embeddedFallback + } + } else { + fetchedConfigData = nil + result = .embedded + } + + return result + } +} + +func privacyConfiguration(withData data: PrivacyConfigurationData, + internalUserDecider: InternalUserDecider) -> PrivacyConfiguration { + let domain = MockDomainsProtectionStore() + return AppPrivacyConfiguration(data: data, + identifier: UUID().uuidString, + localProtection: domain, + internalUserDecider: internalUserDecider) +} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift index c32392586c..f603e01043 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift @@ -154,7 +154,7 @@ final class DataBrokerRunCustomJSONViewModel: ObservableObject { private let authenticationManager: DataBrokerProtectionAuthenticationManaging init(authenticationManager: DataBrokerProtectionAuthenticationManaging) { - let privacyConfigurationManager = PrivacyConfigurationManagingMock() + let privacyConfigurationManager = DBBPPrivacyConfigurationManager.shared let features = ContentScopeFeatureToggles(emailProtection: false, emailProtectionIncontextSignup: false, credentialsAutofill: false, diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift index b57f7dc2d3..b2ca8ecb3b 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift @@ -19,6 +19,7 @@ import Foundation import Common import BrowserServicesKit +import Configuration import PixelKit enum ErrorCategory: Equatable { @@ -180,6 +181,9 @@ public enum DataBrokerProtectionPixels { case entitlementCheckInvalid case entitlementCheckError + // Configuration + case invalidPayload(Configuration) + // Measure success/failure rate of Personal Information Removal Pixels // https://app.asana.com/0/1204006570077678/1206889724879222/f case globalMetricsWeeklyStats(profilesFound: Int, optOutsInProgress: Int, successfulOptOuts: Int, failedOptOuts: Int, durationOfFirstOptOut: Int, numberOfNewRecordsFound: Int) @@ -309,6 +313,9 @@ extension DataBrokerProtectionPixels: PixelKitEvent { // Feature Gatekeeper case .gatekeeperNotAuthenticated: return "m_mac_dbp_gatekeeper_not_authenticated" case .gatekeeperEntitlementsInvalid: return "m_mac_dbp_gatekeeper_entitlements_invalid" + + // Configuration + case .invalidPayload(let configuration): return "m_mac_dbp_\(configuration.rawValue)_invalid_payload".lowercased() } } @@ -411,7 +418,8 @@ extension DataBrokerProtectionPixels: PixelKitEvent { .secureVaultKeyStoreUpdateError, .secureVaultError, .gatekeeperNotAuthenticated, - .gatekeeperEntitlementsInvalid: + .gatekeeperEntitlementsInvalid, + .invalidPayload: return [:] case .ipcServerProfileSavedCalledByApp, .ipcServerProfileSavedReceivedByAgent, @@ -551,7 +559,8 @@ public class DataBrokerProtectionPixelsHandler: EventMapping = .init(Just(())) - - public var privacyConfig: BrowserServicesKit.PrivacyConfiguration { - guard let privacyConfigurationData = try? PrivacyConfigurationData(data: data) else { - fatalError("Could not retrieve privacy configuration data") - } - let privacyConfig = privacyConfiguration(withData: privacyConfigurationData, - internalUserDecider: internalUserDecider) - return privacyConfig - } - - public var internalUserDecider: InternalUserDecider = DefaultInternalUserDecider(store: InternalUserDeciderStoreMock()) - - public func reload(etag: String?, data: Data?) -> PrivacyConfigurationManager.ReloadResult { - .downloaded - } -} - -func privacyConfiguration(withData data: PrivacyConfigurationData, - internalUserDecider: InternalUserDecider) -> PrivacyConfiguration { - let domain = MockDomainsProtectionStore() - return AppPrivacyConfiguration(data: data, - identifier: UUID().uuidString, - localProtection: domain, - internalUserDecider: internalUserDecider) -} final class MockDomainsProtectionStore: DomainsProtectionStore { var unprotectedDomains = Set() diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift index f39b398e5e..74101e1a0e 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift @@ -32,7 +32,7 @@ public class DataBrokerProtectionAgentManagerProvider { let notificationService = DefaultDataBrokerProtectionUserNotificationService(pixelHandler: pixelHandler) ConfigurationManager.shared.start() - let privacyConfigurationManager = PrivacyConfigurationManagingMock() // Forgive me, for I have sinned + let privacyConfigurationManager = DBBPPrivacyConfigurationManager.shared let ipcServer = DefaultDataBrokerProtectionIPCServer(machServiceName: Bundle.main.bundleIdentifier!) let features = ContentScopeFeatureToggles(emailProtection: false, From 1de097353ee5e5fff9989a09c1c2dd3da92700b3 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Mon, 19 Aug 2024 14:20:05 -0500 Subject: [PATCH 04/34] Move config log to BSK --- DuckDuckGo/Common/Logging/Logging.swift | 2 -- .../DataBrokerProtectionPixelsHandler.swift | 3 ++- DuckDuckGo/Menus/MainMenuActions.swift | 4 ++-- .../Configuration/ConfigurationManager.swift | 9 ++++---- .../Configuration/ConfigurationStore.swift | 22 +++++++++---------- .../Pixels/DataBrokerProtectionPixels.swift | 7 ++++++ 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/DuckDuckGo/Common/Logging/Logging.swift b/DuckDuckGo/Common/Logging/Logging.swift index 32395eaa8d..bc1c5c19ee 100644 --- a/DuckDuckGo/Common/Logging/Logging.swift +++ b/DuckDuckGo/Common/Logging/Logging.swift @@ -24,7 +24,6 @@ extension OSLog { enum AppCategories: String, CaseIterable { case atb = "ATB" - case config = "Configuration Downloading" case downloads = "Downloads" case fire = "Fire" case dataImportExport = "Data Import/Export" @@ -53,7 +52,6 @@ extension OSLog { } @OSLogWrapper(AppCategories.atb) static var atb - @OSLogWrapper(AppCategories.config) static var config @OSLogWrapper(AppCategories.downloads) static var downloads @OSLogWrapper(AppCategories.fire) static var fire @OSLogWrapper(AppCategories.dataImportExport) static var dataImportExport diff --git a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift index de97f53d17..1d79f9d50a 100644 --- a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift +++ b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift @@ -32,7 +32,8 @@ public class DataBrokerProtectionPixelsHandler: EventMapping { event, error, _, _ in @@ -40,8 +40,8 @@ final class ConfigurationManager: DefaultConfigurationManager { } func log() { - os_log("last update %{public}s", log: .default, type: .default, String(describing: lastUpdateTime)) - os_log("last refresh check %{public}s", log: .default, type: .default, String(describing: lastRefreshCheckTime)) + os_log("last update %{public}s", log: .config, type: .default, String(describing: lastUpdateTime)) + os_log("last refresh check %{public}s", log: .config, type: .default, String(describing: lastRefreshCheckTime)) } override public func refreshNow(isDebug: Bool = false) async { @@ -65,7 +65,7 @@ final class ConfigurationManager: DefaultConfigurationManager { return true } catch { os_log("Failed to complete configuration update to %@: %@", - log: .default, + log: .config, type: .error, Configuration.privacyConfiguration.rawValue, error.localizedDescription) @@ -76,7 +76,6 @@ final class ConfigurationManager: DefaultConfigurationManager { } func updateConfigDependencies() { - // TODO: Update lastConfigurationInstallDate DBBPPrivacyConfigurationManager.shared.reload( etag: ConfigurationStore.shared.loadEtag(for: .privacyConfiguration), data: ConfigurationStore.shared.loadData(for: .privacyConfiguration) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift index 46d369a09f..ec7d73f2d4 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift @@ -20,6 +20,7 @@ import Foundation import BrowserServicesKit import Common import Configuration +import PixelKit final class ConfigurationStore: ConfigurationStoring { @@ -55,7 +56,7 @@ final class ConfigurationStore: ConfigurationStoring { } func log() { - os_log("privacyConfigurationEtag %{public}s", log: .default, type: .default, privacyConfigurationEtag ?? "") + os_log("privacyConfigurationEtag %{public}s", log: .config, type: .default, privacyConfigurationEtag ?? "") } func loadData(for configuration: Configuration) -> Data? { @@ -65,38 +66,35 @@ final class ConfigurationStore: ConfigurationStoring { do { return try Data(contentsOf: file) } catch { - // TODO: Fire error pixel - + PixelKit.fire(DataBrokerProtectionPixels.errorLoadingCachedConfig(error)) return nil } } - + func loadEtag(for configuration: Configuration) -> String? { guard configuration == .privacyConfiguration else { return nil } return privacyConfigurationEtag } - - func loadEmbeddedEtag(for configuration: Configuration) -> String? { - guard configuration == .privacyConfiguration else { return nil } - // TODO: Handle embedded config - return privacyConfigurationEtag + func loadEmbeddedEtag(for configuration: Configuration) -> String? { + // If we embed the full config somday we need to load the etag for it here + return nil } - + func saveData(_ data: Data, for configuration: Configuration) throws { guard configuration == .privacyConfiguration else { throw Error.unsupportedConfig } let file = fileUrl(for: configuration) try data.write(to: file, options: .atomic) } - + func saveEtag(_ etag: String, for configuration: Configuration) throws { guard configuration == .privacyConfiguration else { throw Error.unsupportedConfig } privacyConfigurationEtag = etag } - + func fileUrl(for config: Configuration) -> URL { let fm = FileManager.default diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift index b2ca8ecb3b..7d3b42164b 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift @@ -55,6 +55,7 @@ public enum DataBrokerProtectionPixels { static let triesKey = "tries" static let errorCategoryKey = "error_category" static let errorDetailsKey = "error_details" + static let errorDomainKey = "error_domain" static let pattern = "pattern" static let isParent = "is_parent" static let actionIDKey = "action_id" @@ -183,6 +184,7 @@ public enum DataBrokerProtectionPixels { // Configuration case invalidPayload(Configuration) + case errorLoadingCachedConfig(Error) // Measure success/failure rate of Personal Information Removal Pixels // https://app.asana.com/0/1204006570077678/1206889724879222/f @@ -316,6 +318,7 @@ extension DataBrokerProtectionPixels: PixelKitEvent { // Configuration case .invalidPayload(let configuration): return "m_mac_dbp_\(configuration.rawValue)_invalid_payload".lowercased() + case .errorLoadingCachedConfig: return "m_mac_dbp_configuration_error_loading_cached_config" } } @@ -475,6 +478,8 @@ extension DataBrokerProtectionPixels: PixelKitEvent { Consts.durationOfFirstOptOut: String(durationOfFirstOptOut), Consts.numberOfNewRecordsFound: String(numberOfNewRecordsFound), Consts.numberOfReappereances: String(numberOfReappereances)] + case .errorLoadingCachedConfig(let error): + return [Consts.errorDomainKey: (error as NSError).domain] } } } @@ -492,6 +497,8 @@ public class DataBrokerProtectionPixelsHandler: EventMapping Date: Tue, 20 Aug 2024 13:42:03 -0500 Subject: [PATCH 05/34] Draft code for pixel test --- .../DataBrokerProtectionPixelsHandler.swift | 3 +- .../Configuration/ConfigurationManager.swift | 2 +- .../DBPAgentConfigurationURLProvider.swift | 29 +++++++++++++++++++ .../DBPPrivacyConfigurationManager.swift | 4 +-- .../DataBrokerRunCustomJSONViewModel.swift | 2 +- .../Pixels/DataBrokerProtectionPixels.swift | 8 +++-- .../DataBrokerProtectionAgentManager.swift | 11 ++++++- 7 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPAgentConfigurationURLProvider.swift diff --git a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift index 1d79f9d50a..ed84d37425 100644 --- a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift +++ b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift @@ -100,7 +100,8 @@ public class DataBrokerProtectionPixelsHandler: EventMapping URL { + guard configuration == .privacyConfiguration else { fatalError("\(configuration.rawValue) is not supported on this target") } + + return URL(string: "http://localhost:3000/generated/v4/macos-config.json")! + } +} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift index 96cd9354a5..ce22b177ca 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift @@ -21,9 +21,9 @@ import BrowserServicesKit import Combine import Common -public final class DBBPPrivacyConfigurationManager: PrivacyConfigurationManaging { +public final class DBPPrivacyConfigurationManager: PrivacyConfigurationManaging { - static let shared = DBBPPrivacyConfigurationManager() + static let shared = DBPPrivacyConfigurationManager() private let lock = NSLock() diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift index f603e01043..1287cb0951 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift @@ -154,7 +154,7 @@ final class DataBrokerRunCustomJSONViewModel: ObservableObject { private let authenticationManager: DataBrokerProtectionAuthenticationManaging init(authenticationManager: DataBrokerProtectionAuthenticationManaging) { - let privacyConfigurationManager = DBBPPrivacyConfigurationManager.shared + let privacyConfigurationManager = DBPPrivacyConfigurationManager.shared let features = ContentScopeFeatureToggles(emailProtection: false, emailProtectionIncontextSignup: false, credentialsAutofill: false, diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift index 7d3b42164b..981dc39e3a 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift @@ -185,6 +185,7 @@ public enum DataBrokerProtectionPixels { // Configuration case invalidPayload(Configuration) case errorLoadingCachedConfig(Error) + case pixelTest // Measure success/failure rate of Personal Information Removal Pixels // https://app.asana.com/0/1204006570077678/1206889724879222/f @@ -319,6 +320,7 @@ extension DataBrokerProtectionPixels: PixelKitEvent { // Configuration case .invalidPayload(let configuration): return "m_mac_dbp_\(configuration.rawValue)_invalid_payload".lowercased() case .errorLoadingCachedConfig: return "m_mac_dbp_configuration_error_loading_cached_config" + case .pixelTest: return "m_mac_dbp_configuration_pixel_test" } } @@ -422,7 +424,8 @@ extension DataBrokerProtectionPixels: PixelKitEvent { .secureVaultError, .gatekeeperNotAuthenticated, .gatekeeperEntitlementsInvalid, - .invalidPayload: + .invalidPayload, + .pixelTest: return [:] case .ipcServerProfileSavedCalledByApp, .ipcServerProfileSavedReceivedByAgent, @@ -567,7 +570,8 @@ public class DataBrokerProtectionPixelsHandler: EventMapping Date: Wed, 21 Aug 2024 08:29:42 -0500 Subject: [PATCH 06/34] Setup file dispatch to observe file changes --- .../Configuration/ConfigurationManager.swift | 25 +++++++++++++++++++ .../Configuration/ConfigurationManager.swift | 25 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/DuckDuckGo/Configuration/ConfigurationManager.swift b/DuckDuckGo/Configuration/ConfigurationManager.swift index 9703726b07..9df3ad3c1c 100644 --- a/DuckDuckGo/Configuration/ConfigurationManager.swift +++ b/DuckDuckGo/Configuration/ConfigurationManager.swift @@ -43,6 +43,31 @@ final class ConfigurationManager: DefaultConfigurationManager { PixelKit.fire(DebugEvent(domainEvent, error: error)) } + private var fileDispatchSource: DispatchSourceFileSystemObject? + + override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults()) { + super.init(fetcher: fetcher, defaults: defaults) + + do { + let fileHandle = try FileHandle(forReadingFrom: ConfigurationStore.shared.fileUrl(for: .privacyConfiguration)) + fileDispatchSource = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: fileHandle.fileDescriptor, + eventMask: .write, + queue: ConfigurationManager.queue + ) + fileDispatchSource?.setEventHandler { [weak self] in + self?.updateTrackerBlockingDependencies() + } + fileDispatchSource?.resume() + } catch { + os_log("unable to set up configuration dispatch source: %{public}s", log: .config, type: .error, error.localizedDescription) + } + } + + deinit { + fileDispatchSource?.cancel() + } + func log() { os_log("last update %{public}s", log: .config, type: .default, String(describing: lastUpdateTime)) os_log("last refresh check %{public}s", log: .config, type: .default, String(describing: lastRefreshCheckTime)) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift index dc05064f1a..ff9a3da836 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -39,6 +39,31 @@ final class ConfigurationManager: DefaultConfigurationManager { PixelKit.fire(DebugEvent(domainEvent, error: error)) } + private var fileDispatchSource: DispatchSourceFileSystemObject? + + override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults()) { + super.init(fetcher: fetcher, defaults: defaults) + + do { + let fileHandle = try FileHandle(forReadingFrom: ConfigurationStore.shared.fileUrl(for: .privacyConfiguration)) + fileDispatchSource = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: fileHandle.fileDescriptor, + eventMask: .write, + queue: ConfigurationManager.queue + ) + fileDispatchSource?.setEventHandler { [weak self] in + self?.updateConfigDependencies() + } + fileDispatchSource?.resume() + } catch { + os_log("unable to set up configuration dispatch source: %{public}s", log: .config, type: .error, error.localizedDescription) + } + } + + deinit { + fileDispatchSource?.cancel() + } + func log() { os_log("last update %{public}s", log: .config, type: .default, String(describing: lastUpdateTime)) os_log("last refresh check %{public}s", log: .config, type: .default, String(describing: lastRefreshCheckTime)) From 95f95152d39b9e22317882d0b3670db88c4e21bc Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 21 Aug 2024 12:45:21 -0500 Subject: [PATCH 07/34] Add VPN config setup --- DuckDuckGo.xcodeproj/project.pbxproj | 40 ++++++ .../NetworkProtectionPixelEvent.swift | 27 +++- DuckDuckGoVPN/ConfigurationManager.swift | 109 +++++++++++++++ DuckDuckGoVPN/ConfigurationStore.swift | 123 ++++++++++++++++ DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift | 14 ++ .../VPNAgentConfigurationURLProvider.swift | 29 ++++ .../VPNPrivacyConfigurationManager.swift | 131 ++++++++++++++++++ .../Configuration/ConfigurationStore.swift | 2 +- 8 files changed, 471 insertions(+), 4 deletions(-) create mode 100644 DuckDuckGoVPN/ConfigurationManager.swift create mode 100644 DuckDuckGoVPN/ConfigurationStore.swift create mode 100644 DuckDuckGoVPN/VPNAgentConfigurationURLProvider.swift create mode 100644 DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index bf884fdc8c..679bfebb52 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -16,6 +16,17 @@ 026ADE1426C3010C002518EE /* macos-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 026ADE1326C3010C002518EE /* macos-config.json */; }; 028904202A7B25380028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; + 02FDA6572C764B740024CD8B /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02FDA6562C764B740024CD8B /* Configuration */; }; + 02FDA6592C764B970024CD8B /* ConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6582C764B970024CD8B /* ConfigurationManager.swift */; }; + 02FDA65B2C764C200024CD8B /* ConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA65A2C764C200024CD8B /* ConfigurationStore.swift */; }; + 02FDA65C2C764CB00024CD8B /* ConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6582C764B970024CD8B /* ConfigurationManager.swift */; }; + 02FDA65D2C764CB30024CD8B /* ConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA65A2C764C200024CD8B /* ConfigurationStore.swift */; }; + 02FDA65F2C764E220024CD8B /* VPNPrivacyConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA65E2C764E220024CD8B /* VPNPrivacyConfigurationManager.swift */; }; + 02FDA6602C764E220024CD8B /* VPNPrivacyConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA65E2C764E220024CD8B /* VPNPrivacyConfigurationManager.swift */; }; + 02FDA6622C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */; }; + 02FDA6632C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */; }; + 02FDA6642C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */; }; + 02FDA6662C765E050024CD8B /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02FDA6652C765E050024CD8B /* Configuration */; }; 142879DA24CE1179005419BB /* SuggestionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142879D924CE1179005419BB /* SuggestionViewModelTests.swift */; }; 142879DC24CE1185005419BB /* SuggestionContainerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142879DB24CE1185005419BB /* SuggestionContainerViewModelTests.swift */; }; 1430DFF524D0580F00B8978C /* TabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1430DFF424D0580F00B8978C /* TabBarViewController.swift */; }; @@ -2955,6 +2966,10 @@ 0230C0A2272080090018F728 /* KeyedCodingExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedCodingExtension.swift; sourceTree = ""; }; 026ADE1326C3010C002518EE /* macos-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "macos-config.json"; sourceTree = ""; }; 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigurationURLProviderTests.swift; sourceTree = ""; }; + 02FDA6582C764B970024CD8B /* ConfigurationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationManager.swift; sourceTree = ""; }; + 02FDA65A2C764C200024CD8B /* ConfigurationStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationStore.swift; sourceTree = ""; }; + 02FDA65E2C764E220024CD8B /* VPNPrivacyConfigurationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNPrivacyConfigurationManager.swift; sourceTree = ""; }; + 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNAgentConfigurationURLProvider.swift; sourceTree = ""; }; 142879D924CE1179005419BB /* SuggestionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionViewModelTests.swift; sourceTree = ""; }; 142879DB24CE1185005419BB /* SuggestionContainerViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionContainerViewModelTests.swift; sourceTree = ""; }; 1430DFF424D0580F00B8978C /* TabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarViewController.swift; sourceTree = ""; }; @@ -4424,6 +4439,7 @@ 7BBE2B7B2B61663C00697445 /* NetworkProtectionProxy in Frameworks */, 4B2D062C2A11C0E100DE1F49 /* Networking in Frameworks */, 4B25375B2A11BE7300610219 /* NetworkExtension.framework in Frameworks */, + 02FDA6662C765E050024CD8B /* Configuration in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4432,6 +4448,7 @@ buildActionMask = 2147483647; files = ( F198C71A2BD18A5B000BF24D /* PixelKit in Frameworks */, + 02FDA6572C764B740024CD8B /* Configuration in Frameworks */, 4B41EDAB2B1544B2001EEDF4 /* LoginItems in Frameworks */, 7B00997D2B6508B700FE7C31 /* NetworkProtectionProxy in Frameworks */, 7BEEA5122AD1235B00A9E72B /* NetworkProtectionIPC in Frameworks */, @@ -6199,6 +6216,9 @@ isa = PBXGroup; children = ( 7BA7CC132AD11DC80042E5CE /* AppLauncher+DefaultInitializer.swift */, + 02FDA6582C764B970024CD8B /* ConfigurationManager.swift */, + 02FDA65A2C764C200024CD8B /* ConfigurationStore.swift */, + 02FDA65E2C764E220024CD8B /* VPNPrivacyConfigurationManager.swift */, 7B60AFF92C511B65008E32A3 /* VPNUIActionHandler.swift */, 7BA7CC0E2AD11DC80042E5CE /* DuckDuckGoVPNAppDelegate.swift */, 7BD1688D2AD4A4C400D24876 /* NetworkExtensionController.swift */, @@ -6214,6 +6234,7 @@ 7BA7CC142AD11DC80042E5CE /* DuckDuckGoVPNAppStore.entitlements */, 7BA7CC1A2AD11DC80042E5CE /* Info.plist */, 7BA7CC102AD11DC80042E5CE /* Info-AppStore.plist */, + 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */, ); path = DuckDuckGoVPN; sourceTree = ""; @@ -8806,6 +8827,7 @@ 7B37C7A42BAA32A50062546A /* Subscription */, F198C7172BD18A4C000BF24D /* PixelKit */, 9D9DE57A2C63AA1F00D20B15 /* AppKitExtensions */, + 02FDA6652C765E050024CD8B /* Configuration */, ); productName = NetworkProtectionSystemExtension; productReference = 4B25375A2A11BE7300610219 /* com.duckduckgo.macos.vpn.network-extension.debug.systemextension */; @@ -8841,6 +8863,7 @@ BDADBDC82BD2BC2200421B9B /* Lottie */, 7B23668B2C09FAF1002D393F /* VPNAppLauncher */, 9D9DE5802C63BA0B00D20B15 /* AppKitExtensions */, + 02FDA6562C764B740024CD8B /* Configuration */, ); productName = DuckDuckGoAgent; productReference = 4B2D06392A11CFBB00DE1F49 /* DuckDuckGo VPN.app */; @@ -11148,6 +11171,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 02FDA6632C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift in Sources */, B6F92BA22A691580002ABA6B /* UserDefaultsWrapper.swift in Sources */, 4B2D065B2A11D1FF00DE1F49 /* Logging.swift in Sources */, 7BA7CC3A2AD11E2D0042E5CE /* DuckDuckGoVPNAppDelegate.swift in Sources */, @@ -11158,9 +11182,11 @@ F1DA51942BF6081E00CF29FA /* AttributionPixelHandler.swift in Sources */, 7BA7CC562AD11FFB0042E5CE /* NetworkProtectionOptionKeyExtension.swift in Sources */, 7B2DDCFA2A93B25F0039D884 /* KeychainType+ClientDefault.swift in Sources */, + 02FDA6592C764B970024CD8B /* ConfigurationManager.swift in Sources */, 7BA7CC4C2AD11EC70042E5CE /* NetworkProtectionControllerErrorStore.swift in Sources */, F1D0429D2BFBABA100A31506 /* SubscriptionManager+StandardConfiguration.swift in Sources */, 7B8DB31A2B504D7500EC16DA /* VPNAppEventsHandler.swift in Sources */, + 02FDA65F2C764E220024CD8B /* VPNPrivacyConfigurationManager.swift in Sources */, 7BA7CC532AD11FCE0042E5CE /* Bundle+VPN.swift in Sources */, 7BA7CC5D2AD120C30042E5CE /* EventMapping+NetworkProtectionError.swift in Sources */, 7BA7CC4A2AD11EA00042E5CE /* NetworkProtectionTunnelController.swift in Sources */, @@ -11171,6 +11197,7 @@ F1DA51982BF6083B00CF29FA /* PrivacyProPixel.swift in Sources */, F1C70D802BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, 7BA7CC402AD11E3D0042E5CE /* AppLauncher+DefaultInitializer.swift in Sources */, + 02FDA65B2C764C200024CD8B /* ConfigurationStore.swift in Sources */, 7B0694982B6E980F00FA4DBA /* VPNProxyLauncher.swift in Sources */, BDA764842BC49E3F00D0400C /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */, 7B60AFFA2C511B65008E32A3 /* VPNUIActionHandler.swift in Sources */, @@ -11189,6 +11216,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 02FDA6642C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift in Sources */, 7B2DDCFB2A93B25F0039D884 /* KeychainType+ClientDefault.swift in Sources */, B6F92BA32A691583002ABA6B /* UserDefaultsWrapper.swift in Sources */, 4BA7C4DB2B3F63AE00AFE511 /* NetworkExtensionController.swift in Sources */, @@ -11199,9 +11227,11 @@ F1DA51952BF6081E00CF29FA /* AttributionPixelHandler.swift in Sources */, 4BA7C4D92B3F61FB00AFE511 /* BundleExtension.swift in Sources */, F1D0429E2BFBABA100A31506 /* SubscriptionManager+StandardConfiguration.swift in Sources */, + 02FDA65C2C764CB00024CD8B /* ConfigurationManager.swift in Sources */, 7BA7CC3F2AD11E3D0042E5CE /* AppLauncher+DefaultInitializer.swift in Sources */, 4B0EF7292B5780EB009D6481 /* VPNAppEventsHandler.swift in Sources */, 4BF0E5082AD2551A00FFEC9E /* NetworkProtectionPixelEvent.swift in Sources */, + 02FDA6602C764E220024CD8B /* VPNPrivacyConfigurationManager.swift in Sources */, 7BA7CC582AD1203A0042E5CE /* UserText+NetworkProtection.swift in Sources */, 7BA7CC4B2AD11EC60042E5CE /* NetworkProtectionControllerErrorStore.swift in Sources */, EE3424612BA0853900173B1B /* VPNUninstaller.swift in Sources */, @@ -11212,6 +11242,7 @@ 7BA7CC5C2AD120C30042E5CE /* EventMapping+NetworkProtectionError.swift in Sources */, B65DA5F02A77CC3C00CBEE8D /* Bundle+NetworkProtectionExtensions.swift in Sources */, BDA764852BC49E4000D0400C /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */, + 02FDA65D2C764CB30024CD8B /* ConfigurationStore.swift in Sources */, 7BAF9E4D2A8A3CCB002D3B6E /* UserDefaults+NetworkProtectionShared.swift in Sources */, 7B60AFFB2C511C68008E32A3 /* VPNUIActionHandler.swift in Sources */, 7BA7CC392AD11E2D0042E5CE /* DuckDuckGoVPNAppDelegate.swift in Sources */, @@ -11236,6 +11267,7 @@ 4B4BEC432A11B5C7001D9AC5 /* Bundle+VPN.swift in Sources */, 4B4BEC452A11B5EE001D9AC5 /* UserText+NetworkProtectionExtensions.swift in Sources */, 4B4BEC422A11B5C7001D9AC5 /* NetworkProtectionOptionKeyExtension.swift in Sources */, + 02FDA6622C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift in Sources */, B602E8222A1E2603006D261F /* Bundle+NetworkProtectionExtensions.swift in Sources */, B602E81A2A1E2570006D261F /* URL+NetworkProtection.swift in Sources */, ); @@ -13545,6 +13577,14 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Configuration; }; + 02FDA6562C764B740024CD8B /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + productName = Configuration; + }; + 02FDA6652C765E050024CD8B /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + productName = Configuration; + }; 08D4923DC968236E22E373E2 /* Crashes */ = { isa = XCSwiftPackageProductDependency; package = FAE06B199CA1F209B55B34E9 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift b/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift index 3a89fcb611..0549801f66 100644 --- a/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift +++ b/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift @@ -19,6 +19,7 @@ import Foundation import PixelKit import NetworkProtection +import Configuration enum NetworkProtectionPixelEvent: PixelKitEventV2 { static let vpnErrorDomain = "com.duckduckgo.vpn.errorDomain" @@ -112,6 +113,10 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { case networkProtectionSystemExtensionActivationFailure(_ error: Error) + case networkProtectionConfigurationInvalidPayload(configuration: Configuration) + case networkProtectionConfigurationErrorLoadingCachedConfig(_ error: Error) + case networkProtectionConfigurationPixelTest + case networkProtectionUnhandledError(function: String, line: Int, error: Error) /// Name of the pixel event @@ -330,6 +335,15 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { case .networkProtectionDNSUpdateDefault: return "netp_ev_update_dns_default" + case .networkProtectionConfigurationInvalidPayload(let config): + return "netp_ev_configuration_\(config.rawValue)_invalid_payload".lowercased() + + case .networkProtectionConfigurationErrorLoadingCachedConfig: + return "netp_ev_configuration_error_loading_cached_config" + + case .networkProtectionConfigurationPixelTest: + return "netp_ev_configuration_pixel_test" + case .networkProtectionUnhandledError: return "netp_unhandled_error" } @@ -394,6 +408,8 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { return [PixelKit.Parameters.reason: reason] case .networkProtectionServerMigrationFailure: return error?.pixelParameters + case .networkProtectionConfigurationErrorLoadingCachedConfig(let error): + return error.pixelParameters case .networkProtectionActiveUser, .networkProtectionNewUser, .networkProtectionControllerStartAttempt, @@ -442,7 +458,9 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { .networkProtectionServerMigrationAttempt, .networkProtectionServerMigrationSuccess, .networkProtectionDNSUpdateCustom, - .networkProtectionDNSUpdateDefault: + .networkProtectionDNSUpdateDefault, + .networkProtectionConfigurationInvalidPayload, + .networkProtectionConfigurationPixelTest: return nil } } @@ -469,7 +487,8 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { .networkProtectionRekeyFailure(let error), .networkProtectionUnhandledError(_, _, let error), .networkProtectionSystemExtensionActivationFailure(let error), - .networkProtectionServerMigrationFailure(let error): + .networkProtectionServerMigrationFailure(let error), + .networkProtectionConfigurationErrorLoadingCachedConfig(let error): return error case .networkProtectionActiveUser, .networkProtectionNewUser, @@ -521,7 +540,9 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { .networkProtectionServerMigrationAttempt, .networkProtectionServerMigrationSuccess, .networkProtectionDNSUpdateCustom, - .networkProtectionDNSUpdateDefault: + .networkProtectionDNSUpdateDefault, + .networkProtectionConfigurationInvalidPayload, + .networkProtectionConfigurationPixelTest: return nil } } diff --git a/DuckDuckGoVPN/ConfigurationManager.swift b/DuckDuckGoVPN/ConfigurationManager.swift new file mode 100644 index 0000000000..844af97a68 --- /dev/null +++ b/DuckDuckGoVPN/ConfigurationManager.swift @@ -0,0 +1,109 @@ +// +// ConfigurationManager.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 BrowserServicesKit +import Common +import Configuration +import Networking +import PixelKit + +final class ConfigurationManager: DefaultConfigurationManager { + + static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, + log: .config, + eventMapping: configurationDebugEvents)) + + static let configurationDebugEvents = EventMapping { event, error, _, _ in + let domainEvent: NetworkProtectionPixelEvent + switch event { + case .invalidPayload(let configuration): + domainEvent = .networkProtectionConfigurationInvalidPayload(configuration: configuration) + } + + PixelKit.fire(DebugEvent(domainEvent, error: error)) + } + + private var fileDispatchSource: DispatchSourceFileSystemObject? + + override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults()) { + super.init(fetcher: fetcher, defaults: defaults) + + do { + let fileHandle = try FileHandle(forReadingFrom: ConfigurationStore.shared.fileUrl(for: .privacyConfiguration)) + fileDispatchSource = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: fileHandle.fileDescriptor, + eventMask: .write, + queue: ConfigurationManager.queue + ) + fileDispatchSource?.setEventHandler { [weak self] in + self?.updateConfigDependencies() + } + fileDispatchSource?.resume() + } catch { + os_log("unable to set up configuration dispatch source: %{public}s", log: .config, type: .error, error.localizedDescription) + } + } + + deinit { + fileDispatchSource?.cancel() + } + + func log() { + os_log("last update %{public}s", log: .config, type: .default, String(describing: lastUpdateTime)) + os_log("last refresh check %{public}s", log: .config, type: .default, String(describing: lastRefreshCheckTime)) + } + + override public func refreshNow(isDebug: Bool = false) async { + let updateConfigDependenciesTask = Task { + let didFetchConfig = await fetchConfigDependencies(isDebug: isDebug) + if didFetchConfig { + updateConfigDependencies() + tryAgainLater() + } + } + + await updateConfigDependenciesTask.value + + ConfigurationStore.shared.log() + log() + } + + func fetchConfigDependencies(isDebug: Bool) async -> Bool { + do { + try await fetcher.fetch(.privacyConfiguration, isDebug: isDebug) + return true + } catch { + os_log("Failed to complete configuration update to %@: %@", + log: .config, + type: .error, + Configuration.privacyConfiguration.rawValue, + error.localizedDescription) + tryAgainSoon() + } + + return false + } + + func updateConfigDependencies() { + VPNPrivacyConfigurationManager.shared.reload( + etag: ConfigurationStore.shared.loadEtag(for: .privacyConfiguration), + data: ConfigurationStore.shared.loadData(for: .privacyConfiguration) + ) + } +} diff --git a/DuckDuckGoVPN/ConfigurationStore.swift b/DuckDuckGoVPN/ConfigurationStore.swift new file mode 100644 index 0000000000..ce948fb9c6 --- /dev/null +++ b/DuckDuckGoVPN/ConfigurationStore.swift @@ -0,0 +1,123 @@ +// +// ConfigurationStore.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 BrowserServicesKit +import Common +import Configuration +import PixelKit + +final class ConfigurationStore: ConfigurationStoring { + + private static let fileLocations: [Configuration: String] = [ + .privacyConfiguration: "macos-config.json", + ] + + enum Error: Swift.Error { + + case unsupportedConfig + + func withUnderlyingError(_ underlyingError: Swift.Error) -> Swift.Error { + let nsError = self as NSError + return NSError(domain: nsError.domain, code: nsError.code, userInfo: [NSUnderlyingErrorKey: underlyingError]) + } + + } + + private var privacyConfigurationEtagKey: String { + return "configurationPrivacyConfigurationEtag" + } + + static let shared = ConfigurationStore() + let defaults = UserDefaults.netP + + var privacyConfigurationEtag: String? { + get { + defaults.string(forKey: privacyConfigurationEtagKey) + } + set { + defaults.setValue(newValue, forKey: privacyConfigurationEtagKey) + } + } + + func log() { + os_log("privacyConfigurationEtag %{public}s", log: .config, type: .default, privacyConfigurationEtag ?? "") + } + + func loadData(for configuration: Configuration) -> Data? { + guard configuration == .privacyConfiguration else { return nil } + + let file = fileUrl(for: configuration) + do { + return try Data(contentsOf: file) + } catch { + PixelKit.fire(NetworkProtectionPixelEvent.networkProtectionConfigurationErrorLoadingCachedConfig(error)) + return nil + } + } + + func loadEtag(for configuration: Configuration) -> String? { + guard configuration == .privacyConfiguration else { return nil } + + return privacyConfigurationEtag + } + + func loadEmbeddedEtag(for configuration: Configuration) -> String? { + // If we embed the full config some day we need to load the etag for it here + return nil + } + + func saveData(_ data: Data, for configuration: Configuration) throws { + guard configuration == .privacyConfiguration else { throw Error.unsupportedConfig } + + let file = fileUrl(for: configuration) + try data.write(to: file, options: .atomic) + } + + func saveEtag(_ etag: String, for configuration: Configuration) throws { + guard configuration == .privacyConfiguration else { throw Error.unsupportedConfig } + + privacyConfigurationEtag = etag + } + + func fileUrl(for config: Configuration) -> URL { + let fm = FileManager.default + + guard let dir = fm.containerURL(forSecurityApplicationGroupIdentifier: Bundle.main.bundleIdentifier!) else { // TODO: Change to Configuration group + fatalError("Failed to get application group URL") + } + let subDir = dir.appendingPathComponent("Configuration") + + var isDir: ObjCBool = false + if !fm.fileExists(atPath: subDir.path, isDirectory: &isDir) { + do { + try fm.createDirectory(at: subDir, withIntermediateDirectories: true, attributes: nil) + isDir = true + } catch { + fatalError("Failed to create directory at \(subDir.path)") + } + } + + if !isDir.boolValue { + fatalError("Configuration folder at \(subDir.path) is not a directory") + } + + return subDir.appendingPathComponent(Self.fileLocations[config]!) + } + +} diff --git a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift index 1f5ada33ed..009840cba0 100644 --- a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift +++ b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift @@ -19,7 +19,9 @@ import AppLauncher import Cocoa import Combine +import BrowserServicesKit import Common +import Configuration import LoginItems import Networking import NetworkExtension @@ -363,6 +365,14 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { APIRequest.Headers.setUserAgent(UserAgent.duckDuckGoUserAgent()) os_log("DuckDuckGoVPN started", log: .networkProtectionLoginItemLog) + // Setup Remote Configuration + Configuration.setURLProvider(VPNAgentConfigurationURLProvider()) + ConfigurationManager.shared.start() + let privacyConfigurationManager = VPNPrivacyConfigurationManager.shared + // Load cached config (if any) + let configStore = ConfigurationStore.shared + privacyConfigurationManager.reload(etag: configStore.loadEtag(for: .privacyConfiguration), data: configStore.loadData(for: .privacyConfiguration)) + setupMenuVisibility() Task { @MainActor in @@ -378,6 +388,10 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { setUpSubscriptionMonitoring() + if privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(BackgroundAgentPixelTestSubfeature.pixelTest) { + PixelKit.fire(NetworkProtectionPixelEvent.networkProtectionConfigurationPixelTest) + } + if launchedOnStartup { Task { let isConnected = await tunnelController.isConnected diff --git a/DuckDuckGoVPN/VPNAgentConfigurationURLProvider.swift b/DuckDuckGoVPN/VPNAgentConfigurationURLProvider.swift new file mode 100644 index 0000000000..bc5f2134a7 --- /dev/null +++ b/DuckDuckGoVPN/VPNAgentConfigurationURLProvider.swift @@ -0,0 +1,29 @@ +// +// VPNAgentConfigurationURLProvider.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 BrowserServicesKit +import Configuration + +struct VPNAgentConfigurationURLProvider: ConfigurationURLProviding { + func url(for configuration: Configuration) -> URL { + guard configuration == .privacyConfiguration else { fatalError("\(configuration.rawValue) is not supported on this target") } + + return URL(string: "http://localhost:3000/generated/v4/macos-config.json")! + } +} diff --git a/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift b/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift new file mode 100644 index 0000000000..e910e3a440 --- /dev/null +++ b/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift @@ -0,0 +1,131 @@ +// +// VPNPrivacyConfigurationManager.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. +// + +import Foundation +import BrowserServicesKit +import Combine +import Common + +public final class VPNPrivacyConfigurationManager: PrivacyConfigurationManaging { + + static let shared = VPNPrivacyConfigurationManager() + + private let lock = NSLock() + + var embeddedConfigData: Data { + let configString = """ + { + "readme": "https://github.com/duckduckgo/privacy-configuration", + "version": 1693838894358, + "features": { + "networkProtection": { + "state": "enabled", + "exceptions": [], + "settings": {} + } + }, + "unprotectedTemporary": [] + } + """ + let data = configString.data(using: .utf8) + return data! + } + + private var _fetchedConfigData: PrivacyConfigurationManager.ConfigurationData? + private(set) public var fetchedConfigData: PrivacyConfigurationManager.ConfigurationData? { + get { + lock.lock() + let data = _fetchedConfigData + lock.unlock() + return data + } + set { + lock.lock() + _fetchedConfigData = newValue + lock.unlock() + } + } + + public var currentConfig: Data { + if let fetchedData = fetchedConfigData { + return fetchedData.rawData + } + return embeddedConfigData + } + + public var updatesPublisher: AnyPublisher = .init(Just(())) + + public var privacyConfig: BrowserServicesKit.PrivacyConfiguration { + guard let privacyConfigurationData = try? PrivacyConfigurationData(data: currentConfig) else { + fatalError("Could not retrieve privacy configuration data") + } + let privacyConfig = privacyConfiguration(withData: privacyConfigurationData, + internalUserDecider: internalUserDecider) + return privacyConfig + } + + public var internalUserDecider: InternalUserDecider = DefaultInternalUserDecider(store: InternalUserDeciderStoreMock()) + + @discardableResult + public func reload(etag: String?, data: Data?) -> PrivacyConfigurationManager.ReloadResult { + let result: PrivacyConfigurationManager.ReloadResult + + if let etag = etag, let data = data { + result = .downloaded + + do { + let configData = try PrivacyConfigurationData(data: data) + fetchedConfigData = (data, configData, etag) + } catch { + // TODO: Fire failed to parse pixel + fetchedConfigData = nil + return .embeddedFallback + } + } else { + fetchedConfigData = nil + result = .embedded + } + + return result + } +} + +func privacyConfiguration(withData data: PrivacyConfigurationData, + internalUserDecider: InternalUserDecider) -> PrivacyConfiguration { + let domain = MockDomainsProtectionStore() + return AppPrivacyConfiguration(data: data, + identifier: UUID().uuidString, + localProtection: domain, + internalUserDecider: internalUserDecider) +} + +final class MockDomainsProtectionStore: DomainsProtectionStore { + var unprotectedDomains = Set() + + func disableProtection(forDomain domain: String) { + unprotectedDomains.insert(domain) + } + + func enableProtection(forDomain domain: String) { + unprotectedDomains.remove(domain) + } +} + +final class InternalUserDeciderStoreMock: InternalUserStoring { + var isInternalUser: Bool = false +} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift index ec7d73f2d4..c68c183363 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift @@ -78,7 +78,7 @@ final class ConfigurationStore: ConfigurationStoring { } func loadEmbeddedEtag(for configuration: Configuration) -> String? { - // If we embed the full config somday we need to load the etag for it here + // If we embed the full config some day we need to load the etag for it here return nil } From 739bd8deeacf28ad270df203e10bb5507c9e9a9b Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Thu, 22 Aug 2024 10:31:34 -0500 Subject: [PATCH 08/34] Migrate to new logging system --- DuckDuckGo.xcodeproj/project.pbxproj | 7 +++++++ .../Configuration/ConfigurationManager.swift | 18 ++++++++--------- .../Configuration/ConfigurationStore.swift | 17 ++++++++-------- DuckDuckGo/Menus/MainMenuActions.swift | 4 ---- DuckDuckGoVPN/ConfigurationManager.swift | 16 +++++++-------- DuckDuckGoVPN/ConfigurationStore.swift | 3 ++- .../Configuration/ConfigurationManager.swift | 20 ++++++++++--------- .../Configuration/ConfigurationStore.swift | 3 ++- 8 files changed, 46 insertions(+), 42 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 9a3ecb7e48..b7afd76679 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 021EA0842BD6E01A00772C9A /* TabsPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021EA0822BD6DF1B00772C9A /* TabsPreferencesTests.swift */; }; 021EA0852BD6E0EB00772C9A /* TabsPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021EA0822BD6DF1B00772C9A /* TabsPreferencesTests.swift */; }; 0230C0A3272080090018F728 /* KeyedCodingExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0230C0A2272080090018F728 /* KeyedCodingExtension.swift */; }; + 0234D1082C778E82003A65EA /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 0234D1072C778E82003A65EA /* Configuration */; }; 026ADE1426C3010C002518EE /* macos-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 026ADE1326C3010C002518EE /* macos-config.json */; }; 028904202A7B25380028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; @@ -4480,6 +4481,7 @@ buildActionMask = 2147483647; files = ( 7B23668A2C09FAE8002D393F /* VPNAppLauncher in Frameworks */, + 0234D1082C778E82003A65EA /* Configuration in Frameworks */, 7B624F172BA25C1F00A6C544 /* NetworkProtectionUI in Frameworks */, 37269F052B3332C2005E8E46 /* Common in Frameworks */, ); @@ -8912,6 +8914,7 @@ 37269F042B3332C2005E8E46 /* Common */, 7B624F162BA25C1F00A6C544 /* NetworkProtectionUI */, 7B2366892C09FAE8002D393F /* VPNAppLauncher */, + 0234D1072C778E82003A65EA /* Configuration */, ); productName = DuckDuckGoNotifications; productReference = 4B4BEC202A11B4E2001D9AC5 /* DuckDuckGo Notifications.app */; @@ -13563,6 +13566,10 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Configuration; }; + 0234D1072C778E82003A65EA /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + productName = Configuration; + }; 02FDA6562C764B740024CD8B /* Configuration */ = { isa = XCSwiftPackageProductDependency; productName = Configuration; diff --git a/DuckDuckGo/Configuration/ConfigurationManager.swift b/DuckDuckGo/Configuration/ConfigurationManager.swift index 9df3ad3c1c..8cbb41dfc3 100644 --- a/DuckDuckGo/Configuration/ConfigurationManager.swift +++ b/DuckDuckGo/Configuration/ConfigurationManager.swift @@ -17,6 +17,7 @@ // import Foundation +import os.log import Combine import BrowserServicesKit import Configuration @@ -27,7 +28,6 @@ import PixelKit final class ConfigurationManager: DefaultConfigurationManager { static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, - log: .config, eventMapping: configurationDebugEvents)) @UserDefaultsWrapper(key: .configLastInstalled, defaultValue: nil) @@ -60,7 +60,7 @@ final class ConfigurationManager: DefaultConfigurationManager { } fileDispatchSource?.resume() } catch { - os_log("unable to set up configuration dispatch source: %{public}s", log: .config, type: .error, error.localizedDescription) + Logger.config.error("unable to set up configuration dispatch source: \(error.localizedDescription, privacy: .public)") } } @@ -69,8 +69,8 @@ final class ConfigurationManager: DefaultConfigurationManager { } func log() { - os_log("last update %{public}s", log: .config, type: .default, String(describing: lastUpdateTime)) - os_log("last refresh check %{public}s", log: .config, type: .default, String(describing: lastRefreshCheckTime)) + Logger.config.log("last update \(String(describing: self.lastUpdateTime), privacy: .public)") + Logger.config.log("last refresh check \(String(describing: self.lastRefreshCheckTime), privacy: .public)") } override public func refreshNow(isDebug: Bool = false) async { @@ -123,11 +123,9 @@ final class ConfigurationManager: DefaultConfigurationManager { try await task.value didFetchAnyTrackerBlockingDependencies = true } catch { - os_log("Failed to complete configuration update to %@: %@", - log: .config, - type: .error, - configuration.rawValue, - error.localizedDescription) + Logger.config.error( + "Failed to complete configuration update to \(configuration.rawValue, privacy: .public): \(error.localizedDescription, privacy: .public)" + ) tryAgainSoon() } } @@ -142,7 +140,7 @@ final class ConfigurationManager: DefaultConfigurationManager { return } - os_log("Failed to complete configuration update %@", log: .config, type: .error, error.localizedDescription) + Logger.config.error("Failed to complete configuration update \(error.localizedDescription, privacy: .public)") PixelKit.fire(DebugEvent(GeneralPixel.configurationFetchError(error: error))) tryAgainSoon() } diff --git a/DuckDuckGo/Configuration/ConfigurationStore.swift b/DuckDuckGo/Configuration/ConfigurationStore.swift index 3a754dccb3..f77cdddc7f 100644 --- a/DuckDuckGo/Configuration/ConfigurationStore.swift +++ b/DuckDuckGo/Configuration/ConfigurationStore.swift @@ -17,6 +17,7 @@ // import Common +import os.log import Foundation import Configuration import PixelKit @@ -119,14 +120,14 @@ final class ConfigurationStore: ConfigurationStoring { } func log() { - os_log("bloomFilterBinaryEtag %{public}s", log: .config, type: .default, bloomFilterBinaryEtag ?? "") - os_log("bloomFilterSpecEtag %{public}s", log: .config, type: .default, bloomFilterSpecEtag ?? "") - os_log("bloomFilterExcludedDomainsEtag %{public}s", log: .config, type: .default, bloomFilterExcludedDomainsEtag ?? "") - os_log("surrogatesEtag %{public}s", log: .config, type: .default, surrogatesEtag ?? "") - os_log("trackerRadarEtag %{public}s", log: .config, type: .default, trackerRadarEtag ?? "") - os_log("privacyConfigurationEtag %{public}s", log: .config, type: .default, privacyConfigurationEtag ?? "") - os_log("FBConfigEtag %{public}s", log: .config, type: .default, FBConfigEtag ?? "") - os_log("remoteMessagingConfig %{public}s", log: .config, type: .default, remoteMessagingConfigEtag ?? "") + Logger.config.log("bloomFilterBinaryEtag \(self.bloomFilterBinaryEtag ?? "", privacy: .public)") + Logger.config.log("bloomFilterSpecEtag \(self.bloomFilterSpecEtag ?? "", privacy: .public)") + Logger.config.log("bloomFilterExcludedDomainsEtag \(self.bloomFilterExcludedDomainsEtag ?? "", privacy: .public)") + Logger.config.log("surrogatesEtag \(self.surrogatesEtag ?? "", privacy: .public)") + Logger.config.log("trackerRadarEtag \(self.trackerRadarEtag ?? "", privacy: .public)") + Logger.config.log("privacyConfigurationEtag \(self.privacyConfigurationEtag ?? "", privacy: .public)") + Logger.config.log("FBConfigEtag \(self.FBConfigEtag ?? "", privacy: .public)") + Logger.config.log("remoteMessagingConfig \(self.remoteMessagingConfigEtag ?? "", privacy: .public)") } func fileUrl(for config: Configuration) -> URL { diff --git a/DuckDuckGo/Menus/MainMenuActions.swift b/DuckDuckGo/Menus/MainMenuActions.swift index ff7e81994b..b0b7a0ad8a 100644 --- a/DuckDuckGo/Menus/MainMenuActions.swift +++ b/DuckDuckGo/Menus/MainMenuActions.swift @@ -911,14 +911,10 @@ extension MainViewController { } @objc func reloadConfigurationNow(_ sender: Any?) { - OSLog.loggingCategories.insert(OSLog.Categories.config.rawValue) - ConfigurationManager.shared.forceRefresh(isDebug: true) } private func setConfigurationUrl(_ configurationUrl: URL?) { - OSLog.loggingCategories.insert(OSLog.Categories.config.rawValue) - var configurationProvider = AppConfigurationURLProvider(customPrivacyConfiguration: configurationUrl) if configurationUrl == nil { configurationProvider.resetToDefaultConfigurationUrl() diff --git a/DuckDuckGoVPN/ConfigurationManager.swift b/DuckDuckGoVPN/ConfigurationManager.swift index 844af97a68..0d9a08e71e 100644 --- a/DuckDuckGoVPN/ConfigurationManager.swift +++ b/DuckDuckGoVPN/ConfigurationManager.swift @@ -17,6 +17,7 @@ // import Foundation +import os.log import BrowserServicesKit import Common import Configuration @@ -26,7 +27,6 @@ import PixelKit final class ConfigurationManager: DefaultConfigurationManager { static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, - log: .config, eventMapping: configurationDebugEvents)) static let configurationDebugEvents = EventMapping { event, error, _, _ in @@ -56,7 +56,7 @@ final class ConfigurationManager: DefaultConfigurationManager { } fileDispatchSource?.resume() } catch { - os_log("unable to set up configuration dispatch source: %{public}s", log: .config, type: .error, error.localizedDescription) + Logger.config.error("unable to set up configuration dispatch source: \(error.localizedDescription, privacy: .public)") } } @@ -65,8 +65,8 @@ final class ConfigurationManager: DefaultConfigurationManager { } func log() { - os_log("last update %{public}s", log: .config, type: .default, String(describing: lastUpdateTime)) - os_log("last refresh check %{public}s", log: .config, type: .default, String(describing: lastRefreshCheckTime)) + Logger.config.log("last update \(String(describing: self.lastUpdateTime), privacy: .public)") + Logger.config.log("last refresh check \(String(describing: self.lastRefreshCheckTime), privacy: .public)") } override public func refreshNow(isDebug: Bool = false) async { @@ -89,11 +89,9 @@ final class ConfigurationManager: DefaultConfigurationManager { try await fetcher.fetch(.privacyConfiguration, isDebug: isDebug) return true } catch { - os_log("Failed to complete configuration update to %@: %@", - log: .config, - type: .error, - Configuration.privacyConfiguration.rawValue, - error.localizedDescription) + Logger.config.error( + "Failed to complete configuration update to \(Configuration.privacyConfiguration.rawValue, privacy: .public): \(error.localizedDescription, privacy: .public)" + ) tryAgainSoon() } diff --git a/DuckDuckGoVPN/ConfigurationStore.swift b/DuckDuckGoVPN/ConfigurationStore.swift index ce948fb9c6..945d7315d6 100644 --- a/DuckDuckGoVPN/ConfigurationStore.swift +++ b/DuckDuckGoVPN/ConfigurationStore.swift @@ -17,6 +17,7 @@ // import Foundation +import os.log import BrowserServicesKit import Common import Configuration @@ -56,7 +57,7 @@ final class ConfigurationStore: ConfigurationStoring { } func log() { - os_log("privacyConfigurationEtag %{public}s", log: .config, type: .default, privacyConfigurationEtag ?? "") + Logger.config.log("privacyConfigurationEtag \(self.privacyConfigurationEtag ?? "", privacy: .public)") } func loadData(for configuration: Configuration) -> Data? { diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift index ff9a3da836..d7bb60e1f6 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -17,16 +17,20 @@ // import Foundation +import os.log import BrowserServicesKit import Configuration import Common import Networking import PixelKit +public extension Logger { + static var config: Logger = { Logger(subsystem: Bundle.main.bundleIdentifier ?? "DuckDuckGo", category: "Configuration") }() +} + final class ConfigurationManager: DefaultConfigurationManager { static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, - log: .config, eventMapping: configurationDebugEvents)) static let configurationDebugEvents = EventMapping { event, error, _, _ in @@ -56,7 +60,7 @@ final class ConfigurationManager: DefaultConfigurationManager { } fileDispatchSource?.resume() } catch { - os_log("unable to set up configuration dispatch source: %{public}s", log: .config, type: .error, error.localizedDescription) + Logger.config.error("unable to set up configuration dispatch source: \(error.localizedDescription, privacy: .public)") } } @@ -65,8 +69,8 @@ final class ConfigurationManager: DefaultConfigurationManager { } func log() { - os_log("last update %{public}s", log: .config, type: .default, String(describing: lastUpdateTime)) - os_log("last refresh check %{public}s", log: .config, type: .default, String(describing: lastRefreshCheckTime)) + Logger.config.log("last update \(String(describing: self.lastUpdateTime), privacy: .public)") + Logger.config.log("last refresh check \(String(describing: self.lastRefreshCheckTime), privacy: .public)") } override public func refreshNow(isDebug: Bool = false) async { @@ -89,11 +93,9 @@ final class ConfigurationManager: DefaultConfigurationManager { try await fetcher.fetch(.privacyConfiguration, isDebug: isDebug) return true } catch { - os_log("Failed to complete configuration update to %@: %@", - log: .config, - type: .error, - Configuration.privacyConfiguration.rawValue, - error.localizedDescription) + Logger.config.error( + "Failed to complete configuration update to \(Configuration.privacyConfiguration.rawValue, privacy: .public): \(error.localizedDescription, privacy: .public)" + ) tryAgainSoon() } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift index c68c183363..284e784baf 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift @@ -17,6 +17,7 @@ // import Foundation +import os.log import BrowserServicesKit import Common import Configuration @@ -56,7 +57,7 @@ final class ConfigurationStore: ConfigurationStoring { } func log() { - os_log("privacyConfigurationEtag %{public}s", log: .config, type: .default, privacyConfigurationEtag ?? "") + Logger.config.log("privacyConfigurationEtag \(self.privacyConfigurationEtag ?? "", privacy: .public)") } func loadData(for configuration: Configuration) -> Data? { From 3abce5202d6af224a5671628e133dc830bbbcfd1 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Thu, 22 Aug 2024 11:01:38 -0500 Subject: [PATCH 09/34] Migrate to config app group --- .../Configuration/ConfigurationManager.swift | 2 +- .../Configuration/ConfigurationStore.swift | 20 ++++++++++--------- DuckDuckGoVPN/ConfigurationManager.swift | 2 +- DuckDuckGoVPN/ConfigurationStore.swift | 4 ++-- .../Bundle/BundleExtension.swift | 8 ++++++++ .../Configuration/ConfigurationManager.swift | 2 +- .../Configuration/ConfigurationStore.swift | 4 ++-- .../DataBrokerProtectionBundleExtension.swift | 8 ++++++++ 8 files changed, 34 insertions(+), 16 deletions(-) diff --git a/DuckDuckGo/Configuration/ConfigurationManager.swift b/DuckDuckGo/Configuration/ConfigurationManager.swift index 8cbb41dfc3..cb9b05464d 100644 --- a/DuckDuckGo/Configuration/ConfigurationManager.swift +++ b/DuckDuckGo/Configuration/ConfigurationManager.swift @@ -45,7 +45,7 @@ final class ConfigurationManager: DefaultConfigurationManager { private var fileDispatchSource: DispatchSourceFileSystemObject? - override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults()) { + override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults.appConfiguration) { super.init(fetcher: fetcher, defaults: defaults) do { diff --git a/DuckDuckGo/Configuration/ConfigurationStore.swift b/DuckDuckGo/Configuration/ConfigurationStore.swift index f77cdddc7f..c8f97e92d6 100644 --- a/DuckDuckGo/Configuration/ConfigurationStore.swift +++ b/DuckDuckGo/Configuration/ConfigurationStore.swift @@ -37,28 +37,28 @@ final class ConfigurationStore: ConfigurationStoring { static let shared = ConfigurationStore() - @UserDefaultsWrapper(key: .configStorageTrackerRadarEtag, defaultValue: nil) + @UserDefaultsWrapper(key: .configStorageTrackerRadarEtag, defaultValue: nil, defaults: .appConfiguration) private var trackerRadarEtag: String? - @UserDefaultsWrapper(key: .configStorageBloomFilterSpecEtag, defaultValue: nil) + @UserDefaultsWrapper(key: .configStorageBloomFilterSpecEtag, defaultValue: nil, defaults: .appConfiguration) private var bloomFilterSpecEtag: String? - @UserDefaultsWrapper(key: .configStorageBloomFilterBinaryEtag, defaultValue: nil) + @UserDefaultsWrapper(key: .configStorageBloomFilterBinaryEtag, defaultValue: nil, defaults: .appConfiguration) private var bloomFilterBinaryEtag: String? - @UserDefaultsWrapper(key: .configStorageBloomFilterExclusionsEtag, defaultValue: nil) + @UserDefaultsWrapper(key: .configStorageBloomFilterExclusionsEtag, defaultValue: nil, defaults: .appConfiguration) private var bloomFilterExcludedDomainsEtag: String? - @UserDefaultsWrapper(key: .configStorageSurrogatesEtag, defaultValue: nil) + @UserDefaultsWrapper(key: .configStorageSurrogatesEtag, defaultValue: nil, defaults: .appConfiguration) private var surrogatesEtag: String? - @UserDefaultsWrapper(key: .configStoragePrivacyConfigurationEtag, defaultValue: nil) + @UserDefaultsWrapper(key: .configStoragePrivacyConfigurationEtag, defaultValue: nil, defaults: .appConfiguration) private var privacyConfigurationEtag: String? - @UserDefaultsWrapper(key: .configFBConfigEtag, defaultValue: nil) + @UserDefaultsWrapper(key: .configFBConfigEtag, defaultValue: nil, defaults: .appConfiguration) private var FBConfigEtag: String? - @UserDefaultsWrapper(key: .configStorageRemoteMessagingConfigEtag, defaultValue: nil) + @UserDefaultsWrapper(key: .configStorageRemoteMessagingConfigEtag, defaultValue: nil, defaults: .appConfiguration) private var remoteMessagingConfigEtag: String? private init() { } @@ -133,7 +133,9 @@ final class ConfigurationStore: ConfigurationStoring { func fileUrl(for config: Configuration) -> URL { let fm = FileManager.default - let dir = URL.sandboxApplicationSupportURL + guard let dir = fm.containerURL(forSecurityApplicationGroupIdentifier: Bundle.main.appGroup(bundle: .appConfiguration)) else { + fatalError("Failed to get application group URL") + } let subDir = dir.appendingPathComponent("Configuration") var isDir: ObjCBool = false diff --git a/DuckDuckGoVPN/ConfigurationManager.swift b/DuckDuckGoVPN/ConfigurationManager.swift index 0d9a08e71e..58757fd7a0 100644 --- a/DuckDuckGoVPN/ConfigurationManager.swift +++ b/DuckDuckGoVPN/ConfigurationManager.swift @@ -41,7 +41,7 @@ final class ConfigurationManager: DefaultConfigurationManager { private var fileDispatchSource: DispatchSourceFileSystemObject? - override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults()) { + override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults.appConfiguration) { super.init(fetcher: fetcher, defaults: defaults) do { diff --git a/DuckDuckGoVPN/ConfigurationStore.swift b/DuckDuckGoVPN/ConfigurationStore.swift index 945d7315d6..db0bf2b190 100644 --- a/DuckDuckGoVPN/ConfigurationStore.swift +++ b/DuckDuckGoVPN/ConfigurationStore.swift @@ -45,7 +45,7 @@ final class ConfigurationStore: ConfigurationStoring { } static let shared = ConfigurationStore() - let defaults = UserDefaults.netP + let defaults = UserDefaults.appConfiguration var privacyConfigurationEtag: String? { get { @@ -99,7 +99,7 @@ final class ConfigurationStore: ConfigurationStoring { func fileUrl(for config: Configuration) -> URL { let fm = FileManager.default - guard let dir = fm.containerURL(forSecurityApplicationGroupIdentifier: Bundle.main.bundleIdentifier!) else { // TODO: Change to Configuration group + guard let dir = fm.containerURL(forSecurityApplicationGroupIdentifier: Bundle.main.appGroup(bundle: .appConfiguration)) else { fatalError("Failed to get application group URL") } let subDir = dir.appendingPathComponent("Configuration") diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Bundle/BundleExtension.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Bundle/BundleExtension.swift index 4dc092e9b3..6818f2f23d 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Bundle/BundleExtension.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Bundle/BundleExtension.swift @@ -25,6 +25,7 @@ protocol GroupNameProviding { extension Bundle: GroupNameProviding { static let dbpAppGroupName = "DBP_APP_GROUP" + static let configAppGroupName = "APP_CONFIGURATION_APP_GROUP" var appGroupName: String { guard let appGroup = object(forInfoDictionaryKey: Bundle.dbpAppGroupName) as? String else { @@ -32,4 +33,11 @@ extension Bundle: GroupNameProviding { } return appGroup } + + var configAppGroupName: String { + guard let appGroup = object(forInfoDictionaryKey: Bundle.configAppGroupName) as? String else { + fatalError("Info.plist is missing \(Bundle.configAppGroupName)") + } + return appGroup + } } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift index d7bb60e1f6..a96cec2911 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -45,7 +45,7 @@ final class ConfigurationManager: DefaultConfigurationManager { private var fileDispatchSource: DispatchSourceFileSystemObject? - override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults()) { + override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults.config) { super.init(fetcher: fetcher, defaults: defaults) do { diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift index 284e784baf..909b2691fa 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift @@ -45,7 +45,7 @@ final class ConfigurationStore: ConfigurationStoring { } static let shared = ConfigurationStore() - let defaults = UserDefaults.dbp + let defaults = UserDefaults.config var privacyConfigurationEtag: String? { get { @@ -99,7 +99,7 @@ final class ConfigurationStore: ConfigurationStoring { func fileUrl(for config: Configuration) -> URL { let fm = FileManager.default - guard let dir = fm.containerURL(forSecurityApplicationGroupIdentifier: Bundle.main.dbpAppGroup) else { // TODO: Change to Configuration group + guard let dir = fm.containerURL(forSecurityApplicationGroupIdentifier: Bundle.main.configAppGroup) else { fatalError("Failed to get application group URL") } let subDir = dir.appendingPathComponent("Configuration") diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionBundleExtension.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionBundleExtension.swift index d6f4b9a011..87047625ee 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionBundleExtension.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionBundleExtension.swift @@ -20,6 +20,7 @@ import Foundation extension UserDefaults { static let dbp = UserDefaults(suiteName: Bundle.main.dbpAppGroup)! + static let config = UserDefaults(suiteName: Bundle.main.configAppGroup)! } extension Bundle { @@ -30,4 +31,11 @@ extension Bundle { } return appGroup } + + var configAppGroup: String { + guard let appGroup = object(forInfoDictionaryKey: Bundle.configAppGroupName) as? String else { + fatalError("Info.plist is missing \(Bundle.configAppGroupName)") + } + return appGroup + } } From 4bd55732fb0ea0b4a833b0f29ea808062982df9c Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Tue, 27 Aug 2024 12:58:35 -0500 Subject: [PATCH 10/34] Refactor to use NSFileCoordinator and KeyValueStoring --- .../Utilities/UserDefaultsWrapper.swift | 12 -- .../Configuration/ConfigurationManager.swift | 51 +++---- .../Configuration/ConfigurationStore.swift | 136 ++++++++++++++---- DuckDuckGoVPN/ConfigurationManager.swift | 35 ++--- DuckDuckGoVPN/ConfigurationStore.swift | 54 +++++-- .../Configuration/ConfigurationManager.swift | 35 ++--- .../Configuration/ConfigurationStore.swift | 54 +++++-- 7 files changed, 239 insertions(+), 138 deletions(-) diff --git a/DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift b/DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift index 20214ac6cc..7e0c028d81 100644 --- a/DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift +++ b/DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift @@ -44,18 +44,6 @@ public struct UserDefaultsWrapper { /// system setting defining window title double-click action case appleActionOnDoubleClick = "AppleActionOnDoubleClick" - case configLastUpdated = "config.last.updated" - case configStorageTrackerRadarEtag = "config.storage.trackerradar.etag" - case configStorageBloomFilterSpecEtag = "config.storage.bloomfilter.spec.etag" - case configStorageBloomFilterBinaryEtag = "config.storage.bloomfilter.binary.etag" - case configStorageBloomFilterExclusionsEtag = "config.storage.bloomfilter.exclusions.etag" - case configStorageSurrogatesEtag = "config.storage.surrogates.etag" - case configStoragePrivacyConfigurationEtag = "config.storage.privacyconfiguration.etag" - case configFBConfigEtag = "config.storage.fbconfig.etag" - case configStorageRemoteMessagingConfigEtag = "config.storage.remotemessagingconfig.etag" - - case configLastInstalled = "config.last.installed" - case fireproofDomains = "com.duckduckgo.fireproofing.allowedDomains" case areDomainsMigratedToETLDPlus1 = "com.duckduckgo.are-domains-migrated-to-etldplus1" case unprotectedDomains = "com.duckduckgo.contentblocker.unprotectedDomains" diff --git a/DuckDuckGo/Configuration/ConfigurationManager.swift b/DuckDuckGo/Configuration/ConfigurationManager.swift index cb9b05464d..b1865d3d9e 100644 --- a/DuckDuckGo/Configuration/ConfigurationManager.swift +++ b/DuckDuckGo/Configuration/ConfigurationManager.swift @@ -20,6 +20,7 @@ import Foundation import os.log import Combine import BrowserServicesKit +import Persistence import Configuration import Common import Networking @@ -27,11 +28,23 @@ import PixelKit final class ConfigurationManager: DefaultConfigurationManager { + private enum Constants { + static let lastConfigurationInstallDateKey = "config.last.installed" + } + static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, eventMapping: configurationDebugEvents)) - @UserDefaultsWrapper(key: .configLastInstalled, defaultValue: nil) - private(set) var lastConfigurationInstallDate: Date? + private var defaults: KeyValueStoring + + private(set) var lastConfigurationInstallDate: Date? { + get { + defaults.object(forKey: Constants.lastConfigurationInstallDateKey) as? Date + } + set { + defaults.set(newValue, forKey: Constants.lastConfigurationInstallDateKey) + } + } static let configurationDebugEvents = EventMapping { event, error, _, _ in let domainEvent: GeneralPixel @@ -43,29 +56,9 @@ final class ConfigurationManager: DefaultConfigurationManager { PixelKit.fire(DebugEvent(domainEvent, error: error)) } - private var fileDispatchSource: DispatchSourceFileSystemObject? - - override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults.appConfiguration) { + override init(fetcher: ConfigurationFetcher, defaults: any KeyValueStoring = UserDefaults.appConfiguration) { + self.defaults = defaults super.init(fetcher: fetcher, defaults: defaults) - - do { - let fileHandle = try FileHandle(forReadingFrom: ConfigurationStore.shared.fileUrl(for: .privacyConfiguration)) - fileDispatchSource = DispatchSource.makeFileSystemObjectSource( - fileDescriptor: fileHandle.fileDescriptor, - eventMask: .write, - queue: ConfigurationManager.queue - ) - fileDispatchSource?.setEventHandler { [weak self] in - self?.updateTrackerBlockingDependencies() - } - fileDispatchSource?.resume() - } catch { - Logger.config.error("unable to set up configuration dispatch source: \(error.localizedDescription, privacy: .public)") - } - } - - deinit { - fileDispatchSource?.cancel() } func log() { @@ -189,3 +182,13 @@ final class ConfigurationManager: DefaultConfigurationManager { } } + +extension ConfigurationManager { + override var presentedItemURL: URL? { + ConfigurationStore.shared.fileUrl(for: .privacyConfiguration) + } + + override func presentedItemDidChange() { + updateTrackerBlockingDependencies() + } +} diff --git a/DuckDuckGo/Configuration/ConfigurationStore.swift b/DuckDuckGo/Configuration/ConfigurationStore.swift index c8f97e92d6..6700b74c10 100644 --- a/DuckDuckGo/Configuration/ConfigurationStore.swift +++ b/DuckDuckGo/Configuration/ConfigurationStore.swift @@ -20,6 +20,7 @@ import Common import os.log import Foundation import Configuration +import Persistence import PixelKit final class ConfigurationStore: ConfigurationStoring { @@ -35,33 +36,95 @@ final class ConfigurationStore: ConfigurationStoring { .remoteMessagingConfig: "remote-messaging-config.json" ] + private enum Constants { + static let configStorageTrackerRadarEtag = "config.storage.trackerradar.etag" + static let configStorageBloomFilterSpecEtag = "config.storage.bloomfilter.spec.etag" + static let configStorageBloomFilterBinaryEtag = "config.storage.bloomfilter.binary.etag" + static let configStorageBloomFilterExclusionsEtag = "config.storage.bloomfilter.exclusions.etag" + static let configStorageSurrogatesEtag = "config.storage.surrogates.etag" + static let configStoragePrivacyConfigurationEtag = "config.storage.privacyconfiguration.etag" + static let configFBConfigEtag = "config.storage.fbconfig.etag" + static let configStorageRemoteMessagingConfigEtag = "config.storage.remotemessagingconfig.etag" + } + static let shared = ConfigurationStore() + private let defaults: KeyValueStoring - @UserDefaultsWrapper(key: .configStorageTrackerRadarEtag, defaultValue: nil, defaults: .appConfiguration) - private var trackerRadarEtag: String? + private var trackerRadarEtag: String? { + get { + defaults.object(forKey: Constants.configStorageTrackerRadarEtag) as? String + } + set { + defaults.set(newValue, forKey: Constants.configStorageTrackerRadarEtag) + } + } - @UserDefaultsWrapper(key: .configStorageBloomFilterSpecEtag, defaultValue: nil, defaults: .appConfiguration) - private var bloomFilterSpecEtag: String? + private var bloomFilterSpecEtag: String? { + get { + defaults.object(forKey: Constants.configStorageBloomFilterSpecEtag) as? String + } + set { + defaults.set(newValue, forKey: Constants.configStorageBloomFilterSpecEtag) + } + } - @UserDefaultsWrapper(key: .configStorageBloomFilterBinaryEtag, defaultValue: nil, defaults: .appConfiguration) - private var bloomFilterBinaryEtag: String? + private var bloomFilterBinaryEtag: String? { + get { + defaults.object(forKey: Constants.configStorageBloomFilterBinaryEtag) as? String + } + set { + defaults.set(newValue, forKey: Constants.configStorageBloomFilterBinaryEtag) + } + } - @UserDefaultsWrapper(key: .configStorageBloomFilterExclusionsEtag, defaultValue: nil, defaults: .appConfiguration) - private var bloomFilterExcludedDomainsEtag: String? + private var bloomFilterExcludedDomainsEtag: String? { + get { + defaults.object(forKey: Constants.configStorageBloomFilterExclusionsEtag) as? String + } + set { + defaults.set(newValue, forKey: Constants.configStorageBloomFilterExclusionsEtag) + } + } - @UserDefaultsWrapper(key: .configStorageSurrogatesEtag, defaultValue: nil, defaults: .appConfiguration) - private var surrogatesEtag: String? + private var surrogatesEtag: String? { + get { + defaults.object(forKey: Constants.configStorageSurrogatesEtag) as? String + } + set { + defaults.set(newValue, forKey: Constants.configStorageSurrogatesEtag) + } + } - @UserDefaultsWrapper(key: .configStoragePrivacyConfigurationEtag, defaultValue: nil, defaults: .appConfiguration) - private var privacyConfigurationEtag: String? + private var privacyConfigurationEtag: String? { + get { + defaults.object(forKey: Constants.configStoragePrivacyConfigurationEtag) as? String + } + set { + defaults.set(newValue, forKey: Constants.configStoragePrivacyConfigurationEtag) + } + } - @UserDefaultsWrapper(key: .configFBConfigEtag, defaultValue: nil, defaults: .appConfiguration) - private var FBConfigEtag: String? + private var FBConfigEtag: String? { + get { + defaults.object(forKey: Constants.configFBConfigEtag) as? String + } + set { + defaults.set(newValue, forKey: Constants.configFBConfigEtag) + } + } - @UserDefaultsWrapper(key: .configStorageRemoteMessagingConfigEtag, defaultValue: nil, defaults: .appConfiguration) - private var remoteMessagingConfigEtag: String? + private var remoteMessagingConfigEtag: String? { + get { + defaults.object(forKey: Constants.configStorageRemoteMessagingConfigEtag) as? String + } + set { + defaults.set(newValue, forKey: Constants.configStorageRemoteMessagingConfigEtag) + } + } - private init() { } + init(defaults: KeyValueStoring = UserDefaults.appConfiguration) { + self.defaults = defaults + } func loadEtag(for configuration: Configuration) -> String? { switch configuration { @@ -99,24 +162,45 @@ final class ConfigurationStore: ConfigurationStoring { func loadData(for config: Configuration) -> Data? { let file = fileUrl(for: config) - do { - return try Data(contentsOf: file) - } catch { - guard NSApp.runType.requiresEnvironment else { return nil } + var data: Data? + var coordinatorError: NSError? + + NSFileCoordinator().coordinate(readingItemAt: file, error: &coordinatorError) { fileUrl in + do { + data = try Data(contentsOf: fileUrl) + } catch { + guard NSApp.runType.requiresEnvironment else { return } - let nserror = error as NSError + let nserror = error as NSError - if nserror.domain != NSCocoaErrorDomain || nserror.code != NSFileReadNoSuchFileError { - PixelKit.fire(DebugEvent(GeneralPixel.trackerDataCouldNotBeLoaded, error: error)) + if nserror.domain != NSCocoaErrorDomain || nserror.code != NSFileReadNoSuchFileError { + PixelKit.fire(DebugEvent(GeneralPixel.trackerDataCouldNotBeLoaded, error: error)) + } } + } - return nil + if let coordinatorError { + Logger.config.error("Unable to read \(config.rawValue, privacy: .public): \(coordinatorError.localizedDescription, privacy: .public)") } + + return data } func saveData(_ data: Data, for config: Configuration) throws { let file = fileUrl(for: config) - try data.write(to: file, options: .atomic) + var coordinatorError: NSError? + + NSFileCoordinator().coordinate(writingItemAt: file, options: .forReplacing, error: &coordinatorError) { fileUrl in + do { + try data.write(to: fileUrl, options: .atomic) + } catch { + Logger.config.error("Unable to write \(config.rawValue, privacy: .public): \(error.localizedDescription, privacy: .public)") + } + } + + if let coordinatorError { + Logger.config.error("Unable to write \(config.rawValue, privacy: .public): \(coordinatorError.localizedDescription, privacy: .public)") + } } func log() { diff --git a/DuckDuckGoVPN/ConfigurationManager.swift b/DuckDuckGoVPN/ConfigurationManager.swift index 58757fd7a0..af999621dc 100644 --- a/DuckDuckGoVPN/ConfigurationManager.swift +++ b/DuckDuckGoVPN/ConfigurationManager.swift @@ -39,31 +39,6 @@ final class ConfigurationManager: DefaultConfigurationManager { PixelKit.fire(DebugEvent(domainEvent, error: error)) } - private var fileDispatchSource: DispatchSourceFileSystemObject? - - override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults.appConfiguration) { - super.init(fetcher: fetcher, defaults: defaults) - - do { - let fileHandle = try FileHandle(forReadingFrom: ConfigurationStore.shared.fileUrl(for: .privacyConfiguration)) - fileDispatchSource = DispatchSource.makeFileSystemObjectSource( - fileDescriptor: fileHandle.fileDescriptor, - eventMask: .write, - queue: ConfigurationManager.queue - ) - fileDispatchSource?.setEventHandler { [weak self] in - self?.updateConfigDependencies() - } - fileDispatchSource?.resume() - } catch { - Logger.config.error("unable to set up configuration dispatch source: \(error.localizedDescription, privacy: .public)") - } - } - - deinit { - fileDispatchSource?.cancel() - } - func log() { Logger.config.log("last update \(String(describing: self.lastUpdateTime), privacy: .public)") Logger.config.log("last refresh check \(String(describing: self.lastRefreshCheckTime), privacy: .public)") @@ -105,3 +80,13 @@ final class ConfigurationManager: DefaultConfigurationManager { ) } } + +extension ConfigurationManager { + override var presentedItemURL: URL? { + ConfigurationStore.shared.fileUrl(for: .privacyConfiguration) + } + + override func presentedItemDidChange() { + updateConfigDependencies() + } +} diff --git a/DuckDuckGoVPN/ConfigurationStore.swift b/DuckDuckGoVPN/ConfigurationStore.swift index db0bf2b190..47bbe0eff1 100644 --- a/DuckDuckGoVPN/ConfigurationStore.swift +++ b/DuckDuckGoVPN/ConfigurationStore.swift @@ -19,6 +19,7 @@ import Foundation import os.log import BrowserServicesKit +import Persistence import Common import Configuration import PixelKit @@ -45,31 +46,47 @@ final class ConfigurationStore: ConfigurationStoring { } static let shared = ConfigurationStore() - let defaults = UserDefaults.appConfiguration + let defaults: KeyValueStoring var privacyConfigurationEtag: String? { get { - defaults.string(forKey: privacyConfigurationEtagKey) + defaults.object(forKey: privacyConfigurationEtagKey) as? String } set { - defaults.setValue(newValue, forKey: privacyConfigurationEtagKey) + defaults.set(newValue, forKey: privacyConfigurationEtagKey) } } + init(defaults: KeyValueStoring = UserDefaults.appConfiguration) { + self.defaults = defaults + } + func log() { Logger.config.log("privacyConfigurationEtag \(self.privacyConfigurationEtag ?? "", privacy: .public)") } - func loadData(for configuration: Configuration) -> Data? { - guard configuration == .privacyConfiguration else { return nil } + func loadData(for config: Configuration) -> Data? { + let file = fileUrl(for: config) + var data: Data? + var coordinatorError: NSError? - let file = fileUrl(for: configuration) - do { - return try Data(contentsOf: file) - } catch { - PixelKit.fire(NetworkProtectionPixelEvent.networkProtectionConfigurationErrorLoadingCachedConfig(error)) - return nil + NSFileCoordinator().coordinate(readingItemAt: file, error: &coordinatorError) { fileUrl in + do { + data = try Data(contentsOf: fileUrl) + } catch { + let nserror = error as NSError + + if nserror.domain != NSCocoaErrorDomain || nserror.code != NSFileReadNoSuchFileError { + PixelKit.fire(DebugEvent(NetworkProtectionPixelEvent.networkProtectionConfigurationErrorLoadingCachedConfig(error))) + } + } + } + + if let coordinatorError { + Logger.config.error("Unable to read \(config.rawValue, privacy: .public): \(coordinatorError.localizedDescription, privacy: .public)") } + + return data } func loadEtag(for configuration: Configuration) -> String? { @@ -85,9 +102,20 @@ final class ConfigurationStore: ConfigurationStoring { func saveData(_ data: Data, for configuration: Configuration) throws { guard configuration == .privacyConfiguration else { throw Error.unsupportedConfig } - let file = fileUrl(for: configuration) - try data.write(to: file, options: .atomic) + var coordinatorError: NSError? + + NSFileCoordinator().coordinate(writingItemAt: file, options: .forReplacing, error: &coordinatorError) { fileUrl in + do { + try data.write(to: fileUrl, options: .atomic) + } catch { + Logger.config.error("Unable to write \(configuration.rawValue, privacy: .public): \(error.localizedDescription, privacy: .public)") + } + } + + if let coordinatorError { + Logger.config.error("Unable to write \(configuration.rawValue, privacy: .public): \(coordinatorError.localizedDescription, privacy: .public)") + } } func saveEtag(_ etag: String, for configuration: Configuration) throws { diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift index a96cec2911..4242384149 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -43,31 +43,6 @@ final class ConfigurationManager: DefaultConfigurationManager { PixelKit.fire(DebugEvent(domainEvent, error: error)) } - private var fileDispatchSource: DispatchSourceFileSystemObject? - - override init(fetcher: ConfigurationFetcher, defaults: UserDefaults = UserDefaults.config) { - super.init(fetcher: fetcher, defaults: defaults) - - do { - let fileHandle = try FileHandle(forReadingFrom: ConfigurationStore.shared.fileUrl(for: .privacyConfiguration)) - fileDispatchSource = DispatchSource.makeFileSystemObjectSource( - fileDescriptor: fileHandle.fileDescriptor, - eventMask: .write, - queue: ConfigurationManager.queue - ) - fileDispatchSource?.setEventHandler { [weak self] in - self?.updateConfigDependencies() - } - fileDispatchSource?.resume() - } catch { - Logger.config.error("unable to set up configuration dispatch source: \(error.localizedDescription, privacy: .public)") - } - } - - deinit { - fileDispatchSource?.cancel() - } - func log() { Logger.config.log("last update \(String(describing: self.lastUpdateTime), privacy: .public)") Logger.config.log("last refresh check \(String(describing: self.lastRefreshCheckTime), privacy: .public)") @@ -109,3 +84,13 @@ final class ConfigurationManager: DefaultConfigurationManager { ) } } + +extension ConfigurationManager { + override var presentedItemURL: URL? { + ConfigurationStore.shared.fileUrl(for: .privacyConfiguration) + } + + override func presentedItemDidChange() { + updateConfigDependencies() + } +} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift index 909b2691fa..b8688d0b88 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift @@ -19,6 +19,7 @@ import Foundation import os.log import BrowserServicesKit +import Persistence import Common import Configuration import PixelKit @@ -45,31 +46,47 @@ final class ConfigurationStore: ConfigurationStoring { } static let shared = ConfigurationStore() - let defaults = UserDefaults.config + let defaults: KeyValueStoring var privacyConfigurationEtag: String? { get { - defaults.string(forKey: privacyConfigurationEtagKey) + defaults.object(forKey: privacyConfigurationEtagKey) as? String } set { - defaults.setValue(newValue, forKey: privacyConfigurationEtagKey) + defaults.set(newValue, forKey: privacyConfigurationEtagKey) } } + init(defaults: KeyValueStoring = UserDefaults.config) { + self.defaults = defaults + } + func log() { Logger.config.log("privacyConfigurationEtag \(self.privacyConfigurationEtag ?? "", privacy: .public)") } - func loadData(for configuration: Configuration) -> Data? { - guard configuration == .privacyConfiguration else { return nil } + func loadData(for config: Configuration) -> Data? { + let file = fileUrl(for: config) + var data: Data? + var coordinatorError: NSError? - let file = fileUrl(for: configuration) - do { - return try Data(contentsOf: file) - } catch { - PixelKit.fire(DataBrokerProtectionPixels.errorLoadingCachedConfig(error)) - return nil + NSFileCoordinator().coordinate(readingItemAt: file, error: &coordinatorError) { fileUrl in + do { + data = try Data(contentsOf: fileUrl) + } catch { + let nserror = error as NSError + + if nserror.domain != NSCocoaErrorDomain || nserror.code != NSFileReadNoSuchFileError { + PixelKit.fire(DebugEvent(DataBrokerProtectionPixels.errorLoadingCachedConfig(error))) + } + } + } + + if let coordinatorError { + Logger.config.error("Unable to read \(config.rawValue, privacy: .public): \(coordinatorError.localizedDescription, privacy: .public)") } + + return data } func loadEtag(for configuration: Configuration) -> String? { @@ -85,9 +102,20 @@ final class ConfigurationStore: ConfigurationStoring { func saveData(_ data: Data, for configuration: Configuration) throws { guard configuration == .privacyConfiguration else { throw Error.unsupportedConfig } - let file = fileUrl(for: configuration) - try data.write(to: file, options: .atomic) + var coordinatorError: NSError? + + NSFileCoordinator().coordinate(writingItemAt: file, options: .forReplacing, error: &coordinatorError) { fileUrl in + do { + try data.write(to: fileUrl, options: .atomic) + } catch { + Logger.config.error("Unable to write \(configuration.rawValue, privacy: .public): \(error.localizedDescription, privacy: .public)") + } + } + + if let coordinatorError { + Logger.config.error("Unable to write \(configuration.rawValue, privacy: .public): \(coordinatorError.localizedDescription, privacy: .public)") + } } func saveEtag(_ etag: String, for configuration: Configuration) throws { From 2a105c5bcdad02398943b8ab5c0a69d85b8be37a Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 28 Aug 2024 15:25:42 -0500 Subject: [PATCH 11/34] Refactoring --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 13 ++----------- DuckDuckGo/Configuration/ConfigurationStore.swift | 1 + 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index b7afd76679..c7762f0bbf 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -6211,6 +6211,7 @@ 02FDA6582C764B970024CD8B /* ConfigurationManager.swift */, 02FDA65A2C764C200024CD8B /* ConfigurationStore.swift */, 02FDA65E2C764E220024CD8B /* VPNPrivacyConfigurationManager.swift */, + 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */, 7B60AFF92C511B65008E32A3 /* VPNUIActionHandler.swift */, 7BA7CC0E2AD11DC80042E5CE /* DuckDuckGoVPNAppDelegate.swift */, 7BD1688D2AD4A4C400D24876 /* NetworkExtensionController.swift */, @@ -6226,7 +6227,6 @@ 7BA7CC142AD11DC80042E5CE /* DuckDuckGoVPNAppStore.entitlements */, 7BA7CC1A2AD11DC80042E5CE /* Info.plist */, 7BA7CC102AD11DC80042E5CE /* Info-AppStore.plist */, - 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */, ); path = DuckDuckGoVPN; sourceTree = ""; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7679a06de7..2cd2dc9500 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/privacy-dashboard", "state" : { - "revision" : "36dc07cba4bc1e7e0c1d1fb679c3cd077694a072", - "version" : "5.0.0" + "revision" : "665b23dc656c9f787494620494f8e56098a900b2", + "version" : "5.1.1" } }, { @@ -170,15 +170,6 @@ "revision" : "1403e17eeeb8493b92fb9d11eb8c846bb9776581", "version" : "2.1.2" } - }, - { - "identity" : "wireguard-apple", - "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/wireguard-apple", - "state" : { - "revision" : "13fd026384b1af11048451061cc1b21434990668", - "version" : "1.1.3" - } } ], "version" : 2 diff --git a/DuckDuckGo/Configuration/ConfigurationStore.swift b/DuckDuckGo/Configuration/ConfigurationStore.swift index 6700b74c10..9b059c9307 100644 --- a/DuckDuckGo/Configuration/ConfigurationStore.swift +++ b/DuckDuckGo/Configuration/ConfigurationStore.swift @@ -180,6 +180,7 @@ final class ConfigurationStore: ConfigurationStoring { } if let coordinatorError { + // TODO: Fire pixel Logger.config.error("Unable to read \(config.rawValue, privacy: .public): \(coordinatorError.localizedDescription, privacy: .public)") } From 73995f52c85f668c26114e3345bdbe2dd71b9bdf Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Thu, 29 Aug 2024 09:18:22 -0500 Subject: [PATCH 12/34] Refactor away from singletons --- .../xcshareddata/swiftpm/Package.resolved | 9 +++++++++ DuckDuckGo/Application/AppDelegate.swift | 4 +++- .../Configuration/ConfigurationManager.swift | 20 ++++++++++--------- DuckDuckGo/Menus/MainMenu.swift | 2 +- DuckDuckGo/Menus/MainMenuActions.swift | 10 +++++----- DuckDuckGoVPN/ConfigurationManager.swift | 16 +++++++++------ DuckDuckGoVPN/ConfigurationStore.swift | 1 - DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift | 5 +++-- .../Configuration/ConfigurationManager.swift | 16 +++++++++------ .../Configuration/ConfigurationStore.swift | 1 - .../DataBrokerProtectionAgentManager.swift | 13 ++++++++---- 11 files changed, 61 insertions(+), 36 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 2cd2dc9500..a6830d2309 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -170,6 +170,15 @@ "revision" : "1403e17eeeb8493b92fb9d11eb8c846bb9776581", "version" : "2.1.2" } + }, + { + "identity" : "wireguard-apple", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/wireguard-apple", + "state" : { + "revision" : "13fd026384b1af11048451061cc1b21434990668", + "version" : "1.1.3" + } } ], "version" : 2 diff --git a/DuckDuckGo/Application/AppDelegate.swift b/DuckDuckGo/Application/AppDelegate.swift index 3699ba82d2..bb2315f227 100644 --- a/DuckDuckGo/Application/AppDelegate.swift +++ b/DuckDuckGo/Application/AppDelegate.swift @@ -97,6 +97,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate { public let vpnSettings = VPNSettings(defaults: .netP) + var configurationManager = ConfigurationManager() + // MARK: - VPN private var networkProtectionSubscriptionEventHandler: NetworkProtectionSubscriptionEventHandler? @@ -308,7 +310,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { if case .normal = NSApp.runType { FaviconManager.shared.loadFavicons() } - ConfigurationManager.shared.start() + configurationManager.start() _ = DownloadListCoordinator.shared _ = RecentlyClosedCoordinator.shared diff --git a/DuckDuckGo/Configuration/ConfigurationManager.swift b/DuckDuckGo/Configuration/ConfigurationManager.swift index 81c43cf9b8..05e8296232 100644 --- a/DuckDuckGo/Configuration/ConfigurationManager.swift +++ b/DuckDuckGo/Configuration/ConfigurationManager.swift @@ -25,7 +25,6 @@ import Configuration import Common import Networking import PixelKit -import os.log final class ConfigurationManager: DefaultConfigurationManager { @@ -33,9 +32,6 @@ final class ConfigurationManager: DefaultConfigurationManager { static let lastConfigurationInstallDateKey = "config.last.installed" } - static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, - eventMapping: configurationDebugEvents)) - private var defaults: KeyValueStoring private(set) var lastConfigurationInstallDate: Date? { @@ -57,9 +53,11 @@ final class ConfigurationManager: DefaultConfigurationManager { PixelKit.fire(DebugEvent(domainEvent, error: error)) } - override init(fetcher: ConfigurationFetcher, defaults: any KeyValueStoring = UserDefaults.appConfiguration) { + override init(fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), + store: ConfigurationStoring = ConfigurationStore(), + defaults: KeyValueStoring = UserDefaults.appConfiguration) { self.defaults = defaults - super.init(fetcher: fetcher, defaults: defaults) + super.init(fetcher: fetcher, store: store, defaults: defaults) } func log() { @@ -88,7 +86,7 @@ final class ConfigurationManager: DefaultConfigurationManager { let updateBloomFilterExclusionsTask = Task { do { - try await fetcher.fetch(.bloomFilterExcludedDomains) + try await fetcher.fetch(.bloomFilterExcludedDomains, isDebug: isDebug) try await updateBloomFilterExclusions() tryAgainLater() } catch { @@ -110,8 +108,8 @@ final class ConfigurationManager: DefaultConfigurationManager { var didFetchAnyTrackerBlockingDependencies = false var tasks = [Configuration: Task<(), Swift.Error>]() - tasks[.trackerDataSet] = Task { try await fetcher.fetch(.trackerDataSet) } - tasks[.surrogates] = Task { try await fetcher.fetch(.surrogates) } + tasks[.trackerDataSet] = Task { try await fetcher.fetch(.trackerDataSet, isDebug: isDebug) } + tasks[.surrogates] = Task { try await fetcher.fetch(.surrogates, isDebug: isDebug) } tasks[.privacyConfiguration] = Task { try await fetcher.fetch(.privacyConfiguration, isDebug: isDebug) } for (configuration, task) in tasks { @@ -192,6 +190,10 @@ extension ConfigurationManager { } override func presentedItemDidChange() { + // Check for significant time since last install to prevent double config install in short time + if let lastConfigurationInstallDate, lastUpdateTime.timeIntervalSince(lastConfigurationInstallDate) < 1 { + return + } updateTrackerBlockingDependencies() } } diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index 478ddd8dc9..2ff8f81cda 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -682,7 +682,7 @@ final class MainMenu: NSMenu { private func updateRemoteConfigurationInfo() { var dateString: String - if let date = ConfigurationManager.shared.lastConfigurationInstallDate { + if let date = Application.appDelegate.configurationManager.lastConfigurationInstallDate { dateString = DateFormatter.localizedString(from: date, dateStyle: .short, timeStyle: .medium) configurationDateAndTimeMenuItem.title = "Last Update Time: \(dateString)" } else { diff --git a/DuckDuckGo/Menus/MainMenuActions.swift b/DuckDuckGo/Menus/MainMenuActions.swift index 314d57a9d5..279aaca5d0 100644 --- a/DuckDuckGo/Menus/MainMenuActions.swift +++ b/DuckDuckGo/Menus/MainMenuActions.swift @@ -917,7 +917,7 @@ extension MainViewController { } @objc func reloadConfigurationNow(_ sender: Any?) { - ConfigurationManager.shared.forceRefresh(isDebug: true) + Application.appDelegate.configurationManager.forceRefresh(isDebug: true) } private func setConfigurationUrl(_ configurationUrl: URL?) { @@ -926,11 +926,11 @@ extension MainViewController { configurationProvider.resetToDefaultConfigurationUrl() } Configuration.setURLProvider(configurationProvider) - ConfigurationManager.shared.forceRefresh(isDebug: true) + Application.appDelegate.configurationManager.forceRefresh(isDebug: true) if let configurationUrl { - Logger.general.debug("New configuration URL set to \(configurationUrl.absoluteString)") + Logger.config.debug("New configuration URL set to \(configurationUrl.absoluteString)") } else { - Logger.general.log("New configuration URL reset to default") + Logger.config.log("New configuration URL reset to default") } } @@ -940,7 +940,7 @@ extension MainViewController { if alert.runModal() != .cancel { guard let textField = alert.accessoryView as? NSTextField, let newConfigurationUrl = URL(string: textField.stringValue) else { - Logger.general.error("Failed to set custom configuration URL") + Logger.config.error("Failed to set custom configuration URL") return } diff --git a/DuckDuckGoVPN/ConfigurationManager.swift b/DuckDuckGoVPN/ConfigurationManager.swift index af999621dc..3fa5a0b270 100644 --- a/DuckDuckGoVPN/ConfigurationManager.swift +++ b/DuckDuckGoVPN/ConfigurationManager.swift @@ -19,6 +19,7 @@ import Foundation import os.log import BrowserServicesKit +import Persistence import Common import Configuration import Networking @@ -26,8 +27,11 @@ import PixelKit final class ConfigurationManager: DefaultConfigurationManager { - static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, - eventMapping: configurationDebugEvents)) + override init(fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), + store: ConfigurationStoring = ConfigurationStore(), + defaults: KeyValueStoring = UserDefaults.appConfiguration) { + super.init(fetcher: fetcher, store: store, defaults: defaults) + } static let configurationDebugEvents = EventMapping { event, error, _, _ in let domainEvent: NetworkProtectionPixelEvent @@ -55,7 +59,7 @@ final class ConfigurationManager: DefaultConfigurationManager { await updateConfigDependenciesTask.value - ConfigurationStore.shared.log() + (store as? ConfigurationStore)?.log() log() } @@ -75,15 +79,15 @@ final class ConfigurationManager: DefaultConfigurationManager { func updateConfigDependencies() { VPNPrivacyConfigurationManager.shared.reload( - etag: ConfigurationStore.shared.loadEtag(for: .privacyConfiguration), - data: ConfigurationStore.shared.loadData(for: .privacyConfiguration) + etag: store.loadEtag(for: .privacyConfiguration), + data: store.loadData(for: .privacyConfiguration) ) } } extension ConfigurationManager { override var presentedItemURL: URL? { - ConfigurationStore.shared.fileUrl(for: .privacyConfiguration) + store.fileUrl(for: .privacyConfiguration) } override func presentedItemDidChange() { diff --git a/DuckDuckGoVPN/ConfigurationStore.swift b/DuckDuckGoVPN/ConfigurationStore.swift index 47bbe0eff1..844d4f3950 100644 --- a/DuckDuckGoVPN/ConfigurationStore.swift +++ b/DuckDuckGoVPN/ConfigurationStore.swift @@ -45,7 +45,6 @@ final class ConfigurationStore: ConfigurationStoring { return "configurationPrivacyConfigurationEtag" } - static let shared = ConfigurationStore() let defaults: KeyValueStoring var privacyConfigurationEtag: String? { diff --git a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift index 84e2d487ba..962c6844b8 100644 --- a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift +++ b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift @@ -130,6 +130,7 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { private let appLauncher = AppLauncher() private let accountManager: AccountManager private let accessTokenStorage: SubscriptionTokenKeychainStorage + private let configurattionManager = ConfigurationManager() public init(accountManager: AccountManager, accessTokenStorage: SubscriptionTokenKeychainStorage, @@ -364,10 +365,10 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { // Setup Remote Configuration Configuration.setURLProvider(VPNAgentConfigurationURLProvider()) - ConfigurationManager.shared.start() + configurattionManager.start() let privacyConfigurationManager = VPNPrivacyConfigurationManager.shared // Load cached config (if any) - let configStore = ConfigurationStore.shared + let configStore = ConfigurationStore() privacyConfigurationManager.reload(etag: configStore.loadEtag(for: .privacyConfiguration), data: configStore.loadData(for: .privacyConfiguration)) setupMenuVisibility() diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift index 4242384149..d4d69b513f 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -19,6 +19,7 @@ import Foundation import os.log import BrowserServicesKit +import Persistence import Configuration import Common import Networking @@ -30,8 +31,11 @@ public extension Logger { final class ConfigurationManager: DefaultConfigurationManager { - static let shared = ConfigurationManager(fetcher: ConfigurationFetcher(store: ConfigurationStore.shared, - eventMapping: configurationDebugEvents)) + override init(fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), + store: ConfigurationStoring = ConfigurationStore(), + defaults: KeyValueStoring = UserDefaults.config) { + super.init(fetcher: fetcher, store: store, defaults: defaults) + } static let configurationDebugEvents = EventMapping { event, error, _, _ in let domainEvent: DataBrokerProtectionPixels @@ -59,7 +63,7 @@ final class ConfigurationManager: DefaultConfigurationManager { await updateConfigDependenciesTask.value - ConfigurationStore.shared.log() + (store as? ConfigurationStore)?.log() log() } @@ -79,15 +83,15 @@ final class ConfigurationManager: DefaultConfigurationManager { func updateConfigDependencies() { DBPPrivacyConfigurationManager.shared.reload( - etag: ConfigurationStore.shared.loadEtag(for: .privacyConfiguration), - data: ConfigurationStore.shared.loadData(for: .privacyConfiguration) + etag: store.loadEtag(for: .privacyConfiguration), + data: store.loadData(for: .privacyConfiguration) ) } } extension ConfigurationManager { override var presentedItemURL: URL? { - ConfigurationStore.shared.fileUrl(for: .privacyConfiguration) + store.fileUrl(for: .privacyConfiguration) } override func presentedItemDidChange() { diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift index b8688d0b88..a870961d3c 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationStore.swift @@ -45,7 +45,6 @@ final class ConfigurationStore: ConfigurationStoring { return "configurationPrivacyConfigurationEtag" } - static let shared = ConfigurationStore() let defaults: KeyValueStoring var privacyConfigurationEtag: String? { diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift index a60ce54b30..eeb14753b7 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift @@ -34,10 +34,11 @@ public class DataBrokerProtectionAgentManagerProvider { let notificationService = DefaultDataBrokerProtectionUserNotificationService(pixelHandler: pixelHandler) Configuration.setURLProvider(DBPAgentConfigurationURLProvider()) - ConfigurationManager.shared.start() + let configurationManager = ConfigurationManager() + configurationManager.start() let privacyConfigurationManager = DBPPrivacyConfigurationManager.shared // Load cached config (if any) - let configStore = ConfigurationStore.shared + let configStore = ConfigurationStore() privacyConfigurationManager.reload(etag: configStore.loadEtag(for: .privacyConfiguration), data: configStore.loadData(for: .privacyConfiguration)) let ipcServer = DefaultDataBrokerProtectionIPCServer(machServiceName: Bundle.main.bundleIdentifier!) @@ -100,7 +101,8 @@ public class DataBrokerProtectionAgentManagerProvider { dataManager: dataManager, operationDependencies: operationDependencies, pixelHandler: pixelHandler, - agentStopper: agentstopper) + agentStopper: agentstopper, + configurationManager: configurationManager) } } @@ -114,6 +116,7 @@ public final class DataBrokerProtectionAgentManager { private let operationDependencies: DataBrokerOperationDependencies private let pixelHandler: EventMapping private let agentStopper: DataBrokerProtectionAgentStopper + private let configurationManger: ConfigurationManager // Used for debug functions only, so not injected private lazy var browserWindowManager = BrowserWindowManager() @@ -127,7 +130,8 @@ public final class DataBrokerProtectionAgentManager { dataManager: DataBrokerProtectionDataManaging, operationDependencies: DataBrokerOperationDependencies, pixelHandler: EventMapping, - agentStopper: DataBrokerProtectionAgentStopper + agentStopper: DataBrokerProtectionAgentStopper, + configurationManager: ConfigurationManager ) { self.userNotificationService = userNotificationService self.activityScheduler = activityScheduler @@ -137,6 +141,7 @@ public final class DataBrokerProtectionAgentManager { self.operationDependencies = operationDependencies self.pixelHandler = pixelHandler self.agentStopper = agentStopper + self.configurationManger = configurationManager self.activityScheduler.delegate = self self.ipcServer.serverDelegate = self From 636d5659a51eb2446715e72b26f26886fe4a09fe Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Fri, 30 Aug 2024 10:05:05 -0500 Subject: [PATCH 13/34] Fix tests --- .../Mocks/MockPrivacyConfiguration.swift | 2 +- .../HTTPSUpgradeIntegrationTests.swift | 1 - .../DataBrokerProtection/Package.swift | 2 + .../DataBrokerProtectionAgentManager.swift | 4 +- ...ataBrokerProtectionAgentManagerTests.swift | 82 ++++++++++++++++--- .../DataBrokerProtectionTests/Mocks.swift | 15 ++++ .../Common/UserDefaultsWrapperTests.swift | 50 +++++------ .../MockConfigurationStore.swift | 4 + 8 files changed, 120 insertions(+), 40 deletions(-) diff --git a/DuckDuckGo/ContentBlocker/Mocks/MockPrivacyConfiguration.swift b/DuckDuckGo/ContentBlocker/Mocks/MockPrivacyConfiguration.swift index 472cf2fe7c..77b7e2506f 100644 --- a/DuckDuckGo/ContentBlocker/Mocks/MockPrivacyConfiguration.swift +++ b/DuckDuckGo/ContentBlocker/Mocks/MockPrivacyConfiguration.swift @@ -91,7 +91,7 @@ final class MockPrivacyConfigurationManager: NSObject, PrivacyConfigurationManag } func reload(etag: String?, data: Data?) -> BrowserServicesKit.PrivacyConfigurationManager.ReloadResult { - fatalError("not implemented") + return .embedded } var updatesPublisher: AnyPublisher = Just(()).eraseToAnyPublisher() diff --git a/IntegrationTests/HTTPSUpgrade/HTTPSUpgradeIntegrationTests.swift b/IntegrationTests/HTTPSUpgrade/HTTPSUpgradeIntegrationTests.swift index 9d8f51425d..2609eb2f98 100644 --- a/IntegrationTests/HTTPSUpgrade/HTTPSUpgradeIntegrationTests.swift +++ b/IntegrationTests/HTTPSUpgrade/HTTPSUpgradeIntegrationTests.swift @@ -43,7 +43,6 @@ class HTTPSUpgradeIntegrationTests: XCTestCase { window = WindowsManager.openNewWindow(with: .none)! XCTAssertTrue(AppPrivacyFeatures.shared.contentBlocking.privacyConfigurationManager.privacyConfig.isFeature(.httpsUpgrade, enabledForDomain: "privacy-test-pages.site")) - await ConfigurationManager.shared.refreshIfNeeded()?.value } @MainActor diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index a79685ddb4..30afef8b97 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -41,6 +41,8 @@ let package = Package( .product(name: "SwiftUIExtensions", package: "SwiftUIExtensions"), .byName(name: "XPCHelper"), .product(name: "PixelKit", package: "BrowserServicesKit"), + .product(name: "Configuration", package: "BrowserServicesKit"), + .product(name: "Persistence", package: "BrowserServicesKit") ], resources: [.process("Resources")], swiftSettings: [ diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift index eeb14753b7..0e33c1728c 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift @@ -116,7 +116,7 @@ public final class DataBrokerProtectionAgentManager { private let operationDependencies: DataBrokerOperationDependencies private let pixelHandler: EventMapping private let agentStopper: DataBrokerProtectionAgentStopper - private let configurationManger: ConfigurationManager + private let configurationManger: DefaultConfigurationManager // Used for debug functions only, so not injected private lazy var browserWindowManager = BrowserWindowManager() @@ -131,7 +131,7 @@ public final class DataBrokerProtectionAgentManager { operationDependencies: DataBrokerOperationDependencies, pixelHandler: EventMapping, agentStopper: DataBrokerProtectionAgentStopper, - configurationManager: ConfigurationManager + configurationManager: DefaultConfigurationManager ) { self.userNotificationService = userNotificationService self.activityScheduler = activityScheduler diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift index 185e21f2f8..40f037538d 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift @@ -17,6 +17,8 @@ // import XCTest +import Configuration +import Persistence @testable import DataBrokerProtection final class DataBrokerProtectionAgentManagerTests: XCTestCase { @@ -32,6 +34,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { private var mockDependencies: DefaultDataBrokerOperationDependencies! private var mockProfile: DataBrokerProtectionProfile! private var mockAgentStopper: MockAgentStopper! + private var mockConfigurationManager: MockConfigurationManager! override func setUpWithError() throws { @@ -39,6 +42,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { mockActivityScheduler = MockDataBrokerProtectionBackgroundActivityScheduler() mockNotificationService = MockUserNotificationService() mockAgentStopper = MockAgentStopper() + mockConfigurationManager = MockConfigurationManager() let mockDatabase = MockDatabase() let mockMismatchCalculator = MockMismatchCalculator(database: mockDatabase, pixelHandler: mockPixelHandler) @@ -78,7 +82,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) mockDataManager.profileToReturn = mockProfile @@ -121,7 +126,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: agentStopper) + agentStopper: agentStopper, + configurationManager: mockConfigurationManager) mockDataManager.profileToReturn = nil @@ -153,7 +159,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) mockDataManager.profileToReturn = nil @@ -190,7 +197,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) mockDataManager.profileToReturn = mockProfile @@ -216,7 +224,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) mockDataManager.profileToReturn = mockProfile @@ -242,7 +251,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) mockNotificationService.reset() @@ -263,7 +273,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) mockNotificationService.reset() @@ -284,7 +295,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) mockNotificationService.reset() mockQueueManager.startImmediateOperationsIfPermittedCompletionError = DataBrokerProtectionAgentErrorCollection(oneTimeError: NSError(domain: "test", code: 10)) @@ -306,7 +318,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) mockNotificationService.reset() mockDataManager.shouldReturnHasMatches = true @@ -328,7 +341,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) mockNotificationService.reset() mockDataManager.shouldReturnHasMatches = false @@ -350,7 +364,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { dataManager: mockDataManager, operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, - agentStopper: mockAgentStopper) + agentStopper: mockAgentStopper, + configurationManager: mockConfigurationManager) var startScheduledScansCalled = false mockQueueManager.startScheduledOperationsIfPermittedCalledCompletion = { _ in @@ -364,3 +379,48 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { XCTAssertTrue(startScheduledScansCalled) } } + +struct MockConfigurationFetcher: ConfigurationFetching { + func fetch(_ configuration: Configuration, isDebug: Bool) async throws { + return + } + + func fetch(all configurations: [Configuration]) async throws { + return + } +} + +struct MockConfigurationStore: ConfigurationStoring { + func loadData(for configuration: Configuration) -> Data? { + return nil + } + + func loadEtag(for configuration: Configuration) -> String? { + return nil + } + + func loadEmbeddedEtag(for configuration: Configuration) -> String? { + return nil + } + + mutating func saveData(_ data: Data, for configuration: Configuration) throws { + return + } + + mutating func saveEtag(_ etag: String, for configuration: Configuration) throws { + return + } + + func fileUrl(for configuration: Configuration) -> URL { + return URL(string: "file:///\(configuration.rawValue)")! + } + +} + +final class MockConfigurationManager: DefaultConfigurationManager { + override init(fetcher: ConfigurationFetching = MockConfigurationFetcher(), + store: ConfigurationStoring = MockConfigurationStore(), + defaults: KeyValueStoring = UserDefaults()) { + super.init(fetcher: fetcher, store: store) + } +} diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift index 27874b9905..8cb1b5a407 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift @@ -19,6 +19,7 @@ import BrowserServicesKit import Combine import Common +import Configuration import Foundation import GRDB import SecureStorage @@ -1929,3 +1930,17 @@ struct MockMigrationsProvider: DataBrokerProtectionDatabaseMigrationsProvider { return { _ in } } } + +//struct MockConfigurationFetcher: ConfigurationFetching { +// func fetch(_ configuration: Configuration, isDebug: Bool) async throws { +// return +// } +// +// func fetch(all configurations: [Configuration]) async throws { +// return +// } +//} +// +//final class MockConfigurationManager: DefaultConfigurationManager { +// +//} diff --git a/UnitTests/Common/UserDefaultsWrapperTests.swift b/UnitTests/Common/UserDefaultsWrapperTests.swift index daf2f90f89..8d4e2f2f12 100644 --- a/UnitTests/Common/UserDefaultsWrapperTests.swift +++ b/UnitTests/Common/UserDefaultsWrapperTests.swift @@ -304,24 +304,24 @@ final class UserDefaultsWrapperTests: XCTestCase { // MARK: Optional with enum Key func testOptionalRawRepresentableValueDefaultValueWithEnumKey() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaultValue: MyRawRepresentable(rawValue: "value"), defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaultValue: MyRawRepresentable(rawValue: "value"), defaults: defaults) XCTAssertEqual(wrapper.wrappedValue, MyRawRepresentable(rawValue: "value")) } func testOptionalRawRepresentableValueNilValueWithEnumKey() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaults: defaults) XCTAssertNil(wrapper.wrappedValue) } func testOptionalRawRepresentableValueUpdatingWithEnumKey() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaults: defaults) wrapper.wrappedValue = MyRawRepresentable(rawValue: "new") XCTAssertEqual(wrapper.wrappedValue, MyRawRepresentable(rawValue: "new")) - XCTAssertEqual(defaults.dictionary as! [String: String], [UserDefaultsWrapper.Key.configLastUpdated.rawValue: "new"]) + XCTAssertEqual(defaults.dictionary as! [String: String], [UserDefaultsWrapper.Key.lastCrashReportCheckDate.rawValue: "new"]) } func testOptionalRawRepresentableValueRemovalWithEnumKey() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaults: defaults) wrapper.wrappedValue = MyRawRepresentable(rawValue: "new") wrapper.wrappedValue = nil XCTAssertNil(wrapper.wrappedValue) @@ -329,7 +329,7 @@ final class UserDefaultsWrapperTests: XCTestCase { } func testOptionalRawRepresentableValueClearWithEnumKey() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaults: defaults) wrapper.wrappedValue = MyRawRepresentable(rawValue: "new") wrapper.clear() XCTAssertNil(wrapper.wrappedValue) @@ -337,14 +337,14 @@ final class UserDefaultsWrapperTests: XCTestCase { } func testOptionalRawRepresentableValueSharedDefaultsWithEnumKey() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate) XCTAssertNil(wrapper.wrappedValue) wrapper.wrappedValue = MyRawRepresentable(rawValue: "new") XCTAssertEqual(wrapper.wrappedValue, MyRawRepresentable(rawValue: "new")) - UserDefaultsWrapper.clear(.configLastUpdated) + UserDefaultsWrapper.clear(.lastCrashReportCheckDate) XCTAssertNil(wrapper.wrappedValue) - XCTAssertNil(UserDefaultsWrapper.sharedDefaults.object(forKey: UserDefaultsWrapper.Key.configLastUpdated.rawValue)) + XCTAssertNil(UserDefaultsWrapper.sharedDefaults.object(forKey: UserDefaultsWrapper.Key.lastCrashReportCheckDate.rawValue)) } // MARK: Date @@ -353,19 +353,19 @@ final class UserDefaultsWrapperTests: XCTestCase { let date2 = Date().addingTimeInterval(1) func testDateValueDefaultValue() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaultValue: date1, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaultValue: date1, defaults: defaults) XCTAssertEqual(wrapper.wrappedValue, date1) } func testDateValueUpdating() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaultValue: date1, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaultValue: date1, defaults: defaults) wrapper.wrappedValue = date2 XCTAssertEqual(wrapper.wrappedValue, date2) - XCTAssertEqual(defaults.dictionary as! [String: Date], [UserDefaultsWrapper.Key.configLastUpdated.rawValue: date2]) + XCTAssertEqual(defaults.dictionary as! [String: Date], [UserDefaultsWrapper.Key.lastCrashReportCheckDate.rawValue: date2]) } func testDateValueClear() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaultValue: date1, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaultValue: date1, defaults: defaults) wrapper.wrappedValue = date2 wrapper.clear() XCTAssertEqual(wrapper.wrappedValue, date1) @@ -373,37 +373,37 @@ final class UserDefaultsWrapperTests: XCTestCase { } func testDateValueSharedDefaults() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaultValue: date1) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaultValue: date1) XCTAssertEqual(wrapper.wrappedValue, date1) wrapper.wrappedValue = date2 XCTAssertEqual(wrapper.wrappedValue, date2) - UserDefaultsWrapper.clear(.configLastUpdated) + UserDefaultsWrapper.clear(.lastCrashReportCheckDate) XCTAssertEqual(wrapper.wrappedValue, date1) - XCTAssertNil(UserDefaultsWrapper.sharedDefaults.object(forKey: UserDefaultsWrapper.Key.configLastUpdated.rawValue)) + XCTAssertNil(UserDefaultsWrapper.sharedDefaults.object(forKey: UserDefaultsWrapper.Key.lastCrashReportCheckDate.rawValue)) } // MARK: Optional func testOptionalDateValueDefaultValue() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaultValue: date1, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaultValue: date1, defaults: defaults) XCTAssertEqual(wrapper.wrappedValue, date1) } func testOptionalDateValueNilValue() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaults: defaults) XCTAssertNil(wrapper.wrappedValue) } func testOptionalDateValueUpdating() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaults: defaults) wrapper.wrappedValue = date2 XCTAssertEqual(wrapper.wrappedValue, date2) - XCTAssertEqual(defaults.dictionary as! [String: Date], [UserDefaultsWrapper.Key.configLastUpdated.rawValue: date2]) + XCTAssertEqual(defaults.dictionary as! [String: Date], [UserDefaultsWrapper.Key.lastCrashReportCheckDate.rawValue: date2]) } func testOptionalDateValueRemoval() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaults: defaults) wrapper.wrappedValue = date2 wrapper.wrappedValue = nil XCTAssertNil(wrapper.wrappedValue) @@ -411,7 +411,7 @@ final class UserDefaultsWrapperTests: XCTestCase { } func testOptionalDateValueClear() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated, defaults: defaults) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate, defaults: defaults) wrapper.wrappedValue = date2 wrapper.clear() XCTAssertNil(wrapper.wrappedValue) @@ -419,14 +419,14 @@ final class UserDefaultsWrapperTests: XCTestCase { } func testOptionalDateValueSharedDefaults() { - let wrapper = UserDefaultsWrapper(key: .configLastUpdated) + let wrapper = UserDefaultsWrapper(key: .lastCrashReportCheckDate) XCTAssertNil(wrapper.wrappedValue) wrapper.wrappedValue = date2 XCTAssertEqual(wrapper.wrappedValue, date2) - UserDefaultsWrapper.clear(.configLastUpdated) + UserDefaultsWrapper.clear(.lastCrashReportCheckDate) XCTAssertNil(wrapper.wrappedValue) - XCTAssertNil(UserDefaultsWrapper.sharedDefaults.object(forKey: UserDefaultsWrapper.Key.configLastUpdated.rawValue)) + XCTAssertNil(UserDefaultsWrapper.sharedDefaults.object(forKey: UserDefaultsWrapper.Key.lastCrashReportCheckDate.rawValue)) } } diff --git a/UnitTests/ContentBlocker/MockConfigurationStore.swift b/UnitTests/ContentBlocker/MockConfigurationStore.swift index f7d19af95d..1204b3cf8c 100644 --- a/UnitTests/ContentBlocker/MockConfigurationStore.swift +++ b/UnitTests/ContentBlocker/MockConfigurationStore.swift @@ -60,4 +60,8 @@ final class MockConfigurationStore: ConfigurationStoring { func log() { } + func fileUrl(for configuration: Configuration) -> URL { + return URL(string: "file///\(configuration.rawValue)")! + } + } From 998fb7a93a3d4db5e2d6078c36b09bcc0319967d Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Tue, 3 Sep 2024 09:10:54 -0500 Subject: [PATCH 14/34] More test fixing --- DuckDuckGo/Application/AppDelegate.swift | 2 +- .../Configuration/ConfigurationManager.swift | 18 +++++++++--------- .../Configuration/ConfigurationStore.swift | 1 - .../ContentBlocker/ContentBlocking.swift | 6 +++--- .../Mocks/ContentBlockingMock.swift | 4 ++-- .../ContentBlocker/ScriptSourceProviding.swift | 2 +- .../RemoteMessagingClient.swift | 2 +- .../AutoconsentMessageProtocolTests.swift | 4 ++-- .../ConfigurationStorageTests.swift | 12 +++++++----- .../ContentBlockingUpdatingTests.swift | 9 +++++---- 10 files changed, 31 insertions(+), 29 deletions(-) diff --git a/DuckDuckGo/Application/AppDelegate.swift b/DuckDuckGo/Application/AppDelegate.swift index e571cbefb2..c3349d0d83 100644 --- a/DuckDuckGo/Application/AppDelegate.swift +++ b/DuckDuckGo/Application/AppDelegate.swift @@ -234,7 +234,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { appearancePreferences: .shared, pinnedTabsManager: pinnedTabsManager, internalUserDecider: internalUserDecider, - configurationStore: ConfigurationStore.shared, + configurationStore: ConfigurationStore(), remoteMessagingAvailabilityProvider: PrivacyConfigurationRemoteMessagingAvailabilityProvider( privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager ) diff --git a/DuckDuckGo/Configuration/ConfigurationManager.swift b/DuckDuckGo/Configuration/ConfigurationManager.swift index 05e8296232..93b7ab5807 100644 --- a/DuckDuckGo/Configuration/ConfigurationManager.swift +++ b/DuckDuckGo/Configuration/ConfigurationManager.swift @@ -98,7 +98,7 @@ final class ConfigurationManager: DefaultConfigurationManager { await updateBloomFilterTask.value await updateBloomFilterExclusionsTask.value - ConfigurationStore.shared.log() + (store as? ConfigurationStore)?.log() Logger.config.info("last update \(String(describing: self.lastUpdateTime), privacy: .public)") Logger.config.info("last refresh check \(String(describing: self.lastRefreshCheckTime), privacy: .public)") @@ -141,18 +141,18 @@ final class ConfigurationManager: DefaultConfigurationManager { private func updateTrackerBlockingDependencies() { lastConfigurationInstallDate = Date() - ContentBlocking.shared.trackerDataManager.reload(etag: ConfigurationStore.shared.loadEtag(for: .trackerDataSet), - data: ConfigurationStore.shared.loadData(for: .trackerDataSet)) - ContentBlocking.shared.privacyConfigurationManager.reload(etag: ConfigurationStore.shared.loadEtag(for: .privacyConfiguration), - data: ConfigurationStore.shared.loadData(for: .privacyConfiguration)) + ContentBlocking.shared.trackerDataManager.reload(etag: store.loadEtag(for: .trackerDataSet), + data: store.loadData(for: .trackerDataSet)) + ContentBlocking.shared.privacyConfigurationManager.reload(etag: store.loadEtag(for: .privacyConfiguration), + data: store.loadData(for: .privacyConfiguration)) ContentBlocking.shared.contentBlockingManager.scheduleCompilation() } private func updateBloomFilter() async throws { - guard let specData = ConfigurationStore.shared.loadData(for: .bloomFilterSpec) else { + guard let specData = store.loadData(for: .bloomFilterSpec) else { throw Error.bloomFilterSpecNotFound } - guard let bloomFilterData = ConfigurationStore.shared.loadData(for: .bloomFilterBinary) else { + guard let bloomFilterData = store.loadData(for: .bloomFilterBinary) else { throw Error.bloomFilterBinaryNotFound } try await Task.detached { @@ -168,7 +168,7 @@ final class ConfigurationManager: DefaultConfigurationManager { } private func updateBloomFilterExclusions() async throws { - guard let bloomFilterExclusions = ConfigurationStore.shared.loadData(for: .bloomFilterExcludedDomains) else { + guard let bloomFilterExclusions = store.loadData(for: .bloomFilterExcludedDomains) else { throw Error.bloomFilterExclusionsNotFound } try await Task.detached { @@ -186,7 +186,7 @@ final class ConfigurationManager: DefaultConfigurationManager { extension ConfigurationManager { override var presentedItemURL: URL? { - ConfigurationStore.shared.fileUrl(for: .privacyConfiguration) + store.fileUrl(for: .privacyConfiguration) } override func presentedItemDidChange() { diff --git a/DuckDuckGo/Configuration/ConfigurationStore.swift b/DuckDuckGo/Configuration/ConfigurationStore.swift index e2a48a23d1..1d0c6bfe3b 100644 --- a/DuckDuckGo/Configuration/ConfigurationStore.swift +++ b/DuckDuckGo/Configuration/ConfigurationStore.swift @@ -48,7 +48,6 @@ final class ConfigurationStore: ConfigurationStoring { static let configStorageRemoteMessagingConfigEtag = "config.storage.remotemessagingconfig.etag" } - static let shared = ConfigurationStore() private let defaults: KeyValueStoring private var trackerRadarEtag: String? { diff --git a/DuckDuckGo/ContentBlocker/ContentBlocking.swift b/DuckDuckGo/ContentBlocker/ContentBlocking.swift index 973622888a..4413bfd2f3 100644 --- a/DuckDuckGo/ContentBlocker/ContentBlocking.swift +++ b/DuckDuckGo/ContentBlocker/ContentBlocking.swift @@ -60,7 +60,7 @@ final class AppContentBlocking { // keeping whole ContentBlocking state initialization in one place to avoid races between updates publishing and rules storing @MainActor init(internalUserDecider: InternalUserDecider) { - let configStorage = ConfigurationStore.shared + let configStorage = ConfigurationStore() privacyConfigurationManager = PrivacyConfigurationManager(fetchedETag: configStorage.loadEtag(for: .privacyConfiguration), fetchedData: configStorage.loadData(for: .privacyConfiguration), embeddedDataProvider: AppPrivacyConfigurationDataProvider(), @@ -68,8 +68,8 @@ final class AppContentBlocking { errorReporting: Self.debugEvents, internalUserDecider: internalUserDecider) - trackerDataManager = TrackerDataManager(etag: ConfigurationStore.shared.loadEtag(for: .trackerDataSet), - data: ConfigurationStore.shared.loadData(for: .trackerDataSet), + trackerDataManager = TrackerDataManager(etag: configStorage.loadEtag(for: .trackerDataSet), + data: configStorage.loadData(for: .trackerDataSet), embeddedDataProvider: AppTrackerDataSetProvider(), errorReporting: Self.debugEvents) diff --git a/DuckDuckGo/ContentBlocker/Mocks/ContentBlockingMock.swift b/DuckDuckGo/ContentBlocker/Mocks/ContentBlockingMock.swift index 6da3d7024c..fb9e5620d5 100644 --- a/DuckDuckGo/ContentBlocker/Mocks/ContentBlockingMock.swift +++ b/DuckDuckGo/ContentBlocker/Mocks/ContentBlockingMock.swift @@ -30,8 +30,8 @@ final class ContentBlockingMock: NSObject, ContentBlockingProtocol, AdClickAttri var embeddedDataEtag: String = "" var embeddedData: Data = .init() } - var trackerDataManager = TrackerDataManager(etag: ConfigurationStore.shared.loadEtag(for: .trackerDataSet), - data: ConfigurationStore.shared.loadData(for: .trackerDataSet), + var trackerDataManager = TrackerDataManager(etag: ConfigurationStore().loadEtag(for: .trackerDataSet), + data: ConfigurationStore().loadData(for: .trackerDataSet), embeddedDataProvider: AppTrackerDataSetProvider(), errorReporting: nil) diff --git a/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift b/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift index 8f76e1596c..66a65a040a 100644 --- a/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift +++ b/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift @@ -38,7 +38,7 @@ protocol ScriptSourceProviding { // refactor: ScriptSourceProvider to be passed to init methods as `some ScriptSourceProviding`, DefaultScriptSourceProvider to be killed // swiftlint:disable:next identifier_name @MainActor func DefaultScriptSourceProvider() -> ScriptSourceProviding { - ScriptSourceProvider(configStorage: ConfigurationStore.shared, privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, webTrackingProtectionPreferences: WebTrackingProtectionPreferences.shared, contentBlockingManager: ContentBlocking.shared.contentBlockingManager, trackerDataManager: ContentBlocking.shared.trackerDataManager, tld: ContentBlocking.shared.tld) + ScriptSourceProvider(configStorage: ConfigurationStore(), privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, webTrackingProtectionPreferences: WebTrackingProtectionPreferences.shared, contentBlockingManager: ContentBlocking.shared.contentBlockingManager, trackerDataManager: ContentBlocking.shared.trackerDataManager, tld: ContentBlocking.shared.tld) } struct ScriptSourceProvider: ScriptSourceProviding { diff --git a/DuckDuckGo/RemoteMessaging/RemoteMessagingClient.swift b/DuckDuckGo/RemoteMessaging/RemoteMessagingClient.swift index 4151ed5164..a428829526 100644 --- a/DuckDuckGo/RemoteMessaging/RemoteMessagingClient.swift +++ b/DuckDuckGo/RemoteMessaging/RemoteMessagingClient.swift @@ -99,7 +99,7 @@ final class RemoteMessagingClient: RemoteMessagingProcessing { urlSession: .session(), eventMapping: ConfigurationManager.configurationDebugEvents ), - configurationStore: ConfigurationStore.shared + configurationStore: ConfigurationStore() ) self.configMatcherProvider = configMatcherProvider self.remoteMessagingAvailabilityProvider = remoteMessagingAvailabilityProvider diff --git a/UnitTests/Autoconsent/AutoconsentMessageProtocolTests.swift b/UnitTests/Autoconsent/AutoconsentMessageProtocolTests.swift index cdc70e2ca2..e19f21d6b9 100644 --- a/UnitTests/Autoconsent/AutoconsentMessageProtocolTests.swift +++ b/UnitTests/Autoconsent/AutoconsentMessageProtocolTests.swift @@ -29,8 +29,8 @@ class AutoconsentMessageProtocolTests: XCTestCase { privacyConfigurationManager: MockPrivacyConfigurationManager(), webTrackingProtectionPreferences: WebTrackingProtectionPreferences.shared, // mock contentBlockingManager: ContentBlockerRulesManagerMock(), - trackerDataManager: TrackerDataManager(etag: ConfigurationStore.shared.loadEtag(for: .trackerDataSet), - data: ConfigurationStore.shared.loadData(for: .trackerDataSet), + trackerDataManager: TrackerDataManager(etag: ConfigurationStore().loadEtag(for: .trackerDataSet), + data: ConfigurationStore().loadData(for: .trackerDataSet), embeddedDataProvider: AppTrackerDataSetProvider(), errorReporting: nil), tld: TLD()), diff --git a/UnitTests/Configuration/ConfigurationStorageTests.swift b/UnitTests/Configuration/ConfigurationStorageTests.swift index 81d4b7a890..9b2876ddfb 100644 --- a/UnitTests/Configuration/ConfigurationStorageTests.swift +++ b/UnitTests/Configuration/ConfigurationStorageTests.swift @@ -23,10 +23,12 @@ import Configuration final class ConfigurationStorageTests: XCTestCase { + var configurationStore: ConfigurationStore = ConfigurationStore() + override func tearDown() { super.tearDown() for config in Configuration.allCases { - let url = ConfigurationStore.shared.fileUrl(for: config) + let url = configurationStore.fileUrl(for: config) try? FileManager.default.removeItem(at: url) } } @@ -34,16 +36,16 @@ final class ConfigurationStorageTests: XCTestCase { func test_when_data_is_saved_for_config_then_it_can_be_loaded_correctly() { for config in Configuration.allCases { let uuid = UUID().uuidString - try? ConfigurationStore.shared.saveData(uuid.data(using: .utf8)!, for: config) - XCTAssertEqual(uuid, ConfigurationStore.shared.loadData(for: config)?.utf8String()) + try? configurationStore.saveData(uuid.data(using: .utf8)!, for: config) + XCTAssertEqual(uuid, configurationStore.loadData(for: config)?.utf8String()) } } func test_when_etag_is_saved_for_config_then_it_can_be_loaded_correctly() { for config in Configuration.allCases { let etag = UUID().uuidString - try? ConfigurationStore.shared.saveEtag(etag, for: config) - XCTAssertEqual(etag, ConfigurationStore.shared.loadEtag(for: config)) + try? configurationStore.saveEtag(etag, for: config) + XCTAssertEqual(etag, configurationStore.loadEtag(for: config)) } } diff --git a/UnitTests/ContentBlocker/ContentBlockingUpdatingTests.swift b/UnitTests/ContentBlocker/ContentBlockingUpdatingTests.swift index da76ce79d6..2e17ee672f 100644 --- a/UnitTests/ContentBlocker/ContentBlockingUpdatingTests.swift +++ b/UnitTests/ContentBlocker/ContentBlockingUpdatingTests.swift @@ -31,12 +31,13 @@ final class ContentBlockingUpdatingTests: XCTestCase { @MainActor override func setUp() { + let configStore = ConfigurationStore() updating = UserContentUpdating(contentBlockerRulesManager: rulesManager, privacyConfigurationManager: MockPrivacyConfigurationManager(), - trackerDataManager: TrackerDataManager(etag: ConfigurationStore.shared.loadEtag(for: .trackerDataSet), - data: ConfigurationStore.shared.loadData(for: .trackerDataSet), - embeddedDataProvider: AppTrackerDataSetProvider(), - errorReporting: nil), + trackerDataManager: TrackerDataManager(etag: configStore.loadEtag(for: .trackerDataSet), + data: configStore.loadData(for: .trackerDataSet), + embeddedDataProvider: AppTrackerDataSetProvider(), + errorReporting: nil), configStorage: MockConfigurationStore(), webTrackingProtectionPreferences: preferences, tld: TLD()) From e928aeac96447815968ad0dfe1f00a63ccf1470d Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Tue, 3 Sep 2024 09:57:02 -0500 Subject: [PATCH 15/34] Remove revived logging file --- DuckDuckGo/Common/Logging/Logging.swift | 179 ------------------------ 1 file changed, 179 deletions(-) delete mode 100644 DuckDuckGo/Common/Logging/Logging.swift diff --git a/DuckDuckGo/Common/Logging/Logging.swift b/DuckDuckGo/Common/Logging/Logging.swift deleted file mode 100644 index cdca95d9ae..0000000000 --- a/DuckDuckGo/Common/Logging/Logging.swift +++ /dev/null @@ -1,179 +0,0 @@ -// -// Logging.swift -// -// Copyright © 2021 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 os.log - -extension OSLog { - - enum AppCategories: String, CaseIterable { - case atb = "ATB" - case downloads = "Downloads" - case fire = "Fire" - case dataImportExport = "Data Import/Export" - case pixel = "Pixel" - case httpsUpgrade = "HTTPS Upgrade" - case favicons = "Favicons" - case autoLock = "Auto-Lock" - case tabLazyLoading = "Lazy Loading" - case autoconsent = "Autoconsent" - case bookmarks = "Bookmarks" - case attribution = "Ad Attribution" - case bitwarden = "Bitwarden" - case navigation = "Navigation" - case duckPlayer = "Duck Player" - case tabSnapshots = "Tab Snapshots" - case updates = "Updates" - case sync = "Sync" - case dbp = "dbp" - } - - enum AllCategories { - static var allCases: [String] { - AppCategories.allCases.map(\.rawValue) - } - } - - @OSLogWrapper(AppCategories.atb) static var atb - @OSLogWrapper(AppCategories.downloads) static var downloads - @OSLogWrapper(AppCategories.fire) static var fire - @OSLogWrapper(AppCategories.dataImportExport) static var dataImportExport - @OSLogWrapper(AppCategories.pixel) static var pixel - @OSLogWrapper(AppCategories.httpsUpgrade) static var httpsUpgrade - @OSLogWrapper(AppCategories.favicons) static var favicons - @OSLogWrapper(AppCategories.autoLock) static var autoLock - @OSLogWrapper(AppCategories.tabLazyLoading) static var tabLazyLoading - @OSLogWrapper(AppCategories.autoconsent) static var autoconsent - @OSLogWrapper(AppCategories.bookmarks) static var bookmarks - @OSLogWrapper(AppCategories.attribution) static var attribution - @OSLogWrapper(AppCategories.bitwarden) static var bitwarden - @OSLogWrapper(AppCategories.navigation) static var navigation - @OSLogWrapper(AppCategories.duckPlayer) static var duckPlayer - @OSLogWrapper(AppCategories.tabSnapshots) static var tabSnapshots - @OSLogWrapper(AppCategories.updates) static var updates - @OSLogWrapper(AppCategories.sync) static var sync - @OSLogWrapper(AppCategories.dbp) static var dbp - - // Debug->Logging categories will only be enabled for one day - @UserDefaultsWrapper(key: .loggingEnabledDate, defaultValue: .distantPast) - private static var loggingEnabledDate: Date - private static var isLoggingEnabledToday: Bool { - NSCalendar.current.isDate(Date(), inSameDayAs: loggingEnabledDate) - } - - @UserDefaultsWrapper(key: .loggingCategories, defaultValue: []) - private static var loggingCategoriesSetting: [String] { - didSet { - loggingEnabledDate = Date() - } - } - static var loggingCategories: Set { - get { - guard isLoggingEnabledToday else { return [] } - return Set(loggingCategoriesSetting) - } - set { - loggingCategoriesSetting = Array(newValue) - enabledLoggingCategories = loggingCategories - } - } - - static let isRunningInDebugEnvironment: Bool = { - ProcessInfo().environment[ProcessInfo.Constants.osActivityMode] == ProcessInfo.Constants.debug - || ProcessInfo().environment[ProcessInfo.Constants.osActivityDtMode] == ProcessInfo.Constants.yes - }() - - static let subsystem = Bundle.main.bundleIdentifier! - -} - -extension ProcessInfo { - enum Constants { - static let osActivityMode = "OS_ACTIVITY_MODE" - static let osActivityDtMode = "OS_ACTIVITY_DT_MODE" - static let debug = "debug" - static let yes = "YES" - } -} - -extension OSLog.OSLogWrapper { - - private static let enableLoggingCategoriesOnce: Void = { -#if CI - OSLog.enabledLoggingCategories = Set(OSLog.AllCategories.allCases) -#else - OSLog.enabledLoggingCategories = OSLog.loggingCategories -#endif - }() - - init(_ category: OSLog.AppCategories) { - _=Self.enableLoggingCategoriesOnce - self.init(rawValue: category.rawValue) - } - -} - -func logOrAssertionFailure(_ message: String) { -#if DEBUG && !CI - assertionFailure(message) -#else - os_log("%{public}s", type: .error, message) -#endif -} - -#if DEBUG - -func breakByRaisingSigInt(_ description: String, file: StaticString = #file, line: Int = #line) { - let fileLine = "\(("\(file)" as NSString).lastPathComponent):\(line)" - os_log(""" - - - ------------------------------------------------------------------------------------------------------ - BREAK at %s: - ------------------------------------------------------------------------------------------------------ - - %s - - Hit Continue (^⌘Y) to continue program execution - ------------------------------------------------------------------------------------------------------ - - """, type: .debug, fileLine, description.components(separatedBy: "\n").map { " " + $0.trimmingWhitespace() }.joined(separator: "\n")) - raise(SIGINT) -} - -// get symbol from stack trace for a caller of a calling method -func callingSymbol() -> String { - let stackTrace = Thread.callStackSymbols - // find `callingSymbol` itself or dispatch_once_callout - var callingSymbolIdx = stackTrace.firstIndex(where: { $0.contains("_dispatch_once_callout") }) - ?? stackTrace.firstIndex(where: { $0.contains("callingSymbol") })! - // procedure calling `callingSymbol` - callingSymbolIdx += 1 - - var symbolName: String - repeat { - // caller for the procedure - callingSymbolIdx += 1 - let line = stackTrace[callingSymbolIdx].replacingOccurrences(of: Bundle.main.executableURL!.lastPathComponent, with: "DDG") - symbolName = String(line.split(separator: " ", maxSplits: 3)[3]).components(separatedBy: " + ")[0] - } while stackTrace[callingSymbolIdx - 1].contains(symbolName.dropping(suffix: "To")) // skip objc wrappers - - return symbolName -} - -#endif From bb636af45075ac829d882396edb2a4c8b41c2ae5 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 4 Sep 2024 11:02:16 -0500 Subject: [PATCH 16/34] Update config managers to watch directory --- .../Configuration/ConfigurationManager.swift | 14 ++++++++------ DuckDuckGoVPN/ConfigurationManager.swift | 10 ++++++++-- .../Configuration/ConfigurationManager.swift | 10 ++++++++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/DuckDuckGo/Configuration/ConfigurationManager.swift b/DuckDuckGo/Configuration/ConfigurationManager.swift index 93b7ab5807..f193bb836a 100644 --- a/DuckDuckGo/Configuration/ConfigurationManager.swift +++ b/DuckDuckGo/Configuration/ConfigurationManager.swift @@ -186,14 +186,16 @@ final class ConfigurationManager: DefaultConfigurationManager { extension ConfigurationManager { override var presentedItemURL: URL? { - store.fileUrl(for: .privacyConfiguration) + store.fileUrl(for: .privacyConfiguration).deletingLastPathComponent() } - override func presentedItemDidChange() { - // Check for significant time since last install to prevent double config install in short time - if let lastConfigurationInstallDate, lastUpdateTime.timeIntervalSince(lastConfigurationInstallDate) < 1 { - return - } + override func presentedSubitemDidAppear(at url: URL) { + guard url == store.fileUrl(for: .privacyConfiguration) else { return } + updateTrackerBlockingDependencies() + } + + override func presentedSubitemDidChange(at url: URL) { + guard url == store.fileUrl(for: .privacyConfiguration) else { return } updateTrackerBlockingDependencies() } } diff --git a/DuckDuckGoVPN/ConfigurationManager.swift b/DuckDuckGoVPN/ConfigurationManager.swift index 3fa5a0b270..9a143f2041 100644 --- a/DuckDuckGoVPN/ConfigurationManager.swift +++ b/DuckDuckGoVPN/ConfigurationManager.swift @@ -87,10 +87,16 @@ final class ConfigurationManager: DefaultConfigurationManager { extension ConfigurationManager { override var presentedItemURL: URL? { - store.fileUrl(for: .privacyConfiguration) + store.fileUrl(for: .privacyConfiguration).deletingLastPathComponent() } - override func presentedItemDidChange() { + override func presentedSubitemDidAppear(at url: URL) { + guard url == store.fileUrl(for: .privacyConfiguration) else { return } + updateConfigDependencies() + } + + override func presentedSubitemDidChange(at url: URL) { + guard url == store.fileUrl(for: .privacyConfiguration) else { return } updateConfigDependencies() } } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift index d4d69b513f..c2c549e7f8 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -91,10 +91,16 @@ final class ConfigurationManager: DefaultConfigurationManager { extension ConfigurationManager { override var presentedItemURL: URL? { - store.fileUrl(for: .privacyConfiguration) + store.fileUrl(for: .privacyConfiguration).deletingLastPathComponent() } - override func presentedItemDidChange() { + override func presentedSubitemDidAppear(at url: URL) { + guard url == store.fileUrl(for: .privacyConfiguration) else { return } + updateConfigDependencies() + } + + override func presentedSubitemDidChange(at url: URL) { + guard url == store.fileUrl(for: .privacyConfiguration) else { return } updateConfigDependencies() } } From 1e32b7ed6a039725e9b16a92c5d33863672f3d8f Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 4 Sep 2024 12:57:03 -0500 Subject: [PATCH 17/34] Use BSK commit --- DuckDuckGo.xcodeproj/project.pbxproj | 27 ++----------------- .../xcshareddata/swiftpm/Package.resolved | 8 ++++++ .../DataBrokerProtection/Package.swift | 2 +- .../NetworkProtectionMac/Package.swift | 2 +- LocalPackages/SubscriptionUI/Package.swift | 2 +- 5 files changed, 13 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index fcad0f7799..033c258dc5 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -13,11 +13,9 @@ 021EA0842BD6E01A00772C9A /* TabsPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021EA0822BD6DF1B00772C9A /* TabsPreferencesTests.swift */; }; 021EA0852BD6E0EB00772C9A /* TabsPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021EA0822BD6DF1B00772C9A /* TabsPreferencesTests.swift */; }; 0230C0A3272080090018F728 /* KeyedCodingExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0230C0A2272080090018F728 /* KeyedCodingExtension.swift */; }; - 0234D1082C778E82003A65EA /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 0234D1072C778E82003A65EA /* Configuration */; }; 026ADE1426C3010C002518EE /* macos-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 026ADE1326C3010C002518EE /* macos-config.json */; }; 028904202A7B25380028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; - 02FDA6572C764B740024CD8B /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02FDA6562C764B740024CD8B /* Configuration */; }; 02FDA6592C764B970024CD8B /* ConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6582C764B970024CD8B /* ConfigurationManager.swift */; }; 02FDA65B2C764C200024CD8B /* ConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA65A2C764C200024CD8B /* ConfigurationStore.swift */; }; 02FDA65C2C764CB00024CD8B /* ConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6582C764B970024CD8B /* ConfigurationManager.swift */; }; @@ -27,7 +25,6 @@ 02FDA6622C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */; }; 02FDA6632C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */; }; 02FDA6642C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6612C765D0F0024CD8B /* VPNAgentConfigurationURLProvider.swift */; }; - 02FDA6662C765E050024CD8B /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02FDA6652C765E050024CD8B /* Configuration */; }; 142879DA24CE1179005419BB /* SuggestionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142879D924CE1179005419BB /* SuggestionViewModelTests.swift */; }; 142879DC24CE1185005419BB /* SuggestionContainerViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142879DB24CE1185005419BB /* SuggestionContainerViewModelTests.swift */; }; 1430DFF524D0580F00B8978C /* TabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1430DFF424D0580F00B8978C /* TabBarViewController.swift */; }; @@ -3041,7 +3038,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 020807B52C6D00A8006F94C4 /* BrowserServicesKit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = BrowserServicesKit; path = ../BrowserServicesKit; sourceTree = ""; }; 021EA07F2BD2A9D500772C9A /* TabsPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsPreferences.swift; sourceTree = ""; }; 021EA0822BD6DF1B00772C9A /* TabsPreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsPreferencesTests.swift; sourceTree = ""; }; 0230C0A2272080090018F728 /* KeyedCodingExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedCodingExtension.swift; sourceTree = ""; }; @@ -4560,7 +4556,6 @@ 7BBE2B7B2B61663C00697445 /* NetworkProtectionProxy in Frameworks */, 4B2D062C2A11C0E100DE1F49 /* Networking in Frameworks */, 4B25375B2A11BE7300610219 /* NetworkExtension.framework in Frameworks */, - 02FDA6662C765E050024CD8B /* Configuration in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4569,7 +4564,6 @@ buildActionMask = 2147483647; files = ( F198C71A2BD18A5B000BF24D /* PixelKit in Frameworks */, - 02FDA6572C764B740024CD8B /* Configuration in Frameworks */, 4B41EDAB2B1544B2001EEDF4 /* LoginItems in Frameworks */, 7B00997D2B6508B700FE7C31 /* NetworkProtectionProxy in Frameworks */, 7BEEA5122AD1235B00A9E72B /* NetworkProtectionIPC in Frameworks */, @@ -4608,7 +4602,6 @@ buildActionMask = 2147483647; files = ( 7B23668A2C09FAE8002D393F /* VPNAppLauncher in Frameworks */, - 0234D1082C778E82003A65EA /* Configuration in Frameworks */, 7B624F172BA25C1F00A6C544 /* NetworkProtectionUI in Frameworks */, 37269F052B3332C2005E8E46 /* Common in Frameworks */, ); @@ -7023,7 +7016,6 @@ AA585D75248FD31100E9A3E2 = { isa = PBXGroup; children = ( - 020807B52C6D00A8006F94C4 /* BrowserServicesKit */, 378B5886295CF2A4002C0CC0 /* Configuration */, 378E279C2970217400FCADA2 /* LocalPackages */, 7BB108552A43375D000AB95F /* LocalThirdParty */, @@ -9026,7 +9018,6 @@ 7B37C7A42BAA32A50062546A /* Subscription */, F198C7172BD18A4C000BF24D /* PixelKit */, 9D9DE57A2C63AA1F00D20B15 /* AppKitExtensions */, - 02FDA6652C765E050024CD8B /* Configuration */, 4B5235442C7BB14D00AFAF64 /* WireGuard */, ); productName = NetworkProtectionSystemExtension; @@ -9063,7 +9054,6 @@ BDADBDC82BD2BC2200421B9B /* Lottie */, 7B23668B2C09FAF1002D393F /* VPNAppLauncher */, 9D9DE5802C63BA0B00D20B15 /* AppKitExtensions */, - 02FDA6562C764B740024CD8B /* Configuration */, ); productName = DuckDuckGoAgent; productReference = 4B2D06392A11CFBB00DE1F49 /* DuckDuckGo VPN.app */; @@ -9122,7 +9112,6 @@ 37269F042B3332C2005E8E46 /* Common */, 7B624F162BA25C1F00A6C544 /* NetworkProtectionUI */, 7B2366892C09FAE8002D393F /* VPNAppLauncher */, - 0234D1072C778E82003A65EA /* Configuration */, ); productName = DuckDuckGoNotifications; productReference = 4B4BEC202A11B4E2001D9AC5 /* DuckDuckGo Notifications.app */; @@ -13787,8 +13776,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { - kind = exactVersion; - version = 191.1.0; + kind = revision; + revision = 42105b6fd50f45d9bcab839f646f7b696ce71602; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { @@ -13863,18 +13852,6 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Configuration; }; - 0234D1072C778E82003A65EA /* Configuration */ = { - isa = XCSwiftPackageProductDependency; - productName = Configuration; - }; - 02FDA6562C764B740024CD8B /* Configuration */ = { - isa = XCSwiftPackageProductDependency; - productName = Configuration; - }; - 02FDA6652C765E050024CD8B /* Configuration */ = { - isa = XCSwiftPackageProductDependency; - productName = Configuration; - }; 08D4923DC968236E22E373E2 /* Crashes */ = { isa = XCSwiftPackageProductDependency; package = FAE06B199CA1F209B55B34E9 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c77166b9c5..f3ff662287 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -27,6 +27,14 @@ "version" : "3.0.0" } }, + { + "identity" : "browserserviceskit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/BrowserServicesKit", + "state" : { + "revision" : "42105b6fd50f45d9bcab839f646f7b696ce71602" + } + }, { "identity" : "content-scope-scripts", "kind" : "remoteSourceControl", diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index a9266c048f..6783cc729a 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -29,7 +29,7 @@ let package = Package( targets: ["DataBrokerProtection"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "191.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "42105b6fd50f45d9bcab839f646f7b696ce71602"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper"), ], diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index 1a4855c3bd..e7c7079f75 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -32,7 +32,7 @@ let package = Package( .library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "191.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "42105b6fd50f45d9bcab839f646f7b696ce71602"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"), .package(path: "../AppLauncher"), .package(path: "../UDSHelper"), diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index 3484bafa20..adf610e4c2 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "191.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "42105b6fd50f45d9bcab839f646f7b696ce71602"), .package(path: "../SwiftUIExtensions") ], targets: [ From 65fc13ea281786ff51b6f9218bc4abc65baf1bd0 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 4 Sep 2024 13:00:13 -0500 Subject: [PATCH 18/34] Add libraries to targets --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 033c258dc5..5c65a797cc 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -16,6 +16,13 @@ 026ADE1426C3010C002518EE /* macos-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 026ADE1326C3010C002518EE /* macos-config.json */; }; 028904202A7B25380028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; + 02A15D8C2C88D763001A4237 /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02A15D8B2C88D763001A4237 /* Configuration */; }; + 02A15D8E2C88D76A001A4237 /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02A15D8D2C88D76A001A4237 /* Persistence */; }; + 02A15D902C88D773001A4237 /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02A15D8F2C88D773001A4237 /* Persistence */; }; + 02A15D922C88D789001A4237 /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02A15D912C88D789001A4237 /* Configuration */; }; + 02A15D942C88D78F001A4237 /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02A15D932C88D78F001A4237 /* Persistence */; }; + 02A15D962C88D797001A4237 /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02A15D952C88D797001A4237 /* Configuration */; }; + 02A15D982C88D79D001A4237 /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02A15D972C88D79D001A4237 /* Persistence */; }; 02FDA6592C764B970024CD8B /* ConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6582C764B970024CD8B /* ConfigurationManager.swift */; }; 02FDA65B2C764C200024CD8B /* ConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA65A2C764C200024CD8B /* ConfigurationStore.swift */; }; 02FDA65C2C764CB00024CD8B /* ConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDA6582C764B970024CD8B /* ConfigurationManager.swift */; }; @@ -4564,10 +4571,12 @@ buildActionMask = 2147483647; files = ( F198C71A2BD18A5B000BF24D /* PixelKit in Frameworks */, + 02A15D922C88D789001A4237 /* Configuration in Frameworks */, 4B41EDAB2B1544B2001EEDF4 /* LoginItems in Frameworks */, 7B00997D2B6508B700FE7C31 /* NetworkProtectionProxy in Frameworks */, 7BEEA5122AD1235B00A9E72B /* NetworkProtectionIPC in Frameworks */, 7BA7CC5F2AD1210C0042E5CE /* Networking in Frameworks */, + 02A15D942C88D78F001A4237 /* Persistence in Frameworks */, 7BEEA5162AD1236E00A9E72B /* NetworkProtectionUI in Frameworks */, 7B8594192B5B26230007EB3E /* UDSHelper in Frameworks */, BDADBDC92BD2BC2200421B9B /* Lottie in Frameworks */, @@ -4587,7 +4596,9 @@ 7B23668E2C09FAFA002D393F /* VPNAppLauncher in Frameworks */, 7BA7CC612AD1211C0042E5CE /* Networking in Frameworks */, F198C71C2BD18A61000BF24D /* PixelKit in Frameworks */, + 02A15D982C88D79D001A4237 /* Persistence in Frameworks */, 7BEEA5142AD1236300A9E72B /* NetworkProtectionIPC in Frameworks */, + 02A15D962C88D797001A4237 /* Configuration in Frameworks */, BDADBDCB2BD2BC2800421B9B /* Lottie in Frameworks */, 9D9DE5832C63BE9600D20B15 /* AppKitExtensions in Frameworks */, 7B00997F2B6508C200FE7C31 /* NetworkProtectionProxy in Frameworks */, @@ -4657,6 +4668,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 02A15D902C88D773001A4237 /* Persistence in Frameworks */, 9DEF97E12B06C4EE00764F03 /* Networking in Frameworks */, 020807B22C6CFF95006F94C4 /* Configuration in Frameworks */, F1D0428E2BFB9F9C00A31506 /* Subscription in Frameworks */, @@ -4669,6 +4681,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 02A15D8E2C88D76A001A4237 /* Persistence in Frameworks */, + 02A15D8C2C88D763001A4237 /* Configuration in Frameworks */, 315A023F2B6421AE00BFA577 /* Networking in Frameworks */, F1D042902BFB9FA300A31506 /* Subscription in Frameworks */, C18BF9D22C736C9700ED6B8A /* Freemium in Frameworks */, @@ -9054,6 +9068,8 @@ BDADBDC82BD2BC2200421B9B /* Lottie */, 7B23668B2C09FAF1002D393F /* VPNAppLauncher */, 9D9DE5802C63BA0B00D20B15 /* AppKitExtensions */, + 02A15D912C88D789001A4237 /* Configuration */, + 02A15D932C88D78F001A4237 /* Persistence */, ); productName = DuckDuckGoAgent; productReference = 4B2D06392A11CFBB00DE1F49 /* DuckDuckGo VPN.app */; @@ -9089,6 +9105,8 @@ BDADBDCA2BD2BC2800421B9B /* Lottie */, 7B23668D2C09FAFA002D393F /* VPNAppLauncher */, 9D9DE5822C63BE9600D20B15 /* AppKitExtensions */, + 02A15D952C88D797001A4237 /* Configuration */, + 02A15D972C88D79D001A4237 /* Persistence */, ); productName = DuckDuckGoAgentAppStore; productReference = 4B2D06692A13318400DE1F49 /* DuckDuckGo VPN App Store.app */; @@ -9229,6 +9247,7 @@ F1D0428D2BFB9F9C00A31506 /* Subscription */, 020807B12C6CFF95006F94C4 /* Configuration */, C18BF9CF2C736C9100ED6B8A /* Freemium */, + 02A15D8F2C88D773001A4237 /* Persistence */, ); productName = DuckDuckGoAgent; productReference = 9D9AE8D12AAA39A70026E7DC /* DuckDuckGo Personal Information Removal.app */; @@ -9253,6 +9272,8 @@ 315A023E2B6421AE00BFA577 /* Networking */, F1D0428F2BFB9FA300A31506 /* Subscription */, C18BF9D12C736C9700ED6B8A /* Freemium */, + 02A15D8B2C88D763001A4237 /* Configuration */, + 02A15D8D2C88D76A001A4237 /* Persistence */, ); productName = DuckDuckGoAgent; productReference = 9D9AE8F22AAA39D30026E7DC /* DuckDuckGo Personal Information Removal App Store.app */; @@ -13852,6 +13873,41 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Configuration; }; + 02A15D8B2C88D763001A4237 /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Configuration; + }; + 02A15D8D2C88D76A001A4237 /* Persistence */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Persistence; + }; + 02A15D8F2C88D773001A4237 /* Persistence */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Persistence; + }; + 02A15D912C88D789001A4237 /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Configuration; + }; + 02A15D932C88D78F001A4237 /* Persistence */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Persistence; + }; + 02A15D952C88D797001A4237 /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Configuration; + }; + 02A15D972C88D79D001A4237 /* Persistence */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Persistence; + }; 08D4923DC968236E22E373E2 /* Crashes */ = { isa = XCSwiftPackageProductDependency; package = FAE06B199CA1F209B55B34E9 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; From 378d80d788437451b788d02666910f3e58359a1a Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 4 Sep 2024 14:10:48 -0500 Subject: [PATCH 19/34] Trying to fix test compilation failure --- DuckDuckGo.xcodeproj/project.pbxproj | 16 ++++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 4856b7b0d3..51bcb25a7e 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 021EA0842BD6E01A00772C9A /* TabsPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021EA0822BD6DF1B00772C9A /* TabsPreferencesTests.swift */; }; 021EA0852BD6E0EB00772C9A /* TabsPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021EA0822BD6DF1B00772C9A /* TabsPreferencesTests.swift */; }; 0230C0A3272080090018F728 /* KeyedCodingExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0230C0A2272080090018F728 /* KeyedCodingExtension.swift */; }; + 02589D9D2C88E8210093940D /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02589D9C2C88E8210093940D /* Configuration */; }; + 02589D9F2C88E8270093940D /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02589D9E2C88E8270093940D /* Persistence */; }; 026ADE1426C3010C002518EE /* macos-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 026ADE1326C3010C002518EE /* macos-config.json */; }; 028904202A7B25380028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; @@ -4621,11 +4623,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 02589D9F2C88E8270093940D /* Persistence in Frameworks */, F1DF95E52BD1807C0045E591 /* Subscription in Frameworks */, 4B5235472C7BB15700AFAF64 /* WireGuard in Frameworks */, 37269EFF2B332FBB005E8E46 /* Common in Frameworks */, EE7295E72A545BBB008C0991 /* NetworkProtection in Frameworks */, F198C7162BD18A44000BF24D /* PixelKit in Frameworks */, + 02589D9D2C88E8210093940D /* Configuration in Frameworks */, 4B4D60AF2A0C837F00BCD287 /* Networking in Frameworks */, 7B2366882C09FADA002D393F /* VPNAppLauncher in Frameworks */, 7B25856E2BA2F2ED00D49F79 /* NetworkProtectionUI in Frameworks */, @@ -9158,6 +9162,8 @@ 7B2366872C09FADA002D393F /* VPNAppLauncher */, 9D9DE5762C63AA1600D20B15 /* AppKitExtensions */, 4B5235462C7BB15700AFAF64 /* WireGuard */, + 02589D9C2C88E8210093940D /* Configuration */, + 02589D9E2C88E8270093940D /* Persistence */, ); productName = NetworkProtectionAppExtension; productReference = 4B4D603D2A0B290200BCD287 /* NetworkProtectionAppExtension.appex */; @@ -13871,6 +13877,16 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Configuration; }; + 02589D9C2C88E8210093940D /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Configuration; + }; + 02589D9E2C88E8270093940D /* Persistence */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Persistence; + }; 02A15D8B2C88D763001A4237 /* Configuration */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8b11aa3f3a..f3ff662287 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -40,8 +40,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/content-scope-scripts", "state" : { - "revision" : "1164f9579d226cf16c0c1c79c0928f28a4f3c421", - "version" : "6.14.0" + "revision" : "5876a5d2e2e7f5a2e11f6419c6c3fafb7cafdfca", + "version" : "6.12.0" } }, { From 8774ed5e42c6fa339537afc881a88c86480e80a8 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 4 Sep 2024 14:24:29 -0500 Subject: [PATCH 20/34] Add more libraries --- DuckDuckGo.xcodeproj/project.pbxproj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 51bcb25a7e..5ce10c8498 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 0230C0A3272080090018F728 /* KeyedCodingExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0230C0A2272080090018F728 /* KeyedCodingExtension.swift */; }; 02589D9D2C88E8210093940D /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02589D9C2C88E8210093940D /* Configuration */; }; 02589D9F2C88E8270093940D /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02589D9E2C88E8270093940D /* Persistence */; }; + 02589DA12C88EB570093940D /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA02C88EB570093940D /* Configuration */; }; + 02589DA32C88EB5D0093940D /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA22C88EB5D0093940D /* Persistence */; }; 026ADE1426C3010C002518EE /* macos-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 026ADE1326C3010C002518EE /* macos-config.json */; }; 028904202A7B25380028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; @@ -4560,10 +4562,12 @@ 9D9DE57B2C63AA1F00D20B15 /* AppKitExtensions in Frameworks */, EE7295E92A545BC4008C0991 /* NetworkProtection in Frameworks */, 7B37C7A52BAA32A50062546A /* Subscription in Frameworks */, + 02589DA12C88EB570093940D /* Configuration in Frameworks */, F198C7182BD18A4C000BF24D /* PixelKit in Frameworks */, 7BBE2B7B2B61663C00697445 /* NetworkProtectionProxy in Frameworks */, 4B2D062C2A11C0E100DE1F49 /* Networking in Frameworks */, 4B25375B2A11BE7300610219 /* NetworkExtension.framework in Frameworks */, + 02589DA32C88EB5D0093940D /* Persistence in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9036,6 +9040,8 @@ F198C7172BD18A4C000BF24D /* PixelKit */, 9D9DE57A2C63AA1F00D20B15 /* AppKitExtensions */, 4B5235442C7BB14D00AFAF64 /* WireGuard */, + 02589DA02C88EB570093940D /* Configuration */, + 02589DA22C88EB5D0093940D /* Persistence */, ); productName = NetworkProtectionSystemExtension; productReference = 4B25375A2A11BE7300610219 /* com.duckduckgo.macos.vpn.network-extension.debug.systemextension */; @@ -13887,6 +13893,16 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Persistence; }; + 02589DA02C88EB570093940D /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Configuration; + }; + 02589DA22C88EB5D0093940D /* Persistence */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Persistence; + }; 02A15D8B2C88D763001A4237 /* Configuration */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; From 2c7e3c38d6b3097ed220a3a2039962d9732df44e Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 4 Sep 2024 14:31:50 -0500 Subject: [PATCH 21/34] Link BSK --- DuckDuckGo.xcodeproj/project.pbxproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 5ce10c8498..7b0da347ea 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 02589D9F2C88E8270093940D /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02589D9E2C88E8270093940D /* Persistence */; }; 02589DA12C88EB570093940D /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA02C88EB570093940D /* Configuration */; }; 02589DA32C88EB5D0093940D /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA22C88EB5D0093940D /* Persistence */; }; + 02589DA52C88ED190093940D /* BrowserServicesKit in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA42C88ED190093940D /* BrowserServicesKit */; }; 026ADE1426C3010C002518EE /* macos-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 026ADE1326C3010C002518EE /* macos-config.json */; }; 028904202A7B25380028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; @@ -4619,6 +4620,7 @@ files = ( 7B23668A2C09FAE8002D393F /* VPNAppLauncher in Frameworks */, 7B624F172BA25C1F00A6C544 /* NetworkProtectionUI in Frameworks */, + 02589DA52C88ED190093940D /* BrowserServicesKit in Frameworks */, 37269F052B3332C2005E8E46 /* Common in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -9139,6 +9141,7 @@ 37269F042B3332C2005E8E46 /* Common */, 7B624F162BA25C1F00A6C544 /* NetworkProtectionUI */, 7B2366892C09FAE8002D393F /* VPNAppLauncher */, + 02589DA42C88ED190093940D /* BrowserServicesKit */, ); productName = DuckDuckGoNotifications; productReference = 4B4BEC202A11B4E2001D9AC5 /* DuckDuckGo Notifications.app */; @@ -13903,6 +13906,11 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Persistence; }; + 02589DA42C88ED190093940D /* BrowserServicesKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = BrowserServicesKit; + }; 02A15D8B2C88D763001A4237 /* Configuration */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; From f827478a6daffa99c8b324b34b24ec45daa0e28a Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Wed, 4 Sep 2024 15:23:01 -0500 Subject: [PATCH 22/34] Link and lint --- DuckDuckGo.xcodeproj/project.pbxproj | 8 ++++++++ DuckDuckGo/Configuration/ConfigurationStore.swift | 1 - .../Tests/DataBrokerProtectionTests/Mocks.swift | 14 -------------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 7b0da347ea..42aab1a7fb 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 02589DA12C88EB570093940D /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA02C88EB570093940D /* Configuration */; }; 02589DA32C88EB5D0093940D /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA22C88EB5D0093940D /* Persistence */; }; 02589DA52C88ED190093940D /* BrowserServicesKit in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA42C88ED190093940D /* BrowserServicesKit */; }; + 02589DA72C88F8E90093940D /* PixelKit in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA62C88F8E90093940D /* PixelKit */; }; 026ADE1426C3010C002518EE /* macos-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 026ADE1326C3010C002518EE /* macos-config.json */; }; 028904202A7B25380028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; @@ -4622,6 +4623,7 @@ 7B624F172BA25C1F00A6C544 /* NetworkProtectionUI in Frameworks */, 02589DA52C88ED190093940D /* BrowserServicesKit in Frameworks */, 37269F052B3332C2005E8E46 /* Common in Frameworks */, + 02589DA72C88F8E90093940D /* PixelKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9142,6 +9144,7 @@ 7B624F162BA25C1F00A6C544 /* NetworkProtectionUI */, 7B2366892C09FAE8002D393F /* VPNAppLauncher */, 02589DA42C88ED190093940D /* BrowserServicesKit */, + 02589DA62C88F8E90093940D /* PixelKit */, ); productName = DuckDuckGoNotifications; productReference = 4B4BEC202A11B4E2001D9AC5 /* DuckDuckGo Notifications.app */; @@ -13911,6 +13914,11 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = BrowserServicesKit; }; + 02589DA62C88F8E90093940D /* PixelKit */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = PixelKit; + }; 02A15D8B2C88D763001A4237 /* Configuration */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo/Configuration/ConfigurationStore.swift b/DuckDuckGo/Configuration/ConfigurationStore.swift index af9f800413..9505ec9bcb 100644 --- a/DuckDuckGo/Configuration/ConfigurationStore.swift +++ b/DuckDuckGo/Configuration/ConfigurationStore.swift @@ -22,7 +22,6 @@ import Foundation import Configuration import Persistence import PixelKit -import os.log final class ConfigurationStore: ConfigurationStoring { diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift index 5e78fc5f17..9019814fd1 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift @@ -1931,17 +1931,3 @@ struct MockMigrationsProvider: DataBrokerProtectionDatabaseMigrationsProvider { return { _ in } } } - -//struct MockConfigurationFetcher: ConfigurationFetching { -// func fetch(_ configuration: Configuration, isDebug: Bool) async throws { -// return -// } -// -// func fetch(all configurations: [Configuration]) async throws { -// return -// } -//} -// -//final class MockConfigurationManager: DefaultConfigurationManager { -// -//} From 44f440a2cbfd33093b296ca6933c28f0a1c08404 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Thu, 5 Sep 2024 08:36:54 -0500 Subject: [PATCH 23/34] Fix mock --- .../ContentBlocker/Mocks/ContentBlockerRulesManagerMock.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/ContentBlocker/Mocks/ContentBlockerRulesManagerMock.swift b/DuckDuckGo/ContentBlocker/Mocks/ContentBlockerRulesManagerMock.swift index 7df870692b..048a4a1367 100644 --- a/DuckDuckGo/ContentBlocker/Mocks/ContentBlockerRulesManagerMock.swift +++ b/DuckDuckGo/ContentBlocker/Mocks/ContentBlockerRulesManagerMock.swift @@ -23,7 +23,7 @@ import Combine final class ContentBlockerRulesManagerMock: NSObject, ContentBlockerRulesManagerProtocol { func scheduleCompilation() -> BrowserServicesKit.ContentBlockerRulesManager.CompletionToken { - fatalError() + return BrowserServicesKit.ContentBlockerRulesManager.CompletionToken() } var currentMainRules: BrowserServicesKit.ContentBlockerRulesManager.Rules? From 4077d9e57307324c35124724e889ce9a051c47bc Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Thu, 5 Sep 2024 08:57:56 -0500 Subject: [PATCH 24/34] More library linking --- DuckDuckGo.xcodeproj/project.pbxproj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 42aab1a7fb..1eb8500415 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -19,6 +19,8 @@ 02589DA32C88EB5D0093940D /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA22C88EB5D0093940D /* Persistence */; }; 02589DA52C88ED190093940D /* BrowserServicesKit in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA42C88ED190093940D /* BrowserServicesKit */; }; 02589DA72C88F8E90093940D /* PixelKit in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA62C88F8E90093940D /* PixelKit */; }; + 02589DA92C89F0420093940D /* Configuration in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DA82C89F0420093940D /* Configuration */; }; + 02589DAB2C89F0490093940D /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 02589DAA2C89F0490093940D /* Persistence */; }; 026ADE1426C3010C002518EE /* macos-config.json in Resources */ = {isa = PBXBuildFile; fileRef = 026ADE1326C3010C002518EE /* macos-config.json */; }; 028904202A7B25380028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0289041E2A7B23CE0028369C /* AppConfigurationURLProviderTests.swift */; }; @@ -4621,9 +4623,11 @@ files = ( 7B23668A2C09FAE8002D393F /* VPNAppLauncher in Frameworks */, 7B624F172BA25C1F00A6C544 /* NetworkProtectionUI in Frameworks */, + 02589DAB2C89F0490093940D /* Persistence in Frameworks */, 02589DA52C88ED190093940D /* BrowserServicesKit in Frameworks */, 37269F052B3332C2005E8E46 /* Common in Frameworks */, 02589DA72C88F8E90093940D /* PixelKit in Frameworks */, + 02589DA92C89F0420093940D /* Configuration in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9145,6 +9149,8 @@ 7B2366892C09FAE8002D393F /* VPNAppLauncher */, 02589DA42C88ED190093940D /* BrowserServicesKit */, 02589DA62C88F8E90093940D /* PixelKit */, + 02589DA82C89F0420093940D /* Configuration */, + 02589DAA2C89F0490093940D /* Persistence */, ); productName = DuckDuckGoNotifications; productReference = 4B4BEC202A11B4E2001D9AC5 /* DuckDuckGo Notifications.app */; @@ -13919,6 +13925,16 @@ package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = PixelKit; }; + 02589DA82C89F0420093940D /* Configuration */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Configuration; + }; + 02589DAA2C89F0490093940D /* Persistence */ = { + isa = XCSwiftPackageProductDependency; + package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Persistence; + }; 02A15D8B2C88D763001A4237 /* Configuration */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; From 83d5eff1967276f13d95c3666756b09d9475cc15 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Thu, 5 Sep 2024 09:37:15 -0500 Subject: [PATCH 25/34] Fix TODOs --- DuckDuckGo/Configuration/ConfigurationStore.swift | 2 +- .../NetworkProtectionPixelEvent.swift | 9 ++++++++- DuckDuckGo/Statistics/GeneralPixel.swift | 5 +++++ DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift | 3 ++- .../Configuration/DBPPrivacyConfigurationManager.swift | 3 ++- .../Pixels/DataBrokerProtectionPixels.swift | 8 ++++++-- 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo/Configuration/ConfigurationStore.swift b/DuckDuckGo/Configuration/ConfigurationStore.swift index 9505ec9bcb..3c930dc581 100644 --- a/DuckDuckGo/Configuration/ConfigurationStore.swift +++ b/DuckDuckGo/Configuration/ConfigurationStore.swift @@ -176,7 +176,7 @@ final class ConfigurationStore: ConfigurationStoring { } if let coordinatorError { - // TODO: Fire pixel + PixelKit.fire(DebugEvent(GeneralPixel.configurationFileCoordinatorError, error: coordinatorError)) Logger.config.error("Unable to read \(config.rawValue, privacy: .public): \(coordinatorError.localizedDescription, privacy: .public)") } diff --git a/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift b/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift index 0549801f66..b76f79f60b 100644 --- a/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift +++ b/DuckDuckGo/NetworkProtection/AppAndExtensionAndAgentTargets/NetworkProtectionPixelEvent.swift @@ -116,6 +116,7 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { case networkProtectionConfigurationInvalidPayload(configuration: Configuration) case networkProtectionConfigurationErrorLoadingCachedConfig(_ error: Error) case networkProtectionConfigurationPixelTest + case networkProtectionConfigurationFailedToParse(_ error: Error) case networkProtectionUnhandledError(function: String, line: Int, error: Error) @@ -344,6 +345,9 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { case .networkProtectionConfigurationPixelTest: return "netp_ev_configuration_pixel_test" + case .networkProtectionConfigurationFailedToParse: + return "netp_ev_configuration_failed_to_parse" + case .networkProtectionUnhandledError: return "netp_unhandled_error" } @@ -410,6 +414,8 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { return error?.pixelParameters case .networkProtectionConfigurationErrorLoadingCachedConfig(let error): return error.pixelParameters + case .networkProtectionConfigurationFailedToParse(let error): + return error.pixelParameters case .networkProtectionActiveUser, .networkProtectionNewUser, .networkProtectionControllerStartAttempt, @@ -488,7 +494,8 @@ enum NetworkProtectionPixelEvent: PixelKitEventV2 { .networkProtectionUnhandledError(_, _, let error), .networkProtectionSystemExtensionActivationFailure(let error), .networkProtectionServerMigrationFailure(let error), - .networkProtectionConfigurationErrorLoadingCachedConfig(let error): + .networkProtectionConfigurationErrorLoadingCachedConfig(let error), + .networkProtectionConfigurationFailedToParse(let error): return error case .networkProtectionActiveUser, .networkProtectionNewUser, diff --git a/DuckDuckGo/Statistics/GeneralPixel.swift b/DuckDuckGo/Statistics/GeneralPixel.swift index 78b911cb8b..f7c307033a 100644 --- a/DuckDuckGo/Statistics/GeneralPixel.swift +++ b/DuckDuckGo/Statistics/GeneralPixel.swift @@ -275,6 +275,8 @@ enum GeneralPixel: PixelKitEventV2 { case privacyConfigurationReloadFailed case privacyConfigurationCouldNotBeLoaded + case configurationFileCoordinatorError + case fileStoreWriteFailed case fileMoveToDownloadsFailed case fileAccessRelatedItemFailed @@ -805,6 +807,9 @@ enum GeneralPixel: PixelKitEventV2 { case .privacyConfigurationCouldNotBeLoaded: return "pcf_l" + case .configurationFileCoordinatorError: + return "configuration_file_coordinator_error" + case .fileStoreWriteFailed: return "fswf" case .fileMoveToDownloadsFailed: diff --git a/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift b/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift index e910e3a440..54b82e6130 100644 --- a/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift +++ b/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift @@ -20,6 +20,7 @@ import Foundation import BrowserServicesKit import Combine import Common +import PixelKit public final class VPNPrivacyConfigurationManager: PrivacyConfigurationManaging { @@ -92,7 +93,7 @@ public final class VPNPrivacyConfigurationManager: PrivacyConfigurationManaging let configData = try PrivacyConfigurationData(data: data) fetchedConfigData = (data, configData, etag) } catch { - // TODO: Fire failed to parse pixel + PixelKit.fire(NetworkProtectionPixelEvent.networkProtectionConfigurationFailedToParse(error)) fetchedConfigData = nil return .embeddedFallback } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift index ce22b177ca..d0bc1a3735 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift @@ -20,6 +20,7 @@ import Foundation import BrowserServicesKit import Combine import Common +import PixelKit public final class DBPPrivacyConfigurationManager: PrivacyConfigurationManaging { @@ -92,7 +93,7 @@ public final class DBPPrivacyConfigurationManager: PrivacyConfigurationManaging let configData = try PrivacyConfigurationData(data: data) fetchedConfigData = (data, configData, etag) } catch { - // TODO: Fire failed to parse pixel + PixelKit.fire(DebugEvent(DataBrokerProtectionPixels.failedToParsePrivacyConfig(error), error: error)) fetchedConfigData = nil return .embeddedFallback } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift index 9cd02d7d1e..d7b4cbd67c 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift @@ -195,6 +195,7 @@ public enum DataBrokerProtectionPixels { case invalidPayload(Configuration) case errorLoadingCachedConfig(Error) case pixelTest + case failedToParsePrivacyConfig(Error) // Measure success/failure rate of Personal Information Removal Pixels // https://app.asana.com/0/1204006570077678/1206889724879222/f @@ -342,6 +343,7 @@ extension DataBrokerProtectionPixels: PixelKitEvent { case .invalidPayload(let configuration): return "m_mac_dbp_\(configuration.rawValue)_invalid_payload".lowercased() case .errorLoadingCachedConfig: return "m_mac_dbp_configuration_error_loading_cached_config" case .pixelTest: return "m_mac_dbp_configuration_pixel_test" + case .failedToParsePrivacyConfig: return "m_mac_dbp_configuration_failed_to_parse" case .customDataBrokerStatsOptoutSubmit: return "m_mac_dbp_databroker_custom_stats_optoutsubmit" case .customGlobalStatsOptoutSubmit: return "m_mac_dbp_custom_stats_optoutsubmit" @@ -456,7 +458,8 @@ extension DataBrokerProtectionPixels: PixelKitEvent { .gatekeeperNotAuthenticated, .gatekeeperEntitlementsInvalid, .invalidPayload, - .pixelTest: + .pixelTest, + .failedToParsePrivacyConfig: return [:] case .ipcServerProfileSavedCalledByApp, .ipcServerProfileSavedReceivedByAgent, @@ -541,7 +544,8 @@ public class DataBrokerProtectionPixelsHandler: EventMapping Date: Thu, 5 Sep 2024 09:50:11 -0500 Subject: [PATCH 26/34] Compilation error --- DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift index b562578a10..31983f62aa 100644 --- a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift +++ b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift @@ -33,7 +33,8 @@ public class DataBrokerProtectionPixelsHandler: EventMapping Date: Thu, 5 Sep 2024 12:43:58 -0500 Subject: [PATCH 27/34] Make pixel test one time only --- DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift | 4 +++- .../Scheduler/DataBrokerProtectionAgentManager.swift | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift index 962c6844b8..e49a267655 100644 --- a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift +++ b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift @@ -386,8 +386,10 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { setUpSubscriptionMonitoring() - if privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(BackgroundAgentPixelTestSubfeature.pixelTest) { + if privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(BackgroundAgentPixelTestSubfeature.pixelTest) + && !UserDefaults.appConfiguration.bool(forKey: BackgroundAgentPixelTestSubfeature.pixelTest.rawValue) { PixelKit.fire(NetworkProtectionPixelEvent.networkProtectionConfigurationPixelTest) + UserDefaults.appConfiguration.set(true, forKey: BackgroundAgentPixelTestSubfeature.pixelTest.rawValue) } if launchedOnStartup { diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift index 071ae1be4e..3be2fb88e7 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift @@ -166,8 +166,10 @@ public final class DataBrokerProtectionAgentManager { /// While keeping the agent active with invalid entitlement has no significant risk, setting the monitoring interval at 60 minutes is a good balance to minimize backend checks. agentStopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: .minutes(60)) - if DBPPrivacyConfigurationManager.shared.privacyConfig.isSubfeatureEnabled(BackgroundAgentPixelTestSubfeature.pixelTest) { + if DBPPrivacyConfigurationManager.shared.privacyConfig.isSubfeatureEnabled(BackgroundAgentPixelTestSubfeature.pixelTest) + && !UserDefaults.config.bool(forKey: BackgroundAgentPixelTestSubfeature.pixelTest.rawValue) { PixelKit.fire(DataBrokerProtectionPixels.pixelTest) + UserDefaults.config.set(true, forKey: BackgroundAgentPixelTestSubfeature.pixelTest.rawValue) } } } From 06ee6aebb8235e07d838707856b5b4d46ba5ec30 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Thu, 5 Sep 2024 14:04:05 -0500 Subject: [PATCH 28/34] Switch to real config urls --- DuckDuckGoVPN/VPNAgentConfigurationURLProvider.swift | 2 +- .../Configuration/DBPAgentConfigurationURLProvider.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DuckDuckGoVPN/VPNAgentConfigurationURLProvider.swift b/DuckDuckGoVPN/VPNAgentConfigurationURLProvider.swift index bc5f2134a7..0068cf3291 100644 --- a/DuckDuckGoVPN/VPNAgentConfigurationURLProvider.swift +++ b/DuckDuckGoVPN/VPNAgentConfigurationURLProvider.swift @@ -24,6 +24,6 @@ struct VPNAgentConfigurationURLProvider: ConfigurationURLProviding { func url(for configuration: Configuration) -> URL { guard configuration == .privacyConfiguration else { fatalError("\(configuration.rawValue) is not supported on this target") } - return URL(string: "http://localhost:3000/generated/v4/macos-config.json")! + return URL(string: "https://staticcdn.duckduckgo.com/trackerblocking/config/v4/macos-config.json")! } } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPAgentConfigurationURLProvider.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPAgentConfigurationURLProvider.swift index d312569635..78f38b6f05 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPAgentConfigurationURLProvider.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPAgentConfigurationURLProvider.swift @@ -24,6 +24,6 @@ struct DBPAgentConfigurationURLProvider: ConfigurationURLProviding { func url(for configuration: Configuration) -> URL { guard configuration == .privacyConfiguration else { fatalError("\(configuration.rawValue) is not supported on this target") } - return URL(string: "http://localhost:3000/generated/v4/macos-config.json")! + return URL(string: "https://staticcdn.duckduckgo.com/trackerblocking/config/v4/macos-config.json")! } } From 036d3d37f7d5e30a39e759690cbf54b877c65ef4 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Fri, 13 Sep 2024 08:42:51 -0500 Subject: [PATCH 29/34] PR comments --- DuckDuckGo/Application/AppDelegate.swift | 9 +++-- .../Configuration/ConfigurationStore.swift | 10 ------ .../ContentBlocker/ContentBlocking.swift | 13 ++++--- .../ScriptSourceProviding.swift | 2 +- .../RemoteMessagingClient.swift | 2 +- DuckDuckGoVPN/ConfigurationManager.swift | 8 +++-- DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift | 24 +++++++------ .../VPNPrivacyConfigurationManager.swift | 7 ++-- .../Configuration/ConfigurationManager.swift | 8 +++-- .../DBPPrivacyConfigurationManager.swift | 8 +++-- .../DataBrokerRunCustomJSONViewModel.swift | 2 +- .../DataBrokerProtectionAgentManager.swift | 28 +++++++++------ ...ataBrokerProtectionAgentManagerTests.swift | 34 +++++++++++++------ 13 files changed, 91 insertions(+), 64 deletions(-) diff --git a/DuckDuckGo/Application/AppDelegate.swift b/DuckDuckGo/Application/AppDelegate.swift index d950cd9d21..58bab843d3 100644 --- a/DuckDuckGo/Application/AppDelegate.swift +++ b/DuckDuckGo/Application/AppDelegate.swift @@ -98,7 +98,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate { public let vpnSettings = VPNSettings(defaults: .netP) - var configurationManager = ConfigurationManager() + var configurationStore = ConfigurationStore() + var configurationManager: ConfigurationManager // MARK: - VPN @@ -176,6 +177,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate { let internalUserDeciderStore = InternalUserDeciderStore(fileStore: fileStore) internalUserDecider = DefaultInternalUserDecider(store: internalUserDeciderStore) + configurationManager = ConfigurationManager(store: configurationStore) + if NSApplication.runType.requiresEnvironment { Self.configurePixelKit() @@ -223,7 +226,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { #if DEBUG AppPrivacyFeatures.shared = NSApplication.runType.requiresEnvironment // runtime mock-replacement for Unit Tests, to be redone when we‘ll be doing Dependency Injection - ? AppPrivacyFeatures(contentBlocking: AppContentBlocking(internalUserDecider: internalUserDecider), database: Database.shared) + ? AppPrivacyFeatures(contentBlocking: AppContentBlocking(internalUserDecider: internalUserDecider, configurationStore: configurationStore), database: Database.shared) : AppPrivacyFeatures(contentBlocking: ContentBlockingMock(), httpsUpgradeStore: HTTPSUpgradeStoreMock()) #else AppPrivacyFeatures.shared = AppPrivacyFeatures(contentBlocking: AppContentBlocking(internalUserDecider: internalUserDecider), database: Database.shared) @@ -235,7 +238,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { appearancePreferences: .shared, pinnedTabsManager: pinnedTabsManager, internalUserDecider: internalUserDecider, - configurationStore: ConfigurationStore(), + configurationStore: configurationStore, remoteMessagingAvailabilityProvider: PrivacyConfigurationRemoteMessagingAvailabilityProvider( privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager ) diff --git a/DuckDuckGo/Configuration/ConfigurationStore.swift b/DuckDuckGo/Configuration/ConfigurationStore.swift index 3c930dc581..657eff7609 100644 --- a/DuckDuckGo/Configuration/ConfigurationStore.swift +++ b/DuckDuckGo/Configuration/ConfigurationStore.swift @@ -42,7 +42,6 @@ final class ConfigurationStore: ConfigurationStoring { static let configStorageBloomFilterExclusionsEtag = "config.storage.bloomfilter.exclusions.etag" static let configStorageSurrogatesEtag = "config.storage.surrogates.etag" static let configStoragePrivacyConfigurationEtag = "config.storage.privacyconfiguration.etag" - static let configFBConfigEtag = "config.storage.fbconfig.etag" static let configStorageRemoteMessagingConfigEtag = "config.storage.remotemessagingconfig.etag" } @@ -102,15 +101,6 @@ final class ConfigurationStore: ConfigurationStoring { } } - private var FBConfigEtag: String? { - get { - defaults.object(forKey: Constants.configFBConfigEtag) as? String - } - set { - defaults.set(newValue, forKey: Constants.configFBConfigEtag) - } - } - private var remoteMessagingConfigEtag: String? { get { defaults.object(forKey: Constants.configStorageRemoteMessagingConfigEtag) as? String diff --git a/DuckDuckGo/ContentBlocker/ContentBlocking.swift b/DuckDuckGo/ContentBlocker/ContentBlocking.swift index 4413bfd2f3..7a3186b95e 100644 --- a/DuckDuckGo/ContentBlocker/ContentBlocking.swift +++ b/DuckDuckGo/ContentBlocker/ContentBlocking.swift @@ -59,17 +59,16 @@ final class AppContentBlocking { // keeping whole ContentBlocking state initialization in one place to avoid races between updates publishing and rules storing @MainActor - init(internalUserDecider: InternalUserDecider) { - let configStorage = ConfigurationStore() - privacyConfigurationManager = PrivacyConfigurationManager(fetchedETag: configStorage.loadEtag(for: .privacyConfiguration), - fetchedData: configStorage.loadData(for: .privacyConfiguration), + init(internalUserDecider: InternalUserDecider, configurationStore: ConfigurationStore) { + privacyConfigurationManager = PrivacyConfigurationManager(fetchedETag: configurationStore.loadEtag(for: .privacyConfiguration), + fetchedData: configurationStore.loadData(for: .privacyConfiguration), embeddedDataProvider: AppPrivacyConfigurationDataProvider(), localProtection: LocalUnprotectedDomains.shared, errorReporting: Self.debugEvents, internalUserDecider: internalUserDecider) - trackerDataManager = TrackerDataManager(etag: configStorage.loadEtag(for: .trackerDataSet), - data: configStorage.loadData(for: .trackerDataSet), + trackerDataManager = TrackerDataManager(etag: configurationStore.loadEtag(for: .trackerDataSet), + data: configurationStore.loadData(for: .trackerDataSet), embeddedDataProvider: AppTrackerDataSetProvider(), errorReporting: Self.debugEvents) @@ -85,7 +84,7 @@ final class AppContentBlocking { userContentUpdating = UserContentUpdating(contentBlockerRulesManager: contentBlockingManager, privacyConfigurationManager: privacyConfigurationManager, trackerDataManager: trackerDataManager, - configStorage: configStorage, + configStorage: configurationStore, webTrackingProtectionPreferences: WebTrackingProtectionPreferences.shared, tld: tld) diff --git a/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift b/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift index 66a65a040a..4e3cad6696 100644 --- a/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift +++ b/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift @@ -38,7 +38,7 @@ protocol ScriptSourceProviding { // refactor: ScriptSourceProvider to be passed to init methods as `some ScriptSourceProviding`, DefaultScriptSourceProvider to be killed // swiftlint:disable:next identifier_name @MainActor func DefaultScriptSourceProvider() -> ScriptSourceProviding { - ScriptSourceProvider(configStorage: ConfigurationStore(), privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, webTrackingProtectionPreferences: WebTrackingProtectionPreferences.shared, contentBlockingManager: ContentBlocking.shared.contentBlockingManager, trackerDataManager: ContentBlocking.shared.trackerDataManager, tld: ContentBlocking.shared.tld) + ScriptSourceProvider(configStorage: Application.appDelegate.configurationStore, privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, webTrackingProtectionPreferences: WebTrackingProtectionPreferences.shared, contentBlockingManager: ContentBlocking.shared.contentBlockingManager, trackerDataManager: ContentBlocking.shared.trackerDataManager, tld: ContentBlocking.shared.tld) } struct ScriptSourceProvider: ScriptSourceProviding { diff --git a/DuckDuckGo/RemoteMessaging/RemoteMessagingClient.swift b/DuckDuckGo/RemoteMessaging/RemoteMessagingClient.swift index a428829526..753ab02bab 100644 --- a/DuckDuckGo/RemoteMessaging/RemoteMessagingClient.swift +++ b/DuckDuckGo/RemoteMessaging/RemoteMessagingClient.swift @@ -99,7 +99,7 @@ final class RemoteMessagingClient: RemoteMessagingProcessing { urlSession: .session(), eventMapping: ConfigurationManager.configurationDebugEvents ), - configurationStore: ConfigurationStore() + configurationStore: configurationStore ) self.configMatcherProvider = configMatcherProvider self.remoteMessagingAvailabilityProvider = remoteMessagingAvailabilityProvider diff --git a/DuckDuckGoVPN/ConfigurationManager.swift b/DuckDuckGoVPN/ConfigurationManager.swift index 9a143f2041..3aaed77f46 100644 --- a/DuckDuckGoVPN/ConfigurationManager.swift +++ b/DuckDuckGoVPN/ConfigurationManager.swift @@ -27,9 +27,13 @@ import PixelKit final class ConfigurationManager: DefaultConfigurationManager { - override init(fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), + private let privacyConfigManager: VPNPrivacyConfigurationManager + + init(privacyConfigManager: VPNPrivacyConfigurationManager, + fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), store: ConfigurationStoring = ConfigurationStore(), defaults: KeyValueStoring = UserDefaults.appConfiguration) { + self.privacyConfigManager = privacyConfigManager super.init(fetcher: fetcher, store: store, defaults: defaults) } @@ -78,7 +82,7 @@ final class ConfigurationManager: DefaultConfigurationManager { } func updateConfigDependencies() { - VPNPrivacyConfigurationManager.shared.reload( + privacyConfigManager.reload( etag: store.loadEtag(for: .privacyConfiguration), data: store.loadData(for: .privacyConfiguration) ) diff --git a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift index 0f7d680dd5..0d1a02448e 100644 --- a/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift +++ b/DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift @@ -130,7 +130,11 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { private let appLauncher = AppLauncher() private let accountManager: AccountManager private let accessTokenStorage: SubscriptionTokenKeychainStorage - private let configurattionManager = ConfigurationManager() + + private let configurationStore = ConfigurationStore() + private let configurationManager: ConfigurationManager + private let privacyConfigurationManager = VPNPrivacyConfigurationManager() + private var configurationSubscription: AnyCancellable? public init(accountManager: AccountManager, accessTokenStorage: SubscriptionTokenKeychainStorage, @@ -140,6 +144,7 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { self.accessTokenStorage = accessTokenStorage self.tunnelSettings = VPNSettings(defaults: .netP) self.tunnelSettings.alignTo(subscriptionEnvironment: subscriptionEnvironment) + self.configurationManager = ConfigurationManager(privacyConfigManager: privacyConfigurationManager, store: configurationStore) } private var cancellables = Set() @@ -362,11 +367,9 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { // Setup Remote Configuration Configuration.setURLProvider(VPNAgentConfigurationURLProvider()) - configurattionManager.start() - let privacyConfigurationManager = VPNPrivacyConfigurationManager.shared + configurationManager.start() // Load cached config (if any) - let configStore = ConfigurationStore() - privacyConfigurationManager.reload(etag: configStore.loadEtag(for: .privacyConfiguration), data: configStore.loadData(for: .privacyConfiguration)) + privacyConfigurationManager.reload(etag: configurationStore.loadEtag(for: .privacyConfiguration), data: configurationStore.loadData(for: .privacyConfiguration)) setupMenuVisibility() @@ -383,11 +386,12 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate { setUpSubscriptionMonitoring() - if privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(BackgroundAgentPixelTestSubfeature.pixelTest) - && !UserDefaults.appConfiguration.bool(forKey: BackgroundAgentPixelTestSubfeature.pixelTest.rawValue) { - PixelKit.fire(NetworkProtectionPixelEvent.networkProtectionConfigurationPixelTest) - UserDefaults.appConfiguration.set(true, forKey: BackgroundAgentPixelTestSubfeature.pixelTest.rawValue) - } + configurationSubscription = privacyConfigurationManager.updatesPublisher + .sink { [weak self] in + if self?.privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(BackgroundAgentPixelTestSubfeature.pixelTest) ?? false { + PixelKit.fire(NetworkProtectionPixelEvent.networkProtectionConfigurationPixelTest, frequency: .daily) + } + } if launchedOnStartup { Task { diff --git a/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift b/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift index 54b82e6130..e25274db35 100644 --- a/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift +++ b/DuckDuckGoVPN/VPNPrivacyConfigurationManager.swift @@ -24,8 +24,6 @@ import PixelKit public final class VPNPrivacyConfigurationManager: PrivacyConfigurationManaging { - static let shared = VPNPrivacyConfigurationManager() - private let lock = NSLock() var embeddedConfigData: Data { @@ -69,7 +67,10 @@ public final class VPNPrivacyConfigurationManager: PrivacyConfigurationManaging return embeddedConfigData } - public var updatesPublisher: AnyPublisher = .init(Just(())) + private let updatesSubject = PassthroughSubject() + public var updatesPublisher: AnyPublisher { + updatesSubject.eraseToAnyPublisher() + } public var privacyConfig: BrowserServicesKit.PrivacyConfiguration { guard let privacyConfigurationData = try? PrivacyConfigurationData(data: currentConfig) else { diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift index c2c549e7f8..b59b91e0bb 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -31,9 +31,13 @@ public extension Logger { final class ConfigurationManager: DefaultConfigurationManager { - override init(fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), + private let privacyConfigManager: DBPPrivacyConfigurationManager + + init(privacyConfigManager: DBPPrivacyConfigurationManager, + fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), store: ConfigurationStoring = ConfigurationStore(), defaults: KeyValueStoring = UserDefaults.config) { + self.privacyConfigManager = privacyConfigManager super.init(fetcher: fetcher, store: store, defaults: defaults) } @@ -82,7 +86,7 @@ final class ConfigurationManager: DefaultConfigurationManager { } func updateConfigDependencies() { - DBPPrivacyConfigurationManager.shared.reload( + privacyConfigManager.reload( etag: store.loadEtag(for: .privacyConfiguration), data: store.loadData(for: .privacyConfiguration) ) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift index d0bc1a3735..10a734da85 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/DBPPrivacyConfigurationManager.swift @@ -24,8 +24,6 @@ import PixelKit public final class DBPPrivacyConfigurationManager: PrivacyConfigurationManaging { - static let shared = DBPPrivacyConfigurationManager() - private let lock = NSLock() var embeddedConfigData: Data { @@ -69,7 +67,10 @@ public final class DBPPrivacyConfigurationManager: PrivacyConfigurationManaging return embeddedConfigData } - public var updatesPublisher: AnyPublisher = .init(Just(())) + private let updatesSubject = PassthroughSubject() + public var updatesPublisher: AnyPublisher { + updatesSubject.eraseToAnyPublisher() + } public var privacyConfig: BrowserServicesKit.PrivacyConfiguration { guard let privacyConfigurationData = try? PrivacyConfigurationData(data: currentConfig) else { @@ -92,6 +93,7 @@ public final class DBPPrivacyConfigurationManager: PrivacyConfigurationManaging do { let configData = try PrivacyConfigurationData(data: data) fetchedConfigData = (data, configData, etag) + updatesSubject.send(()) } catch { PixelKit.fire(DebugEvent(DataBrokerProtectionPixels.failedToParsePrivacyConfig(error), error: error)) fetchedConfigData = nil diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift index 523857756c..b2e8656204 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/DebugUI/DataBrokerRunCustomJSONViewModel.swift @@ -155,7 +155,7 @@ final class DataBrokerRunCustomJSONViewModel: ObservableObject { private let authenticationManager: DataBrokerProtectionAuthenticationManaging init(authenticationManager: DataBrokerProtectionAuthenticationManaging) { - let privacyConfigurationManager = DBPPrivacyConfigurationManager.shared + let privacyConfigurationManager = DBPPrivacyConfigurationManager() let features = ContentScopeFeatureToggles(emailProtection: false, emailProtectionIncontextSignup: false, credentialsAutofill: false, diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift index 3be2fb88e7..22df56a835 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Scheduler/DataBrokerProtectionAgentManager.swift @@ -17,6 +17,7 @@ // import Foundation +import Combine import Common import BrowserServicesKit import Configuration @@ -34,11 +35,11 @@ public class DataBrokerProtectionAgentManagerProvider { let notificationService = DefaultDataBrokerProtectionUserNotificationService(pixelHandler: pixelHandler) Configuration.setURLProvider(DBPAgentConfigurationURLProvider()) - let configurationManager = ConfigurationManager() + let configStore = ConfigurationStore() + let privacyConfigurationManager = DBPPrivacyConfigurationManager() + let configurationManager = ConfigurationManager(privacyConfigManager: privacyConfigurationManager, store: configStore) configurationManager.start() - let privacyConfigurationManager = DBPPrivacyConfigurationManager.shared // Load cached config (if any) - let configStore = ConfigurationStore() privacyConfigurationManager.reload(etag: configStore.loadEtag(for: .privacyConfiguration), data: configStore.loadData(for: .privacyConfiguration)) let ipcServer = DefaultDataBrokerProtectionIPCServer(machServiceName: Bundle.main.bundleIdentifier!) @@ -103,7 +104,8 @@ public class DataBrokerProtectionAgentManagerProvider { operationDependencies: operationDependencies, pixelHandler: pixelHandler, agentStopper: agentstopper, - configurationManager: configurationManager) + configurationManager: configurationManager, + privacyConfigurationManager: privacyConfigurationManager) } } @@ -118,12 +120,15 @@ public final class DataBrokerProtectionAgentManager { private let pixelHandler: EventMapping private let agentStopper: DataBrokerProtectionAgentStopper private let configurationManger: DefaultConfigurationManager + private let privacyConfigurationManager: DBPPrivacyConfigurationManager // Used for debug functions only, so not injected private lazy var browserWindowManager = BrowserWindowManager() private var didStartActivityScheduler = false + private var configurationSubscription: AnyCancellable? + init(userNotificationService: DataBrokerProtectionUserNotificationService, activityScheduler: DataBrokerProtectionBackgroundActivityScheduler, ipcServer: DataBrokerProtectionIPCServer, @@ -132,7 +137,8 @@ public final class DataBrokerProtectionAgentManager { operationDependencies: DataBrokerOperationDependencies, pixelHandler: EventMapping, agentStopper: DataBrokerProtectionAgentStopper, - configurationManager: DefaultConfigurationManager + configurationManager: DefaultConfigurationManager, + privacyConfigurationManager: DBPPrivacyConfigurationManager ) { self.userNotificationService = userNotificationService self.activityScheduler = activityScheduler @@ -143,6 +149,7 @@ public final class DataBrokerProtectionAgentManager { self.pixelHandler = pixelHandler self.agentStopper = agentStopper self.configurationManger = configurationManager + self.privacyConfigurationManager = privacyConfigurationManager self.activityScheduler.delegate = self self.ipcServer.serverDelegate = self @@ -166,11 +173,12 @@ public final class DataBrokerProtectionAgentManager { /// While keeping the agent active with invalid entitlement has no significant risk, setting the monitoring interval at 60 minutes is a good balance to minimize backend checks. agentStopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: .minutes(60)) - if DBPPrivacyConfigurationManager.shared.privacyConfig.isSubfeatureEnabled(BackgroundAgentPixelTestSubfeature.pixelTest) - && !UserDefaults.config.bool(forKey: BackgroundAgentPixelTestSubfeature.pixelTest.rawValue) { - PixelKit.fire(DataBrokerProtectionPixels.pixelTest) - UserDefaults.config.set(true, forKey: BackgroundAgentPixelTestSubfeature.pixelTest.rawValue) - } + configurationSubscription = privacyConfigurationManager.updatesPublisher + .sink { [weak self] _ in + if self?.privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(BackgroundAgentPixelTestSubfeature.pixelTest) ?? false { + PixelKit.fire(DataBrokerProtectionPixels.pixelTest, frequency: .daily) + } + } } } } diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift index 40f037538d..7b51ec03af 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift @@ -35,6 +35,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { private var mockProfile: DataBrokerProtectionProfile! private var mockAgentStopper: MockAgentStopper! private var mockConfigurationManager: MockConfigurationManager! + private var mockPrivacyConfigurationManager: DBPPrivacyConfigurationManager! override func setUpWithError() throws { @@ -83,7 +84,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockDataManager.profileToReturn = mockProfile @@ -127,7 +129,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: agentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockDataManager.profileToReturn = nil @@ -160,7 +163,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockDataManager.profileToReturn = nil @@ -198,7 +202,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockDataManager.profileToReturn = mockProfile @@ -225,7 +230,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockDataManager.profileToReturn = mockProfile @@ -252,7 +258,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockNotificationService.reset() @@ -274,7 +281,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockNotificationService.reset() @@ -296,7 +304,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockNotificationService.reset() mockQueueManager.startImmediateOperationsIfPermittedCompletionError = DataBrokerProtectionAgentErrorCollection(oneTimeError: NSError(domain: "test", code: 10)) @@ -319,7 +328,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockNotificationService.reset() mockDataManager.shouldReturnHasMatches = true @@ -342,7 +352,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) mockNotificationService.reset() mockDataManager.shouldReturnHasMatches = false @@ -365,7 +376,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { operationDependencies: mockDependencies, pixelHandler: mockPixelHandler, agentStopper: mockAgentStopper, - configurationManager: mockConfigurationManager) + configurationManager: mockConfigurationManager, + privacyConfigurationManager: mockPrivacyConfigurationManager) var startScheduledScansCalled = false mockQueueManager.startScheduledOperationsIfPermittedCalledCompletion = { _ in From c2d91e8e1ceb15cd73d7cf4c70da3b09a27bb9cf Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Fri, 13 Sep 2024 08:46:28 -0500 Subject: [PATCH 30/34] lint --- DuckDuckGoVPN/ConfigurationManager.swift | 6 +++--- .../Configuration/ConfigurationManager.swift | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGoVPN/ConfigurationManager.swift b/DuckDuckGoVPN/ConfigurationManager.swift index 3aaed77f46..3dcbea0b00 100644 --- a/DuckDuckGoVPN/ConfigurationManager.swift +++ b/DuckDuckGoVPN/ConfigurationManager.swift @@ -30,9 +30,9 @@ final class ConfigurationManager: DefaultConfigurationManager { private let privacyConfigManager: VPNPrivacyConfigurationManager init(privacyConfigManager: VPNPrivacyConfigurationManager, - fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), - store: ConfigurationStoring = ConfigurationStore(), - defaults: KeyValueStoring = UserDefaults.appConfiguration) { + fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), + store: ConfigurationStoring = ConfigurationStore(), + defaults: KeyValueStoring = UserDefaults.appConfiguration) { self.privacyConfigManager = privacyConfigManager super.init(fetcher: fetcher, store: store, defaults: defaults) } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift index b59b91e0bb..380619c261 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Configuration/ConfigurationManager.swift @@ -34,9 +34,9 @@ final class ConfigurationManager: DefaultConfigurationManager { private let privacyConfigManager: DBPPrivacyConfigurationManager init(privacyConfigManager: DBPPrivacyConfigurationManager, - fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), - store: ConfigurationStoring = ConfigurationStore(), - defaults: KeyValueStoring = UserDefaults.config) { + fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents), + store: ConfigurationStoring = ConfigurationStore(), + defaults: KeyValueStoring = UserDefaults.config) { self.privacyConfigManager = privacyConfigManager super.init(fetcher: fetcher, store: store, defaults: defaults) } From 0265f30cb7c1d8c852c12ca556aac463f4a68c90 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Fri, 13 Sep 2024 09:37:05 -0500 Subject: [PATCH 31/34] Fix tests --- .../DataBrokerProtectionAgentManagerTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift index 7b51ec03af..06bcbb9eed 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift @@ -44,6 +44,7 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { mockNotificationService = MockUserNotificationService() mockAgentStopper = MockAgentStopper() mockConfigurationManager = MockConfigurationManager() + mockPrivacyConfigurationManager = DBPPrivacyConfigurationManager() let mockDatabase = MockDatabase() let mockMismatchCalculator = MockMismatchCalculator(database: mockDatabase, pixelHandler: mockPixelHandler) From 054335f2b6bf459b0daec816baf7348a2a505a43 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Fri, 13 Sep 2024 10:02:33 -0500 Subject: [PATCH 32/34] Fix for release build --- DuckDuckGo/Application/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Application/AppDelegate.swift b/DuckDuckGo/Application/AppDelegate.swift index 58bab843d3..6431b1ca29 100644 --- a/DuckDuckGo/Application/AppDelegate.swift +++ b/DuckDuckGo/Application/AppDelegate.swift @@ -229,7 +229,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { ? AppPrivacyFeatures(contentBlocking: AppContentBlocking(internalUserDecider: internalUserDecider, configurationStore: configurationStore), database: Database.shared) : AppPrivacyFeatures(contentBlocking: ContentBlockingMock(), httpsUpgradeStore: HTTPSUpgradeStoreMock()) #else - AppPrivacyFeatures.shared = AppPrivacyFeatures(contentBlocking: AppContentBlocking(internalUserDecider: internalUserDecider), database: Database.shared) + AppPrivacyFeatures.shared = AppPrivacyFeatures(contentBlocking: AppContentBlocking(internalUserDecider: internalUserDecider, configurationStore: configurationStore), database: Database.shared) #endif if NSApplication.runType.requiresEnvironment { remoteMessagingClient = RemoteMessagingClient( From 8103be09f015651bbf8e35314e69ccbbea34cbe8 Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Mon, 16 Sep 2024 09:35:35 -0500 Subject: [PATCH 33/34] Update BSK --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- LocalPackages/DataBrokerProtection/Package.swift | 2 +- .../DataBrokerProtectionAgentManagerTests.swift | 2 +- LocalPackages/NetworkProtectionMac/Package.swift | 2 +- LocalPackages/SubscriptionUI/Package.swift | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 94cb215193..1bec42df1c 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -14226,7 +14226,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = revision; - revision = 3d6f83e973f511fcabb75bf5a16e1f9cda252c17; + revision = 9658daf6fae3d4370d07145e89a8b97e3f49f471; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 48fa227982..a8ca3d4da4 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,7 +32,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "3d6f83e973f511fcabb75bf5a16e1f9cda252c17" + "revision" : "9658daf6fae3d4370d07145e89a8b97e3f49f471" } }, { diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index d0f1579877..7a99c86ba2 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -29,7 +29,7 @@ let package = Package( targets: ["DataBrokerProtection"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "3d6f83e973f511fcabb75bf5a16e1f9cda252c17"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "9658daf6fae3d4370d07145e89a8b97e3f49f471"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper"), ], diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift index 06bcbb9eed..d0ccaec883 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift @@ -434,6 +434,6 @@ final class MockConfigurationManager: DefaultConfigurationManager { override init(fetcher: ConfigurationFetching = MockConfigurationFetcher(), store: ConfigurationStoring = MockConfigurationStore(), defaults: KeyValueStoring = UserDefaults()) { - super.init(fetcher: fetcher, store: store) + super.init(fetcher: fetcher, store: store, defaults: defaults) } } diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index 25201b8f8b..c3b5461c8c 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -32,7 +32,7 @@ let package = Package( .library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "3d6f83e973f511fcabb75bf5a16e1f9cda252c17"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "9658daf6fae3d4370d07145e89a8b97e3f49f471"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"), .package(path: "../AppLauncher"), .package(path: "../UDSHelper"), diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index fcf0444444..42f1a02bde 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "3d6f83e973f511fcabb75bf5a16e1f9cda252c17"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "9658daf6fae3d4370d07145e89a8b97e3f49f471"), .package(path: "../SwiftUIExtensions") ], targets: [ From 163a815e2961716ae9370baa04510047f484c4ae Mon Sep 17 00:00:00 2001 From: Brad Slayter Date: Tue, 17 Sep 2024 08:41:44 -0500 Subject: [PATCH 34/34] PR nits --- DuckDuckGo.xcodeproj/project.pbxproj | 4 +-- .../xcshareddata/swiftpm/Package.resolved | 3 +- .../Configuration/ConfigurationStore.swift | 30 +++++++++---------- .../ContentBlockerRulesManagerMock.swift | 2 +- .../DataBrokerProtection/Package.swift | 2 +- .../NetworkProtectionMac/Package.swift | 2 +- LocalPackages/SubscriptionUI/Package.swift | 2 +- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 1bec42df1c..1035013e92 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -14225,8 +14225,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { - kind = revision; - revision = 9658daf6fae3d4370d07145e89a8b97e3f49f471; + kind = exactVersion; + version = 196.1.0; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a8ca3d4da4..9df44c6b18 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,7 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "9658daf6fae3d4370d07145e89a8b97e3f49f471" + "revision" : "f7083a3c74a4aa1f6a0f4ab65265eb2f422a2cf0", + "version" : "196.1.0" } }, { diff --git a/DuckDuckGo/Configuration/ConfigurationStore.swift b/DuckDuckGo/Configuration/ConfigurationStore.swift index 657eff7609..404daa77ad 100644 --- a/DuckDuckGo/Configuration/ConfigurationStore.swift +++ b/DuckDuckGo/Configuration/ConfigurationStore.swift @@ -35,7 +35,7 @@ final class ConfigurationStore: ConfigurationStoring { .remoteMessagingConfig: "remote-messaging-config.json" ] - private enum Constants { + private enum Etag { static let configStorageTrackerRadarEtag = "config.storage.trackerradar.etag" static let configStorageBloomFilterSpecEtag = "config.storage.bloomfilter.spec.etag" static let configStorageBloomFilterBinaryEtag = "config.storage.bloomfilter.binary.etag" @@ -49,64 +49,64 @@ final class ConfigurationStore: ConfigurationStoring { private var trackerRadarEtag: String? { get { - defaults.object(forKey: Constants.configStorageTrackerRadarEtag) as? String + defaults.object(forKey: Etag.configStorageTrackerRadarEtag) as? String } set { - defaults.set(newValue, forKey: Constants.configStorageTrackerRadarEtag) + defaults.set(newValue, forKey: Etag.configStorageTrackerRadarEtag) } } private var bloomFilterSpecEtag: String? { get { - defaults.object(forKey: Constants.configStorageBloomFilterSpecEtag) as? String + defaults.object(forKey: Etag.configStorageBloomFilterSpecEtag) as? String } set { - defaults.set(newValue, forKey: Constants.configStorageBloomFilterSpecEtag) + defaults.set(newValue, forKey: Etag.configStorageBloomFilterSpecEtag) } } private var bloomFilterBinaryEtag: String? { get { - defaults.object(forKey: Constants.configStorageBloomFilterBinaryEtag) as? String + defaults.object(forKey: Etag.configStorageBloomFilterBinaryEtag) as? String } set { - defaults.set(newValue, forKey: Constants.configStorageBloomFilterBinaryEtag) + defaults.set(newValue, forKey: Etag.configStorageBloomFilterBinaryEtag) } } private var bloomFilterExcludedDomainsEtag: String? { get { - defaults.object(forKey: Constants.configStorageBloomFilterExclusionsEtag) as? String + defaults.object(forKey: Etag.configStorageBloomFilterExclusionsEtag) as? String } set { - defaults.set(newValue, forKey: Constants.configStorageBloomFilterExclusionsEtag) + defaults.set(newValue, forKey: Etag.configStorageBloomFilterExclusionsEtag) } } private var surrogatesEtag: String? { get { - defaults.object(forKey: Constants.configStorageSurrogatesEtag) as? String + defaults.object(forKey: Etag.configStorageSurrogatesEtag) as? String } set { - defaults.set(newValue, forKey: Constants.configStorageSurrogatesEtag) + defaults.set(newValue, forKey: Etag.configStorageSurrogatesEtag) } } private var privacyConfigurationEtag: String? { get { - defaults.object(forKey: Constants.configStoragePrivacyConfigurationEtag) as? String + defaults.object(forKey: Etag.configStoragePrivacyConfigurationEtag) as? String } set { - defaults.set(newValue, forKey: Constants.configStoragePrivacyConfigurationEtag) + defaults.set(newValue, forKey: Etag.configStoragePrivacyConfigurationEtag) } } private var remoteMessagingConfigEtag: String? { get { - defaults.object(forKey: Constants.configStorageRemoteMessagingConfigEtag) as? String + defaults.object(forKey: Etag.configStorageRemoteMessagingConfigEtag) as? String } set { - defaults.set(newValue, forKey: Constants.configStorageRemoteMessagingConfigEtag) + defaults.set(newValue, forKey: Etag.configStorageRemoteMessagingConfigEtag) } } diff --git a/DuckDuckGo/ContentBlocker/Mocks/ContentBlockerRulesManagerMock.swift b/DuckDuckGo/ContentBlocker/Mocks/ContentBlockerRulesManagerMock.swift index 048a4a1367..60a95541ea 100644 --- a/DuckDuckGo/ContentBlocker/Mocks/ContentBlockerRulesManagerMock.swift +++ b/DuckDuckGo/ContentBlocker/Mocks/ContentBlockerRulesManagerMock.swift @@ -23,7 +23,7 @@ import Combine final class ContentBlockerRulesManagerMock: NSObject, ContentBlockerRulesManagerProtocol { func scheduleCompilation() -> BrowserServicesKit.ContentBlockerRulesManager.CompletionToken { - return BrowserServicesKit.ContentBlockerRulesManager.CompletionToken() + BrowserServicesKit.ContentBlockerRulesManager.CompletionToken() } var currentMainRules: BrowserServicesKit.ContentBlockerRulesManager.Rules? diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index 7a99c86ba2..7323dce45a 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -29,7 +29,7 @@ let package = Package( targets: ["DataBrokerProtection"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "9658daf6fae3d4370d07145e89a8b97e3f49f471"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "196.1.0"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper"), ], diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index c3b5461c8c..d9ab035a2d 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -32,7 +32,7 @@ let package = Package( .library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "9658daf6fae3d4370d07145e89a8b97e3f49f471"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "196.1.0"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"), .package(path: "../AppLauncher"), .package(path: "../UDSHelper"), diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index 42f1a02bde..16a305fb81 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", revision: "9658daf6fae3d4370d07145e89a8b97e3f49f471"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "196.1.0"), .package(path: "../SwiftUIExtensions") ], targets: [