Introduce KeyImporter protocol to reduce code duplications in SSH key importers

This commit is contained in:
Danny Moesch 2020-02-15 18:12:58 +01:00 committed by Mingshen Sun
parent 6aa39db657
commit 94a5f8c501
14 changed files with 189 additions and 144 deletions

View file

@ -213,78 +213,65 @@ class GitRepositorySettingsTableViewController: UITableViewController {
}
}
@IBAction func importSSHKey(segue: UIStoryboardSegue) {
guard let sourceController = segue.source as? KeyImporter, sourceController.isReadyToUse() else {
return
}
importSSHKey(using: sourceController)
}
private func importSSHKey(using keyImporter: KeyImporter) {
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do {
try keyImporter.importKeys()
DispatchQueue.main.async {
SVProgressHUD.showSuccess(withStatus: "Imported".localize())
SVProgressHUD.dismiss(withDelay: 1)
}
Defaults.gitSSHKeySource = type(of: keyImporter).keySource
self.gitAuthenticationMethod = .key
self.sshLabel?.isEnabled = true
} catch {
Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self)
}
}
}
// MARK: - Helper Functions
private func showSSHKeyActionSheet() {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var urlActionTitle = "DownloadFromUrl".localize()
var armorActionTitle = "AsciiArmorEncryptedKey".localize()
var fileActionTitle = "ITunesFileSharing".localize()
switch Defaults.gitSSHKeySource {
case .url: urlActionTitle = "\(urlActionTitle)"
case .armor: armorActionTitle = "\(armorActionTitle)"
case .file: fileActionTitle = "\(fileActionTitle)"
case .none: break
}
let urlAction = UIAlertAction(title: urlActionTitle, style: .default) { _ in
optionMenu.addAction(UIAlertAction(title: SSHKeyUrlImportTableViewController.menuLabel, style: .default) { _ in
self.performSegue(withIdentifier: "setGitSSHKeyByURLSegue", sender: self)
}
optionMenu.addAction(urlAction)
let armorAction = UIAlertAction(title: armorActionTitle, style: .default) { _ in
})
optionMenu.addAction(UIAlertAction(title: SSHKeyArmorImportTableViewController.menuLabel, style: .default) { _ in
self.performSegue(withIdentifier: "setGitSSHKeyByArmorSegue", sender: self)
})
if isReadyToUse() {
optionMenu.addAction(UIAlertAction(title: "\(Self.menuLabel) (\("Import".localize()))", style: .default) { _ in
self.importSSHKey(using: self)
})
} else {
optionMenu.addAction(UIAlertAction(title: "\(Self.menuLabel) (\("Tips".localize()))", style: .default) { _ in
let title = "Tips".localize()
let message = "SshCopyPrivateKeyToPass.".localize()
Utils.alert(title: title, message: message, controller: self)
})
}
optionMenu.addAction(armorAction)
let fileAction: UIAlertAction = {
if KeyFileManager.PrivateSsh.doesKeyFileExist() {
fileActionTitle.append(" (\("Import".localize()))")
let action = UIAlertAction(title: fileActionTitle, style: .default) { _ in
do {
try self.passwordStore.gitSSHKeyImportFromFileSharing()
Defaults.gitSSHKeySource = .file
SVProgressHUD.showSuccess(withStatus: "Imported".localize())
SVProgressHUD.dismiss(withDelay: 1)
self.sshLabel?.isEnabled = true
self.gitAuthenticationMethod = .key
self.updateAuthenticationMethodCheckView(for: self.gitAuthenticationMethod)
} catch {
Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self)
}
}
return action
} else {
fileActionTitle.append(" (\("Tips".localize()))")
let action = UIAlertAction(title: fileActionTitle, style: .default) { _ in
let title = "Tips".localize()
let message = "SshCopyPrivateKeyToPass.".localize()
Utils.alert(title: title, message: message, controller: self)
}
return action
}
}()
optionMenu.addAction(fileAction)
if Defaults.gitSSHKeySource != nil {
let deleteAction = UIAlertAction(title: "RemoveSShKeys".localize(), style: .destructive) { _ in
optionMenu.addAction(UIAlertAction(title: "RemoveSShKeys".localize(), style: .destructive) { _ in
self.passwordStore.removeGitSSHKeys()
Defaults.gitSSHKeySource = nil
self.sshLabel?.isEnabled = false
self.updateAuthenticationMethodCheckView(for: .password)
}
optionMenu.addAction(deleteAction)
self.gitAuthenticationMethod = .password
})
}
let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)
optionMenu.addAction(cancelAction)
optionMenu.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil))
optionMenu.popoverPresentationController?.sourceView = authSSHKeyCell
optionMenu.popoverPresentationController?.sourceRect = authSSHKeyCell.bounds
self.present(optionMenu, animated: true)
present(optionMenu, animated: true)
}
private func requestCredentialPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? {
@ -313,5 +300,18 @@ class GitRepositorySettingsTableViewController: UITableViewController {
"""
Utils.alert(title: "Git URL Format", message: message, controller: self)
}
}
extension GitRepositorySettingsTableViewController: KeyImporter {
static let keySource = KeySource.itunes
static let label = "ITunesFileSharing".localize()
func isReadyToUse() -> Bool {
return KeyFileManager.PrivateSsh.doesKeyFileExist()
}
func importKeys() throws {
try KeyFileManager.PrivateSsh.importKeyFromFileSharing()
}
}

View file

@ -0,0 +1,36 @@
//
// KeyImporter.swift
// pass
//
// Created by Danny Moesch on 15.02.20.
// Copyright © 2020 Bob Sun. All rights reserved.
//
import passKit
protocol KeyImporter {
static var keySource: KeySource { get }
static var label: String { get }
static var menuLabel: String { get }
func isReadyToUse() -> Bool
func importKeys() throws
}
extension KeyImporter {
static var isCurrentKeySource: Bool {
return Defaults.gitSSHKeySource == Self.keySource
}
static var menuLabel: String {
if isCurrentKeySource {
return "\(Self.label)"
}
return Self.label
}
}

View file

@ -10,13 +10,11 @@ import UIKit
import passKit
class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController, UITextViewDelegate, QRScannerControllerDelegate {
@IBOutlet weak var armorPublicKeyTextView: UITextView!
@IBOutlet weak var armorPrivateKeyTextView: UITextView!
@IBOutlet weak var scanPublicKeyCell: UITableViewCell!
@IBOutlet weak var scanPrivateKeyCell: UITableViewCell!
let passwordStore = PasswordStore.shared
let keychain = AppKeychain.shared
class ScannedPGPKey {
enum KeyType {
@ -132,7 +130,7 @@ class PGPKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
extension PGPKeyArmorImportTableViewController: PGPKeyImporter {
static let keySource = PGPKeySource.armor
static let keySource = KeySource.armor
static let label = "AsciiArmorEncryptedKey".localize()
func isReadyToUse() -> Bool {
@ -152,10 +150,6 @@ extension PGPKeyArmorImportTableViewController: PGPKeyImporter {
try KeyFileManager.PrivatePgp.importKey(from: armorPrivateKeyTextView.text ?? "")
}
func doAfterImport() {
}
func saveImportedKeys() {
performSegue(withIdentifier: "savePGPKeySegue", sender: self)
}

View file

@ -13,9 +13,6 @@ class PGPKeyFileImportTableViewController: AutoCellHeightUITableViewController {
@IBOutlet weak var pgpPublicKeyFile: UITableViewCell!
@IBOutlet weak var pgpPrivateKeyFile: UITableViewCell!
private let passwordStore = PasswordStore.shared
private let keychain = AppKeychain.shared
private var publicKey: String? = nil
private var privateKey: String? = nil
@ -72,7 +69,7 @@ extension PGPKeyFileImportTableViewController: UIDocumentPickerDelegate {
extension PGPKeyFileImportTableViewController: PGPKeyImporter {
static let keySource = PGPKeySource.files
static let keySource = KeySource.file
static let label = "LoadFromFiles".localize()
func isReadyToUse() -> Bool {

View file

@ -8,17 +8,7 @@
import passKit
protocol PGPKeyImporter {
static var keySource: PGPKeySource { get }
static var label: String { get }
static var menuLabel: String { get }
func isReadyToUse() -> Bool
func importKeys() throws
protocol PGPKeyImporter: KeyImporter {
func doAfterImport()
@ -26,12 +16,13 @@ protocol PGPKeyImporter {
}
extension PGPKeyImporter {
static var menuLabel: String {
if Defaults.pgpKeySource == Self.keySource {
return "\(Self.label)"
}
return Self.label
static var isCurrentKeySource: Bool {
return Defaults.pgpKeySource == Self.keySource
}
func doAfterImport() {
}
}

View file

@ -14,9 +14,6 @@ class PGPKeyUrlImportTableViewController: AutoCellHeightUITableViewController {
@IBOutlet weak var pgpPublicKeyURLTextField: UITextField!
@IBOutlet weak var pgpPrivateKeyURLTextField: UITextField!
let passwordStore = PasswordStore.shared
let keychain = AppKeychain.shared
override func viewDidLoad() {
super.viewDidLoad()
pgpPublicKeyURLTextField.text = Defaults.pgpPublicKeyURL?.absoluteString
@ -46,7 +43,7 @@ class PGPKeyUrlImportTableViewController: AutoCellHeightUITableViewController {
extension PGPKeyUrlImportTableViewController: PGPKeyImporter {
static let keySource = PGPKeySource.url
static let keySource = KeySource.url
static let label = "DownloadFromUrl".localize()
func isReadyToUse() -> Bool {

View file

@ -10,11 +10,11 @@ import UIKit
import passKit
class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController, UITextViewDelegate, QRScannerControllerDelegate {
@IBOutlet weak var armorPrivateKeyTextView: UITextView!
@IBOutlet weak var scanPrivateKeyCell: UITableViewCell!
var gitSSHPrivateKeyPassphrase: String?
let passwordStore = PasswordStore.shared
class ScannedSSHKey {
var segments = [String]()
@ -60,10 +60,7 @@ class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
}
@IBAction func doneButtonTapped(_ sender: Any) {
AppKeychain.shared.add(string: armorPrivateKeyTextView.text, for: SshKey.PRIVATE.getKeychainKey())
Defaults.gitSSHKeySource = .armor
Defaults.gitAuthenticationMethod = .key
self.navigationController!.popViewController(animated: true)
performSegue(withIdentifier: "importSSHKeySegue", sender: self)
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
@ -109,5 +106,22 @@ class SSHKeyArmorImportTableViewController: AutoCellHeightUITableViewController,
@IBAction private func cancelSSHScanner(segue: UIStoryboardSegue) {
}
}
extension SSHKeyArmorImportTableViewController: KeyImporter {
static let keySource = KeySource.armor
static let label = "AsciiArmorEncryptedKey".localize()
func isReadyToUse() -> Bool {
guard !armorPrivateKeyTextView.text.isEmpty else {
Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKey.".localize(), controller: self)
return false
}
return true
}
func importKeys() throws {
try KeyFileManager.PrivateSsh.importKey(from: armorPrivateKeyTextView.text ?? "")
}
}

View file

@ -6,34 +6,55 @@
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SVProgressHUD
import passKit
class SSHKeyUrlImportTableViewController: AutoCellHeightUITableViewController {
@IBOutlet weak var privateKeyURLTextField: UITextField!
let passwordStore = PasswordStore.shared
@IBAction func doneButtonTapped(_ sender: UIButton) {
guard let privateKeyURL = URL(string: privateKeyURLTextField.text!.trimmed) else {
Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKeyUrl.".localize(), controller: self, completion: nil)
return
}
do {
try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: SshKey.PRIVATE.getFileSharingPath()), options: .atomic)
try self.passwordStore.gitSSHKeyImportFromFileSharing()
Defaults.gitSSHKeySource = .file
Defaults.gitAuthenticationMethod = .key
SVProgressHUD.showSuccess(withStatus: "Imported".localize())
SVProgressHUD.dismiss(withDelay: 1)
} catch {
Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
}
Defaults.gitSSHKeySource = .url
self.navigationController!.popViewController(animated: true)
override func viewDidLoad() {
super.viewDidLoad()
privateKeyURLTextField.text = Defaults.gitSSHPrivateKeyURL?.absoluteString
}
@IBAction func doneButtonTapped(_ sender: UIButton) {
if getScheme(from: privateKeyURLTextField.text?.trimmed) == "http" {
let savePassphraseAlert = UIAlertController(title: "HttpNotSecure".localize(), message: "ReallyUseHttp?".localize(), preferredStyle: .alert)
savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: .default) { _ in })
savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: .destructive) { _ in
self.performSegue(withIdentifier: "importSSHKeySegue", sender: self)
})
return present(savePassphraseAlert, animated: true)
}
performSegue(withIdentifier: "importSSHKeySegue", sender: self)
}
private func getScheme(from url: String?) -> String? {
return url.flatMap(URL.init(string:))?.scheme
}
}
extension SSHKeyUrlImportTableViewController: KeyImporter {
static let keySource = KeySource.url
static let label = "DownloadFromUrl".localize()
func isReadyToUse() -> Bool {
guard let scheme = getScheme(from: privateKeyURLTextField.text?.trimmed) else {
Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKeyUrl.".localize(), controller: self)
return false
}
guard scheme == "https" || scheme == "http" else {
Utils.alert(title: "CannotSave".localize(), message: "UseEitherHttpsOrHttp.".localize(), controller: self)
return false
}
return true
}
func importKeys() throws {
Defaults.gitSSHPrivateKeyURL = URL(string: privateKeyURLTextField.text!.trimmed)
try KeyFileManager.PrivateSsh.importKey(from: Defaults.gitSSHPrivateKeyURL!)
}
}

View file

@ -153,19 +153,18 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
}
if Defaults.pgpKeySource != nil {
let deleteAction = UIAlertAction(title: "RemovePgpKeys".localize(), style: .destructive) { _ in
optionMenu.addAction(UIAlertAction(title: "RemovePgpKeys".localize(), style: .destructive) { _ in
self.keychain.removeContent(for: PgpKey.PUBLIC.getKeychainKey())
self.keychain.removeContent(for: PgpKey.PRIVATE.getKeychainKey())
PGPAgent.shared.uninitKeys()
self.pgpKeyTableViewCell.detailTextLabel?.text = "NotSet".localize()
Defaults.pgpKeySource = nil
}
optionMenu.addAction(deleteAction)
})
}
optionMenu.addAction(UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil))
optionMenu.popoverPresentationController?.sourceView = pgpKeyTableViewCell
optionMenu.popoverPresentationController?.sourceRect = pgpKeyTableViewCell.bounds
self.present(optionMenu, animated: true, completion: nil)
present(optionMenu, animated: true)
}
func showPasscodeActionSheet() {
@ -243,7 +242,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
extension SettingsTableViewController: PGPKeyImporter {
static let keySource = PGPKeySource.itunes
static let keySource = KeySource.itunes
static let label = "ITunesFileSharing".localize()
func isReadyToUse() -> Bool {
@ -255,11 +254,7 @@ extension SettingsTableViewController: PGPKeyImporter {
try KeyFileManager.PrivatePgp.importKeyFromFileSharing()
}
func doAfterImport() {
}
func saveImportedKeys() {
self.savePGPKey(using: self)
savePGPKey(using: self)
}
}