From cfb93abffda4898bbd766986c292157a0eb509fe Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Wed, 17 Jan 2018 10:54:52 -0800 Subject: [PATCH 01/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73a639f..d0d9098 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Pass -[![GitHub release](https://img.shields.io/github/release/mssun/pass-ios.svg)](https://github.com/mssun/pass-ios/releases) +[![GitHub release](https://img.shields.io/github/release/mssun/passforios.svg)](https://github.com/mssun/passforios/releases) ![Swift 3.1](https://img.shields.io/badge/Swift-4.0-orange.svg) [![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/passforios/passforios) [![Build Status](https://travis-ci.org/mssun/passforios.svg?branch=develop)](https://travis-ci.org/mssun/passforios) From bea7157375daee3d14c049151d7e4abcb2d38e7a Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 20 Jan 2018 15:40:56 -0800 Subject: [PATCH 02/31] Version bump to 0.3.1 --- pass/Info.plist | 2 +- passExtension/Info.plist | 2 +- passKit/Info.plist | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pass/Info.plist b/pass/Info.plist index dd7b02f..843bff0 100644 --- a/pass/Info.plist +++ b/pass/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.3.0 + 0.3.1 CFBundleURLTypes diff --git a/passExtension/Info.plist b/passExtension/Info.plist index 5e96e11..e345f72 100644 --- a/passExtension/Info.plist +++ b/passExtension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.3.0 + 0.3.1 CFBundleVersion 1 NSExtension diff --git a/passKit/Info.plist b/passKit/Info.plist index caf06e5..6612af0 100644 --- a/passKit/Info.plist +++ b/passKit/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.3.0 + 0.3.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass From 30ae08bed5914b1e64fc82b627648c2f78a9f1e9 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Sun, 28 Jan 2018 11:43:00 +0800 Subject: [PATCH 03/31] Update provisioning profiles --- pass.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 24f7730..f07384e 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -1530,7 +1530,7 @@ OTHER_LDFLAGS = "${inherited}"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "2e72f4af-b935-4970-9cd3-44d4cc24b646"; + PROVISIONING_PROFILE = "6bb836f2-68a1-44cc-8c45-f6b6e53ba64a"; PROVISIONING_PROFILE_SPECIFIER = "match Development me.mssun.passforios"; SWIFT_OBJC_BRIDGING_HEADER = "pass/Helpers/Objective-CBridgingHeader.h"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; From da3c4f0bc0849dc61d08454b0728c9d4da1d7e82 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Mon, 29 Jan 2018 03:23:34 +0800 Subject: [PATCH 04/31] 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() - } -} From 13537130a0809dead6ccb5b3a5f32561a9309077 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Mon, 29 Jan 2018 04:00:36 +0800 Subject: [PATCH 05/31] Dismiss passcode alert after unlock --- passKit/Controllers/PasscodeLockViewController.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/passKit/Controllers/PasscodeLockViewController.swift b/passKit/Controllers/PasscodeLockViewController.swift index 534c4e3..9663d64 100644 --- a/passKit/Controllers/PasscodeLockViewController.swift +++ b/passKit/Controllers/PasscodeLockViewController.swift @@ -57,15 +57,20 @@ open class PasscodeLockViewController: UIViewController { internal func dismissPasscodeLock(completionHandler: (() -> Void)? = nil) { // clean up the textfield - enterPasscodeAlert.textFields?[0].text = "" + DispatchQueue.main.async { + self.enterPasscodeAlert.textFields?[0].text = "" + self.enterPasscodeAlert.dismiss(animated: false, completion: nil) + } + + // pop 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 { + // if pushed in a navigation controller _ = navigationController?.popViewController(animated: true) dismissCompletionCallback?() completionHandler?() @@ -83,8 +88,6 @@ open class PasscodeLockViewController: UIViewController { } public func authenticate() { - print(enterPasscodeAlert.isBeingPresented) - let myContext = LAContext() let myLocalizedReasonString = "Authentication is needed to access Pass." var authError: NSError? From b10078af517488245cbe752a699785a3696dde63 Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 15:37:03 -0700 Subject: [PATCH 06/31] Update url in the Matchfile --- fastlane/Matchfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Matchfile b/fastlane/Matchfile index 7a59cec..b5aee64 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -1,4 +1,4 @@ -git_url "https://github.com/mssun/certificates-passforios.git" +git_url "git@github.com:mssun/certificates-passforios.git" type "development" # The default type, can be: appstore, adhoc, enterprise or development From d8f7067d7119c34d00ebf4993e7028406d4fc7bb Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 17:44:13 -0700 Subject: [PATCH 07/31] Add libxml2 header path --- pass.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index f07384e..4081695 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -1441,6 +1441,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; @@ -1492,6 +1493,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2"; IPHONEOS_DEPLOYMENT_TARGET = 10.2; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios; From d30c62993a6d150acbe60cee8ee10ce316fc114b Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 17:45:03 -0700 Subject: [PATCH 08/31] Update FavIcon --- Cartfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile b/Cartfile index 42a324b..8a5489e 100644 --- a/Cartfile +++ b/Cartfile @@ -3,6 +3,6 @@ github "radex/SwiftyUserDefaults" github "libgit2/objective-git" # github "zahlz/SwiftPasscodeLock" "master" github "yishilin14/SwiftPasscodeLock" "app-extension-support" -github "bitserf/FavIcon" +github "mssun/FavIcon" "master" github "kishikawakatsumi/KeychainAccess" github "mattrubin/OneTimePassword" From 853cb2eaf5c3fbfac581173ca1fdad03e9b09ecf Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 17:54:33 -0700 Subject: [PATCH 09/31] Update Podfile --- Podfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile b/Podfile index a95879a..aa80ddc 100644 --- a/Podfile +++ b/Podfile @@ -2,7 +2,7 @@ platform :ios, '10.2' use_frameworks! target 'passKit' do - pod 'ObjectivePGP', :git => 'https://github.com/krzyzanowskim/ObjectivePGP.git', :tag => '0.10.0-beta2' + pod 'ObjectivePGP', :git => 'https://github.com/krzyzanowskim/ObjectivePGP.git', :tag => '0.10.0-beta3' target 'pass' do inherit! :search_paths end From ab7e332bfb74d39e05a5e964e32cb63b62b03bfa Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 17 Mar 2018 18:18:19 -0700 Subject: [PATCH 10/31] Revert "Update url in the Matchfile" This reverts commit b10078af517488245cbe752a699785a3696dde63. --- fastlane/Matchfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Matchfile b/fastlane/Matchfile index b5aee64..7a59cec 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -1,4 +1,4 @@ -git_url "git@github.com:mssun/certificates-passforios.git" +git_url "https://github.com/mssun/certificates-passforios.git" type "development" # The default type, can be: appstore, adhoc, enterprise or development From caa5ce77a3feb4694308b511d094b4ea39ed71b9 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Mon, 26 Feb 2018 16:53:39 +0100 Subject: [PATCH 11/31] Use YAML library to parse password files --- Cartfile | 1 + pass.xcodeproj/project.pbxproj | 5 ++ ...nSourceComponentsTableViewController.swift | 3 ++ .../PasswordEditorTableViewController.swift | 2 +- passKit/Models/Password.swift | 48 ++++++++++++------- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/Cartfile b/Cartfile index 8a5489e..60924ca 100644 --- a/Cartfile +++ b/Cartfile @@ -6,3 +6,4 @@ github "yishilin14/SwiftPasscodeLock" "app-extension-support" github "mssun/FavIcon" "master" github "kishikawakatsumi/KeychainAccess" github "mattrubin/OneTimePassword" +github "jpsim/Yams" diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 4081695..b58cef1 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 18F19A67B0C07F13C17169E0 /* Pods_pass.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A5620D17DF5E86B61761D0E /* Pods_pass.framework */; }; 23B82F0228254275DBA609E7 /* Pods_passExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B975797E0F0B7476CADD6A7D /* Pods_passExtension.framework */; }; + 3012B06D2039D6E400BE1793 /* Yams.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3012B06C2039D6E400BE1793 /* Yams.framework */; }; 61326CDA7A73757FB68DCB04 /* Pods_passKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAB3F5541E51ADC8C6B56642 /* Pods_passKit.framework */; }; A20691F41F2A3D0E0096483D /* SecurePasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */; }; A2168A7F1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2168A7E1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift */; }; @@ -157,6 +158,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3012B06C2039D6E400BE1793 /* Yams.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Yams.framework; path = Carthage/Build/iOS/Yams.framework; sourceTree = ""; }; 31C3033E8868D05B2C55C8B1 /* Pods-passExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-passExtension/Pods-passExtension.debug.xcconfig"; sourceTree = ""; }; 3A5620D17DF5E86B61761D0E /* Pods_pass.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_pass.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 666769E0B255666D02945C15 /* Pods-passKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-passKitTests/Pods-passKitTests.release.xcconfig"; sourceTree = ""; }; @@ -317,6 +319,7 @@ DCC408C71E307DBB00F29B0E /* SVProgressHUD.framework in Frameworks */, DC193FFC1E49E0340077E0A3 /* PasscodeLock.framework in Frameworks */, 18F19A67B0C07F13C17169E0 /* Pods_pass.framework in Frameworks */, + 3012B06D2039D6E400BE1793 /* Yams.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -542,6 +545,7 @@ DC917BED1E2F38C4000FDF54 /* Frameworks */ = { isa = PBXGroup; children = ( + 3012B06C2039D6E400BE1793 /* Yams.framework */, A2A61C101EEF8E3500CFE063 /* libPods-passKit.a */, A2A61C0C1EEF8DFE00CFE063 /* libPods-passExtension.a */, A2227D541EEE5E78002A69A9 /* libObjectivePGP.a */, @@ -989,6 +993,7 @@ "$(SRCROOT)/Carthage/Build/iOS/KeychainAccess.framework", "$(SRCROOT)/Carthage/Build/iOS/OneTimePassword.framework", "$(SRCROOT)/Carthage/Build/iOS/Base32.framework", + "$(SRCROOT)/Carthage/Build/iOS/Yams.framework", ); name = "Run Script"; outputPaths = ( diff --git a/pass/Controllers/OpenSourceComponentsTableViewController.swift b/pass/Controllers/OpenSourceComponentsTableViewController.swift index b67fb40..b220434 100644 --- a/pass/Controllers/OpenSourceComponentsTableViewController.swift +++ b/pass/Controllers/OpenSourceComponentsTableViewController.swift @@ -35,6 +35,9 @@ class OpenSourceComponentsTableViewController: BasicStaticTableViewController { ["SVProgressHUD", "https://github.com/SVProgressHUD/SVProgressHUD", "https://github.com/SVProgressHUD/SVProgressHUD/blob/master/LICENSE.txt"], + ["Yams", + "https://github.com/jpsim/Yams", + "https://github.com/jpsim/Yams/blob/master/LICENSE"], ] override func viewDidLoad() { diff --git a/pass/Controllers/PasswordEditorTableViewController.swift b/pass/Controllers/PasswordEditorTableViewController.swift index 7ed2ed6..e03eb48 100644 --- a/pass/Controllers/PasswordEditorTableViewController.swift +++ b/pass/Controllers/PasswordEditorTableViewController.swift @@ -28,7 +28,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl private var navigationItemTitle: String? private var sectionHeaderTitles = ["name", "password", "additions",""].map {$0.uppercased()} - private var sectionFooterTitles = ["", "", "Use \"key: value\" format for additional fields.", ""] + private var sectionFooterTitles = ["", "", "Use YAML format for additional fields.", ""] private let nameSection = 0 private let passwordSection = 1 private let additionsSection = 2 diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index cae9fee..9a9c15f 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -10,6 +10,7 @@ import Foundation import SwiftyUserDefaults import OneTimePassword import Base32 +import Yams struct AdditionField { var title: String @@ -84,29 +85,25 @@ public class Password { additions.removeAll() // split the plain text - let plainTextSplit = plainText.split(maxSplits: 1, omittingEmptySubsequences: false) { + let plainTextSplit = self.plainText.split(maxSplits: 1, omittingEmptySubsequences: false) { $0 == "\n" || $0 == "\r\n" }.map(String.init) // get password password = plainTextSplit.first ?? "" - // get additonal fields - if plainTextSplit.count == 2 { - var unknownIndex = 0 - plainTextSplit[1].enumerateLines() { line, _ in - if !line.isEmpty { - var (key, value) = Password.getKeyValuePair(from: line) - if key == nil { - unknownIndex += 1 - key = "unknown \(unknownIndex)" - } - self.additions.append((key!, value)) - } - } + // get remaining lines + let additionalFields = plainTextSplit.last ?? "" + + // try to interpret the text format as YAML first + do { + try getAdditionalFields(fromYaml: additionalFields) } - - // check whether the first line of the plainText looks like an otp entry + catch { + getAdditionalFields(fromPlainText: additionalFields) + } + + // check whether the first line looks like an otp entry let (key, value) = Password.getKeyValuePair(from: self.password) if Password.otpKeywords.contains(key ?? "") { firstLineIsOTPField = true @@ -118,6 +115,25 @@ public class Password { // construct the otp token self.updateOtpToken() } + + private func getAdditionalFields(fromYaml: String) throws { + let yamlFile = try Yams.load(yaml: fromYaml) as! [String: Any] + additions.append(contentsOf: yamlFile.map { ($0, String(describing: $1)) }) + } + + private func getAdditionalFields(fromPlainText: String) { + var unknownIndex = 0 + fromPlainText.enumerateLines() { line, _ in + if !line.isEmpty { + var (key, value) = Password.getKeyValuePair(from: line) + if key == nil { + unknownIndex += 1 + key = "unknown \(unknownIndex)" + } + self.additions.append((key!, value)) + } + } + } public func getFilteredAdditions() -> [(String, String)] { var filteredAdditions = [(String, String)]() From 69bafeb672c1b9da8c342ddfd283540822e7e124 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 11 Mar 2018 12:57:13 +0100 Subject: [PATCH 12/31] Introduce class constants for the strings "otpauth" and "otpauth://" --- passKit/Models/Password.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index 9a9c15f..d5828ae 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -25,6 +25,8 @@ enum PasswordChange: Int { public class Password { public static let otpKeywords = ["otp_secret", "otp_type", "otp_algorithm", "otp_period", "otp_digits", "otp_counter", "otpauth"] + private static let OTPAUTH = "otpauth" + private static let OTPAUTH_URL_START = "\(OTPAUTH)://" public var name = "" public var url: URL? @@ -169,8 +171,8 @@ public class Password { // no ": " found, or empty on both sides of ": " value = line // otpauth special case - if value.hasPrefix("otpauth://") { - key = "otpauth" + if value.hasPrefix(Password.OTPAUTH_URL_START) { + key = Password.OTPAUTH } } else { if !items[0].isEmpty { @@ -237,9 +239,9 @@ public class Password { self.otpToken = nil // get otpauth, if we are able to generate a token, return - if var otpauthString = getAdditionValue(withKey: "otpauth") { - if !otpauthString.hasPrefix("otpauth:") { - otpauthString = "otpauth:\(otpauthString)" + if var otpauthString = getAdditionValue(withKey: Password.OTPAUTH) { + if !otpauthString.hasPrefix("\(Password.OTPAUTH):") { + otpauthString = "\(Password.OTPAUTH):\(otpauthString)" } if let otpauthUrl = URL(string: otpauthString), let token = Token(url: otpauthUrl) { @@ -363,7 +365,7 @@ public class Password { let (key, _) = Password.getKeyValuePair(from: line) if !Password.otpKeywords.contains(key ?? "") { lines.append(line) - } else if key == "otpauth" && newOtpauth != nil { + } else if key == Password.OTPAUTH && newOtpauth != nil { lines.append(newOtpauth!) // set to nil to prevent duplication newOtpauth = nil From 673aee1e7af3665461e66faf86fe2f41a2bbaf7a Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 11 Mar 2018 12:59:10 +0100 Subject: [PATCH 13/31] Protect YAML parser against empty input string --- passKit/Models/Password.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index d5828ae..c23de5a 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -119,6 +119,7 @@ public class Password { } private func getAdditionalFields(fromYaml: String) throws { + guard !fromYaml.isEmpty else { return } let yamlFile = try Yams.load(yaml: fromYaml) as! [String: Any] additions.append(contentsOf: yamlFile.map { ($0, String(describing: $1)) }) } From 3a0e6fa3f6cbccf8f2d6340559e6aab2d128ab62 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 11 Mar 2018 13:01:46 +0100 Subject: [PATCH 14/31] Consider OTP tokens as possible lines in password files --- passKit/Models/Password.swift | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index c23de5a..d610e84 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -87,7 +87,7 @@ public class Password { additions.removeAll() // split the plain text - let plainTextSplit = self.plainText.split(maxSplits: 1, omittingEmptySubsequences: false) { + let plainTextSplit = self.plainText.split(omittingEmptySubsequences: true) { $0 == "\n" || $0 == "\r\n" }.map(String.init) @@ -95,16 +95,25 @@ public class Password { password = plainTextSplit.first ?? "" // get remaining lines - let additionalFields = plainTextSplit.last ?? "" - + let additionalLines = plainTextSplit[1...] + + // separate normal lines (no otp tokens) + let normalAdditionalLines = additionalLines.filter { + !$0.hasPrefix(Password.OTPAUTH_URL_START) + }.joined(separator: "\n") + // try to interpret the text format as YAML first do { - try getAdditionalFields(fromYaml: additionalFields) + try getAdditionalFields(fromYaml: normalAdditionalLines) } catch { - getAdditionalFields(fromPlainText: additionalFields) + getAdditionalFields(fromPlainText: normalAdditionalLines) } + // get and append otp tokens + let otpAdditionalLines = additionalLines.filter { $0.hasPrefix(Password.OTPAUTH_URL_START) } + otpAdditionalLines.forEach { self.additions.append((Password.OTPAUTH, $0)) } + // check whether the first line looks like an otp entry let (key, value) = Password.getKeyValuePair(from: self.password) if Password.otpKeywords.contains(key ?? "") { From f5af2a0d2174fe35696d8a9c3c3b3608f7760234 Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 31 Mar 2018 23:01:40 -0700 Subject: [PATCH 15/31] Use mainstream FavIcon --- Cartfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile b/Cartfile index 60924ca..0a50465 100644 --- a/Cartfile +++ b/Cartfile @@ -3,7 +3,7 @@ github "radex/SwiftyUserDefaults" github "libgit2/objective-git" # github "zahlz/SwiftPasscodeLock" "master" github "yishilin14/SwiftPasscodeLock" "app-extension-support" -github "mssun/FavIcon" "master" +github "leonbreedt/FavIcon" github "kishikawakatsumi/KeychainAccess" github "mattrubin/OneTimePassword" github "jpsim/Yams" From 97712aa052185a994628b0f1ce15575a3d1e408d Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sat, 31 Mar 2018 23:06:25 -0700 Subject: [PATCH 16/31] Upgrade to suggested settings of Xcode 9.3 --- pass.xcodeproj/project.pbxproj | 6 +++++- pass.xcodeproj/xcshareddata/xcschemes/pass.xcscheme | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index b58cef1..ef17326 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -697,7 +697,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 0930; ORGANIZATIONNAME = "Bob Sun"; TargetAttributes = { A26075771EEC6F34005DB03E = { @@ -1413,6 +1413,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -1420,6 +1421,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -1471,6 +1473,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -1478,6 +1481,7 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; diff --git a/pass.xcodeproj/xcshareddata/xcschemes/pass.xcscheme b/pass.xcodeproj/xcshareddata/xcschemes/pass.xcscheme index 75208fd..a455f03 100644 --- a/pass.xcodeproj/xcshareddata/xcschemes/pass.xcscheme +++ b/pass.xcodeproj/xcshareddata/xcschemes/pass.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 31 Mar 2018 23:22:29 -0700 Subject: [PATCH 17/31] Fix CI issue --- fastlane/Fastfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 1ce0f8c..51aa52e 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -62,7 +62,7 @@ platform :ios do ) # ensure_git_status_clean increment_build_number( - build_number: latest_testflight_build_number(version: get_version_number, initial_build_number: 0) + 1, + build_number: latest_testflight_build_number(version: get_version_number(target: "pass"), initial_build_number: 0) + 1, xcodeproj: "pass.xcodeproj" ) # commit_version_bump(xcodeproj: "pass.xcodeproj") From f7eabd8258fb115bbbab04fccff8e4da883fe7c8 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Wed, 4 Apr 2018 12:54:35 +0800 Subject: [PATCH 18/31] Recognize *url* as url fields --- pass/Views/LabelTableViewCell.swift | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pass/Views/LabelTableViewCell.swift b/pass/Views/LabelTableViewCell.swift index b1d8def..c49d5c2 100644 --- a/pass/Views/LabelTableViewCell.swift +++ b/pass/Views/LabelTableViewCell.swift @@ -39,8 +39,7 @@ class LabelTableViewCell: UITableViewCell { return } titleLabel.text = title - switch title.lowercased() { - case "password": + if title.caseInsensitiveCompare("password") == .orderedSame { type = .password if isReveal { contentLabel.attributedText = Utils.attributedPassword(plainPassword: content) @@ -52,7 +51,7 @@ class LabelTableViewCell: UITableViewCell { } } contentLabel.font = Globals.passwordFont - case "hmac-based": + } else if title.caseInsensitiveCompare("hmac-based") == .orderedSame { type = .HOTP if isReveal { contentLabel.text = content @@ -60,11 +59,12 @@ class LabelTableViewCell: UITableViewCell { contentLabel.text = Globals.oneTimePasswordDots } contentLabel.font = Globals.passwordFont - case "url": + } else if title.lowercased().range(of: "url") != nil || verifyUrl(content) { type = .URL contentLabel.text = content contentLabel.font = UIFont.systemFont(ofSize: contentLabel.font.pointSize) - default: + } else { + // default type = .other contentLabel.text = content contentLabel.font = UIFont.systemFont(ofSize: contentLabel.font.pointSize) @@ -198,4 +198,12 @@ class LabelTableViewCell: UITableViewCell { } self.accessoryView = buttons } + + private func verifyUrl(_ urlString: String?) -> Bool { + guard let urlString = urlString, + let _ = URL(string: urlString) else { + return false + } + return true + } } From 54c7acf65132ebacb13aebbc164836b22d58c730 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Sat, 7 Apr 2018 00:16:06 +0800 Subject: [PATCH 19/31] Check duplicated tags before parsing a file as yaml --- passKit/Helpers/AppError.swift | 3 +++ passKit/Models/Password.swift | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/passKit/Helpers/AppError.swift b/passKit/Helpers/AppError.swift index 5386ee1..515c18a 100644 --- a/passKit/Helpers/AppError.swift +++ b/passKit/Helpers/AppError.swift @@ -16,6 +16,7 @@ public enum AppError: Error { case GitResetError case PGPPublicKeyNotExistError case WrongPasswordFilename + case YamlLoadError case UnknownError } @@ -36,6 +37,8 @@ extension AppError: LocalizedError { return "PGP public key doesn't exist." case .WrongPasswordFilename: return "Cannot write to the password file." + case .YamlLoadError: + return "Cannot be parsed as a YAML file." case .UnknownError: return "Unknown error." } diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index d610e84..662286d 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -126,9 +126,29 @@ public class Password { // construct the otp token self.updateOtpToken() } + + // check whether the file has lines with duplicated field names + private func checkDuplicatedFields(lines: String) -> Bool { + var keys = Set() + var hasDuplicatedFields = false + lines.enumerateLines { (line, stop) -> () in + let (key, _) = Password.getKeyValuePair(from: line) + if let key = key { + if keys.contains(key) { + hasDuplicatedFields = true + stop = true + } + keys.insert(key) + } + } + return hasDuplicatedFields + } private func getAdditionalFields(fromYaml: String) throws { guard !fromYaml.isEmpty else { return } + if checkDuplicatedFields(lines: fromYaml) { + throw AppError.YamlLoadError + } let yamlFile = try Yams.load(yaml: fromYaml) as! [String: Any] additions.append(contentsOf: yamlFile.map { ($0, String(describing: $1)) }) } From 3ffbf68ab860cda74113b183813711b9294be406 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Sat, 7 Apr 2018 00:17:00 +0800 Subject: [PATCH 20/31] Avoid crash during yaml parsing --- passKit/Models/Password.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index 662286d..51f223c 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -149,7 +149,9 @@ public class Password { if checkDuplicatedFields(lines: fromYaml) { throw AppError.YamlLoadError } - let yamlFile = try Yams.load(yaml: fromYaml) as! [String: Any] + guard let yamlFile = try Yams.load(yaml: fromYaml) as? [String: Any] else { + throw AppError.YamlLoadError + } additions.append(contentsOf: yamlFile.map { ($0, String(describing: $1)) }) } From 867a59b513e1f895a4b8a42d2a851754be8d1baf Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Sat, 7 Apr 2018 00:39:56 +0800 Subject: [PATCH 21/31] Keep the first line as the password even if it is empty --- passKit/Models/Password.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index 51f223c..ce9eff9 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -87,15 +87,15 @@ public class Password { additions.removeAll() // split the plain text - let plainTextSplit = self.plainText.split(omittingEmptySubsequences: true) { + let plainTextSplit = self.plainText.split(omittingEmptySubsequences: false) { $0 == "\n" || $0 == "\r\n" }.map(String.init) // get password password = plainTextSplit.first ?? "" - // get remaining lines - let additionalLines = plainTextSplit[1...] + // get remaining lines (filter out empty lines) + let additionalLines = plainTextSplit[1...].filter { !$0.isEmpty } // separate normal lines (no otp tokens) let normalAdditionalLines = additionalLines.filter { From 0579662263e0e8f5fde55b4f10b3b4724a83dd41 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Sat, 7 Apr 2018 01:30:25 +0800 Subject: [PATCH 22/31] Update provisioning files --- pass.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index ef17326..983f06c 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -1541,7 +1541,7 @@ OTHER_LDFLAGS = "${inherited}"; PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = "6bb836f2-68a1-44cc-8c45-f6b6e53ba64a"; + PROVISIONING_PROFILE = "3c4f599a-ce77-4184-b4c4-edebf09cba3b"; PROVISIONING_PROFILE_SPECIFIER = "match Development me.mssun.passforios"; SWIFT_OBJC_BRIDGING_HEADER = "pass/Helpers/Objective-CBridgingHeader.h"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; From 6981860caa03306a71cb962ad21be22771941c63 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Sat, 7 Apr 2018 01:40:34 +0800 Subject: [PATCH 23/31] Set the passextension device to universal --- pass.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 983f06c..3c9f607 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -1330,6 +1330,7 @@ SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -1363,6 +1364,7 @@ SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; From a45127be41bb2c167653f74499ed4ade5b2d36b0 Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Tue, 10 Apr 2018 01:17:51 +0800 Subject: [PATCH 24/31] Update pass extension settings --- passExtension/Info.plist | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/passExtension/Info.plist b/passExtension/Info.plist index e345f72..0539a9e 100644 --- a/passExtension/Info.plist +++ b/passExtension/Info.plist @@ -25,14 +25,17 @@ NSExtensionAttributes NSExtensionActivationRule - - NSExtensionActivationSupportsText - - NSExtensionActivationSupportsWebPageWithMaxCount - 1 - NSExtensionActivationSupportsWebURLWithMaxCount - 1 - + SUBQUERY ( + extensionItems, + $extensionItem, + SUBQUERY ( + $extensionItem.attachments, + $attachment, + ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "org.appextension.find-login-action" || + ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" || + ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text" + ).@count == $extensionItem.attachments.@count + ).@count == 1 NSExtensionJavaScriptPreprocessingFile passProcessor From 433562584e00c919e8f603a01ee8694f1209d9cb Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Tue, 10 Apr 2018 00:14:35 +0800 Subject: [PATCH 25/31] Passcode lock --- Cartfile | 2 - ...nSourceComponentsTableViewController.swift | 3 - .../SettingsTableViewController.swift | 5 - .../Controllers/PasscodeLockPresenter.swift | 47 +++--- .../PasscodeLockViewController.swift | 144 +++++++++++++----- passKit/Helpers/UITextFieldExtension.swift | 2 +- 6 files changed, 125 insertions(+), 78 deletions(-) diff --git a/Cartfile b/Cartfile index 0a50465..97ca48d 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 "leonbreedt/FavIcon" github "kishikawakatsumi/KeychainAccess" github "mattrubin/OneTimePassword" diff --git a/pass/Controllers/OpenSourceComponentsTableViewController.swift b/pass/Controllers/OpenSourceComponentsTableViewController.swift index b220434..bdd809a 100644 --- a/pass/Controllers/OpenSourceComponentsTableViewController.swift +++ b/pass/Controllers/OpenSourceComponentsTableViewController.swift @@ -17,9 +17,6 @@ class OpenSourceComponentsTableViewController: BasicStaticTableViewController { ["KeychainAccess", "https://github.com/kishikawakatsumi/KeychainAccess", "https://github.com/kishikawakatsumi/KeychainAccess/blob/master/LICENSE"], - ["PasscodeLock", - "https://github.com/zahlz/SwiftPasscodeLock", - "https://github.com/zahlz/SwiftPasscodeLock/blob/master/LICENSE.txt"], ["ObjectiveGit", "https://github.com/libgit2/objective-git", "https://github.com/libgit2/objective-git/blob/master/LICENSE"], diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 536a25c..46491a4 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -159,9 +159,6 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele setPGPKeyTableViewCellDetailText() setPasswordRepositoryTableViewCellDetailText() setPasscodeLockCell() - - let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window) } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -262,8 +259,6 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele passcodeRemoveViewController.successCallback = { self?.passcodeLock.delete() self?.setPasscodeLockCell() - let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.passcodeLockPresenter = PasscodeLockPresenter(mainWindow: appDelegate.window) } self?.present(passcodeRemoveViewController, animated: true, completion: nil) } diff --git a/passKit/Controllers/PasscodeLockPresenter.swift b/passKit/Controllers/PasscodeLockPresenter.swift index ce1b88b..d9df482 100644 --- a/passKit/Controllers/PasscodeLockPresenter.swift +++ b/passKit/Controllers/PasscodeLockPresenter.swift @@ -2,8 +2,10 @@ // PasscodeLockPresenter.swift // PasscodeLock // -// Created by Yanko Dimitrov on 8/29/15. -// Copyright © 2015 Yanko Dimitrov. All rights reserved. +// Created by Yishi Lin on 10/04/2018. +// Copyright © 2018 Yishi Lin. All rights reserved. +// +// Inspired by SwiftPasscodeLock created by Yanko Dimitrov. // import UIKit @@ -11,49 +13,42 @@ 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 + fileprivate var passcodeLockWindow: UIWindow? + + public init(mainWindow window: UIWindow?) { + self.mainWindow = window } - 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 - + // dismiss the original window + dismiss() + + // new window mainWindow?.endEditing(true) + passcodeLockWindow = UIWindow(frame: self.mainWindow!.frame) moveWindowsToFront(windowLevel: windowLevel) - passcodeLockWindow.isHidden = false - + passcodeLockWindow?.isHidden = false + + // new vc + let passcodeLockVC = PasscodeLockViewController() let userDismissCompletionCallback = passcodeLockVC.dismissCompletionCallback passcodeLockVC.dismissCompletionCallback = { [weak self] in userDismissCompletionCallback?() self?.dismiss() } - passcodeLockWindow.rootViewController = passcodeLockVC + passcodeLockWindow?.rootViewController = passcodeLockVC } open func dismiss() { - isPasscodePresented = false - passcodeLockWindow.isHidden = true - passcodeLockWindow.rootViewController = nil + passcodeLockWindow?.isHidden = true + passcodeLockWindow?.rootViewController = nil } fileprivate func moveWindowsToFront(windowLevel: CGFloat?) { let windowLevel = windowLevel ?? UIWindowLevelNormal let maxWinLevel = max(windowLevel, UIWindowLevelNormal) - passcodeLockWindow.windowLevel = maxWinLevel + 1 + passcodeLockWindow?.windowLevel = maxWinLevel + 1 } } diff --git a/passKit/Controllers/PasscodeLockViewController.swift b/passKit/Controllers/PasscodeLockViewController.swift index 9663d64..b433c99 100644 --- a/passKit/Controllers/PasscodeLockViewController.swift +++ b/passKit/Controllers/PasscodeLockViewController.swift @@ -1,29 +1,69 @@ // -// PasscodeLockViewController.swift +// PasscodeLockPresenter.swift // PasscodeLock // -// Created by Yanko Dimitrov on 8/28/15. -// Copyright © 2015 Yanko Dimitrov. All rights reserved. +// Created by Yishi Lin on 10/04/2018. +// Copyright © 2018 Yishi Lin. All rights reserved. +// +// Inspired by SwiftPasscodeLock created by Yanko Dimitrov. // import UIKit import LocalAuthentication -open class PasscodeLockViewController: UIViewController { +open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { 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() - }) + + weak var passcodeLabel: UILabel? + weak var passcodeWrongAttemptsLabel: UILabel? + weak var passcodeTextField: UITextField? + weak var biometryAuthButton: UIButton? + + var passcodeFailedAttempts = 0 + + open override func loadView() { + super.loadView() + + let passcodeLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40)) + passcodeLabel.text = "Enter passcode for Pass" + passcodeLabel.font = UIFont.boldSystemFont(ofSize: 18) + passcodeLabel.textColor = UIColor.black + passcodeLabel.textAlignment = .center + passcodeLabel.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(passcodeLabel) + self.passcodeLabel = passcodeLabel + + let passcodeWrongAttemptsLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 40)) + passcodeWrongAttemptsLabel.text = "" + passcodeWrongAttemptsLabel.textColor = UIColor.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)) + passcodeTextField.borderStyle = UITextBorderStyle.roundedRect + passcodeTextField.placeholder = "passcode" + passcodeTextField.isSecureTextEntry = true + passcodeTextField.clearButtonMode = UITextFieldViewMode.whileEditing + passcodeTextField.delegate = self + passcodeTextField.addTarget(self, action: #selector(self.passcodeTextFieldDidChange(_:)), for: UIControlEvents.editingChanged) + self.view.backgroundColor = UIColor.white + passcodeTextField.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(passcodeTextField) + self.passcodeTextField = passcodeTextField + + let biometryAuthButton = UIButton(type: .custom) + biometryAuthButton.setTitle("", for: .normal) + biometryAuthButton.setTitleColor(Globals.blue, for: .normal) + biometryAuthButton.addTarget(self, action: #selector(bioButtonPressedAction(_:)), for: .touchUpInside) + biometryAuthButton.isHidden = true + biometryAuthButton.translatesAutoresizingMaskIntoConstraints = false + self.view.addSubview(biometryAuthButton) + self.biometryAuthButton = biometryAuthButton let myContext = LAContext() var authError: NSError? @@ -35,31 +75,50 @@ open class PasscodeLockViewController: UIViewController { biometryType = "Face ID" } } - let bioAction = UIAlertAction(title: "Use " + biometryType, style: .default) { (action:UIAlertAction) -> Void in - self.authenticate() - } - enterPasscodeAlert.addAction(bioAction) + biometryAuthButton.setTitle(biometryType, for: .normal) + biometryAuthButton.isHidden = false } } - return enterPasscodeAlert - }() + NSLayoutConstraint.activate([ + passcodeTextField.widthAnchor.constraint(equalToConstant: 300), + passcodeTextField.heightAnchor.constraint(equalToConstant: 40), + passcodeTextField.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + passcodeTextField.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: -20), + // above passocde + passcodeLabel.widthAnchor.constraint(equalToConstant: 300), + passcodeLabel.heightAnchor.constraint(equalToConstant: 40), + passcodeLabel.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + passcodeLabel.bottomAnchor.constraint(equalTo: passcodeTextField.topAnchor), + // below passcode + passcodeWrongAttemptsLabel.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.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + biometryAuthButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -40), + ]) + + } open override func viewDidLoad() { super.viewDidLoad() - self.view.backgroundColor = UIColor.white } open override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - authenticate() + if let biometryAuthButton = biometryAuthButton { + self.bioButtonPressedAction(biometryAuthButton) + } } internal func dismissPasscodeLock(completionHandler: (() -> Void)? = nil) { // clean up the textfield DispatchQueue.main.async { - self.enterPasscodeAlert.textFields?[0].text = "" - self.enterPasscodeAlert.dismiss(animated: false, completion: nil) + self.passcodeTextField?.text = "" } // pop @@ -80,6 +139,8 @@ open class PasscodeLockViewController: UIViewController { // MARK: - PasscodeLockDelegate open func passcodeLockDidSucceed() { + passcodeFailedAttempts = 0 + passcodeWrongAttemptsLabel?.text = "" dismissPasscodeLock(completionHandler: successCallback) } @@ -87,7 +148,7 @@ open class PasscodeLockViewController: UIViewController { dismissPasscodeLock(completionHandler: cancelCallback) } - public func authenticate() { + @objc public func bioButtonPressedAction(_ uiButton: UIButton) { let myContext = LAContext() let myLocalizedReasonString = "Authentication is needed to access Pass." var authError: NSError? @@ -100,29 +161,30 @@ open class PasscodeLockViewController: UIViewController { // 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 ?? "") { + public override func textFieldShouldReturn(_ textField: UITextField) -> Bool { + if textField == passcodeTextField { + if !PasscodeLock.shared.check(passcode: textField.text ?? "") { + passcodeFailedAttempts = passcodeFailedAttempts + 1 + if passcodeFailedAttempts == 1 { + passcodeWrongAttemptsLabel?.text = "1 wrong attempt" + } else { + passcodeWrongAttemptsLabel?.text = "\(passcodeFailedAttempts) wrong attempts" + } + } + } + textField.resignFirstResponder() + return true + } + + @objc public func passcodeTextFieldDidChange(_ textField: UITextField) { + if PasscodeLock.shared.check(passcode: textField.text ?? "") { self.passcodeLockDidSucceed() } } - - func showPasswordAlert() { - self.present(enterPasscodeAlert, animated: true, completion: nil) - } } diff --git a/passKit/Helpers/UITextFieldExtension.swift b/passKit/Helpers/UITextFieldExtension.swift index 2500baf..0ea3ab2 100644 --- a/passKit/Helpers/UITextFieldExtension.swift +++ b/passKit/Helpers/UITextFieldExtension.swift @@ -23,7 +23,7 @@ extension UITextField { } extension UIViewController { - func textFieldShouldReturn(_ textField: UITextField) -> Bool { + @objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool { if textField.nextField != nil { textField.nextField?.becomeFirstResponder() } else { From 3f899a58d9cdaaef9535993960488157bca9679c Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Wed, 11 Apr 2018 02:05:00 +0800 Subject: [PATCH 26/31] Add a cancel button to passcode lock of app extension --- pass.xcodeproj/project.pbxproj | 4 ++ passExtension/PasscodeExtensionDisplay.swift | 24 +++++++++- .../PasscodeLockViewController.swift | 30 +++++++++++-- passKit/Helpers/UIViewExtension.swift | 45 +++++++++++++++++++ 4 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 passKit/Helpers/UIViewExtension.swift diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 8821919..de182dd 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ 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 */; }; + A2BEC1BB207D2EFE00F3051C /* UIViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2BEC1BA207D2EFE00F3051C /* UIViewExtension.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 */; }; @@ -200,6 +201,7 @@ 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 = ""; }; + A2BEC1BA207D2EFE00F3051C /* UIViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UIViewExtension.swift; path = Helpers/UIViewExtension.swift; 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 = ""; }; @@ -410,6 +412,7 @@ A2F4E21B1EED80160011986E /* NotificationNames.swift */, A2F4E21C1EED80160011986E /* UITextFieldExtension.swift */, A2F4E21D1EED80160011986E /* Utils.swift */, + A2BEC1BA207D2EFE00F3051C /* UIViewExtension.swift */, ); name = Helpers; sourceTree = ""; @@ -1032,6 +1035,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A2BEC1BB207D2EFE00F3051C /* UIViewExtension.swift in Sources */, A2C532BB201E5A9600DB9F53 /* PasscodeLock.swift in Sources */, A2F4E2151EED800F0011986E /* Password.swift in Sources */, A26075AD1EEC7125005DB03E /* pass.xcdatamodeld in Sources */, diff --git a/passExtension/PasscodeExtensionDisplay.swift b/passExtension/PasscodeExtensionDisplay.swift index d0ea6c0..24258af 100644 --- a/passExtension/PasscodeExtensionDisplay.swift +++ b/passExtension/PasscodeExtensionDisplay.swift @@ -9,15 +9,35 @@ import Foundation import passKit +// cancel means cancel the extension +class PasscodeLockViewControllerForExtension: PasscodeLockViewController { + var originalExtensionContest: NSExtensionContext? + public convenience init(extensionContext: NSExtensionContext?) { + self.init() + 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: PasscodeLockViewController + private let passcodeLockVC: PasscodeLockViewControllerForExtension + private let extensionContext: NSExtensionContext? init(extensionContext: NSExtensionContext?) { - passcodeLockVC = PasscodeLockViewController() + self.extensionContext = extensionContext + passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext) passcodeLockVC.dismissCompletionCallback = { [weak self] in self?.dismiss() } + passcodeLockVC.setCancellable(true) } // present the passcode lock view if passcode is set and the view controller is not presented diff --git a/passKit/Controllers/PasscodeLockViewController.swift b/passKit/Controllers/PasscodeLockViewController.swift index b433c99..7be4362 100644 --- a/passKit/Controllers/PasscodeLockViewController.swift +++ b/passKit/Controllers/PasscodeLockViewController.swift @@ -21,8 +21,10 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { weak var passcodeWrongAttemptsLabel: UILabel? weak var passcodeTextField: UITextField? weak var biometryAuthButton: UIButton? + open weak var cancelButton: UIButton? var passcodeFailedAttempts = 0 + var isCancellable: Bool = false open override func loadView() { super.loadView() @@ -80,6 +82,16 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { } } + let cancelButton = UIButton(type: .custom) + cancelButton.setTitle("Cancel", for: .normal) + cancelButton.setTitleColor(Globals.blue, for: .normal) + cancelButton.addTarget(self, action: #selector(passcodeLockDidCancel), for: .touchUpInside) + cancelButton.isHidden = !self.isCancellable + cancelButton.translatesAutoresizingMaskIntoConstraints = false + cancelButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left + self.view.addSubview(cancelButton) + self.cancelButton = cancelButton + NSLayoutConstraint.activate([ passcodeTextField.widthAnchor.constraint(equalToConstant: 300), passcodeTextField.heightAnchor.constraint(equalToConstant: 40), @@ -99,7 +111,12 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { biometryAuthButton.widthAnchor.constraint(equalToConstant: 150), biometryAuthButton.heightAnchor.constraint(equalToConstant: 40), biometryAuthButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), - biometryAuthButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -40), + biometryAuthButton.bottomAnchor.constraint(equalTo: self.view.safeBottomAnchor, constant: -40), + // cancel (top-left of the screen) + cancelButton.widthAnchor.constraint(equalToConstant: 150), + cancelButton.heightAnchor.constraint(equalToConstant: 40), + cancelButton.topAnchor.constraint(equalTo: self.view.safeTopAnchor), + cancelButton.leftAnchor.constraint(equalTo: self.view.safeLeftAnchor, constant: 20) ]) } @@ -144,11 +161,11 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { dismissPasscodeLock(completionHandler: successCallback) } - open func passcodeLockDidCancel() { + @objc func passcodeLockDidCancel() { dismissPasscodeLock(completionHandler: cancelCallback) } - @objc public func bioButtonPressedAction(_ uiButton: UIButton) { + @objc func bioButtonPressedAction(_ uiButton: UIButton) { let myContext = LAContext() let myLocalizedReasonString = "Authentication is needed to access Pass." var authError: NSError? @@ -182,9 +199,14 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate { return true } - @objc public func passcodeTextFieldDidChange(_ textField: UITextField) { + @objc func passcodeTextFieldDidChange(_ textField: UITextField) { if PasscodeLock.shared.check(passcode: textField.text ?? "") { self.passcodeLockDidSucceed() } } + + public func setCancellable(_ isCancellable: Bool) { + self.isCancellable = isCancellable + cancelButton?.isHidden = !isCancellable + } } diff --git a/passKit/Helpers/UIViewExtension.swift b/passKit/Helpers/UIViewExtension.swift new file mode 100644 index 0000000..649b032 --- /dev/null +++ b/passKit/Helpers/UIViewExtension.swift @@ -0,0 +1,45 @@ +// +// UIViewExtension.swift +// passKit +// +// Created by Yishi Lin on 2018/4/11. +// Copyright © 2018 Yishi Lin. All rights reserved. +// + +import Foundation + +extension UIView { + + // Save anchors: https://stackoverflow.com/questions/46317061/use-safe-area-layout-programmatically + var safeTopAnchor: NSLayoutYAxisAnchor { + if #available(iOS 11.0, *) { + return self.safeAreaLayoutGuide.topAnchor + } else { + return self.topAnchor + } + } + + var safeLeftAnchor: NSLayoutXAxisAnchor { + if #available(iOS 11.0, *){ + return self.safeAreaLayoutGuide.leftAnchor + } else { + return self.leftAnchor + } + } + + var safeRightAnchor: NSLayoutXAxisAnchor { + if #available(iOS 11.0, *){ + return self.safeAreaLayoutGuide.rightAnchor + } else { + return self.rightAnchor + } + } + + var safeBottomAnchor: NSLayoutYAxisAnchor { + if #available(iOS 11.0, *) { + return self.safeAreaLayoutGuide.bottomAnchor + } else { + return self.bottomAnchor + } + } +} From 403fc5201e806721243fa42fcbaf1c8a0169cfbe Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Thu, 12 Apr 2018 00:06:49 +0800 Subject: [PATCH 27/31] Fix warning about UI API called on background thread --- pass/Controllers/SettingsTableViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index 46491a4..65f40c8 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -68,8 +68,8 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele SVProgressHUD.show(withStatus: "Fetching PGP Key") DispatchQueue.global(qos: .userInitiated).async { [unowned self] in do { - try self.passwordStore.initPGPKey(with: controller.armorPublicKeyTextView.text, keyType: .public) - try self.passwordStore.initPGPKey(with: controller.armorPrivateKeyTextView.text, keyType: .secret) + try self.passwordStore.initPGPKey(with: SharedDefaults[.pgpPublicKeyArmor] ?? "", keyType: .public) + try self.passwordStore.initPGPKey(with: SharedDefaults[.pgpPrivateKeyArmor] ?? "", keyType: .secret) DispatchQueue.main.async { self.pgpKeyTableViewCell.detailTextLabel?.text = self.passwordStore.pgpKeyID SVProgressHUD.showSuccess(withStatus: "Success") From 6538634680fc9e801d38c534d6a5a5b7812e604b Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Thu, 12 Apr 2018 00:52:10 +0800 Subject: [PATCH 28/31] Delete files before cleaning up dirs --- passKit/Models/PasswordStore.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passKit/Models/PasswordStore.swift b/passKit/Models/PasswordStore.swift index a39b810..5ae5692 100644 --- a/passKit/Models/PasswordStore.swift +++ b/passKit/Models/PasswordStore.swift @@ -654,9 +654,9 @@ public class PasswordStore { public func delete(passwordEntity: PasswordEntity) throws { let deletedFileURL = passwordEntity.getURL()! - try deleteDirectoryTree(at: passwordEntity.getURL()!) - try deletePasswordEntities(passwordEntity: passwordEntity) try gitRm(path: deletedFileURL.path) + try deletePasswordEntities(passwordEntity: passwordEntity) + try deleteDirectoryTree(at: deletedFileURL) let _ = try gitCommit(message: "Remove \(deletedFileURL.deletingPathExtension().path.removingPercentEncoding!) from store using Pass for iOS.") NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil) } From 1afef16fa1955113b29adb090c736b26288b90af Mon Sep 17 00:00:00 2001 From: Yishi Lin Date: Thu, 12 Apr 2018 01:07:19 +0800 Subject: [PATCH 29/31] Support swipe back when viewing folder #141 --- pass/Controllers/PasswordsViewController.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pass/Controllers/PasswordsViewController.swift b/pass/Controllers/PasswordsViewController.swift index 34a208d..2129d8c 100644 --- a/pass/Controllers/PasswordsViewController.swift +++ b/pass/Controllers/PasswordsViewController.swift @@ -229,6 +229,11 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV // reset the data table if the disaply settings have been changed NotificationCenter.default.addObserver(self, selector: #selector(actOnReloadTableViewRelatedNotification), name: .passwordDisplaySettingChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(actOnSearchNotification), name: .passwordSearch, object: nil) + + // listen to the swipe back guesture + let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.respondToSwipeGesture)) + swipeRight.direction = UISwipeGestureRecognizerDirection.right + self.view.addGestureRecognizer(swipeRight) } override func viewWillAppear(_ animated: Bool) { @@ -314,6 +319,15 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV } } + @objc func respondToSwipeGesture(gesture: UIGestureRecognizer) { + if let swipeGesture = gesture as? UISwipeGestureRecognizer { + // swipe right -> swipe back + if swipeGesture.direction == .right && parentPasswordEntity != nil { + self.backAction(nil) + } + } + } + @objc func backAction(_ sender: Any?) { guard SharedDefaults[.isShowFolderOn] else { return } var anim: CATransition? = transitionFromLeft From 47a69d881b8211f64c97eda5f4a358bd5b839bdb Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Sun, 22 Apr 2018 15:07:31 -0700 Subject: [PATCH 30/31] Update ObjectivePGP --- Podfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile b/Podfile index aa80ddc..fca1e49 100644 --- a/Podfile +++ b/Podfile @@ -2,7 +2,7 @@ platform :ios, '10.2' use_frameworks! target 'passKit' do - pod 'ObjectivePGP', :git => 'https://github.com/krzyzanowskim/ObjectivePGP.git', :tag => '0.10.0-beta3' + pod 'ObjectivePGP', :git => 'https://github.com/krzyzanowskim/ObjectivePGP.git', :tag => '0.10.0' target 'pass' do inherit! :search_paths end From 85d9dffd745844eb2c6af2cf4b47894188fdeb27 Mon Sep 17 00:00:00 2001 From: Bob Sun Date: Wed, 25 Apr 2018 09:25:01 -0700 Subject: [PATCH 31/31] Fix #179 --- pass/Views/FillPasswordTableViewCell.xib | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pass/Views/FillPasswordTableViewCell.xib b/pass/Views/FillPasswordTableViewCell.xib index 2ebed2b..3afd825 100644 --- a/pass/Views/FillPasswordTableViewCell.xib +++ b/pass/Views/FillPasswordTableViewCell.xib @@ -1,11 +1,11 @@ - + - + @@ -20,19 +20,19 @@ - + - + - +