Allow resettig app in the passcodelockview
This commit is contained in:
parent
6b3d75c1be
commit
35f599c45b
3 changed files with 65 additions and 26 deletions
|
|
@ -156,6 +156,11 @@
|
||||||
"DiscardExplanation." = "Do you want to permanently discard all changes to the local copy of your password data? You cannot undo this action.";
|
"DiscardExplanation." = "Do you want to permanently discard all changes to the local copy of your password data? You cannot undo this action.";
|
||||||
"Resetting..." = "Resetting ...";
|
"Resetting..." = "Resetting ...";
|
||||||
|
|
||||||
|
// Forget passcode
|
||||||
|
"ForgotYourPasscode?" = "Forgot your passcode?";
|
||||||
|
"ResetPass" = "Reset Pass";
|
||||||
|
"ResetPassExplanation." = "The only thing you can do is to reset the app. This will delete all local data and settings. Password store data on your remote server will not be affected.";
|
||||||
|
|
||||||
// Time related
|
// Time related
|
||||||
"Unknown" = "Unknown";
|
"Unknown" = "Unknown";
|
||||||
"JustNow" = "Just now";
|
"JustNow" = "Just now";
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,14 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
||||||
open var cancelCallback: (()->Void)?
|
open var cancelCallback: (()->Void)?
|
||||||
|
|
||||||
weak var passcodeLabel: UILabel?
|
weak var passcodeLabel: UILabel?
|
||||||
weak var passcodeWrongAttemptsLabel: UILabel?
|
|
||||||
weak var passcodeTextField: UITextField?
|
weak var passcodeTextField: UITextField?
|
||||||
weak var biometryAuthButton: UIButton?
|
weak var biometryAuthButton: UIButton?
|
||||||
|
weak var forgotPasscodeButton: UIButton?
|
||||||
open weak var cancelButton: UIButton?
|
open weak var cancelButton: UIButton?
|
||||||
|
|
||||||
var passcodeFailedAttempts = 0
|
|
||||||
var isCancellable: Bool = false
|
var isCancellable: Bool = false
|
||||||
|
|
||||||
|
private let passwordStore = PasswordStore.shared
|
||||||
|
|
||||||
open override func loadView() {
|
open override func loadView() {
|
||||||
super.loadView()
|
super.loadView()
|
||||||
|
|
@ -37,17 +38,9 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
||||||
self.view.addSubview(passcodeLabel)
|
self.view.addSubview(passcodeLabel)
|
||||||
self.passcodeLabel = passcodeLabel
|
self.passcodeLabel = passcodeLabel
|
||||||
|
|
||||||
let passcodeWrongAttemptsLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
|
|
||||||
passcodeWrongAttemptsLabel.text = ""
|
|
||||||
passcodeWrongAttemptsLabel.textColor = Globals.red
|
|
||||||
passcodeWrongAttemptsLabel.textAlignment = .center
|
|
||||||
passcodeWrongAttemptsLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
self.view.addSubview(passcodeWrongAttemptsLabel)
|
|
||||||
self.passcodeWrongAttemptsLabel = passcodeWrongAttemptsLabel
|
|
||||||
|
|
||||||
let passcodeTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
|
let passcodeTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
|
||||||
passcodeTextField.borderStyle = UITextField.BorderStyle.roundedRect
|
passcodeTextField.borderStyle = UITextField.BorderStyle.roundedRect
|
||||||
passcodeTextField.placeholder = "Passcode".localize()
|
passcodeTextField.placeholder = "EnterPasscode".localize()
|
||||||
passcodeTextField.isSecureTextEntry = true
|
passcodeTextField.isSecureTextEntry = true
|
||||||
passcodeTextField.clearButtonMode = UITextField.ViewMode.whileEditing
|
passcodeTextField.clearButtonMode = UITextField.ViewMode.whileEditing
|
||||||
passcodeTextField.delegate = self
|
passcodeTextField.delegate = self
|
||||||
|
|
@ -87,6 +80,16 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
||||||
biometryAuthButton.isHidden = false
|
biometryAuthButton.isHidden = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let forgotPasscodeButton = UIButton(type: .custom)
|
||||||
|
forgotPasscodeButton.setTitle("ForgotYourPasscode?".localize(), for: .normal)
|
||||||
|
forgotPasscodeButton.setTitleColor(Globals.blue, for: .normal)
|
||||||
|
forgotPasscodeButton.addTarget(self, action: #selector(forgotPasscodeButtonPressedAction(_:)), for: .touchUpInside)
|
||||||
|
// hide the forgotPasscodeButton if the native app is running
|
||||||
|
forgotPasscodeButton.isHidden = self.isCancellable
|
||||||
|
forgotPasscodeButton.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
self.view.addSubview(forgotPasscodeButton)
|
||||||
|
self.forgotPasscodeButton = forgotPasscodeButton
|
||||||
|
|
||||||
let cancelButton = UIButton(type: .custom)
|
let cancelButton = UIButton(type: .custom)
|
||||||
cancelButton.setTitle("Cancel".localize(), for: .normal)
|
cancelButton.setTitle("Cancel".localize(), for: .normal)
|
||||||
|
|
@ -109,20 +112,20 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
||||||
passcodeLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
|
passcodeLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
|
||||||
passcodeLabel.bottomAnchor.constraint(equalTo: passcodeTextField.topAnchor),
|
passcodeLabel.bottomAnchor.constraint(equalTo: passcodeTextField.topAnchor),
|
||||||
// below passcode
|
// below passcode
|
||||||
passcodeWrongAttemptsLabel.widthAnchor.constraint(equalToConstant: 300),
|
biometryAuthButton.widthAnchor.constraint(equalToConstant: 300),
|
||||||
passcodeWrongAttemptsLabel.heightAnchor.constraint(equalToConstant: 40),
|
|
||||||
passcodeWrongAttemptsLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
|
|
||||||
passcodeWrongAttemptsLabel.topAnchor.constraint(equalTo: passcodeTextField.bottomAnchor),
|
|
||||||
// bottom of the screen
|
|
||||||
biometryAuthButton.widthAnchor.constraint(equalToConstant: 150),
|
|
||||||
biometryAuthButton.heightAnchor.constraint(equalToConstant: 40),
|
biometryAuthButton.heightAnchor.constraint(equalToConstant: 40),
|
||||||
biometryAuthButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
|
biometryAuthButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
|
||||||
biometryAuthButton.bottomAnchor.constraint(equalTo: self.view.safeBottomAnchor, constant: -40),
|
biometryAuthButton.topAnchor.constraint(equalTo: passcodeTextField.bottomAnchor),
|
||||||
// cancel (top-left of the screen)
|
// cancel (top-left of the screen)
|
||||||
cancelButton.widthAnchor.constraint(equalToConstant: 150),
|
cancelButton.widthAnchor.constraint(equalToConstant: 150),
|
||||||
cancelButton.heightAnchor.constraint(equalToConstant: 40),
|
cancelButton.heightAnchor.constraint(equalToConstant: 40),
|
||||||
cancelButton.topAnchor.constraint(equalTo: self.view.safeTopAnchor),
|
cancelButton.topAnchor.constraint(equalTo: self.view.safeTopAnchor),
|
||||||
cancelButton.leftAnchor.constraint(equalTo: self.view.safeLeftAnchor, constant: 20)
|
cancelButton.leftAnchor.constraint(equalTo: self.view.safeLeftAnchor, constant: 20),
|
||||||
|
// bottom of the screen
|
||||||
|
forgotPasscodeButton.widthAnchor.constraint(equalToConstant: 300),
|
||||||
|
forgotPasscodeButton.heightAnchor.constraint(equalToConstant: 40),
|
||||||
|
forgotPasscodeButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
|
||||||
|
forgotPasscodeButton.bottomAnchor.constraint(equalTo: self.view.safeBottomAnchor, constant: -40)
|
||||||
])
|
])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -162,8 +165,6 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
||||||
// MARK: - PasscodeLockDelegate
|
// MARK: - PasscodeLockDelegate
|
||||||
|
|
||||||
open func passcodeLockDidSucceed() {
|
open func passcodeLockDidSucceed() {
|
||||||
passcodeFailedAttempts = 0
|
|
||||||
passcodeWrongAttemptsLabel?.text = ""
|
|
||||||
dismissPasscodeLock(completionHandler: successCallback)
|
dismissPasscodeLock(completionHandler: successCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,11 +191,42 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func forgotPasscodeButtonPressedAction(_ uiButton: UIButton) {
|
||||||
|
let alert = UIAlertController(title: "ResetPass".localize(), message: "ResetPassExplanation.".localize(), preferredStyle: UIAlertController.Style.alert)
|
||||||
|
alert.addAction(UIAlertAction(title: "ErasePasswordStoreData".localize(), style: UIAlertAction.Style.destructive, handler: {[unowned self] (action) -> Void in
|
||||||
|
let myContext = LAContext()
|
||||||
|
var error: NSError?
|
||||||
|
// If the device passcode is not set, reset the app.
|
||||||
|
guard myContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else {
|
||||||
|
self.passwordStore.erase()
|
||||||
|
self.passcodeLockDidSucceed()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// If the device passcode is set, authentication is required.
|
||||||
|
myContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "ErasePasswordStoreData".localize()) { (success, error) in
|
||||||
|
if success {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
// User authenticated successfully, take appropriate action
|
||||||
|
self.passwordStore.erase()
|
||||||
|
self.passcodeLockDidSucceed()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
Utils.alert(title: "Error".localize(), message: error?.localizedDescription ?? "", controller: self, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
alert.addAction(UIAlertAction(title: "Dismiss".localize(), style: UIAlertAction.Style.cancel, handler:nil))
|
||||||
|
self.present(alert, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
public override func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
public override func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||||
if textField == passcodeTextField {
|
if textField == passcodeTextField {
|
||||||
if !PasscodeLock.shared.check(passcode: textField.text ?? "") {
|
if !PasscodeLock.shared.check(passcode: textField.text ?? "") {
|
||||||
passcodeFailedAttempts = passcodeFailedAttempts + 1
|
self.passcodeTextField?.placeholder =
|
||||||
passcodeWrongAttemptsLabel?.text = "WrongAttempts(%d)".localize(passcodeFailedAttempts)
|
"TryAgain".localize()
|
||||||
|
self.passcodeTextField?.text = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
textField.resignFirstResponder()
|
textField.resignFirstResponder()
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,11 @@ public class PasscodeLock {
|
||||||
private static let identifier = Globals.bundleIdentifier + "passcode"
|
private static let identifier = Globals.bundleIdentifier + "passcode"
|
||||||
|
|
||||||
/// Cached passcode to avoid frequent access to Keychain
|
/// Cached passcode to avoid frequent access to Keychain
|
||||||
private var passcode: String? = AppKeychain.shared.get(for: PasscodeLock.identifier)
|
private var passcode: String? {
|
||||||
|
get {
|
||||||
|
AppKeychain.shared.get(for: PasscodeLock.identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructor used to migrate passcode from SharedDefaults to Keychain
|
/// Constructor used to migrate passcode from SharedDefaults to Keychain
|
||||||
private init() {
|
private init() {
|
||||||
|
|
@ -28,7 +32,6 @@ public class PasscodeLock {
|
||||||
|
|
||||||
public func save(passcode: String) {
|
public func save(passcode: String) {
|
||||||
AppKeychain.shared.add(string: passcode, for: PasscodeLock.identifier)
|
AppKeychain.shared.add(string: passcode, for: PasscodeLock.identifier)
|
||||||
self.passcode = passcode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func check(passcode: String) -> Bool {
|
public func check(passcode: String) -> Bool {
|
||||||
|
|
@ -37,6 +40,5 @@ public class PasscodeLock {
|
||||||
|
|
||||||
public func delete() {
|
public func delete() {
|
||||||
AppKeychain.shared.removeContent(for: PasscodeLock.identifier)
|
AppKeychain.shared.removeContent(for: PasscodeLock.identifier)
|
||||||
passcode = nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue