Purge stale NetworkExtension/keychain data (#728)
- [x] NE managers were not deleted when unable to be decoded to a profile - [x] Keychain items were not deleted on profile removal - [x] Perform clean-up on app launch - [x] Perform clean-up on app active Prematurely merged as #727 then reverted, this is the complete PR.
This commit is contained in:
parent
1aa393ee02
commit
f2a141a189
|
@ -41,7 +41,7 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
|
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "de9387965025a1f79e060e6bdc862933fe972598"
|
"revision" : "bec0635fe047e09c8b6c894d103ab8dd741b8340"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,6 +39,9 @@ struct PassepartoutApp: App {
|
||||||
private var appDelegate: AppDelegate
|
private var appDelegate: AppDelegate
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@Environment(\.scenePhase)
|
||||||
|
private var scenePhase
|
||||||
|
|
||||||
private let context: AppContext = .shared
|
private let context: AppContext = .shared
|
||||||
// private let context: AppContext = .mock(withRegistry: .shared)
|
// private let context: AppContext = .mock(withRegistry: .shared)
|
||||||
|
|
||||||
|
@ -47,23 +50,25 @@ struct PassepartoutApp: App {
|
||||||
@StateObject
|
@StateObject
|
||||||
private var theme = Theme()
|
private var theme = Theme()
|
||||||
|
|
||||||
var body: some Scene {
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
WindowGroup(content: content)
|
var body: some Scene {
|
||||||
|
WindowGroup(content: contentView)
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
Window(appName, id: appName, content: content)
|
var body: some Scene {
|
||||||
|
Window(appName, id: appName, content: contentView)
|
||||||
.defaultSize(width: 600.0, height: 400.0)
|
.defaultSize(width: 600.0, height: 400.0)
|
||||||
|
|
||||||
Settings {
|
Settings {
|
||||||
SettingsView(profileManager: context.profileManager)
|
SettingsView(profileManager: context.profileManager)
|
||||||
.frame(minWidth: 300, minHeight: 200)
|
.frame(minWidth: 300, minHeight: 200)
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension PassepartoutApp {
|
private extension PassepartoutApp {
|
||||||
func content() -> some View {
|
func contentView() -> some View {
|
||||||
AppCoordinator(
|
AppCoordinator(
|
||||||
profileManager: context.profileManager,
|
profileManager: context.profileManager,
|
||||||
tunnel: context.tunnel,
|
tunnel: context.tunnel,
|
||||||
|
@ -77,6 +82,22 @@ private extension PassepartoutApp {
|
||||||
)
|
)
|
||||||
AppUI.configure(with: context)
|
AppUI.configure(with: context)
|
||||||
}
|
}
|
||||||
|
.onChange(of: scenePhase) {
|
||||||
|
switch $0 {
|
||||||
|
case .active:
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
pp_log(.app, .notice, "Prepare tunnel and purge stale data")
|
||||||
|
try await context.tunnel.prepare(purge: true)
|
||||||
|
} catch {
|
||||||
|
pp_log(.app, .fault, "Unable to prepare tunnel: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
.themeLockScreen()
|
.themeLockScreen()
|
||||||
.withEnvironment(from: context, theme: theme)
|
.withEnvironment(from: context, theme: theme)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ let package = Package(
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.8.0"),
|
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.8.0"),
|
||||||
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "de9387965025a1f79e060e6bdc862933fe972598"),
|
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "bec0635fe047e09c8b6c894d103ab8dd741b8340"),
|
||||||
// .package(path: "../../../passepartoutkit-source"),
|
// .package(path: "../../../passepartoutkit-source"),
|
||||||
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "0.8.0"),
|
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "0.8.0"),
|
||||||
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"),
|
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"),
|
||||||
|
|
|
@ -46,7 +46,7 @@ public final class InMemoryProfileRepository: ProfileRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func saveProfile(_ profile: Profile) async throws {
|
public func saveProfile(_ profile: Profile) async throws {
|
||||||
print("Save profile: \(profile.id))")
|
pp_log(.app, .info, "Save profile: \(profile.id))")
|
||||||
if let index = profiles.firstIndex(where: { $0.id == profile.id }) {
|
if let index = profiles.firstIndex(where: { $0.id == profile.id }) {
|
||||||
profiles[index] = profile
|
profiles[index] = profile
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,7 +55,7 @@ public final class InMemoryProfileRepository: ProfileRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func removeProfiles(withIds ids: [Profile.ID]) async throws {
|
public func removeProfiles(withIds ids: [Profile.ID]) async throws {
|
||||||
print("Remove profiles: \(ids)")
|
pp_log(.app, .info, "Remove profiles: \(ids)")
|
||||||
profiles = profiles.filter {
|
profiles = profiles.filter {
|
||||||
!ids.contains($0.id)
|
!ids.contains($0.id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,20 +55,17 @@ public final class NEProfileRepository: ProfileRepository {
|
||||||
}
|
}
|
||||||
self?.profilesSubject.send(profiles)
|
self?.profilesSubject.send(profiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
Task {
|
|
||||||
do {
|
|
||||||
try await repository.load()
|
|
||||||
} catch {
|
|
||||||
pp_log(.app, .fault, "Unable to load NE profiles: \(error)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public var profilesPublisher: AnyPublisher<[Profile], Never> {
|
public var profilesPublisher: AnyPublisher<[Profile], Never> {
|
||||||
profilesSubject.eraseToAnyPublisher()
|
profilesSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unused in app, rely on Tunnel.prepare()
|
||||||
|
public func loadProfiles(purge: Bool) async throws {
|
||||||
|
try await repository.load(purge: purge)
|
||||||
|
}
|
||||||
|
|
||||||
public func saveProfile(_ profile: Profile) async throws {
|
public func saveProfile(_ profile: Profile) async throws {
|
||||||
try await repository.save(profile, connect: false, title: title)
|
try await repository.save(profile, connect: false, title: title)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import PassepartoutKit
|
||||||
public enum AppUI {
|
public enum AppUI {
|
||||||
public static func configure(with context: AppContext) {
|
public static func configure(with context: AppContext) {
|
||||||
assertMissingModuleImplementations()
|
assertMissingModuleImplementations()
|
||||||
cleanUpOrphanedKeychainEntries()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +45,4 @@ private extension AppUI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func cleanUpOrphanedKeychainEntries() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,6 @@ public final class AppContext: ObservableObject {
|
||||||
subscriptions = []
|
subscriptions = []
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
try await tunnel.prepare()
|
|
||||||
await iapManager.reloadReceipt()
|
await iapManager.reloadReceipt()
|
||||||
connectionObserver.observeObjects()
|
connectionObserver.observeObjects()
|
||||||
profileManager.observeObjects()
|
profileManager.observeObjects()
|
||||||
|
|
|
@ -193,7 +193,6 @@ private extension OnDemandView {
|
||||||
}
|
}
|
||||||
draft.withSSIDs[$0] = false
|
draft.withSSIDs[$0] = false
|
||||||
}
|
}
|
||||||
// print(">>> withSSIDs (allSSIDs): \(withSSIDs)")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +218,6 @@ private extension OnDemandView {
|
||||||
}
|
}
|
||||||
draft.withSSIDs[$0] = true
|
draft.withSSIDs[$0] = true
|
||||||
}
|
}
|
||||||
// print(">>> withSSIDs (onSSIDs): \(withSSIDs)")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ extension IAPManager {
|
||||||
|
|
||||||
extension ProfileProcessor {
|
extension ProfileProcessor {
|
||||||
static let shared = ProfileProcessor {
|
static let shared = ProfileProcessor {
|
||||||
sharedProfileTitle($0)
|
ProfileManager.sharedTitle($0)
|
||||||
} processed: { profile in
|
} processed: { profile in
|
||||||
var builder = profile.builder()
|
var builder = profile.builder()
|
||||||
|
|
||||||
|
@ -157,45 +157,49 @@ extension Tunnel {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var localProfileRepository: ProfileRepository {
|
private extension ProfileManager {
|
||||||
let store = CoreDataPersistentStore(
|
static let localProfileRepository: ProfileRepository = {
|
||||||
logger: .default,
|
let store = CoreDataPersistentStore(
|
||||||
containerName: Constants.shared.containers.local,
|
logger: .default,
|
||||||
model: AppData.cdProfilesModel,
|
containerName: Constants.shared.containers.local,
|
||||||
cloudKitIdentifier: nil,
|
model: AppData.cdProfilesModel,
|
||||||
author: nil
|
cloudKitIdentifier: nil,
|
||||||
)
|
author: nil
|
||||||
return AppData.cdProfileRepositoryV3(
|
)
|
||||||
registry: .shared,
|
return AppData.cdProfileRepositoryV3(
|
||||||
coder: CodableProfileCoder(),
|
registry: .shared,
|
||||||
context: store.context,
|
coder: CodableProfileCoder(),
|
||||||
observingResults: false
|
context: store.context,
|
||||||
) { error in
|
observingResults: false
|
||||||
pp_log(.app, .error, "Unable to decode local result: \(error)")
|
) { error in
|
||||||
return .ignore
|
pp_log(.app, .error, "Unable to decode local result: \(error)")
|
||||||
}
|
return .ignore
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
extension Tunnel {
|
extension Tunnel {
|
||||||
static let shared = Tunnel(
|
static let shared = Tunnel(
|
||||||
strategy: NETunnelStrategy(repository: neRepository)
|
strategy: NETunnelStrategy(repository: ProfileManager.neRepository)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var localProfileRepository: ProfileRepository {
|
private extension ProfileManager {
|
||||||
NEProfileRepository(repository: neRepository) {
|
static let localProfileRepository: ProfileRepository = {
|
||||||
sharedProfileTitle($0)
|
NEProfileRepository(repository: neRepository) {
|
||||||
}
|
ProfileManager.sharedTitle($0)
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
private var neRepository: NETunnelManagerRepository {
|
static let neRepository: NETunnelManagerRepository = {
|
||||||
NETunnelManagerRepository(
|
NETunnelManagerRepository(
|
||||||
bundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
bundleIdentifier: BundleConfiguration.mainString(for: .tunnelId),
|
||||||
coder: Registry.sharedProtocolCoder,
|
coder: Registry.sharedProtocolCoder,
|
||||||
environment: .shared
|
environment: .shared
|
||||||
)
|
)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -212,8 +216,10 @@ extension ProviderFactory {
|
||||||
|
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
|
||||||
private let sharedProfileTitle: (Profile) -> String = {
|
private extension ProfileManager {
|
||||||
"Passepartout: \($0.name)"
|
static let sharedTitle: (Profile) -> String = {
|
||||||
|
"Passepartout: \($0.name)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CoreDataPersistentStoreLogger where Self == DefaultCoreDataPersistentStoreLogger {
|
extension CoreDataPersistentStoreLogger where Self == DefaultCoreDataPersistentStoreLogger {
|
||||||
|
|
Loading…
Reference in New Issue