Skip to content

Commit

Permalink
App Locked for X Minutes (#2665)
Browse files Browse the repository at this point in the history
---------

Signed-off-by: Marino Faggiana <8616947+marinofaggiana@users.noreply.github.com>
  • Loading branch information
marinofaggiana committed Nov 11, 2023
1 parent 06c5117 commit 8a91c3a
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 67 deletions.
111 changes: 103 additions & 8 deletions iOSClient/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
})
}
Expand All @@ -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 }

Expand All @@ -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()
}
Expand All @@ -785,20 +796,107 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func didInputCorrectPasscode(in passcodeViewController: TOPasscodeViewController) {
DispatchQueue.main.async {
passcodeViewController.dismiss(animated: true) {
NCKeychain().passcodeCounterFail = 0
self.hidePrivacyProtectionWindow()
self.requestAccount()
}
}
}

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() {
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion iOSClient/Login/NCLogin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion iOSClient/Login/NCLoginWeb.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
26 changes: 1 addition & 25 deletions iOSClient/Settings/CCAdvanced.m
Original file line number Diff line number Diff line change
Expand Up @@ -455,31 +455,7 @@ - (void)exitNextcloud:(XLFormRowDescriptor *)sender
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) {
Expand Down
37 changes: 37 additions & 0 deletions iOSClient/Settings/NCKeychain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
20 changes: 19 additions & 1 deletion iOSClient/Settings/NCSettings.m
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ - (void)initializeForm
[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 --------------------------------------------------------------

Expand Down Expand Up @@ -261,6 +270,7 @@ - (void)reloadForm
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"];

// ------------------------------------------------------------------
Expand All @@ -272,7 +282,9 @@ - (void)reloadForm
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];
Expand Down Expand Up @@ -315,6 +327,12 @@ - (void)formRowDescriptorValueHasChanged:(XLFormRowDescriptor *)rowDescriptor ol
[[NCKeychain alloc] init].privacyScreenEnabled = false;
}
}

if ([rowDescriptor.tag isEqualToString:@"resetAppAttemps"]) {

NSInteger value = [[rowDescriptor.value valueData] intValue];
[[NCKeychain alloc] init].resetAppCounterFail = value;
}
}

#pragma mark -
Expand Down
15 changes: 2 additions & 13 deletions iOSClient/Settings/NCSettingsBundleHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions iOSClient/Supporting Files/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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";
Expand Down
18 changes: 0 additions & 18 deletions iOSClient/Utility/NCUtilityFileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

0 comments on commit 8a91c3a

Please sign in to comment.