From 3465ce9e38221c39846a8964e4f9018698051b30 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 23 Feb 2020 10:54:34 +0100 Subject: [PATCH 1/5] Update German translation --- pass/de.lproj/Localizable.strings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pass/de.lproj/Localizable.strings b/pass/de.lproj/Localizable.strings index 813d7e8..83eaa9b 100644 --- a/pass/de.lproj/Localizable.strings +++ b/pass/de.lproj/Localizable.strings @@ -51,6 +51,8 @@ "Random" = "Zufällig"; "RandomString" = "Zufällige Zeichenkette"; "ApplesKeychainStyle" = "Apples Keychain-Style"; +"XKCD" = "XKCD"; +"XKCDStyle" = "XKCD-Style"; // Git "FailedToFetchPasswords" = "Abrufen von Passwörtern fehlgeschlagen"; From ea693db86bab891edd5b176e9622c544ea720c1e Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 23 Feb 2020 10:55:15 +0100 Subject: [PATCH 2/5] Rename test class --- pass.xcodeproj/project.pbxproj | 8 ++++---- passKit/Helpers/PasswordGeneratorFlavor.swift | 2 +- ...lavourTest.swift => PasswordGeneratorFlavorTest.swift} | 0 3 files changed, 5 insertions(+), 5 deletions(-) rename passKitTests/Helpers/{PasswordGeneratorFlavourTest.swift => PasswordGeneratorFlavorTest.swift} (100%) diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 3f8de2a..429414d 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -50,7 +50,7 @@ 30697C5F21F674800064FCAC /* String+UtilitiesTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30697C5E21F674800064FCAC /* String+UtilitiesTest.swift */; }; 3087574F2343E42A00B971A2 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3087574E2343E42A00B971A2 /* Colors.swift */; }; 308C273A2279F9CB0016D0E2 /* SearchBarScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302202EE222F14E400555236 /* SearchBarScope.swift */; }; - 30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */; }; + 30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift */; }; 30A1D2A221B2BC6F00E2D1F7 /* TokenBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D2A121B2BC6F00E2D1F7 /* TokenBuilder.swift */; }; 30A1D2A621B2D46100E2D1F7 /* OtpType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D2A521B2D46100E2D1F7 /* OtpType.swift */; }; 30A1D2A821B2D53200E2D1F7 /* PasswordChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D2A721B2D53200E2D1F7 /* PasswordChange.swift */; }; @@ -274,7 +274,7 @@ 30697C5221F63E0B0064FCAC /* CredentialProviderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialProviderViewController.swift; sourceTree = ""; }; 30697C5E21F674800064FCAC /* String+UtilitiesTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+UtilitiesTest.swift"; sourceTree = ""; }; 3087574E2343E42A00B971A2 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = ""; }; - 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordGeneratorFlavourTest.swift; sourceTree = ""; }; + 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordGeneratorFlavorTest.swift; sourceTree = ""; }; 30A1D2A121B2BC6F00E2D1F7 /* TokenBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenBuilder.swift; sourceTree = ""; }; 30A1D2A521B2D46100E2D1F7 /* OtpType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OtpType.swift; sourceTree = ""; }; 30A1D2A721B2D53200E2D1F7 /* PasswordChange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordChange.swift; sourceTree = ""; }; @@ -460,7 +460,7 @@ 301F6464216164670071A4CE /* Helpers */ = { isa = PBXGroup; children = ( - 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */, + 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift */, 3032328922C9FBA2009EBD9C /* KeyFileManagerTest.swift */, ); path = Helpers; @@ -1362,7 +1362,7 @@ 30BAC8C722E3BAAF00438475 /* TestPGPKeys.swift in Sources */, 30A1D2AA21B32A0100E2D1F7 /* OtpTypeTest.swift in Sources */, 301F6468216165290071A4CE /* ConstantsTest.swift in Sources */, - 30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift in Sources */, + 30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift in Sources */, A26075881EEC6F34005DB03E /* passKitTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/passKit/Helpers/PasswordGeneratorFlavor.swift b/passKit/Helpers/PasswordGeneratorFlavor.swift index 362872e..49e1fde 100644 --- a/passKit/Helpers/PasswordGeneratorFlavor.swift +++ b/passKit/Helpers/PasswordGeneratorFlavor.swift @@ -1,5 +1,5 @@ // -// PasswordGeneratorFlavour.swift +// PasswordGeneratorFlavor.swift // passKit // // Created by Danny Moesch on 28.11.18. diff --git a/passKitTests/Helpers/PasswordGeneratorFlavourTest.swift b/passKitTests/Helpers/PasswordGeneratorFlavorTest.swift similarity index 100% rename from passKitTests/Helpers/PasswordGeneratorFlavourTest.swift rename to passKitTests/Helpers/PasswordGeneratorFlavorTest.swift From 780c6c9bcaec7c9f4a0bc41355016af4c7232722 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 23 Feb 2020 11:07:57 +0100 Subject: [PATCH 3/5] Read word list lazyly --- passKit/Helpers/PasswordGeneratorFlavor.swift | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/passKit/Helpers/PasswordGeneratorFlavor.swift b/passKit/Helpers/PasswordGeneratorFlavor.swift index 49e1fde..881709a 100644 --- a/passKit/Helpers/PasswordGeneratorFlavor.swift +++ b/passKit/Helpers/PasswordGeneratorFlavor.swift @@ -13,6 +13,20 @@ public enum PasswordGeneratorFlavor: String { case random = "Random" case xkcd = "XKCD" + private static let words: [String] = { + let bundle = Bundle(identifier: Globals.passKitBundleIdentifier)! + return ["eff_long_wordlist", "eff_short_wordlist"] + .map { name -> String in + guard let asset = NSDataAsset(name: name, bundle: bundle), + let data = String(data: asset.data, encoding: .utf8) else { + return "" + } + return data + } + .joined(separator: "\n") + .splitByNewline() + }() + public var localized: String { return rawValue.localize() } @@ -44,9 +58,9 @@ public enum PasswordGeneratorFlavor: String { case .apple: return Keychain.generatePassword() case .random: - return PasswordGeneratorFlavor.generateRandom(length: length) + return Self.generateRandom(length: length) case .xkcd: - return PasswordGeneratorFlavor.generateXKCD(length: length) + return Self.generateXKCD(length: length) } } @@ -56,22 +70,6 @@ public enum PasswordGeneratorFlavor: String { } private static func generateXKCD(length: Int) -> String { - // Get the word list - let bundle = Bundle(identifier: Globals.passKitBundleIdentifier)! - let wordlistNames = [ - "eff_long_wordlist", - "eff_short_wordlist" - ] - let data = wordlistNames.map{ name -> String in - guard let asset = NSDataAsset(name: name, bundle: bundle), - let data = String(data: asset.data, encoding: .utf8) else { - return "" - } - return data - } - let words = data.joined(separator: "\n").splitByNewline() - - // Generate a password let delimiters = "0123456789!@#$%^&*_+-=" var password = "" (0.. Date: Sun, 23 Feb 2020 11:15:41 +0100 Subject: [PATCH 4/5] Conform to DefaultsSerializable only where needed --- passKit/Helpers/DefaultsKeys.swift | 1 + passKit/Helpers/SearchBarScope.swift | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/passKit/Helpers/DefaultsKeys.swift b/passKit/Helpers/DefaultsKeys.swift index 3f6ab56..05b961a 100644 --- a/passKit/Helpers/DefaultsKeys.swift +++ b/passKit/Helpers/DefaultsKeys.swift @@ -19,6 +19,7 @@ public enum GitAuthenticationMethod: String, DefaultsSerializable { case password, key } +extension SearchBarScope: DefaultsSerializable {} extension PasswordGeneratorFlavor: DefaultsSerializable {} public extension DefaultsKeys { diff --git a/passKit/Helpers/SearchBarScope.swift b/passKit/Helpers/SearchBarScope.swift index 57ba4d6..5eb51cb 100644 --- a/passKit/Helpers/SearchBarScope.swift +++ b/passKit/Helpers/SearchBarScope.swift @@ -8,7 +8,7 @@ import SwiftyUserDefaults -public enum SearchBarScope: Int, CaseIterable, DefaultsSerializable { +public enum SearchBarScope: Int { case current case all @@ -21,3 +21,5 @@ public enum SearchBarScope: Int, CaseIterable, DefaultsSerializable { } } } + +extension SearchBarScope: CaseIterable {} From 0b6ead484de00c527a19ce69f4388edd6732deb9 Mon Sep 17 00:00:00 2001 From: Danny Moesch Date: Sun, 23 Feb 2020 11:49:34 +0100 Subject: [PATCH 5/5] Put conformances to protocols into extensions --- .../PasswordEditorTableViewController.swift | 181 ++++++++++-------- 1 file changed, 103 insertions(+), 78 deletions(-) diff --git a/pass/Controllers/PasswordEditorTableViewController.swift b/pass/Controllers/PasswordEditorTableViewController.swift index 7f5dac5..32aae56 100644 --- a/pass/Controllers/PasswordEditorTableViewController.swift +++ b/pass/Controllers/PasswordEditorTableViewController.swift @@ -19,11 +19,9 @@ enum PasswordEditorCellKey { case type, title, content, placeholders } -class PasswordEditorTableViewController: UITableViewController, FillPasswordTableViewCellDelegate, PasswordSettingSliderTableViewCellDelegate, QRScannerControllerDelegate, UITextFieldDelegate, UITextViewDelegate, SFSafariViewControllerDelegate { +class PasswordEditorTableViewController: UITableViewController { - var tableData = [ - [Dictionary] - ]() + var tableData = [[Dictionary]]() var password: Password? private var navigationItemTitle: String? @@ -90,20 +88,26 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 48 - self.tableView.sectionFooterHeight = UITableView.automaticDimension - self.tableView.estimatedSectionFooterHeight = 0 + tableView.sectionFooterHeight = UITableView.automaticDimension + tableView.estimatedSectionFooterHeight = 0 tableData = [ - [[.type: PasswordEditorCellType.nameCell, .title: "Name".localize(), .content: password?.namePath ?? ""]], [ - [.type: PasswordEditorCellType.fillPasswordCell, .title: "Password".localize(), .content: password?.password ?? ""]], - [[.type: PasswordEditorCellType.additionsCell, .title: "Additions".localize(), .content: password?.additionsPlainText ?? ""]], - [[.type: PasswordEditorCellType.scanQRCodeCell], - [.type: PasswordEditorCellType.deletePasswordCell]] + [.type: PasswordEditorCellType.nameCell, .title: "Name".localize(), .content: password?.namePath ?? ""], + ], + [ + [.type: PasswordEditorCellType.fillPasswordCell, .title: "Password".localize(), .content: password?.password ?? ""], + [.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"], + [.type: PasswordEditorCellType.passwordFlavorCell], + ], + [ + [.type: PasswordEditorCellType.additionsCell, .title: "Additions".localize(), .content: password?.additionsPlainText ?? ""], + ], + [ + [.type: PasswordEditorCellType.scanQRCodeCell], + [.type: PasswordEditorCellType.deletePasswordCell], + ] ] - - tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]) - tableData[1].append([.type: PasswordEditorCellType.passwordFlavorCell]) } override func viewDidLayoutSubviews() { @@ -203,7 +207,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl } } - func showPasswordGeneratorFlavorActionSheet(sourceCell: UITableViewCell, tableView: UITableView) { + private func showPasswordGeneratorFlavorActionSheet(sourceCell: UITableViewCell, tableView: UITableView) { let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) PasswordGeneratorFlavor.allCases.forEach { flavor in @@ -226,23 +230,8 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl self.present(optionMenu, animated: true, completion: nil) } - // generate password, copy to pasteboard, and set the cell - // check whether the current password looks like an OTP field - func generateAndCopyPassword() { - if let currentPassword = fillPasswordCell?.getContent(), Constants.isOtpRelated(line: currentPassword) { - let alert = UIAlertController(title: "Overwrite?".localize(), message: "OverwriteOtpConfiguration?".localize(), preferredStyle: UIAlertController.Style.alert) - alert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive, handler: {_ in - self.generateAndCopyPasswordNoOtpCheck() - })) - alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertAction.Style.cancel, handler: nil)) - self.present(alert, animated: true, completion: nil) - } else { - self.generateAndCopyPasswordNoOtpCheck() - } - } - // generate the password, don't care whether the original line is otp - func generateAndCopyPasswordNoOtpCheck() { + private func generateAndCopyPasswordNoOtpCheck() { // show password settings (e.g., the length slider) showPasswordSettings() @@ -257,20 +246,14 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl } // show password settings (e.g., the length slider) - func showPasswordSettings() { + private func showPasswordSettings() { if hidePasswordSettings == true { hidePasswordSettings = false tableView.reloadSections([passwordSection], with: .fade) } } - // show/hide password settings (e.g., the length slider) - func showHidePasswordSettings() { - hidePasswordSettings = !hidePasswordSettings - tableView.reloadSections([passwordSection], with: .fade) - } - - func insertScannedOTPFields(_ otpauth: String) { + private func insertScannedOTPFields(_ otpauth: String) { // update tableData var additionsString = "" if let additionsPlainText = (tableData[additionsSection][0][PasswordEditorCellKey.content] as? String)?.trimmed, additionsPlainText != "" { @@ -284,20 +267,6 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl additionsCell?.setContent(content: additionsString) } - // MARK: - QRScannerControllerDelegate Methods - func checkScannedOutput(line: String) -> (accept: Bool, message: String) { - if let url = URL(string: line), let _ = Token(url: url) { - return (accept: true, message: "ValidTokenUrl".localize()) - } else { - return (accept: false, message: "InvalidTokenUrl".localize()) - } - } - - // MARK: - QRScannerControllerDelegate Methods - func handleScannedOutput(line: String) { - insertScannedOTPFields(line) - } - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showQRScannerSegue" { if let navController = segue.destination as? UINavigationController { @@ -310,31 +279,6 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl } } - // update tableData so to make sure reloadData() works correctly - func textFieldDidEndEditing(_ textField: UITextField) { - if textField == nameCell?.contentTextField { - tableData[nameSection][0][PasswordEditorCellKey.content] = nameCell?.getContent() - } else if textField == fillPasswordCell?.contentTextField { - if let plainPassword = fillPasswordCell?.getContent() { - tableData[passwordSection][0][PasswordEditorCellKey.content] = plainPassword - } - } - } - - // update tableData so to make sure reloadData() works correctly - func textViewDidEndEditing(_ textView: UITextView) { - if textView == additionsCell?.contentTextView { - tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsCell?.getContent() - } - } - - func textFieldDidBeginEditing(_ textField: UITextField) { - if textField == fillPasswordCell?.contentTextField { - // show password generation settings automatically - showPasswordSettings() - } - } - func getNameURL() -> (String, URL) { let encodedName = (nameCell?.getContent()?.stringByAddingPercentEncodingForRFC3986())! let name = URL(string: encodedName)!.lastPathComponent @@ -375,6 +319,54 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl return true } +} + +// MARK: - FillPasswordTableViewCellDelegate +extension PasswordEditorTableViewController: FillPasswordTableViewCellDelegate { + + // generate password, copy to pasteboard, and set the cell + // check whether the current password looks like an OTP field + func generateAndCopyPassword() { + if let currentPassword = fillPasswordCell?.getContent(), Constants.isOtpRelated(line: currentPassword) { + let alert = UIAlertController(title: "Overwrite?".localize(), message: "OverwriteOtpConfiguration?".localize(), preferredStyle: UIAlertController.Style.alert) + alert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive, handler: {_ in + self.generateAndCopyPasswordNoOtpCheck() + })) + alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertAction.Style.cancel, handler: nil)) + self.present(alert, animated: true, completion: nil) + } else { + self.generateAndCopyPasswordNoOtpCheck() + } + } + + // show/hide password settings (e.g., the length slider) + func showHidePasswordSettings() { + hidePasswordSettings = !hidePasswordSettings + tableView.reloadSections([passwordSection], with: .fade) + } +} + +// MARK: - PasswordSettingSliderTableViewCellDelegate +extension PasswordEditorTableViewController: PasswordSettingSliderTableViewCellDelegate {} + +// MARK: - QRScannerControllerDelegate +extension PasswordEditorTableViewController: QRScannerControllerDelegate { + + func checkScannedOutput(line: String) -> (accept: Bool, message: String) { + if let url = URL(string: line), let _ = Token(url: url) { + return (accept: true, message: "ValidTokenUrl".localize()) + } else { + return (accept: false, message: "InvalidTokenUrl".localize()) + } + } + + func handleScannedOutput(line: String) { + insertScannedOTPFields(line) + } +} + +// MARK: - SFSafariViewControllerDelegate +extension PasswordEditorTableViewController: SFSafariViewControllerDelegate { func safariViewControllerDidFinish(_ controller: SFSafariViewController) { let copiedLinesSplit = UIPasteboard.general.string?.components(separatedBy: CharacterSet.whitespacesAndNewlines).filter({ !$0.isEmpty }) @@ -395,3 +387,36 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl } } } + +// MARK: - UITextFieldDelegate +extension PasswordEditorTableViewController: UITextFieldDelegate { + + // update tableData so to make sure reloadData() works correctly + func textFieldDidEndEditing(_ textField: UITextField) { + if textField == nameCell?.contentTextField { + tableData[nameSection][0][PasswordEditorCellKey.content] = nameCell?.getContent() + } else if textField == fillPasswordCell?.contentTextField { + if let plainPassword = fillPasswordCell?.getContent() { + tableData[passwordSection][0][PasswordEditorCellKey.content] = plainPassword + } + } + } + + func textFieldDidBeginEditing(_ textField: UITextField) { + if textField == fillPasswordCell?.contentTextField { + // show password generation settings automatically + showPasswordSettings() + } + } +} + +// MARK: - UITextViewDelegate +extension PasswordEditorTableViewController: UITextViewDelegate { + + // update tableData so to make sure reloadData() works correctly + func textViewDidEndEditing(_ textView: UITextView) { + if textView == additionsCell?.contentTextView { + tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsCell?.getContent() + } + } +}