diff --git a/pass/Controllers/GeneralSettingsTableViewController.swift b/pass/Controllers/GeneralSettingsTableViewController.swift index 051b19e..209c5ed 100644 --- a/pass/Controllers/GeneralSettingsTableViewController.swift +++ b/pass/Controllers/GeneralSettingsTableViewController.swift @@ -28,12 +28,21 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { return uiSwitch }() - let rememberPassphraseSwitch: UISwitch = { + let rememberPGPPassphraseSwitch: UISwitch = { let uiSwitch = UISwitch() uiSwitch.onTintColor = Globals.blue uiSwitch.sizeToFit() - uiSwitch.addTarget(self, action: #selector(rememberPassphraseSwitchAction(_:)), for: UIControlEvents.valueChanged) - uiSwitch.isOn = SharedDefaults[.isRememberPassphraseOn] + uiSwitch.addTarget(self, action: #selector(rememberPGPPassphraseSwitchAction(_:)), for: UIControlEvents.valueChanged) + uiSwitch.isOn = SharedDefaults[.isRememberPGPPassphraseOn] + return uiSwitch + }() + + let rememberGitCredentialPassphraseSwitch: UISwitch = { + let uiSwitch = UISwitch() + uiSwitch.onTintColor = Globals.blue + uiSwitch.sizeToFit() + uiSwitch.addTarget(self, action: #selector(rememberGitCredentialPassphraseSwitchAction(_:)), for: UIControlEvents.valueChanged) + uiSwitch.isOn = SharedDefaults[.isRememberGitCredentialPassphraseOn] return uiSwitch }() @@ -58,7 +67,8 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { // section 2 [ - [.title: "Remember Passphrase", .action: "none",], + [.title: "Remember PGP Key Passphrase", .action: "none",], + [.title: "Remember Git Credential Passphrase", .action: "none",], ], [ [.title: "Show Folder", .action: "none",], @@ -98,10 +108,14 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { cell.accessoryView = accessoryView cell.selectionStyle = .none hideOTPSwitch.isOn = SharedDefaults[.isHideOTPOn] - case "Remember Passphrase": + case "Remember PGP Key Passphrase": cell.accessoryType = .none cell.selectionStyle = .none - cell.accessoryView = rememberPassphraseSwitch + cell.accessoryView = rememberPGPPassphraseSwitch + case "Remember Git Credential Passphrase": + cell.accessoryType = .none + cell.selectionStyle = .none + cell.accessoryView = rememberGitCredentialPassphraseSwitch case "Show Folder": cell.accessoryType = .none cell.selectionStyle = .none @@ -176,13 +190,21 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController { NotificationCenter.default.post(name: .passwordDetailDisplaySettingChanged, object: nil) } - @objc func rememberPassphraseSwitchAction(_ sender: Any?) { - SharedDefaults[.isRememberPassphraseOn] = rememberPassphraseSwitch.isOn - if rememberPassphraseSwitch.isOn == false { + @objc func rememberPGPPassphraseSwitchAction(_ sender: Any?) { + SharedDefaults[.isRememberPGPPassphraseOn] = rememberPGPPassphraseSwitch.isOn + if rememberPGPPassphraseSwitch.isOn == false { passwordStore.pgpKeyPassphrase = nil } } + @objc func rememberGitCredentialPassphraseSwitchAction(_ sender: Any?) { + SharedDefaults[.isRememberGitCredentialPassphraseOn] = rememberGitCredentialPassphraseSwitch.isOn + if rememberGitCredentialPassphraseSwitch.isOn == false { + passwordStore.gitSSHPrivateKeyPassphrase = nil + passwordStore.gitPassword = nil + } + } + @objc func showFolderSwitchAction(_ sender: Any?) { SharedDefaults[.isShowFolderOn] = showFolderSwitch.isOn NotificationCenter.default.post(name: .passwordDisplaySettingChanged, object: nil) diff --git a/pass/Controllers/GitServerSettingTableViewController.swift b/pass/Controllers/GitServerSettingTableViewController.swift index ab3d76b..8189eb3 100644 --- a/pass/Controllers/GitServerSettingTableViewController.swift +++ b/pass/Controllers/GitServerSettingTableViewController.swift @@ -93,6 +93,8 @@ class GitServerSettingTableViewController: UITableViewController { ) ) } + // Remember git credential password/passphrase temporarily, ask whether users want this after a successful clone. + SharedDefaults[.isRememberGitCredentialPassphraseOn] = true let dispatchQueue = DispatchQueue.global(qos: .userInitiated) dispatchQueue.async { do { @@ -113,9 +115,21 @@ class GitServerSettingTableViewController: UITableViewController { SharedDefaults[.gitURL] = URL(string: gitRepostiroyURL) SharedDefaults[.gitUsername] = username SharedDefaults[.gitAuthenticationMethod] = auth - SVProgressHUD.showSuccess(withStatus: "Done") - SVProgressHUD.dismiss(withDelay: 1) - self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) + SVProgressHUD.dismiss() + let savePassphraseAlert = UIAlertController(title: "Done", message: "Do you want to save the Git credential password/passphrase?", preferredStyle: UIAlertControllerStyle.alert) + // no + savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in + SharedDefaults[.isRememberGitCredentialPassphraseOn] = false + self.passwordStore.gitPassword = nil + self.passwordStore.gitSSHPrivateKeyPassphrase = nil + self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) + }) + // yes + savePassphraseAlert.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.destructive) {_ in + SharedDefaults[.isRememberGitCredentialPassphraseOn] = true + self.performSegue(withIdentifier: "saveGitServerSettingSegue", sender: self) + }) + self.present(savePassphraseAlert, animated: true, completion: nil) } } catch { DispatchQueue.main.async { @@ -257,7 +271,7 @@ class GitServerSettingTableViewController: UITableViewController { case .http: message = "Please fill in the password of your Git account." case .ssh: - message = "Please fill in the password of your SSH key." + message = "Please fill in the passphrase of your SSH key." } DispatchQueue.main.async { diff --git a/pass/Controllers/PGPKeyArmorSettingTableViewController.swift b/pass/Controllers/PGPKeyArmorSettingTableViewController.swift index 30705b2..d3430ed 100644 --- a/pass/Controllers/PGPKeyArmorSettingTableViewController.swift +++ b/pass/Controllers/PGPKeyArmorSettingTableViewController.swift @@ -120,7 +120,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe // no savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in self.pgpPassphrase = nil - SharedDefaults[.isRememberPassphraseOn] = false + SharedDefaults[.isRememberPGPPassphraseOn] = false self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) }) // yes @@ -129,7 +129,7 @@ class PGPKeyArmorSettingTableViewController: UITableViewController, UITextViewDe let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in self.pgpPassphrase = alert.textFields?.first?.text - SharedDefaults[.isRememberPassphraseOn] = true + SharedDefaults[.isRememberPGPPassphraseOn] = true self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) })) alert.addTextField(configurationHandler: {(textField: UITextField!) in diff --git a/pass/Controllers/PGPKeySettingTableViewController.swift b/pass/Controllers/PGPKeySettingTableViewController.swift index 364b82a..ae86c7d 100644 --- a/pass/Controllers/PGPKeySettingTableViewController.swift +++ b/pass/Controllers/PGPKeySettingTableViewController.swift @@ -45,7 +45,7 @@ class PGPKeySettingTableViewController: UITableViewController { // no savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in self.pgpPassphrase = nil - SharedDefaults[.isRememberPassphraseOn] = false + SharedDefaults[.isRememberPGPPassphraseOn] = false self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) }) // yes @@ -54,7 +54,7 @@ class PGPKeySettingTableViewController: UITableViewController { let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in self.pgpPassphrase = alert.textFields?.first?.text - SharedDefaults[.isRememberPassphraseOn] = true + SharedDefaults[.isRememberPGPPassphraseOn] = true self.performSegue(withIdentifier: "savePGPKeySegue", sender: self) })) alert.addTextField(configurationHandler: {(textField: UITextField!) in diff --git a/pass/Controllers/PasswordDetailTableViewController.swift b/pass/Controllers/PasswordDetailTableViewController.swift index 14d9a4a..0209d61 100644 --- a/pass/Controllers/PasswordDetailTableViewController.swift +++ b/pass/Controllers/PasswordDetailTableViewController.swift @@ -134,7 +134,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni self.present(alert, animated: true, completion: nil) } let _ = sem.wait(timeout: DispatchTime.distantFuture) - if SharedDefaults[.isRememberPassphraseOn] { + if SharedDefaults[.isRememberPGPPassphraseOn] { self.passwordStore.pgpKeyPassphrase = passphrase } return passphrase diff --git a/pass/Controllers/PasswordsViewController.swift b/pass/Controllers/PasswordsViewController.swift index 7706157..322752b 100644 --- a/pass/Controllers/PasswordsViewController.swift +++ b/pass/Controllers/PasswordsViewController.swift @@ -174,8 +174,17 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV DispatchQueue.main.async { SVProgressHUD.dismiss() self.syncControl.endRefreshing() + let error = error as NSError + var message = error.localizedDescription + if let underlyingError = error.userInfo[NSUnderlyingErrorKey] as? NSError { + message = "\(message)\nUnderlying error: \(underlyingError.localizedDescription)" + if underlyingError.localizedDescription.contains("Wrong passphrase") { + message = "\(message)\nRecovery suggestion: Wrong credential password/passphrase has been removed, please try again." + gitCredential.delete() + } + } DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(800)) { - Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) + Utils.alert(title: "Error", message: message, controller: self, completion: nil) } } } @@ -369,7 +378,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV // bring back SVProgressHUD.show(withStatus: "Decrypting") } - if SharedDefaults[.isRememberPassphraseOn] { + if SharedDefaults[.isRememberPGPPassphraseOn] { self.passwordStore.pgpKeyPassphrase = passphrase } return passphrase diff --git a/pass/Controllers/SettingsTableViewController.swift b/pass/Controllers/SettingsTableViewController.swift index c8b6986..cc24bc1 100644 --- a/pass/Controllers/SettingsTableViewController.swift +++ b/pass/Controllers/SettingsTableViewController.swift @@ -33,7 +33,7 @@ class SettingsTableViewController: UITableViewController { if let controller = segue.source as? PGPKeySettingTableViewController { SharedDefaults[.pgpPrivateKeyURL] = URL(string: controller.pgpPrivateKeyURLTextField.text!) SharedDefaults[.pgpPublicKeyURL] = URL(string: controller.pgpPublicKeyURLTextField.text!) - if SharedDefaults[.isRememberPassphraseOn] { + if SharedDefaults[.isRememberPGPPassphraseOn] { self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase } SharedDefaults[.pgpKeySource] = "url" @@ -61,7 +61,7 @@ class SettingsTableViewController: UITableViewController { } else if let controller = segue.source as? PGPKeyArmorSettingTableViewController { SharedDefaults[.pgpKeySource] = "armor" - if SharedDefaults[.isRememberPassphraseOn] { + if SharedDefaults[.isRememberPGPPassphraseOn] { self.passwordStore.pgpKeyPassphrase = controller.pgpPassphrase } @@ -259,7 +259,7 @@ class SettingsTableViewController: UITableViewController { // no savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in self.passwordStore.pgpKeyPassphrase = nil - SharedDefaults[.isRememberPassphraseOn] = false + SharedDefaults[.isRememberPGPPassphraseOn] = false self.saveImportedPGPKey() }) // yes @@ -268,7 +268,7 @@ class SettingsTableViewController: UITableViewController { let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in self.passwordStore.pgpKeyPassphrase = alert.textFields?.first?.text - SharedDefaults[.isRememberPassphraseOn] = true + SharedDefaults[.isRememberPGPPassphraseOn] = true self.saveImportedPGPKey() })) alert.addTextField(configurationHandler: {(textField: UITextField!) in diff --git a/passExtension/ExtensionViewController.swift b/passExtension/ExtensionViewController.swift index 29f6f0b..a37912d 100644 --- a/passExtension/ExtensionViewController.swift +++ b/passExtension/ExtensionViewController.swift @@ -223,7 +223,7 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV self.present(alert, animated: true, completion: nil) } let _ = sem.wait(timeout: DispatchTime.distantFuture) - if SharedDefaults[.isRememberPassphraseOn] { + if SharedDefaults[.isRememberPGPPassphraseOn] { self.passwordStore.pgpKeyPassphrase = passphrase } return passphrase diff --git a/passKit/Helpers/DefaultsKeys.swift b/passKit/Helpers/DefaultsKeys.swift index 5a4a597..508f395 100644 --- a/passKit/Helpers/DefaultsKeys.swift +++ b/passKit/Helpers/DefaultsKeys.swift @@ -35,7 +35,8 @@ public extension DefaultsKeys { static let isHideUnknownOn = DefaultsKey("isHideUnknownOn") static let isHideOTPOn = DefaultsKey("isHideOTPOn") - static let isRememberPassphraseOn = DefaultsKey("isRememberPassphraseOn") + static let isRememberPGPPassphraseOn = DefaultsKey("isRememberPGPPassphraseOn") + static let isRememberGitCredentialPassphraseOn = DefaultsKey("isRememberGitCredentialPassphraseOn") static let isShowFolderOn = DefaultsKey("isShowFolderOn") static let passwordGeneratorFlavor = DefaultsKey("passwordGeneratorFlavor") diff --git a/passKit/Models/GitCredential.swift b/passKit/Models/GitCredential.swift index d71aa51..ddf5741 100644 --- a/passKit/Models/GitCredential.swift +++ b/passKit/Models/GitCredential.swift @@ -26,38 +26,39 @@ public struct GitCredential { public func credentialProvider(requestGitPassword: @escaping (Credential, String?) -> String?) throws -> GTCredentialProvider { var attempts = 0 - var lastPassword: String? = nil return GTCredentialProvider { (_, _, _) -> (GTCredential?) in var credential: GTCredential? = nil switch self.credential { case let .http(userName): - var newPassword = self.passwordStore.gitPassword - if newPassword == nil || attempts != 0 { + var lastPassword = self.passwordStore.gitPassword + if lastPassword == nil || attempts != 0 { if let requestedPassword = requestGitPassword(self.credential, lastPassword) { - newPassword = requestedPassword - self.passwordStore.gitPassword = newPassword + if SharedDefaults[.isRememberGitCredentialPassphraseOn] { + self.passwordStore.gitPassword = requestedPassword + } + lastPassword = requestedPassword } else { return nil } } attempts += 1 - lastPassword = newPassword - credential = try? GTCredential(userName: userName, password: newPassword!) + credential = try? GTCredential(userName: userName, password: lastPassword!) case let .ssh(userName, privateKeyFile): // remarks: in fact, attempts > 1 never happens even with the wrong passphrase - var newPassword = self.passwordStore.gitSSHPrivateKeyPassphrase - if newPassword == nil || attempts != 0 { + var lastPassword = self.passwordStore.gitSSHPrivateKeyPassphrase + if lastPassword == nil || attempts != 0 { if let requestedPassword = requestGitPassword(self.credential, lastPassword) { - newPassword = requestedPassword - self.passwordStore.gitSSHPrivateKeyPassphrase = newPassword + if SharedDefaults[.isRememberGitCredentialPassphraseOn] { + self.passwordStore.gitSSHPrivateKeyPassphrase = requestedPassword + } + lastPassword = requestedPassword } else { return nil } } attempts += 1 - lastPassword = newPassword - credential = try? GTCredential(userName: userName, publicKeyURL: nil, privateKeyURL: privateKeyFile, passphrase: newPassword!) + credential = try? GTCredential(userName: userName, publicKeyURL: nil, privateKeyURL: privateKeyFile, passphrase: lastPassword!) } return credential } @@ -66,9 +67,9 @@ public struct GitCredential { public func delete() { switch credential { case .http: - Utils.removeKeychain(name: "gitPassword") + self.passwordStore.gitPassword = nil case .ssh: - Utils.removeKeychain(name: "gitSSHKeyPassphrase") + self.passwordStore.gitSSHPrivateKeyPassphrase = nil } } } diff --git a/passKit/Models/PasswordStore.swift b/passKit/Models/PasswordStore.swift index 537672a..87b5210 100644 --- a/passKit/Models/PasswordStore.swift +++ b/passKit/Models/PasswordStore.swift @@ -120,6 +120,7 @@ public class PasswordStore { print(Globals.documentPathLegacy) print(Globals.libraryPathLegacy) migrateIfNeeded() + backwardCompatibility() do { if fm.fileExists(atPath: storeURL.path) { @@ -166,6 +167,13 @@ public class PasswordStore { updatePasswordEntityCoreData() } + private func backwardCompatibility() { + // For the newly-introduced isRememberGitCredentialPassphraseOn (20171008) + if (self.gitPassword != nil || self.gitSSHPrivateKeyPassphrase != nil) && SharedDefaults[.isRememberGitCredentialPassphraseOn] == false { + SharedDefaults[.isRememberGitCredentialPassphraseOn] = true + } + } + enum SSHKeyType { case `public`, secret } @@ -328,7 +336,6 @@ public class PasswordStore { let remote = try GTRemote(name: "origin", in: storeRepository) try storeRepository.pull(storeRepository.currentBranch(), from: remote, withOptions: options, progress: transferProgressBlock) } catch { - credential.delete() throw(error) } DispatchQueue.main.async { @@ -581,7 +588,6 @@ public class PasswordStore { try storeRepository.push(masterBranch, to: remote, withOptions: options, progress: transferProgressBlock) } } catch { - credential.delete() throw(error) } } @@ -865,7 +871,7 @@ public class PasswordStore { Utils.removeFileIfExists(atPath: Globals.gitSSHPrivateKeyPath) Defaults.remove(.gitSSHPrivateKeyArmor) Defaults.remove(.gitSSHPrivateKeyURL) - Utils.removeKeychain(name: ".gitSSHPrivateKeyPassphrase") + self.gitSSHPrivateKeyPassphrase = nil } public func gitSSHKeyExists(inFileSharing: Bool = false) -> Bool {