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 {
LocalAuthView(authRequest: la)
} else if showSetPasscode {
SetAppPasscodeView {
chatModel.contentViewAccessAuthenticated = true
prefPerformLA = true
showSetPasscode = false
privacyLocalAuthModeDefault.set(.passcode)
alertManager.showAlert(laTurnedOnAlert())
} cancel: {
prefPerformLA = false
showSetPasscode = false
alertManager.showAlert(laPasscodeNotSetAlert())
} else {
if showSetPasscode {
SetAppPasscodeView {
chatModel.contentViewAccessAuthenticated = true
prefPerformLA = true
showSetPasscode = false
privacyLocalAuthModeDefault.set(.passcode)
alertManager.showAlert(laTurnedOnAlert())
} cancel: {
prefPerformLA = false
showSetPasscode = false
alertManager.showAlert(laPasscodeNotSetAlert())
}
} else {
if chatModel.chatDbStatus == nil {
initializationView()
}
}
}
if chatModel.chatDbStatus == nil {
initializationView()
}
}
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
.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
if (!m.chatInitialized) {
m.v3DBMigration = v3DBMigrationDefault.get()

View File

@ -484,6 +484,24 @@ func deleteChatAsync() async throws {
try await apiDeleteStorage()
_ = kcDatabasePassword.remove()
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 {

View File

@ -13,19 +13,28 @@ struct LocalAuthView: View {
@EnvironmentObject var m: ChatModel
var authRequest: LocalAuthRequest
@State private var password = ""
@State private var allowToReact = true
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 {
allowToReact = false
deleteStorageAndRestart(sdPassword) { r in
m.laRequest = nil
authRequest.completed(r)
}
return
}
let r: LAResult = password == authRequest.password
? .success
: .failed(authError: NSLocalizedString("Incorrect passcode", comment: "PIN entry"))
let r: LAResult
if password == authRequest.password {
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
authRequest.completed(r)
} cancel: {
@ -37,8 +46,23 @@ struct LocalAuthView: View {
private func deleteStorageAndRestart(_ password: String, completed: @escaping (LAResult) -> Void) {
Task {
do {
try await stopChatAsync()
try await deleteChatAsync()
if m.chatRunning == true {
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)
_ = kcSelfDestructPassword.remove()
await NtfManager.shared.removeAllNotifications()
@ -53,7 +77,7 @@ struct LocalAuthView: View {
try initializeChat(start: true)
m.chatDbChanged = false
AppChatState.shared.set(.active)
if m.currentUser != nil { return }
if m.currentUser != nil || !m.chatInitialized { return }
var profile: Profile? = nil
if let displayName = displayName, displayName != "" {
profile = Profile(displayName: displayName, fullName: "")

View File

@ -14,6 +14,8 @@ struct PasscodeView: View {
var reason: String? = nil
var submitLabel: LocalizedStringKey
var submitEnabled: ((String) -> Bool)?
@Binding var buttonsEnabled: Bool
var submit: () -> Void
var cancel: () -> Void
@ -70,11 +72,11 @@ struct PasscodeView: View {
@ViewBuilder private func buttonsView() -> some View {
Button(action: cancel) {
Label("Cancel", systemImage: "multiply")
}
}.disabled(!buttonsEnabled)
Button(action: submit) {
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",
reason: "Unlock app",
submitLabel: "Submit",
buttonsEnabled: Binding.constant(true),
submit: {},
cancel: {}
)

View File

@ -41,7 +41,10 @@ struct SetAppPasscodeView: View {
}
}
} 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
passcode = ""
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 {
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()
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 struct KeyChainItem {
var forKey: String
public var forKey: String
public func get() -> String? {
getItemString(forKey: forKey)