From da3c4f0bc0849dc61d08454b0728c9d4da1d7e82 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Mon, 29 Jan 2018 03:23:34 +0800 Subject: [PATCH] Self-maintained passcode lock - No cancel button anywhere in the passcode lock yet - Poor UI --- Cartfile | 2 - pass.xcodeproj/project.pbxproj | 65 ++++---- pass/AppDelegate.swift | 4 +- pass/Base.lproj/Main.storyboard | 22 +-- .../SettingsTableViewController.swift | 139 ++++++++---------- passExtension/PasscodeExtensionDisplay.swift | 38 +---- .../Controllers/PasscodeLockPresenter.swift | 59 ++++++++ .../PasscodeLockViewController.swift | 125 ++++++++++++++++ ...ockRepository.swift => PasscodeLock.swift} | 28 ++-- .../Models/PasscodeLockConfiguration.swift | 26 ---- 10 files changed, 298 insertions(+), 210 deletions(-) create mode 100644 passKit/Controllers/PasscodeLockPresenter.swift create mode 100644 passKit/Controllers/PasscodeLockViewController.swift rename passKit/Models/{PasscodeLockRepository.swift => PasscodeLock.swift} (50%) delete mode 100644 passKit/Models/PasscodeLockConfiguration.swift diff --git a/Cartfile b/Cartfile index 42a324b..dba1439 100644 --- a/Cartfile +++ b/Cartfile @@ -1,8 +1,6 @@ github "SVProgressHUD/SVProgressHUD" github "radex/SwiftyUserDefaults" github "libgit2/objective-git" -# github "zahlz/SwiftPasscodeLock" "master" -github "yishilin14/SwiftPasscodeLock" "app-extension-support" github "bitserf/FavIcon" github "kishikawakatsumi/KeychainAccess" github "mattrubin/OneTimePassword" diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index f07384e..008af7e 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -29,8 +29,6 @@ A26700371EEC475600176B8A /* passProcessor.js in Resources */ = {isa = PBXBuildFile; fileRef = A26700351EEC475600176B8A /* passProcessor.js */; }; A2802BF91E70813A00879216 /* SliderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2802BF71E70813A00879216 /* SliderTableViewCell.swift */; }; A2802BFA1E70813A00879216 /* SliderTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A2802BF81E70813A00879216 /* SliderTableViewCell.xib */; }; - A28C66651EF109D600A398A1 /* PasscodeLockConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A28C66631EF109D600A398A1 /* PasscodeLockConfiguration.swift */; }; - A28C66661EF109D600A398A1 /* PasscodeLockRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = A28C66641EF109D600A398A1 /* PasscodeLockRepository.swift */; }; A28C66681EF10EC900A398A1 /* PasscodeExtensionDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */; }; A2A61C131EEF90CB00CFE063 /* Base32.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A262A58C1E68749C006B0890 /* Base32.framework */; }; A2A61C151EEF90CB00CFE063 /* KeychainAccess.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCA742D91E599ED400D54E16 /* KeychainAccess.framework */; }; @@ -39,6 +37,9 @@ A2A61C201EEFABAD00CFE063 /* UtilsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2A61C1F1EEFABAD00CFE063 /* UtilsExtension.swift */; }; A2A61C2C1EEFDF3300CFE063 /* ExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2A61C2B1EEFDF3300CFE063 /* ExtensionViewController.swift */; }; A2A7813F1E97DBD9001311F5 /* QRScannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2A7813E1E97DBD9001311F5 /* QRScannerController.swift */; }; + A2C532BB201E5A9600DB9F53 /* PasscodeLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2C532BA201E5A9600DB9F53 /* PasscodeLock.swift */; }; + A2C532BE201E5AA100DB9F53 /* PasscodeLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2C532BC201E5AA000DB9F53 /* PasscodeLockViewController.swift */; }; + A2C532BF201E5AA100DB9F53 /* PasscodeLockPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2C532BD201E5AA100DB9F53 /* PasscodeLockPresenter.swift */; }; A2F4E2141EED800F0011986E /* GitCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2F4E2101EED800F0011986E /* GitCredential.swift */; }; A2F4E2151EED800F0011986E /* Password.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2F4E2111EED800F0011986E /* Password.swift */; }; A2F4E2161EED800F0011986E /* PasswordEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2F4E2121EED800F0011986E /* PasswordEntity.swift */; }; @@ -61,7 +62,6 @@ DC037CC01E4ED4E100609409 /* TextViewTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DC037CBE1E4ED4E100609409 /* TextViewTableViewCell.xib */; }; DC13B1511E8640810097803F /* passTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC13B1501E8640810097803F /* passTests.swift */; }; DC193FFA1E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC193FF91E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift */; }; - DC193FFC1E49E0340077E0A3 /* PasscodeLock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC193FFB1E49E0340077E0A3 /* PasscodeLock.framework */; }; DC3E64E61E656F11009A83DE /* CommitLogsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3E64E51E656F11009A83DE /* CommitLogsTableViewController.swift */; }; DC4914961E434301007FF592 /* LabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4914941E434301007FF592 /* LabelTableViewCell.swift */; }; DC4914991E434600007FF592 /* PasswordDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4914981E434600007FF592 /* PasswordDetailTableViewController.swift */; }; @@ -191,8 +191,6 @@ A26700351EEC475600176B8A /* passProcessor.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = passProcessor.js; sourceTree = ""; }; A2802BF71E70813A00879216 /* SliderTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SliderTableViewCell.swift; sourceTree = ""; }; A2802BF81E70813A00879216 /* SliderTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SliderTableViewCell.xib; sourceTree = ""; }; - A28C66631EF109D600A398A1 /* PasscodeLockConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PasscodeLockConfiguration.swift; path = Models/PasscodeLockConfiguration.swift; sourceTree = ""; }; - A28C66641EF109D600A398A1 /* PasscodeLockRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PasscodeLockRepository.swift; path = Models/PasscodeLockRepository.swift; sourceTree = ""; }; A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeExtensionDisplay.swift; sourceTree = ""; }; A2A61C0C1EEF8DFE00CFE063 /* libPods-passExtension.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-passExtension.a"; path = "../../Library/Developer/Xcode/DerivedData/pass-fwlmfsjroyvbfhdyqmglrwfhvjli/Build/Products/Debug-iphonesimulator/libPods-passExtension.a"; sourceTree = ""; }; A2A61C101EEF8E3500CFE063 /* libPods-passKit.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-passKit.a"; path = "Pods/../build/Debug-iphoneos/libPods-passKit.a"; sourceTree = ""; }; @@ -200,6 +198,9 @@ A2A61C2B1EEFDF3300CFE063 /* ExtensionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionViewController.swift; sourceTree = ""; }; A2A7813E1E97DBD9001311F5 /* QRScannerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRScannerController.swift; sourceTree = ""; }; A2BC54C71EEE5669001FAFBD /* Objective-CBridgingHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Objective-CBridgingHeader.h"; sourceTree = ""; }; + A2C532BA201E5A9600DB9F53 /* PasscodeLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PasscodeLock.swift; path = Models/PasscodeLock.swift; sourceTree = ""; }; + A2C532BC201E5AA000DB9F53 /* PasscodeLockViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PasscodeLockViewController.swift; path = Controllers/PasscodeLockViewController.swift; sourceTree = ""; }; + A2C532BD201E5AA100DB9F53 /* PasscodeLockPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PasscodeLockPresenter.swift; path = Controllers/PasscodeLockPresenter.swift; sourceTree = ""; }; A2F4E2101EED800F0011986E /* GitCredential.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GitCredential.swift; path = Models/GitCredential.swift; sourceTree = ""; }; A2F4E2111EED800F0011986E /* Password.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Password.swift; path = Models/Password.swift; sourceTree = ""; }; A2F4E2121EED800F0011986E /* PasswordEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PasswordEntity.swift; path = Models/PasswordEntity.swift; sourceTree = ""; }; @@ -315,7 +316,6 @@ files = ( A260758D1EEC6F34005DB03E /* passKit.framework in Frameworks */, DCC408C71E307DBB00F29B0E /* SVProgressHUD.framework in Frameworks */, - DC193FFC1E49E0340077E0A3 /* PasscodeLock.framework in Frameworks */, 18F19A67B0C07F13C17169E0 /* Pods_pass.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -344,11 +344,12 @@ A26075791EEC6F34005DB03E /* passKit */ = { isa = PBXGroup; children = ( + A2C532B9201DD07500DB9F53 /* Controllers */, + A2F4E20F1EED7F0A0011986E /* Helpers */, + A260757B1EEC6F34005DB03E /* Info.plist */, A2F4E20E1EED7F040011986E /* Models */, A26075A51EEC7125005DB03E /* pass.xcdatamodeld */, - A2F4E20F1EED7F0A0011986E /* Helpers */, A260757A1EEC6F34005DB03E /* passKit.h */, - A260757B1EEC6F34005DB03E /* Info.plist */, ); path = passKit; sourceTree = ""; @@ -376,11 +377,19 @@ path = passExtension; sourceTree = ""; }; + A2C532B9201DD07500DB9F53 /* Controllers */ = { + isa = PBXGroup; + children = ( + A2C532BD201E5AA100DB9F53 /* PasscodeLockPresenter.swift */, + A2C532BC201E5AA000DB9F53 /* PasscodeLockViewController.swift */, + ); + name = Controllers; + sourceTree = ""; + }; A2F4E20E1EED7F040011986E /* Models */ = { isa = PBXGroup; children = ( - A28C66631EF109D600A398A1 /* PasscodeLockConfiguration.swift */, - A28C66641EF109D600A398A1 /* PasscodeLockRepository.swift */, + A2C532BA201E5A9600DB9F53 /* PasscodeLock.swift */, A2F4E2101EED800F0011986E /* GitCredential.swift */, A2F4E2111EED800F0011986E /* Password.swift */, A2F4E2121EED800F0011986E /* PasswordEntity.swift */, @@ -429,30 +438,30 @@ DC19400C1E4B39400077E0A3 /* Controllers */ = { isa = PBXGroup; children = ( - DCD3C65D1EFB9BB400CBE842 /* SettingsSplitViewController.swift */, - DC3E64E51E656F11009A83DE /* CommitLogsTableViewController.swift */, - DC5F385A1E56AADB00C69ACA /* PGPKeyArmorSettingTableViewController.swift */, DC037CB11E4CAB1700609409 /* AboutRepositoryTableViewController.swift */, DC962CDE1E4B62C10033B5D8 /* AboutTableViewController.swift */, DC037CB71E4DD1A500609409 /* AddPasswordTableViewController.swift */, DC193FF91E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift */, DC037CA71E4B898100609409 /* BasicStaticTableViewController.swift */, + DC3E64E51E656F11009A83DE /* CommitLogsTableViewController.swift */, DCFB77A61E502DF9008DE471 /* EditPasswordTableViewController.swift */, DC037CAF1E4CA51F00609409 /* GeneralSettingsTableViewController.swift */, + A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift */, DCA049991E335CC800522E8F /* GitServerSettingTableViewController.swift */, + DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */, DC037CA51E4B883900609409 /* OpenSourceComponentsTableViewController.swift */, + A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */, DC4914981E434600007FF592 /* PasswordDetailTableViewController.swift */, - DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */, DCFB77A81E502FF6008DE471 /* PasswordEditorTableViewController.swift */, DC5734AD1E439AD400D09270 /* PasswordsViewController.swift */, + DC5F385A1E56AADB00C69ACA /* PGPKeyArmorSettingTableViewController.swift */, DCA0499B1E3362F400522E8F /* PGPKeySettingTableViewController.swift */, + A2A7813E1E97DBD9001311F5 /* QRScannerController.swift */, + DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */, + DCD3C65D1EFB9BB400CBE842 /* SettingsSplitViewController.swift */, DCAAF7441E2FA66800AB94BC /* SettingsTableViewController.swift */, DC037CA91E4B8EAE00609409 /* SpecialThanksTableViewController.swift */, DC8963BF1E38EEB900828B09 /* SSHKeySettingTableViewController.swift */, - DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */, - A2A7813E1E97DBD9001311F5 /* QRScannerController.swift */, - A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */, - A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift */, ); path = Controllers; sourceTree = ""; @@ -477,21 +486,21 @@ DC19400F1E4B3A9E0077E0A3 /* Views */ = { isa = PBXGroup; children = ( - A2802BF71E70813A00879216 /* SliderTableViewCell.swift */, - A2802BF81E70813A00879216 /* SliderTableViewCell.xib */, + DCFB77AA1E503729008DE471 /* ContentTableViewCell.swift */, DCFB779C1E4F40C7008DE471 /* FillPasswordTableViewCell.swift */, DCFB779D1E4F40C7008DE471 /* FillPasswordTableViewCell.xib */, - DCFB77981E4F3BCF008DE471 /* TitleTextFieldTableViewCell.swift */, - DCFB77991E4F3BCF008DE471 /* TitleTextFieldTableViewCell.xib */, DC4914941E434301007FF592 /* LabelTableViewCell.swift */, DCDDEAAF1E4639F300F68193 /* LabelTableViewCell.xib */, - DC037CB91E4DD47B00609409 /* TextFieldTableViewCell.swift */, - DC037CBA1E4DD47B00609409 /* TextFieldTableViewCell.xib */, DCDDEAB11E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift */, DCFB77A21E500D9C008DE471 /* PasswordDetailTitleTableViewCell.xib */, + A2802BF71E70813A00879216 /* SliderTableViewCell.swift */, + A2802BF81E70813A00879216 /* SliderTableViewCell.xib */, + DC037CB91E4DD47B00609409 /* TextFieldTableViewCell.swift */, + DC037CBA1E4DD47B00609409 /* TextFieldTableViewCell.xib */, DC037CBD1E4ED4E100609409 /* TextViewTableViewCell.swift */, DC037CBE1E4ED4E100609409 /* TextViewTableViewCell.xib */, - DCFB77AA1E503729008DE471 /* ContentTableViewCell.swift */, + DCFB77981E4F3BCF008DE471 /* TitleTextFieldTableViewCell.swift */, + DCFB77991E4F3BCF008DE471 /* TitleTextFieldTableViewCell.xib */, ); path = Views; sourceTree = ""; @@ -984,7 +993,6 @@ "$(SRCROOT)/Carthage/Build/iOS/SVProgressHUD.framework", "$(SRCROOT)/Carthage/Build/iOS/SwiftyUserDefaults.framework", "$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework", - "$(SRCROOT)/Carthage/Build/iOS/PasscodeLock.framework", "$(SRCROOT)/Carthage/Build/iOS/FavIcon.framework", "$(SRCROOT)/Carthage/Build/iOS/KeychainAccess.framework", "$(SRCROOT)/Carthage/Build/iOS/OneTimePassword.framework", @@ -1019,14 +1027,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A28C66661EF109D600A398A1 /* PasscodeLockRepository.swift in Sources */, + A2C532BB201E5A9600DB9F53 /* PasscodeLock.swift in Sources */, A2F4E2151EED800F0011986E /* Password.swift in Sources */, - A28C66651EF109D600A398A1 /* PasscodeLockConfiguration.swift in Sources */, A26075AD1EEC7125005DB03E /* pass.xcdatamodeld in Sources */, A2F4E21E1EED80160011986E /* AppError.swift in Sources */, A2F4E2171EED800F0011986E /* PasswordStore.swift in Sources */, A2F4E2211EED80160011986E /* NotificationNames.swift in Sources */, A2F4E2221EED80160011986E /* UITextFieldExtension.swift in Sources */, + A2C532BF201E5AA100DB9F53 /* PasscodeLockPresenter.swift in Sources */, + A2C532BE201E5AA100DB9F53 /* PasscodeLockViewController.swift in Sources */, A2F4E2201EED80160011986E /* Globals.swift in Sources */, A2F4E2231EED80160011986E /* Utils.swift in Sources */, A2F4E21F1EED80160011986E /* DefaultsKeys.swift in Sources */, diff --git a/pass/AppDelegate.swift b/pass/AppDelegate.swift index 9b062ee..3ef0c33 100644 --- a/pass/AppDelegate.swift +++ b/pass/AppDelegate.swift @@ -8,7 +8,6 @@ import UIKit import CoreData -import PasscodeLock import SVProgressHUD import passKit @@ -21,7 +20,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } lazy var passcodeLockPresenter: PasscodeLockPresenter = { - let presenter = PasscodeLockPresenter(mainWindow: self.window, configuration: PasscodeLockConfiguration.shared) + let presenter = PasscodeLockPresenter(mainWindow: self.window) return presenter }() @@ -92,7 +91,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. - passcodeLockPresenter = PasscodeLockPresenter(mainWindow: self.window, configuration: PasscodeLockConfiguration.shared) passcodeLockPresenter.present(windowLevel: UIApplication.shared.windows.last?.windowLevel) } diff --git a/pass/Base.lproj/Main.storyboard b/pass/Base.lproj/Main.storyboard index c9f6f36..23ca47e 100644 --- a/pass/Base.lproj/Main.storyboard +++ b/pass/Base.lproj/Main.storyboard @@ -195,29 +195,12 @@ - - - - - - - - - - - - + @@ -241,7 +224,7 @@ - + @@ -273,7 +256,6 @@ - diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 86d6e9a..536a25c 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -9,25 +9,16 @@ import UIKit import SVProgressHUD import CoreData -import PasscodeLock -import LocalAuthentication import passKit class SettingsTableViewController: UITableViewController, UITabBarControllerDelegate { - - lazy var touchIDSwitch: UISwitch = { - let uiSwitch = UISwitch(frame: CGRect.zero) - uiSwitch.onTintColor = Globals.blue - uiSwitch.addTarget(self, action: #selector(touchIDSwitchAction), for: UIControlEvents.valueChanged) - return uiSwitch - }() - @IBOutlet weak var pgpKeyTableViewCell: UITableViewCell! - @IBOutlet weak var touchIDTableViewCell: UITableViewCell! @IBOutlet weak var passcodeTableViewCell: UITableViewCell! @IBOutlet weak var passwordRepositoryTableViewCell: UITableViewCell! + var setPasscodeLockAlert: UIAlertController? + let passwordStore = PasswordStore.shared - var passcodeLockConfig = PasscodeLockConfiguration.shared + var passcodeLock = PasscodeLock.shared func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { navigationController?.popViewController(animated: true) @@ -123,14 +114,6 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - // Security section, hide TouchID if the device doesn't support - if section == 1 { - if hasTouchID() { - return 2 - } else { - return 1 - } - } return super.tableView(tableView, numberOfRowsInSection: section) } @@ -138,10 +121,9 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: .passwordStoreErased, object: nil) self.passwordRepositoryTableViewCell.detailTextLabel?.text = SharedDefaults[.gitURL]?.host - touchIDTableViewCell.accessoryView = touchIDSwitch setPGPKeyTableViewCellDetailText() setPasswordRepositoryTableViewCellDetailText() - setPasscodeLockTouchIDCells() + setPasscodeLockCell() } override func viewWillAppear(_ animated: Bool) { @@ -149,38 +131,11 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele tabBarController!.delegate = self } - private func hasTouchID() -> Bool { - let context = LAContext() - var error: NSError? - if context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: &error) { - return true - } else { - switch error!.code { - case LAError.Code.touchIDNotEnrolled.rawValue: - return true - case LAError.Code.passcodeNotSet.rawValue: - return true - default: - return false - } - } - } - - private func isTouchIDEnabled() -> Bool { - let context = LAContext() - return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) - } - - private func setPasscodeLockTouchIDCells() { - if passcodeLockConfig.repository.hasPasscode { + private func setPasscodeLockCell() { + if passcodeLock.hasPasscode { self.passcodeTableViewCell.detailTextLabel?.text = "On" - passcodeLockConfig.isTouchIDAllowed = SharedDefaults[.isTouchIDOn] - touchIDSwitch.isOn = SharedDefaults[.isTouchIDOn] } else { self.passcodeTableViewCell.detailTextLabel?.text = "Off" - SharedDefaults[.isTouchIDOn] = false - passcodeLockConfig.isTouchIDAllowed = SharedDefaults[.isTouchIDOn] - touchIDSwitch.isOn = SharedDefaults[.isTouchIDOn] } } @@ -203,10 +158,10 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele @objc func actOnPasswordStoreErasedNotification() { setPGPKeyTableViewCellDetailText() setPasswordRepositoryTableViewCellDetailText() - setPasscodeLockTouchIDCells() + setPasscodeLockCell() let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window, configuration: passcodeLockConfig) + appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window) } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -221,22 +176,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele } tableView.deselectRow(at: indexPath, animated: true) } - - @objc func touchIDSwitchAction(uiSwitch: UISwitch) { - if !passcodeLockConfig.repository.hasPasscode || !isTouchIDEnabled() { - // switch off - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { - uiSwitch.isOn = SharedDefaults[.isTouchIDOn] // SharedDefaults[.isTouchIDOn] should be false - Utils.alert(title: "Notice", message: "Please enable Touch ID of your phone and setup the passcode lock for Pass.", controller: self, completion: nil) - } - } else { - SharedDefaults[.isTouchIDOn] = uiSwitch.isOn - passcodeLockConfig.isTouchIDAllowed = SharedDefaults[.isTouchIDOn] - } - let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window, configuration: passcodeLockConfig) - } - + func showPGPKeyActionSheet() { let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) var urlActionTitle = "Download from URL" @@ -314,21 +254,22 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele } func showPasscodeActionSheet() { - let passcodeChangeViewController = PasscodeLockViewController(state: .change, configuration: passcodeLockConfig) - let passcodeRemoveViewController = PasscodeLockViewController(state: .remove, configuration: passcodeLockConfig) - let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let passcodeRemoveViewController = PasscodeLockViewController() + + let removePasscodeAction = UIAlertAction(title: "Remove Passcode", style: .destructive) { [weak self] _ in - passcodeRemoveViewController.successCallback = { _ in - self?.setPasscodeLockTouchIDCells() + passcodeRemoveViewController.successCallback = { + self?.passcodeLock.delete() + self?.setPasscodeLockCell() let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window, configuration: (self?.passcodeLockConfig)!) + appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window) } self?.present(passcodeRemoveViewController, animated: true, completion: nil) } let changePasscodeAction = UIAlertAction(title: "Change Passcode", style: .default) { [weak self] _ in - self?.present(passcodeChangeViewController, animated: true, completion: nil) + self?.setPasscodeLock() } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) @@ -339,12 +280,50 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele optionMenu.popoverPresentationController?.sourceRect = passcodeTableViewCell.bounds self.present(optionMenu, animated: true, completion: nil) } + + @objc func alertTextFieldDidChange(_ sender: UITextField) { + // check whether we should enable the Save button in setPasscodeLockAlert + if let setPasscodeLockAlert = self.setPasscodeLockAlert, + let setPasscodeLockAlertTextFields0 = setPasscodeLockAlert.textFields?[0], + let setPasscodeLockAlertTextFields1 = setPasscodeLockAlert.textFields?[1] { + if sender == setPasscodeLockAlertTextFields0 || sender == setPasscodeLockAlertTextFields1 { + // two passwords should be the same, and length >= 4 + let passcodeText = setPasscodeLockAlertTextFields0.text! + let passcodeConfirmationText = setPasscodeLockAlertTextFields1.text! + setPasscodeLockAlert.actions[0].isEnabled = passcodeText == passcodeConfirmationText && passcodeText.count >= 4 + } + } + } func setPasscodeLock() { - let passcodeSetViewController = PasscodeLockViewController(state: .set, configuration: passcodeLockConfig) - passcodeSetViewController.successCallback = { _ in - self.setPasscodeLockTouchIDCells() + // prepare the alert for setting the passcode + setPasscodeLockAlert = UIAlertController(title: "Set passcode", message: "Fill in your passcode for Pass (at least 4 characters)", preferredStyle: .alert) + setPasscodeLockAlert?.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in + textField.placeholder = "Password" + textField.isSecureTextEntry = true + textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControlEvents.editingChanged) + }) + setPasscodeLockAlert?.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in + textField.placeholder = "Password Confirmation" + textField.isSecureTextEntry = true + textField.addTarget(self, action: #selector(self.alertTextFieldDidChange(_:)), for: UIControlEvents.editingChanged) + }) + + // save action + let saveAction = UIAlertAction(title: "Save", style: .default) { (action:UIAlertAction) -> Void in + let passcode: String = self.setPasscodeLockAlert!.textFields![0].text! + self.passcodeLock.save(passcode: passcode) + // refresh the passcode lock cell ("On") + self.setPasscodeLockCell() } - present(passcodeSetViewController, animated: true, completion: nil) + saveAction.isEnabled = false // disable the Save button by default + + // cancel action + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) + + // present + setPasscodeLockAlert?.addAction(saveAction) + setPasscodeLockAlert?.addAction(cancelAction) + self.present(setPasscodeLockAlert!, animated: true, completion: nil) } } diff --git a/passExtension/PasscodeExtensionDisplay.swift b/passExtension/PasscodeExtensionDisplay.swift index d3a448a..d0ea6c0 100644 --- a/passExtension/PasscodeExtensionDisplay.swift +++ b/passExtension/PasscodeExtensionDisplay.swift @@ -7,46 +7,14 @@ // import Foundation -import PasscodeLock import passKit -// add a cancel button in the passcode lock view -struct CancelableEnterPasscodeState: PasscodeLockStateType { - let title: String = "Enter passcode" - let description: String = "Enter passcode" - let isCancellableAction = true - var isTouchIDAllowed = true - mutating func accept(passcode: String, from lock: PasscodeLockType) { - if lock.repository.check(passcode: passcode) { - lock.delegate?.passcodeLockDidSucceed(lock) - } - } -} - -// cancel means cancel the extension -class PasscodeLockViewControllerForExtension: PasscodeLockViewController { - var originalExtensionContest: NSExtensionContext? - public convenience init(extensionContext: NSExtensionContext?, state: PasscodeLockStateType, configuration: PasscodeLockConfigurationType, animateOnDismiss: Bool = true) { - self.init(state: state, configuration: configuration, animateOnDismiss: animateOnDismiss) - originalExtensionContest = extensionContext - } - override func viewDidLoad() { - super.viewDidLoad() - cancelButton?.removeTarget(nil, action: nil, for: .allEvents) - cancelButton?.addTarget(self, action: #selector(cancelExtension), for: .touchUpInside) - } - @objc func cancelExtension() { - originalExtensionContest?.completeRequest(returningItems: [], completionHandler: nil) - } -} - class PasscodeExtensionDisplay { private var isPasscodePresented = false - private let passcodeLockVC: PasscodeLockViewControllerForExtension + private let passcodeLockVC: PasscodeLockViewController init(extensionContext: NSExtensionContext?) { - let cancelableEnter = CancelableEnterPasscodeState() - passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext, state: cancelableEnter, configuration: PasscodeLockConfiguration.shared) + passcodeLockVC = PasscodeLockViewController() passcodeLockVC.dismissCompletionCallback = { [weak self] in self?.dismiss() } @@ -54,7 +22,7 @@ class PasscodeExtensionDisplay { // present the passcode lock view if passcode is set and the view controller is not presented func presentPasscodeLockIfNeeded(_ extensionVC: ExtensionViewController) { - guard PasscodeLockConfiguration.shared.repository.hasPasscode && !isPasscodePresented == true else { + guard PasscodeLock.shared.hasPasscode && !isPasscodePresented == true else { return } isPasscodePresented = true diff --git a/passKit/Controllers/PasscodeLockPresenter.swift b/passKit/Controllers/PasscodeLockPresenter.swift new file mode 100644 index 0000000..ce1b88b --- /dev/null +++ b/passKit/Controllers/PasscodeLockPresenter.swift @@ -0,0 +1,59 @@ +// +// PasscodeLockPresenter.swift +// PasscodeLock +// +// Created by Yanko Dimitrov on 8/29/15. +// Copyright © 2015 Yanko Dimitrov. All rights reserved. +// + +import UIKit + +open class PasscodeLockPresenter { + + fileprivate var mainWindow: UIWindow? + + fileprivate lazy var passcodeLockWindow = UIWindow(frame: UIScreen.main.bounds) + + open var isPasscodePresented = false + open let passcodeLockVC: PasscodeLockViewController + + public init(mainWindow window: UIWindow?, viewController: PasscodeLockViewController) { + mainWindow = window + passcodeLockVC = viewController + } + + public convenience init(mainWindow window: UIWindow?) { + let passcodeLockVC = PasscodeLockViewController() + self.init(mainWindow: window, viewController: passcodeLockVC) + } + + open func present(windowLevel: CGFloat?) { + guard PasscodeLock.shared.hasPasscode else { return } + guard !isPasscodePresented else { return } + + isPasscodePresented = true + + mainWindow?.endEditing(true) + moveWindowsToFront(windowLevel: windowLevel) + passcodeLockWindow.isHidden = false + + let userDismissCompletionCallback = passcodeLockVC.dismissCompletionCallback + passcodeLockVC.dismissCompletionCallback = { [weak self] in + userDismissCompletionCallback?() + self?.dismiss() + } + passcodeLockWindow.rootViewController = passcodeLockVC + } + + open func dismiss() { + isPasscodePresented = false + passcodeLockWindow.isHidden = true + passcodeLockWindow.rootViewController = nil + } + + fileprivate func moveWindowsToFront(windowLevel: CGFloat?) { + let windowLevel = windowLevel ?? UIWindowLevelNormal + let maxWinLevel = max(windowLevel, UIWindowLevelNormal) + passcodeLockWindow.windowLevel = maxWinLevel + 1 + } +} diff --git a/passKit/Controllers/PasscodeLockViewController.swift b/passKit/Controllers/PasscodeLockViewController.swift new file mode 100644 index 0000000..534c4e3 --- /dev/null +++ b/passKit/Controllers/PasscodeLockViewController.swift @@ -0,0 +1,125 @@ +// +// PasscodeLockViewController.swift +// PasscodeLock +// +// Created by Yanko Dimitrov on 8/28/15. +// Copyright © 2015 Yanko Dimitrov. All rights reserved. +// + +import UIKit +import LocalAuthentication + +open class PasscodeLockViewController: UIViewController { + + open var dismissCompletionCallback: (()->Void)? + open var successCallback: (()->Void)? + open var cancelCallback: (()->Void)? + lazy var enterPasscodeAlert: UIAlertController = { + let enterPasscodeAlert = UIAlertController(title: "Authenticate Pass", message: "Unlock with passcode for Pass", preferredStyle: .alert) + + enterPasscodeAlert.addTextField(configurationHandler: {(_ textField: UITextField) -> Void in + textField.placeholder = "passcode" + textField.isSecureTextEntry = true + textField.addTarget(self, action: #selector(self.passcodeTextFieldDidChange(_:)), for: UIControlEvents.editingChanged) + textField.clearButtonMode = UITextFieldViewMode.whileEditing + textField.becomeFirstResponder() + }) + + let myContext = LAContext() + var authError: NSError? + if #available(iOS 8.0, macOS 10.12.1, *) { + if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) { + var biometryType = "Touch ID" + if #available(iOS 11.0, *) { + if myContext.biometryType == LABiometryType.faceID { + biometryType = "Face ID" + } + } + let bioAction = UIAlertAction(title: "Use " + biometryType, style: .default) { (action:UIAlertAction) -> Void in + self.authenticate() + } + enterPasscodeAlert.addAction(bioAction) + } + } + + return enterPasscodeAlert + }() + + open override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = UIColor.white + } + + open override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + authenticate() + } + + internal func dismissPasscodeLock(completionHandler: (() -> Void)? = nil) { + // clean up the textfield + enterPasscodeAlert.textFields?[0].text = "" + if presentingViewController?.presentedViewController == self { + // if presented as modal + dismiss(animated: true, completion: { [weak self] in + self?.dismissCompletionCallback?() + completionHandler?() + }) + // if pushed in a navigation controller + } else { + _ = navigationController?.popViewController(animated: true) + dismissCompletionCallback?() + completionHandler?() + } + } + + // MARK: - PasscodeLockDelegate + + open func passcodeLockDidSucceed() { + dismissPasscodeLock(completionHandler: successCallback) + } + + open func passcodeLockDidCancel() { + dismissPasscodeLock(completionHandler: cancelCallback) + } + + public func authenticate() { + print(enterPasscodeAlert.isBeingPresented) + + let myContext = LAContext() + let myLocalizedReasonString = "Authentication is needed to access Pass." + var authError: NSError? + + if #available(iOS 8.0, *) { + if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) { + myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in + if success { + DispatchQueue.main.async { + // user authenticated successfully, take appropriate action + self.passcodeLockDidSucceed() + } + } else { + // User did not authenticate successfully + self.showPasswordAlert() + } + } + } else { + // could not evaluate policy; look at authError and present an appropriate message to user + self.showPasswordAlert() + } + } else { + // fallback on earlier versions + self.showPasswordAlert() + } + } + + @objc func passcodeTextFieldDidChange(_ sender: UITextField) { + // check whether the passcode is correct + if PasscodeLock.shared.check(passcode: sender.text ?? "") { + self.passcodeLockDidSucceed() + } + } + + func showPasswordAlert() { + self.present(enterPasscodeAlert, animated: true, completion: nil) + } +} diff --git a/passKit/Models/PasscodeLockRepository.swift b/passKit/Models/PasscodeLock.swift similarity index 50% rename from passKit/Models/PasscodeLockRepository.swift rename to passKit/Models/PasscodeLock.swift index 6e07eae..7e55162 100644 --- a/passKit/Models/PasscodeLockRepository.swift +++ b/passKit/Models/PasscodeLock.swift @@ -1,28 +1,24 @@ // -// PasscodeRepository.swift -// pass +// PasscodeLock.swift +// PassKit // -// Created by Mingshen Sun on 7/2/2017. -// Copyright © 2017 Bob Sun. All rights reserved. +// Created by Yishi Lin on 28/1/2018. +// Copyright © 2017 Yishi Lin. All rights reserved. // import Foundation -import PasscodeLock +import LocalAuthentication -public class PasscodeLockRepository: PasscodeRepositoryType { - private let passcodeKey = "passcode.lock.passcode" +open class PasscodeLock { + public static let shared = PasscodeLock() - public var hasPasscode: Bool { - - if passcode != nil { - return true - } - - return false + fileprivate let passcodeKey = "passcode.lock.passcode" + fileprivate var passcode: String? { + return SharedDefaults[.passcodeKey] } - private var passcode: String? { - return SharedDefaults[.passcodeKey] + public var hasPasscode: Bool { + return passcode != nil } public func save(passcode: String) { diff --git a/passKit/Models/PasscodeLockConfiguration.swift b/passKit/Models/PasscodeLockConfiguration.swift deleted file mode 100644 index 9303521..0000000 --- a/passKit/Models/PasscodeLockConfiguration.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// PasscodeLockConfiguration.swift -// pass -// -// Created by Mingshen Sun on 7/2/2017. -// Copyright © 2017 Bob Sun. All rights reserved. -// - -import Foundation -import PasscodeLock - -public class PasscodeLockConfiguration: PasscodeLockConfigurationType { - - public static let shared = PasscodeLockConfiguration() - - public let repository: PasscodeRepositoryType - public let passcodeLength = 4 - public var isTouchIDAllowed = SharedDefaults[.isTouchIDOn] - - public let shouldRequestTouchIDImmediately = true - public let maximumInccorectPasscodeAttempts = 3 - - init() { - self.repository = PasscodeLockRepository() - } -}