浏览代码

App Locked for X Minutes (#2665)

---------

Signed-off-by: Marino Faggiana <8616947+marinofaggiana@users.noreply.github.com>
Marino Faggiana 1 年之前
父节点
当前提交
8a91c3a126

+ 103 - 8
iOSClient/AppDelegate.swift

@@ -738,7 +738,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         let passcodeViewController = TOPasscodeViewController(passcodeType: .sixDigits, allowCancel: false)
         passcodeViewController.delegate = self
         passcodeViewController.keypadButtonShowLettering = false
-        if NCKeychain().touchFaceID, laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
+        if NCKeychain().touchFaceID,
+           laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error),
+           NCKeychain().passcodeCounterFail < 3 {
             if error == nil {
                 if laContext.biometryType == .faceID {
                     passcodeViewController.biometryType = .faceID
@@ -752,6 +754,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         // show passcode on top of privacy window
         privacyProtectionWindow?.rootViewController?.present(passcodeViewController, animated: true, completion: {
+            let resetAppCounterFail = NCKeychain().resetAppCounterFail
+            let passcodeCounterFail = NCKeychain().passcodeCounterFail
+            if resetAppCounterFail > 0 && (passcodeCounterFail >= resetAppCounterFail) {
+                self.passcodeResetApp(passcodeViewController)
+            } else if passcodeCounterFail >= 3 {
+                self.passcodeAlert(passcodeViewController)
+            }
             completion()
         })
     }
@@ -765,6 +774,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
               NCKeychain().touchFaceID,
               NCKeychain().passcode != nil,
               NCKeychain().requestPasscodeAtStart,
+              NCKeychain().passcodeCounterFail < 3,
               let passcodeViewController = privacyProtectionWindow?.rootViewController?.presentedViewController as? TOPasscodeViewController
         else { return }
 
@@ -773,6 +783,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                 if success {
                     DispatchQueue.main.async {
                         passcodeViewController.dismiss(animated: true) {
+                            NCKeychain().passcodeCounterFail = 0
                             self.hidePrivacyProtectionWindow()
                             self.requestAccount()
                         }
@@ -785,6 +796,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     func didInputCorrectPasscode(in passcodeViewController: TOPasscodeViewController) {
         DispatchQueue.main.async {
             passcodeViewController.dismiss(animated: true) {
+                NCKeychain().passcodeCounterFail = 0
                 self.hidePrivacyProtectionWindow()
                 self.requestAccount()
             }
@@ -792,13 +804,99 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     }
 
     func passcodeViewController(_ passcodeViewController: TOPasscodeViewController, isCorrectCode code: String) -> Bool {
-        return code == NCKeychain().passcode
+        if code == NCKeychain().passcode {
+            return true
+        } else {
+            NCKeychain().passcodeCounterFail += 1
+            let resetAppCounterFail = NCKeychain().resetAppCounterFail
+            let passcodeCounterFail = NCKeychain().passcodeCounterFail
+            if resetAppCounterFail > 0 && (passcodeCounterFail >= resetAppCounterFail) {
+                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.passcodeResetApp(passcodeViewController) }
+            } else if passcodeCounterFail >= 3 {
+                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.passcodeAlert(passcodeViewController) }
+            }
+            return false
+        }
     }
 
     func didPerformBiometricValidationRequest(in passcodeViewController: TOPasscodeViewController) {
         enableTouchFaceID()
     }
 
+    func passcodeAlert(_ passcodeViewController: TOPasscodeViewController) {
+
+        passcodeViewController.biometricButton.isHidden = true
+        let alertController = UIAlertController(title: NSLocalizedString("_passcode_counter_fail_", comment: ""), message: nil, preferredStyle: .alert)
+        passcodeViewController.present(alertController, animated: true, completion: { })
+
+        var seconds = NCKeychain().passcodeSecondsFail * 30
+        _ = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
+            alertController.message = "\(seconds) " + NSLocalizedString("_seconds_", comment: "")
+            seconds -= 1
+            if seconds < 0 {
+                NCKeychain().passcodeSecondsFail += 1
+                timer.invalidate()
+                alertController.dismiss(animated: true)
+            }
+        }
+    }
+
+    func passcodeResetApp(_ passcodeViewController: TOPasscodeViewController) {
+
+        let alertController = UIAlertController(title: NSLocalizedString("_failes_attemps_reset_", comment: ""), message: nil, preferredStyle: .alert)
+        passcodeViewController.present(alertController, animated: true, completion: { })
+
+        DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
+            self.resetApplication()
+        }
+    }
+
+    // MARK: - Remove / Reset App
+
+    @objc func removeAllSettings() {
+
+        let utilityFileSystem = NCUtilityFileSystem()
+
+        URLCache.shared.memoryCapacity = 0
+        URLCache.shared.diskCapacity = 0
+
+        utilityFileSystem.removeGroupDirectoryProviderStorage()
+        utilityFileSystem.removeGroupLibraryDirectory()
+        utilityFileSystem.removeDocumentsDirectory()
+        utilityFileSystem.removeTemporaryDirectory()
+
+        utilityFileSystem.createDirectoryStandard()
+
+        NCKeychain().removeAll()
+        NCManageDatabase.shared.clearDatabase(account: nil, removeAccount: true)
+    }
+
+    @objc func resetApplication() {
+
+        let utilityFileSystem = NCUtilityFileSystem()
+
+        NCNetworking.shared.cancelDataTask()
+        NCNetworking.shared.cancelDownloadTasks()
+        NCNetworking.shared.cancelUploadTasks()
+        NCNetworking.shared.cancelUploadBackgroundTask()
+
+        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
+
+            URLCache.shared.memoryCapacity = 0
+            URLCache.shared.diskCapacity = 0
+
+            utilityFileSystem.removeGroupDirectoryProviderStorage()
+            utilityFileSystem.removeGroupApplicationSupport()
+            utilityFileSystem.removeDocumentsDirectory()
+            utilityFileSystem.removeTemporaryDirectory()
+
+            NCKeychain().removeAll()
+            NCManageDatabase.shared.removeDB()
+
+            exit(0)
+        }
+    }
+
     // MARK: - Privacy Protection
 
     private func showPrivacyProtectionWindow() {
@@ -1025,17 +1123,14 @@ extension AppDelegate: NCAudioRecorderViewControllerDelegate {
 
     func didFinishRecording(_ viewController: NCAudioRecorderViewController, fileName: String) {
 
-        guard
-            let navigationController = UIStoryboard(name: "NCCreateFormUploadVoiceNote", bundle: nil).instantiateInitialViewController() as? UINavigationController,
-                let viewController = navigationController.topViewController as? NCCreateFormUploadVoiceNote
-        else { return }
+        guard let navigationController = UIStoryboard(name: "NCCreateFormUploadVoiceNote", bundle: nil).instantiateInitialViewController() as? UINavigationController,
+              let viewController = navigationController.topViewController as? NCCreateFormUploadVoiceNote else { return }
         navigationController.modalPresentationStyle = .formSheet
         viewController.setup(serverUrl: activeServerUrl, fileNamePath: NSTemporaryDirectory() + fileName, fileName: fileName)
         window?.rootViewController?.present(navigationController, animated: true)
     }
 
-    func didFinishWithoutRecording(_ viewController: NCAudioRecorderViewController, fileName: String) {
-    }
+    func didFinishWithoutRecording(_ viewController: NCAudioRecorderViewController, fileName: String) { }
 }
 
 extension AppDelegate: NCCreateFormUploadConflictDelegate {

+ 1 - 1
iOSClient/Login/NCLogin.swift

@@ -396,7 +396,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
                 if error == .success, let userProfile {
 
                     if NCManageDatabase.shared.getAccounts() == nil {
-                        NCUtilityFileSystem().removeAllSettings()
+                        self.appDelegate.removeAllSettings()
                     }
 
                     NCManageDatabase.shared.deleteAccount(account)

+ 1 - 1
iOSClient/Login/NCLoginWeb.swift

@@ -294,7 +294,7 @@ extension NCLoginWeb: WKNavigationDelegate {
             if error == .success, let userProfile {
 
                 if NCManageDatabase.shared.getAccounts() == nil {
-                    NCUtilityFileSystem().removeAllSettings()
+                    self.appDelegate.removeAllSettings()
                 }
 
                 NCManageDatabase.shared.deleteAccount(account)

+ 1 - 25
iOSClient/Settings/CCAdvanced.m

@@ -455,31 +455,7 @@
     UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:NSLocalizedString(@"_want_exit_", nil) preferredStyle:UIAlertControllerStyleActionSheet];
     
     [alertController addAction: [UIAlertAction actionWithTitle:NSLocalizedString(@"_ok_", nil) style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
-                
-        [[NCNetworking shared] cancelDataTask];
-        [[NCNetworking shared] cancelDownloadTasks];
-        [[NCNetworking shared] cancelUploadTasks];
-        [[NCNetworking shared] cancelUploadBackgroundTask];
-
-        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
-
-            NCUtilityFileSystem *ufs = [[NCUtilityFileSystem alloc] init];
-
-            [[NCManageDatabase shared] removeDB];
-
-            [[NSURLCache sharedURLCache] setMemoryCapacity:0];
-            [[NSURLCache sharedURLCache] setDiskCapacity:0];
-
-            [ufs removeGroupDirectoryProviderStorage];
-            [ufs removeGroupApplicationSupport];
-
-            [ufs removeDocumentsDirectory];
-            [ufs removeTemporaryDirectory];
-
-            [[[NCKeychain alloc] init] removeAll];
-
-            exit(0);
-        });
+        [appDelegate resetApplication];
     }]];
     
     [alertController addAction: [UIAlertAction actionWithTitle:NSLocalizedString(@"_cancel_", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {

+ 37 - 0
iOSClient/Settings/NCKeychain.swift

@@ -54,6 +54,43 @@ import KeychainAccess
         }
     }
 
+    @objc var resetAppCounterFail: Int {
+        get {
+            if let value = try? keychain.get("resetAppCounterFail"), let result = Int(value) {
+                return result
+            }
+            return 0
+        }
+        set {
+            keychain["resetAppCounterFail"] = String(newValue)
+        }
+    }
+
+    var passcodeCounterFail: Int {
+        get {
+            if let value = try? keychain.get("passcodeCounterFail"), let result = Int(value) {
+                return result
+            }
+            return 0
+        }
+        set {
+            if newValue == 0 { passcodeSecondsFail = 1 }
+            keychain["passcodeCounterFail"] = String(newValue)
+        }
+    }
+
+    var passcodeSecondsFail: Int {
+        get {
+            if let value = try? keychain.get("passcodeSecondsFail"), let result = Int(value) {
+                return result
+            }
+            return 1
+        }
+        set {
+            keychain["passcodeSecondsFail"] = String(newValue)
+        }
+    }
+
     @objc var requestPasscodeAtStart: Bool {
         get {
             let keychainOLD = Keychain(service: "Crypto Cloud")

+ 19 - 1
iOSClient/Settings/NCSettings.m

@@ -101,6 +101,15 @@
     [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
     [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
     [section addFormRow:row];
+    // Reset app wrong attemps
+    row = [XLFormRowDescriptor formRowDescriptorWithTag:@"resetAppAttemps" rowType:XLFormRowDescriptorTypeStepCounter title:NSLocalizedString(@"_reset_app_passcode_", nil)];
+    [row.cellConfigAtConfigure setObject:@4 forKey:@"stepControl.stepValue"];
+    [row.cellConfigAtConfigure setObject:@0 forKey:@"stepControl.minimumValue"];
+    [row.cellConfigAtConfigure setObject:@20 forKey:@"stepControl.maximumValue"];
+    row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
+    [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+    [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
+    [section addFormRow:row];
 
     // Section : CALDAV CARDAV --------------------------------------------------------------
 
@@ -261,6 +270,7 @@
     XLFormRowDescriptor *rowBloccoPasscode = [self.form formRowWithTag:@"bloccopasscode"];
     XLFormRowDescriptor *rowNotPasscodeAtStart = [self.form formRowWithTag:@"notPasscodeAtStart"];
     XLFormRowDescriptor *rowEnableTouchDaceID = [self.form formRowWithTag:@"enableTouchDaceID"];
+    XLFormRowDescriptor *rowResetAppAttemps = [self.form formRowWithTag:@"resetAppAttemps"];
     XLFormRowDescriptor *rowPrivacyScreen = [self.form formRowWithTag:@"privacyScreen"];
 
     // ------------------------------------------------------------------
@@ -272,7 +282,9 @@
         rowBloccoPasscode.title = NSLocalizedString(@"_lock_not_active_", nil);
         [rowBloccoPasscode.cellConfig setObject:[[UIImage imageNamed:@"lock_open"] imageWithColor:UIColor.systemGrayColor size:25] forKey:@"imageView.image"];
     }
-    
+    long resetAppCounterFail = [[NCKeychain alloc] init].resetAppCounterFail;
+    [rowResetAppAttemps setValue:[NSString stringWithFormat: @"%ld", resetAppCounterFail]];
+
     if ([[NCKeychain alloc] init].touchFaceID) [rowEnableTouchDaceID setValue:@1]; else [rowEnableTouchDaceID setValue:@0];
     if ([[NCKeychain alloc] init].requestPasscodeAtStart) [rowNotPasscodeAtStart setValue:@0]; else [rowNotPasscodeAtStart setValue:@1];
     if ([[NCKeychain alloc] init].privacyScreenEnabled) [rowPrivacyScreen setValue:@1]; else [rowPrivacyScreen setValue:@0];
@@ -315,6 +327,12 @@
             [[NCKeychain alloc] init].privacyScreenEnabled = false;
         }
     }
+
+    if ([rowDescriptor.tag isEqualToString:@"resetAppAttemps"]) {
+
+        NSInteger value = [[rowDescriptor.value valueData] intValue];
+        [[NCKeychain alloc] init].resetAppCounterFail = value;
+    }
 }
 
 #pragma mark -

+ 2 - 13
iOSClient/Settings/NCSettingsBundleHelper.swift

@@ -38,21 +38,10 @@ class NCSettingsBundleHelper: NSObject {
     class func checkAndExecuteSettings(delay: Double) {
         if UserDefaults.standard.bool(forKey: SettingsBundleKeys.Reset) {
             UserDefaults.standard.set(false, forKey: SettingsBundleKeys.Reset)
-            let utilityFileSystem = NCUtilityFileSystem()
-
-            URLCache.shared.memoryCapacity = 0
-            URLCache.shared.diskCapacity = 0
-
-            utilityFileSystem.removeGroupDirectoryProviderStorage()
-            utilityFileSystem.removeGroupApplicationSupport()
-            utilityFileSystem.removeDocumentsDirectory()
-            utilityFileSystem.removeTemporaryDirectory()
-
-            NCKeychain().removeAll()
-            NCManageDatabase.shared.removeDB()
 
             DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
-                exit(0)
+                let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+                appDelegate.resetApplication()
             }
         }
     }

+ 4 - 0
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -217,6 +217,8 @@
 "_more_action_title_" = "More Details";
 "_wait_file_preparation_" = "Please wait, upload file in preparation";
 "_wait_file_encryption_"  = "Please wait, file encryption";
+"_passcode_counter_fail_" = "Failed attempts, please wait";
+"_seconds_"               = "seconds";
 
 /* Background of the file listing view */
 "_use_as_background_"       = "Use it as a background";
@@ -944,6 +946,8 @@
 "_chunk_move_"              = "The sent file could not be reassembled, please check the server log";
 "_download_image_"          = "Download image";
 "_download_video_"          = "Download video";
+"_reset_app_passcode_"      = "Resets the app after wrong passcode";
+"_failes_attemps_reset_"    = "Number of failed attempts. App reset";
 
 // Video
 "_select_trace_"            = "Select the trace";

+ 0 - 18
iOSClient/Utility/NCUtilityFileSystem.swift

@@ -582,22 +582,4 @@ class NCUtilityFileSystem: NSObject {
             }
         }
     }
-
-    func removeAllSettings() {
-
-        URLCache.shared.memoryCapacity = 0
-        URLCache.shared.diskCapacity = 0
-
-        NCManageDatabase.shared.clearDatabase(account: nil, removeAccount: true)
-
-        removeGroupDirectoryProviderStorage()
-        removeGroupLibraryDirectory()
-
-        removeDocumentsDirectory()
-        removeTemporaryDirectory()
-
-        createDirectoryStandard()
-
-        NCKeychain().removeAll()
-    }
 }