Skip to content

Commit

Permalink
Disables domain exclusions in App Store builds (#3745)
Browse files Browse the repository at this point in the history
Task/Issue URL:
https://app.asana.com/0/1207603085593419/1209168347582143/f

## Description

We're temporarily disabling domain exclusions from App Store builds.

We'll migrate this to a remote feature flag next week, but doing that
later as it requires 4 PRs (vs 1 for this approach).
  • Loading branch information
diegoreymendez authored Jan 17, 2025
1 parent f9c494c commit 7bbf088
Show file tree
Hide file tree
Showing 15 changed files with 170 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager {
activeSitePublisher: activeSitePublisher,
forMenuApp: false,
vpnSettings: vpnSettings,
proxySettings: proxySettings,
logger: Logger(subsystem: "DuckDuckGo", category: "TipKit"))

let popover = NetworkProtectionPopover(
Expand Down
10 changes: 7 additions & 3 deletions DuckDuckGo/Preferences/Model/VPNPreferencesModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Combine
import Foundation
import NetworkProtection
import NetworkProtectionIPC
import NetworkProtectionProxy
import NetworkProtectionUI
import BrowserServicesKit

Expand Down Expand Up @@ -77,8 +78,9 @@ final class VPNPreferencesModel: ObservableObject {
///
/// Only necessary because this is feature flagged to internal users.
///
@Published
var showExcludedSites: Bool
var showExcludedSites: Bool {
proxySettings.proxyAvailable
}

@Published var notifyStatusChanges: Bool {
didSet {
Expand All @@ -100,16 +102,19 @@ final class VPNPreferencesModel: ObservableObject {

private let vpnXPCClient: VPNControllerXPCClient
private let settings: VPNSettings
private let proxySettings: TransparentProxySettings
private let pinningManager: PinningManager
private var cancellables = Set<AnyCancellable>()

init(vpnXPCClient: VPNControllerXPCClient = .shared,
settings: VPNSettings = .init(defaults: .netP),
proxySettings: TransparentProxySettings = .init(defaults: .netP),
pinningManager: PinningManager = LocalPinningManager.shared,
defaults: UserDefaults = .netP) {

self.vpnXPCClient = vpnXPCClient
self.settings = settings
self.proxySettings = proxySettings
self.pinningManager = pinningManager

connectOnLogin = settings.connectOnLogin
Expand All @@ -118,7 +123,6 @@ final class VPNPreferencesModel: ObservableObject {
showInMenuBar = settings.showInMenuBar
showInBrowserToolbar = pinningManager.isPinned(.networkProtection)
showUninstallVPN = defaults.networkProtectionOnboardingStatus != .default
showExcludedSites = true
onboardingStatus = defaults.networkProtectionOnboardingStatus
locationItem = VPNLocationPreferenceItemModel(selectedLocation: settings.selectedLocation)

Expand Down
12 changes: 11 additions & 1 deletion DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,17 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate {

private let tunnelSettings: VPNSettings
private lazy var userDefaults = UserDefaults.netP
private lazy var proxySettings = TransparentProxySettings(defaults: .netP)
private lazy var proxySettings: TransparentProxySettings = {
let settings = TransparentProxySettings(defaults: .netP)

#if APPSTORE
settings.proxyAvailable = false
#else
settings.proxyAvailable = true
#endif

return settings
}()

@MainActor
private lazy var vpnProxyLauncher = VPNProxyLauncher(
Expand Down
1 change: 1 addition & 0 deletions LocalPackages/NetworkProtectionMac/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ let package = Package(
name: "NetworkProtectionUI",
dependencies: [
"VPNPixels",
"NetworkProtectionProxy",
.product(name: "NetworkProtection", package: "BrowserServicesKit"),
.product(name: "PixelKit", package: "BrowserServicesKit"),
.product(name: "SwiftUIExtensions", package: "SwiftUIExtensions"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@ public final class TransparentProxyController {
// MARK: - Start & stop the proxy

public var isRequiredForActiveFeatures: Bool {
settings.appRoutingRules.count > 0 || settings.excludedDomains.count > 0
settings.proxyAvailable
&& (settings.appRoutingRules.count > 0 || settings.excludedDomains.count > 0)
}

public func start() async throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ final class TransparentProxyAppMessageHandler {
settings.appRoutingRules = routingRules
case .excludedDomains(let excludedDomains):
settings.excludedDomains = excludedDomains
case .proxyAvailable(let available):
settings.proxyAvailable = available
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ open class TransparentProxyProvider: NETransparentProxyProvider {
Task {
try await self.updateNetworkSettings()
}
case .proxyAvailable:
// no-op, handled by app
break
}
}.store(in: &cancellables)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public final class TransparentProxySettings {
public enum Change: Codable {
case appRoutingRules(_ routingRules: VPNAppRoutingRules)
case excludedDomains(_ excludedDomains: [String])
case proxyAvailable(_ available: Bool)
}

let defaults: UserDefaults
Expand All @@ -35,6 +36,12 @@ public final class TransparentProxySettings {
.map { routingRules in
Change.appRoutingRules(routingRules)
}.eraseToAnyPublisher(),
defaults.vpnProxyFeatureAvailablePublisher
.dropFirst()
.removeDuplicates()
.map { available in
Change.proxyAvailable(available)
}.eraseToAnyPublisher(),
defaults.vpnProxyExcludedDomainsPublisher
.dropFirst()
.removeDuplicates()
Expand Down Expand Up @@ -70,6 +77,16 @@ public final class TransparentProxySettings {
}
}

public var proxyAvailable: Bool {
get {
defaults.vpnProxyFeatureAvailable
}

set {
defaults.vpnProxyFeatureAvailable = newValue
}
}

// MARK: - Reset to factory defaults

public func resetAll() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// UserDefaults+vpnProxyFeatureAvailable.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 Combine
import Foundation

extension UserDefaults {
@objc
dynamic var vpnProxyFeatureAvailable: Bool {
get {
bool(forKey: #keyPath(vpnProxyFeatureAvailable))
}

set {
set(newValue, forKey: #keyPath(vpnProxyFeatureAvailable))
}
}

var vpnProxyFeatureAvailablePublisher: AnyPublisher<Bool, Never> {
publisher(for: \.vpnProxyFeatureAvailable).eraseToAnyPublisher()
}

func resetVPNProxyFeatureAvailable() {
removeObject(forKey: #keyPath(vpnProxyFeatureAvailable))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Combine
import Common
import LoginItems
import NetworkProtection
import NetworkProtectionProxy
import os.log
import SwiftUI

Expand Down Expand Up @@ -159,6 +160,7 @@ public final class StatusBarMenu: NSObject {
activeSitePublisher: activeSitePublisher,
forMenuApp: true,
vpnSettings: VPNSettings(defaults: userDefaults),
proxySettings: TransparentProxySettings(defaults: userDefaults),
logger: Logger(subsystem: "DuckDuckGo", category: "TipKit"))

let debugInformationViewModel = DebugInformationViewModel(showDebugInformation: isOptionKeyPressed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ extension NetworkProtectionStatusView {
onboardingStatusPublisher: onboardingStatusPublisher,
statusReporter: statusReporter,
vpnSettings: .init(defaults: userDefaults),
proxySettings: .init(defaults: userDefaults),
locationFormatter: locationFormatter,
uiActionHandler: uiActionHandler)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import AppKit
import Combine
import Common
import NetworkProtection
import NetworkProtectionProxy
import os.log
import TipKit
import PixelKit
Expand Down Expand Up @@ -57,6 +58,7 @@ public final class VPNTipsModel: ObservableObject {

private let isMenuApp: Bool
private let vpnSettings: VPNSettings
private let proxySettings: TransparentProxySettings
private let logger: Logger
private var cancellables = Set<AnyCancellable>()

Expand All @@ -65,6 +67,7 @@ public final class VPNTipsModel: ObservableObject {
activeSitePublisher: CurrentValuePublisher<ActiveSiteInfo?, Never>,
forMenuApp isMenuApp: Bool,
vpnSettings: VPNSettings,
proxySettings: TransparentProxySettings,
logger: Logger) {

self.activeSiteInfo = activeSitePublisher.value
Expand All @@ -73,6 +76,7 @@ public final class VPNTipsModel: ObservableObject {
self.isMenuApp = isMenuApp
self.logger = logger
self.vpnSettings = vpnSettings
self.proxySettings = proxySettings

guard !isMenuApp else {
return
Expand Down Expand Up @@ -135,6 +139,44 @@ public final class VPNTipsModel: ObservableObject {

var geoswitchingStatusUpdateTask: Task<Void, Never>?

@available(macOS 14.0, *)
var canShowDomainExclusionsTip: Bool {
guard canShowTips else {
return false
}

// If the proxy is available, we can show this tip after the geoswitchin tip
// Otherwise we can't show this tip
if proxySettings.proxyAvailable,
case .invalidated = geoswitchingTip.status {

return true
}

return false
}

@available(macOS 14.0, *)
var canShowAutoconnectTip: Bool {
guard canShowTips else {
return false
}

// If the proxy is available, we need to wait until the domain exclusions tip was shown.
// If the proxy is not available, we can show this tip after the geoswitchin tip
if proxySettings.proxyAvailable,
case .invalidated = domainExclusionsTip.status {

return true
} else if !proxySettings.proxyAvailable,
case .invalidated = geoswitchingTip.status {

return true
}

return false
}

// MARK: - Tip Action handling

@available(macOS 14.0, *)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ public struct TunnelControllerView: View {
featureToggleRow()

if #available(macOS 14.0, *),
tipsModel.canShowTips,
case .invalidated = tipsModel.domainExclusionsTip.status {
tipsModel.canShowAutoconnectTip {

TipView(tipsModel.autoconnectTip, action: tipsModel.autoconnectTipActionHandler)
.tipImageSize(VPNTipsModel.imageSize)
Expand All @@ -82,34 +81,35 @@ public struct TunnelControllerView: View {
}
}

SiteTroubleshootingView()
.padding(.top, 5)
if model.exclusionsFeatureEnabled {
SiteTroubleshootingView()
.padding(.top, 5)

if #available(macOS 14.0, *),
tipsModel.canShowTips,
case .invalidated = tipsModel.geoswitchingTip.status {

TipView(tipsModel.domainExclusionsTip)
.tipImageSize(VPNTipsModel.imageSize)
.tipBackground(Color(.tipBackground))
.padding(.horizontal, 9)
.padding(.vertical, 6)
.onAppear {
tipsModel.handleDomainExclusionsTipShown()
}
.task {
var previousStatus = tipsModel.domainExclusionsTip.status
if #available(macOS 14.0, *),
tipsModel.canShowDomainExclusionsTip {

for await status in tipsModel.domainExclusionsTip.statusUpdates {
if case .invalidated(let reason) = status {
if case .available = previousStatus {
tipsModel.handleDomainExclusionTipInvalidated(reason)
TipView(tipsModel.domainExclusionsTip)
.tipImageSize(VPNTipsModel.imageSize)
.tipBackground(Color(.tipBackground))
.padding(.horizontal, 9)
.padding(.vertical, 6)
.onAppear {
tipsModel.handleDomainExclusionsTipShown()
}
.task {
var previousStatus = tipsModel.domainExclusionsTip.status

for await status in tipsModel.domainExclusionsTip.statusUpdates {
if case .invalidated(let reason) = status {
if case .available = previousStatus {
tipsModel.handleDomainExclusionTipInvalidated(reason)
}
}
}

previousStatus = status
previousStatus = status
}
}
}
}
}

Divider()
Expand Down
Loading

0 comments on commit 7bbf088

Please sign in to comment.