ios: self destruct improvements

This commit is contained in:
Avently 2024-01-03 04:13:27 -08:00
parent 767522e701
commit 51a16cfdc2
7 changed files with 80 additions and 27 deletions

View File

@ -61,22 +61,25 @@ struct ContentView: View {
} }
if !showSettings, let la = chatModel.laRequest { if !showSettings, let la = chatModel.laRequest {
LocalAuthView(authRequest: la) LocalAuthView(authRequest: la)
} else if showSetPasscode { } else {
SetAppPasscodeView { if showSetPasscode {
chatModel.contentViewAccessAuthenticated = true SetAppPasscodeView {
prefPerformLA = true chatModel.contentViewAccessAuthenticated = true
showSetPasscode = false prefPerformLA = true
privacyLocalAuthModeDefault.set(.passcode) showSetPasscode = false
alertManager.showAlert(laTurnedOnAlert()) privacyLocalAuthModeDefault.set(.passcode)
} cancel: { alertManager.showAlert(laTurnedOnAlert())
prefPerformLA = false } cancel: {
showSetPasscode = false prefPerformLA = false
alertManager.showAlert(laPasscodeNotSetAlert()) showSetPasscode = false
alertManager.showAlert(laPasscodeNotSetAlert())
}
} else {
if chatModel.chatDbStatus == nil {
initializationView()
}
} }
} }
if chatModel.chatDbStatus == nil {
initializationView()
}
} }
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! } .alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
.sheet(isPresented: $showSettings) { .sheet(isPresented: $showSettings) {

View File

@ -101,7 +101,9 @@ func activateChat(appState: AppState = .active) {
} }
} }
func initChatAndMigrate(refreshInvitations: Bool = true) { func initChatAndMigrate(ignoreSelfDestruct: Bool = false, refreshInvitations: Bool = true) {
if !ignoreSelfDestruct && kcSelfDestructPassword.get() != nil { return }
let m = ChatModel.shared let m = ChatModel.shared
if (!m.chatInitialized) { if (!m.chatInitialized) {
m.v3DBMigration = v3DBMigrationDefault.get() m.v3DBMigration = v3DBMigrationDefault.get()

View File

@ -484,6 +484,24 @@ func deleteChatAsync() async throws {
try await apiDeleteStorage() try await apiDeleteStorage()
_ = kcDatabasePassword.remove() _ = kcDatabasePassword.remove()
storeDBPassphraseGroupDefault.set(true) storeDBPassphraseGroupDefault.set(true)
deleteChatDatabaseFiles()
}
func deleteChatDatabaseFiles() {
do {
try FileManager.default.removeItem(atPath: getAppDatabasePath().path + "_chat.db")
try FileManager.default.removeItem(atPath: getAppDatabasePath().path + "_agent.db")
} catch let error {
logger.error("Failed to delete all databases: \(error)")
}
try? FileManager.default.removeItem(atPath: getAppDatabasePath().path + "_chat.db.bak")
try? FileManager.default.removeItem(atPath: getAppDatabasePath().path + "_agent.db.bak")
try? FileManager.default.removeItem(at: getTempFilesDirectory())
try? FileManager.default.createDirectory(at: getTempFilesDirectory(), withIntermediateDirectories: true)
deleteAppFiles()
_ = kcDatabasePassword.remove()
storeDBPassphraseGroupDefault.set(true)
} }
struct DatabaseView_Previews: PreviewProvider { struct DatabaseView_Previews: PreviewProvider {

View File

@ -13,19 +13,28 @@ struct LocalAuthView: View {
@EnvironmentObject var m: ChatModel @EnvironmentObject var m: ChatModel
var authRequest: LocalAuthRequest var authRequest: LocalAuthRequest
@State private var password = "" @State private var password = ""
@State private var allowToReact = true
var body: some View { var body: some View {
PasscodeView(passcode: $password, title: authRequest.title ?? "Enter Passcode", reason: authRequest.reason, submitLabel: "Submit") { PasscodeView(passcode: $password, title: authRequest.title ?? "Enter Passcode", reason: authRequest.reason, submitLabel: "Submit",
buttonsEnabled: $allowToReact) {
if let sdPassword = kcSelfDestructPassword.get(), authRequest.selfDestruct && password == sdPassword { if let sdPassword = kcSelfDestructPassword.get(), authRequest.selfDestruct && password == sdPassword {
allowToReact = false
deleteStorageAndRestart(sdPassword) { r in deleteStorageAndRestart(sdPassword) { r in
m.laRequest = nil m.laRequest = nil
authRequest.completed(r) authRequest.completed(r)
} }
return return
} }
let r: LAResult = password == authRequest.password let r: LAResult
? .success if password == authRequest.password {
: .failed(authError: NSLocalizedString("Incorrect passcode", comment: "PIN entry")) if authRequest.selfDestruct && kcSelfDestructPassword.get() != nil && !m.chatInitialized {
initChatAndMigrate(ignoreSelfDestruct: true)
}
r = .success
} else {
r = .failed(authError: NSLocalizedString("Incorrect passcode", comment: "PIN entry"))
}
m.laRequest = nil m.laRequest = nil
authRequest.completed(r) authRequest.completed(r)
} cancel: { } cancel: {
@ -37,8 +46,23 @@ struct LocalAuthView: View {
private func deleteStorageAndRestart(_ password: String, completed: @escaping (LAResult) -> Void) { private func deleteStorageAndRestart(_ password: String, completed: @escaping (LAResult) -> Void) {
Task { Task {
do { do {
try await stopChatAsync() if m.chatRunning == true {
try await deleteChatAsync() try await stopChatAsync()
}
if m.chatInitialized {
/**
* The following sequence can bring a user here:
* the user opened the app, entered app passcode, went to background, returned back, entered self-destruct code.
* In this case database should be closed to prevent possible situation when OS can deny database removal command
* */
chatCloseStore()
}
deleteChatDatabaseFiles()
// Clear sensitive data on screen just in case ModalManager will fail to prevent hiding its modals while database encrypts itself
m.chatId = nil
m.reversedChatItems = []
m.chats = []
m.users = []
_ = kcAppPassword.set(password) _ = kcAppPassword.set(password)
_ = kcSelfDestructPassword.remove() _ = kcSelfDestructPassword.remove()
await NtfManager.shared.removeAllNotifications() await NtfManager.shared.removeAllNotifications()
@ -53,7 +77,7 @@ struct LocalAuthView: View {
try initializeChat(start: true) try initializeChat(start: true)
m.chatDbChanged = false m.chatDbChanged = false
AppChatState.shared.set(.active) AppChatState.shared.set(.active)
if m.currentUser != nil { return } if m.currentUser != nil || !m.chatInitialized { return }
var profile: Profile? = nil var profile: Profile? = nil
if let displayName = displayName, displayName != "" { if let displayName = displayName, displayName != "" {
profile = Profile(displayName: displayName, fullName: "") profile = Profile(displayName: displayName, fullName: "")

View File

@ -14,6 +14,8 @@ struct PasscodeView: View {
var reason: String? = nil var reason: String? = nil
var submitLabel: LocalizedStringKey var submitLabel: LocalizedStringKey
var submitEnabled: ((String) -> Bool)? var submitEnabled: ((String) -> Bool)?
@Binding var buttonsEnabled: Bool
var submit: () -> Void var submit: () -> Void
var cancel: () -> Void var cancel: () -> Void
@ -70,11 +72,11 @@ struct PasscodeView: View {
@ViewBuilder private func buttonsView() -> some View { @ViewBuilder private func buttonsView() -> some View {
Button(action: cancel) { Button(action: cancel) {
Label("Cancel", systemImage: "multiply") Label("Cancel", systemImage: "multiply")
} }.disabled(!buttonsEnabled)
Button(action: submit) { Button(action: submit) {
Label(submitLabel, systemImage: "checkmark") Label(submitLabel, systemImage: "checkmark")
} }
.disabled(submitEnabled?(passcode) == false || passcode.count < 4) .disabled(submitEnabled?(passcode) == false || passcode.count < 4 || !buttonsEnabled)
} }
} }
@ -85,6 +87,7 @@ struct PasscodeViewView_Previews: PreviewProvider {
title: "Enter Passcode", title: "Enter Passcode",
reason: "Unlock app", reason: "Unlock app",
submitLabel: "Submit", submitLabel: "Submit",
buttonsEnabled: Binding.constant(true),
submit: {}, submit: {},
cancel: {} cancel: {}
) )

View File

@ -41,7 +41,10 @@ struct SetAppPasscodeView: View {
} }
} }
} else { } else {
setPasswordView(title: title, submitLabel: "Save") { setPasswordView(title: title,
submitLabel: "Save",
// Do not allow to set app passcode == selfDestruct passcode
submitEnabled: { pwd in pwd != (passcodeKeychain.forKey == kcSelfDestructPassword.forKey ? kcAppPassword : kcSelfDestructPassword).get() }) {
enteredPassword = passcode enteredPassword = passcode
passcode = "" passcode = ""
confirming = true confirming = true
@ -54,7 +57,7 @@ struct SetAppPasscodeView: View {
} }
private func setPasswordView(title: LocalizedStringKey, submitLabel: LocalizedStringKey, submitEnabled: (((String) -> Bool))? = nil, submit: @escaping () -> Void) -> some View { private func setPasswordView(title: LocalizedStringKey, submitLabel: LocalizedStringKey, submitEnabled: (((String) -> Bool))? = nil, submit: @escaping () -> Void) -> some View {
PasscodeView(passcode: $passcode, title: title, reason: reason, submitLabel: submitLabel, submitEnabled: submitEnabled, submit: submit) { PasscodeView(passcode: $passcode, title: title, reason: reason, submitLabel: submitLabel, submitEnabled: submitEnabled, buttonsEnabled: Binding.constant(true), submit: submit) {
dismiss() dismiss()
cancel() cancel()
} }

View File

@ -22,7 +22,7 @@ public let kcAppPassword = KeyChainItem(forKey: APP_PASSWORD_ITEM)
public let kcSelfDestructPassword = KeyChainItem(forKey: SELF_DESTRUCT_PASSWORD_ITEM) public let kcSelfDestructPassword = KeyChainItem(forKey: SELF_DESTRUCT_PASSWORD_ITEM)
public struct KeyChainItem { public struct KeyChainItem {
var forKey: String public var forKey: String
public func get() -> String? { public func get() -> String? {
getItemString(forKey: forKey) getItemString(forKey: forKey)