diff --git a/pass.xcodeproj/project.pbxproj b/pass.xcodeproj/project.pbxproj index 114019f..96c8606 100644 --- a/pass.xcodeproj/project.pbxproj +++ b/pass.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ 301F646D216166AA0071A4CE /* AdditionFieldTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 301F646C216166AA0071A4CE /* AdditionFieldTest.swift */; }; 302E85612125ECC70031BA64 /* Parser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302E85602125ECC70031BA64 /* Parser.swift */; }; 302E85632125EE550031BA64 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302E85622125EE550031BA64 /* Constants.swift */; }; + 30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */; }; + 30A1D29E21AF468F00E2D1F7 /* PasswordGeneratorFlavour.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D29D21AF468E00E2D1F7 /* PasswordGeneratorFlavour.swift */; }; 30AAC05321989DCE00F656CE /* PasswordHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AAC05221989DCE00F656CE /* PasswordHelpers.swift */; }; 30B04860209A5141001013CA /* PasswordTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30B0485F209A5141001013CA /* PasswordTest.swift */; }; 30FD2F78214D9E0E005E0A92 /* ParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30FD2F77214D9E0E005E0A92 /* ParserTest.swift */; }; @@ -190,6 +192,8 @@ 301F646C216166AA0071A4CE /* AdditionFieldTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdditionFieldTest.swift; sourceTree = ""; }; 302E85602125ECC70031BA64 /* Parser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Parser.swift; sourceTree = ""; }; 302E85622125EE550031BA64 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordGeneratorFlavourTest.swift; sourceTree = ""; }; + 30A1D29D21AF468E00E2D1F7 /* PasswordGeneratorFlavour.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PasswordGeneratorFlavour.swift; path = Helpers/PasswordGeneratorFlavour.swift; sourceTree = ""; }; 30AAC05221989DCE00F656CE /* PasswordHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PasswordHelpers.swift; path = Helpers/PasswordHelpers.swift; sourceTree = ""; }; 30B0485F209A5141001013CA /* PasswordTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordTest.swift; sourceTree = ""; }; 30FD2F77214D9E0E005E0A92 /* ParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParserTest.swift; sourceTree = ""; }; @@ -382,6 +386,7 @@ children = ( 301F6465216164830071A4CE /* PasswordHelpersTest.swift */, 301F6469216166000071A4CE /* StringExtensionTest.swift */, + 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */, ); path = Helpers; sourceTree = ""; @@ -528,6 +533,7 @@ A239F5202157B75E00576CBF /* FileManagerExtension.swift */, A2F4E21A1EED80160011986E /* Globals.swift */, A2F4E21B1EED80160011986E /* NotificationNames.swift */, + 30A1D29D21AF468E00E2D1F7 /* PasswordGeneratorFlavour.swift */, 30AAC05221989DCE00F656CE /* PasswordHelpers.swift */, A239F51E2157B72700576CBF /* StringExtension.swift */, A2F4E21C1EED80160011986E /* UITextFieldExtension.swift */, @@ -1149,6 +1155,7 @@ A2C532BB201E5A9600DB9F53 /* PasscodeLock.swift in Sources */, A2F4E2151EED800F0011986E /* Password.swift in Sources */, A26075AD1EEC7125005DB03E /* pass.xcdatamodeld in Sources */, + 30A1D29E21AF468F00E2D1F7 /* PasswordGeneratorFlavour.swift in Sources */, A239F51F2157B72700576CBF /* StringExtension.swift in Sources */, A239F5212157B75E00576CBF /* FileManagerExtension.swift in Sources */, A2F4E21E1EED80160011986E /* AppError.swift in Sources */, @@ -1177,6 +1184,7 @@ 30FD2F78214D9E0E005E0A92 /* ParserTest.swift in Sources */, 30B04860209A5141001013CA /* PasswordTest.swift in Sources */, 301F6468216165290071A4CE /* ConstantsTest.swift in Sources */, + 30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift in Sources */, A26075881EEC6F34005DB03E /* passKitTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/pass/AppDelegate.swift b/pass/AppDelegate.swift index 3ef0c33..ce0d190 100644 --- a/pass/AppDelegate.swift +++ b/pass/AppDelegate.swift @@ -33,7 +33,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { self.perform(#selector(postSearchNotification), with: nil, afterDelay: 0.4) } } - Utils.initDefaultKeys() return true } diff --git a/pass/Controllers/AddPasswordTableViewController.swift b/pass/Controllers/AddPasswordTableViewController.swift index a0bbeb7..924e5bd 100644 --- a/pass/Controllers/AddPasswordTableViewController.swift +++ b/pass/Controllers/AddPasswordTableViewController.swift @@ -20,8 +20,7 @@ class AddPasswordTableViewController: PasswordEditorTableViewController { [[.type: PasswordEditorCellType.additionsCell, .title: "additions"]], [[.type: PasswordEditorCellType.scanQRCodeCell]] ] - if let lengthSetting = Globals.passwordDefaultLength[SharedDefaults[.passwordGeneratorFlavor]], - lengthSetting.max > lengthSetting.min { + if PasswordGeneratorFlavour.from(SharedDefaults[.passwordGeneratorFlavor]) == PasswordGeneratorFlavour.RANDOM { tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]) } tableData[1].append([.type: PasswordEditorCellType.memorablePasswordGeneratorCell]) diff --git a/pass/Controllers/EditPasswordTableViewController.swift b/pass/Controllers/EditPasswordTableViewController.swift index 0c756ad..35ef7e9 100644 --- a/pass/Controllers/EditPasswordTableViewController.swift +++ b/pass/Controllers/EditPasswordTableViewController.swift @@ -18,8 +18,7 @@ class EditPasswordTableViewController: PasswordEditorTableViewController { [[.type: PasswordEditorCellType.scanQRCodeCell], [.type: PasswordEditorCellType.deletePasswordCell]] ] - if let lengthSetting = Globals.passwordDefaultLength[SharedDefaults[.passwordGeneratorFlavor]], - lengthSetting.max > lengthSetting.min { + if PasswordGeneratorFlavour.from(SharedDefaults[.passwordGeneratorFlavor]) == PasswordGeneratorFlavour.RANDOM { tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]) } tableData[1].append([.type: PasswordEditorCellType.memorablePasswordGeneratorCell]) diff --git a/pass/Controllers/PasswordEditorTableViewController.swift b/pass/Controllers/PasswordEditorTableViewController.swift index 3611de7..a7aa3b9 100644 --- a/pass/Controllers/PasswordEditorTableViewController.swift +++ b/pass/Controllers/PasswordEditorTableViewController.swift @@ -104,11 +104,10 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl return fillPasswordCell! case .passwordLengthCell: passwordLengthCell = tableView.dequeueReusableCell(withIdentifier: "passwordLengthCell", for: indexPath) as? SliderTableViewCell - let lengthSetting = Globals.passwordDefaultLength[SharedDefaults[.passwordGeneratorFlavor]] ?? - Globals.passwordDefaultLength["Random"] - let minimumLength = lengthSetting?.min ?? 0 - let maximumLength = lengthSetting?.max ?? 0 - var defaultLength = lengthSetting?.def ?? 0 + let lengthSetting = PasswordGeneratorFlavour.from(SharedDefaults[.passwordGeneratorFlavor]).defaultLength + let minimumLength = lengthSetting.min + let maximumLength = lengthSetting.max + var defaultLength = lengthSetting.def if let currentPasswordLength = (tableData[passwordSection][0][PasswordEditorCellKey.content] as? String)?.count, currentPasswordLength >= minimumLength, currentPasswordLength <= maximumLength { @@ -203,7 +202,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl showPasswordSettings() let length = passwordLengthCell?.roundedValue ?? 0 - let plainPassword = Password.generatePassword(length: length) + let plainPassword = PasswordGeneratorFlavour.from(SharedDefaults[.passwordGeneratorFlavor]).generatePassword(length: length) SecurePasteboard.shared.copy(textToCopy: plainPassword) // update tableData so to make sure reloadData() works correctly diff --git a/passKit/Helpers/Globals.swift b/passKit/Helpers/Globals.swift index cc7962d..990fc8b 100644 --- a/passKit/Helpers/Globals.swift +++ b/passKit/Helpers/Globals.swift @@ -39,9 +39,6 @@ public class Globals { public static let iTunesFileSharingPGPPrivate = iTunesFileSharingPath + "/gpg_key" public static let iTunesFileSharingSSHPrivate = iTunesFileSharingPath + "/ssh_key" - public static let passwordDefaultLength = ["Random": (min: 4, max: 64, def: 16), - "Apple": (min: 15, max: 15, def: 15)] - public static let gitSignatureDefaultName = "Pass for iOS" public static let gitSignatureDefaultEmail = "user@passforios" diff --git a/passKit/Helpers/PasswordGeneratorFlavour.swift b/passKit/Helpers/PasswordGeneratorFlavour.swift new file mode 100644 index 0000000..6ac9b27 --- /dev/null +++ b/passKit/Helpers/PasswordGeneratorFlavour.swift @@ -0,0 +1,44 @@ +// +// PasswordGeneratorFlavour.swift +// passKit +// +// Created by Danny Moesch on 28.11.18. +// Copyright © 2018 Bob Sun. All rights reserved. +// + +import KeychainAccess + +public enum PasswordGeneratorFlavour: String { + case APPLE = "Apple" + case RANDOM = "Random" + + private static let ALLOWED_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+-=" + + public static func from(_ option: String) -> PasswordGeneratorFlavour { + return PasswordGeneratorFlavour(rawValue: option) ?? PasswordGeneratorFlavour.RANDOM + } + + public var defaultLength: (min: Int, max: Int, def: Int) { + switch self { + case .APPLE: + return (15, 15, 15) + default: + return (4, 64, 16) + } + } + + public func generatePassword(length: Int) -> String { + switch self { + case .APPLE: + return Keychain.generatePassword() + default: + return PasswordGeneratorFlavour.randomString(length: length) + } + } + + private static func randomString(length: Int) -> String { + return String((0.. Void)? = nil, completion: (() -> Void)? = nil) { let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert) diff --git a/passKit/Models/Password.swift b/passKit/Models/Password.swift index 007aa5f..214250e 100644 --- a/passKit/Models/Password.swift +++ b/passKit/Models/Password.swift @@ -9,7 +9,6 @@ import SwiftyUserDefaults import OneTimePassword import Base32 -import KeychainAccess public class Password { @@ -276,31 +275,4 @@ public class Password { // get and return the password return self.otpToken?.currentPassword } - - public static func generatePassword(length: Int) -> String{ - switch SharedDefaults[.passwordGeneratorFlavor] { - case "Random": - return randomString(length: length) - case "Apple": - return Keychain.generatePassword() - default: - return randomString(length: length) - } - } - - private static func randomString(length: Int) -> String { - - let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+-=" - let len = UInt32(letters.length) - - var randomString = "" - - for _ in 0 ..< length { - let rand = arc4random_uniform(len) - var nextChar = letters.character(at: Int(rand)) - randomString += NSString(characters: &nextChar, length: 1) as String - } - - return randomString - } } diff --git a/passKitTests/Helpers/PasswordGeneratorFlavourTest.swift b/passKitTests/Helpers/PasswordGeneratorFlavourTest.swift new file mode 100644 index 0000000..9fe79fa --- /dev/null +++ b/passKitTests/Helpers/PasswordGeneratorFlavourTest.swift @@ -0,0 +1,43 @@ +// +// PasswordGeneratorFlavourTest.swift +// passKitTests +// +// Created by Danny Moesch on 28.11.18. +// Copyright © 2018 Bob Sun. All rights reserved. +// + +import KeychainAccess +import XCTest + +@testable import passKit + +class PasswordGeneratorFlavourTest: XCTestCase { + + private let KEYCHAIN_PASSWORD_LENGTH = Keychain.generatePassword().count + + func testFrom() { + XCTAssertEqual(PasswordGeneratorFlavour.from("Apple"), PasswordGeneratorFlavour.APPLE) + XCTAssertEqual(PasswordGeneratorFlavour.from("Random"), PasswordGeneratorFlavour.RANDOM) + XCTAssertEqual(PasswordGeneratorFlavour.from("Something"), PasswordGeneratorFlavour.RANDOM) + XCTAssertEqual(PasswordGeneratorFlavour.from(""), PasswordGeneratorFlavour.RANDOM) + } + + func testDefaultLength() { + // Ensure properly chosen default length values. So this check no longer needs to be performed in the code. + PasswordGeneratorFlavour.allCases.map { $0.defaultLength }.forEach { defaultLength in + XCTAssertLessThanOrEqual(defaultLength.min, defaultLength.max) + XCTAssertLessThanOrEqual(defaultLength.def, defaultLength.max) + XCTAssertGreaterThanOrEqual(defaultLength.def, defaultLength.min) + } + } + + func testGeneratePassword() { + let apple = PasswordGeneratorFlavour.APPLE + let random = PasswordGeneratorFlavour.RANDOM + + XCTAssertEqual(apple.generatePassword(length: 4).count, KEYCHAIN_PASSWORD_LENGTH) + XCTAssertEqual(random.generatePassword(length: 0).count, 0) + XCTAssertEqual(random.generatePassword(length: 4).count, 4) + XCTAssertEqual(random.generatePassword(length: 100).count, 100) + } +}