#8 add app lock with passcode and biometric unlock
- Gate every layer behind AppLockView when the lock is enabled and lock on launch and on backgrounding; the passcode lives only in the Keychain (AppLockVault) and the enable state defaults to off. - Add the lock section to Settings: enable/setup, change passcode and disable all require the current passcode via a shared passcode sheet; Face ID / Touch ID unlock prompts automatically and can be toggled with passcode confirmation. - Add NSFaceIDUsageDescription to both app targets, cover the passcode policy with tests, guard the feature in verify.sh and document it in SECURITY.md.
This commit is contained in:
1 parent
b0eb44d7a7
commit
0d78e02d2b
13 files changed
+663
-21
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import LocalAuthentication
|
||||
import PhotosUI
|
||||
import SwiftUI
|
||||
|
||||
@@ -7,6 +8,7 @@ struct SettingsView: View {
|
||||
@State private var showingSignOutConfirmation = false
|
||||
@State private var forgetHistory = false
|
||||
@State private var avatarItem: PhotosPickerItem?
|
||||
@State private var passcodeSheetMode: AppLockPasscodeSheet.Mode?
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
@@ -98,6 +100,36 @@ struct SettingsView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Section("Блокировка приложения") {
|
||||
Toggle("Заблокировать приложение", isOn: Binding(
|
||||
get: { model.appLockIsEnabled },
|
||||
set: { enabled in
|
||||
if enabled {
|
||||
passcodeSheetMode = .setup
|
||||
} else {
|
||||
passcodeSheetMode = .verify(.disableLock)
|
||||
}
|
||||
}
|
||||
))
|
||||
if model.appLockIsEnabled {
|
||||
Toggle("Вход по \(biometricName)", isOn: Binding(
|
||||
get: { model.appLockBiometricIsEnabled },
|
||||
set: { enabled in
|
||||
passcodeSheetMode = .verify(
|
||||
enabled ? .enableBiometrics : .disableBiometrics
|
||||
)
|
||||
}
|
||||
))
|
||||
.disabled(!biometricAvailable)
|
||||
Button("Сменить пароль") {
|
||||
passcodeSheetMode = .change
|
||||
}
|
||||
}
|
||||
Text("При включённой блокировке Luma запрашивает пароль при запуске и после сворачивания. Пароль хранится в Keychain.")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
Section("Приватность чата") {
|
||||
Toggle("Показывать статус набора", isOn: Binding(
|
||||
get: { model.typingIndicatorsEnabled },
|
||||
@@ -153,12 +185,31 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(item: $passcodeSheetMode) { mode in
|
||||
AppLockPasscodeSheet(model: model, mode: mode)
|
||||
}
|
||||
}
|
||||
#if os(macOS)
|
||||
.frame(minWidth: 500, minHeight: 560)
|
||||
#endif
|
||||
}
|
||||
|
||||
private var biometricContext: LAContext {
|
||||
LAContext()
|
||||
}
|
||||
|
||||
private var biometricAvailable: Bool {
|
||||
var error: NSError?
|
||||
return biometricContext.canEvaluatePolicy(
|
||||
.deviceOwnerAuthenticationWithBiometrics,
|
||||
error: &error
|
||||
)
|
||||
}
|
||||
|
||||
private var biometricName: String {
|
||||
biometricContext.biometryType == .faceID ? "Face ID" : "Touch ID"
|
||||
}
|
||||
|
||||
private var connectionTitle: String {
|
||||
switch model.connectionStatus {
|
||||
case .connected: return "Подключено"
|
||||
|
||||
Reference in new issue
Block a user