Introduce PGPKeyImporter protocol to reduce code duplications

This commit is contained in:
Danny Moesch 2020-02-08 13:31:49 +01:00 committed by Mingshen Sun
parent 56b2205376
commit e447b1d9d3
9 changed files with 234 additions and 224 deletions

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="YoR-iB-XAd">
<device id="retina5_5" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15510"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15704"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -505,7 +505,7 @@
<!--PGP Key-->
<scene sceneID="8Hi-tl-elT">
<objects>
<tableViewController id="3WR-HY-zYj" customClass="PGPKeySettingTableViewController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
<tableViewController id="3WR-HY-zYj" customClass="PGPKeyUrlTableViewController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="B51-hW-xc5">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -617,7 +617,7 @@
<navigationItem key="navigationItem" title="PGP Key" largeTitleDisplayMode="never" id="eK3-bb-419">
<barButtonItem key="rightBarButtonItem" style="done" systemItem="save" id="TNt-ne-l7W">
<connections>
<action selector="save:" destination="3WR-HY-zYj" id="uLA-oz-lPI"/>
<action selector="save:" destination="3WR-HY-zYj" id="xpE-ow-g9c"/>
</connections>
</barButtonItem>
</navigationItem>

View file

@ -78,37 +78,7 @@ class PGPKeyArmorSettingTableViewController: AutoCellHeightUITableViewController
}
@IBAction func save(_ sender: Any) {
guard armorPublicKeyTextView.text.isEmpty == false else {
Utils.alert(title: "CannotSave".localize(), message: "SetPublicKey.".localize(), controller: self, completion: nil)
return
}
guard armorPrivateKeyTextView.text.isEmpty == false else {
Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKey.".localize(), controller: self, completion: nil)
return
}
let savePassphraseAlert = UIAlertController(title: "Passphrase".localize(), message: "WantToSavePassphrase?".localize(), preferredStyle: UIAlertController.Style.alert)
// no
savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: UIAlertAction.Style.default) { _ in
self.keychain.removeContent(for: Globals.pgpKeyPassphrase)
Defaults.isRememberPGPPassphraseOn = false
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
})
// yes
savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive) {_ in
// ask for the passphrase
let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertAction.Style.default, handler: {_ in
self.keychain.add(string: alert.textFields?.first?.text, for: Globals.pgpKeyPassphrase)
Defaults.isRememberPGPPassphraseOn = true
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = self.keychain.get(for: Globals.pgpKeyPassphrase)
textField.isSecureTextEntry = true
})
self.present(alert, animated: true, completion: nil)
})
self.present(savePassphraseAlert, animated: true, completion: nil)
savePassphraseDialog()
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
@ -159,3 +129,36 @@ class PGPKeyArmorSettingTableViewController: AutoCellHeightUITableViewController
}
}
}
extension PGPKeyArmorSettingTableViewController: PGPKeyImporter {
static let keySource = PGPKeySource.armor
static let label = "AsciiArmorEncryptedKey".localize()
func isReadyToUse() -> Bool {
guard !armorPublicKeyTextView.text.isEmpty else {
Utils.alert(title: "CannotSave".localize(), message: "SetPublicKey.".localize(), controller: self, completion: nil)
return false
}
guard !armorPrivateKeyTextView.text.isEmpty else {
Utils.alert(title: "CannotSave".localize(), message: "SetPrivateKey.".localize(), controller: self, completion: nil)
return false
}
return true
}
func importKeys() throws {
Defaults.pgpKeySource = Self.keySource
try KeyFileManager.PublicPgp.importKey(from: armorPublicKeyTextView.text ?? "")
try KeyFileManager.PrivatePgp.importKey(from: armorPrivateKeyTextView.text ?? "")
}
func doAfterImport() {
}
func saveImportedKeys() {
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
}
}

View file

@ -0,0 +1,65 @@
//
// PGPKeyImporter.swift
// pass
//
// Created by Danny Moesch on 07.02.20.
// Copyright © 2020 Bob Sun. All rights reserved.
//
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
func doAfterImport()
func saveImportedKeys()
}
extension PGPKeyImporter {
static var menuLabel: String {
if Defaults.pgpKeySource == Self.keySource {
return "\(Self.label)"
}
return Self.label
}
}
extension PGPKeyImporter where Self: UIViewController {
func savePassphraseDialog() {
let savePassphraseAlert = UIAlertController(title: "Passphrase".localize(), message: "WantToSavePassphrase?".localize(), preferredStyle: UIAlertController.Style.alert)
// Do not save the key's passphrase.
savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: UIAlertAction.Style.default) { _ in
AppKeychain.shared.removeContent(for: Globals.pgpKeyPassphrase)
Defaults.isRememberPGPPassphraseOn = false
self.saveImportedKeys()
})
// Save the key's passphrase.
savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive) {_ in
// Ask for the passphrase.
let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertAction.Style.default, handler: {_ in
AppKeychain.shared.add(string: alert.textFields?.first?.text, for: Globals.pgpKeyPassphrase)
Defaults.isRememberPGPPassphraseOn = true
self.saveImportedKeys()
}))
alert.addTextField { textField in
textField.text = AppKeychain.shared.get(for: Globals.pgpKeyPassphrase)
textField.isSecureTextEntry = true
}
self.present(alert, animated: true, completion: nil)
})
self.present(savePassphraseAlert, animated: true, completion: nil)
}
}

View file

@ -1,69 +0,0 @@
//
// PGPKeySettingTableViewController.swift
// pass
//
// Created by Mingshen Sun on 21/1/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
class PGPKeySettingTableViewController: 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
pgpPrivateKeyURLTextField.text = Defaults.pgpPrivateKeyURL?.absoluteString
}
private func validatePGPKeyURL(input: String?) -> Bool {
guard let path = input, let url = URL(string: path) else {
Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlFirst.".localize(), controller: self, completion: nil)
return false
}
guard let scheme = url.scheme, scheme == "https" else {
Utils.alert(title: "CannotSavePgpKey".localize(), message: "HttpNotSupported.".localize(), controller: self, completion: nil)
return false
}
return true
}
@IBAction func save(_ sender: Any) {
guard validatePGPKeyURL(input: pgpPublicKeyURLTextField.text) == true,
validatePGPKeyURL(input: pgpPrivateKeyURLTextField.text) == true else {
return
}
let savePassphraseAlert = UIAlertController(title: "Passphrase".localize(), message: "WantToSavePassphrase?".localize(), preferredStyle: UIAlertController.Style.alert)
// no
savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: UIAlertAction.Style.default) { _ in
self.keychain.removeContent(for: Globals.pgpKeyPassphrase)
Defaults.isRememberPGPPassphraseOn = false
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
})
// yes
savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive) {_ in
// ask for the passphrase
let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertAction.Style.default, handler: {_ in
self.keychain.add(string: alert.textFields?.first?.text, for: Globals.pgpKeyPassphrase)
Defaults.isRememberPGPPassphraseOn = true
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = self.keychain.get(for: Globals.pgpKeyPassphrase)
textField.isSecureTextEntry = true
})
self.present(alert, animated: true, completion: nil)
})
self.present(savePassphraseAlert, animated: true, completion: nil)
}
}

View file

@ -0,0 +1,70 @@
//
// PGPKeyUrlTableViewController.swift
// pass
//
// Created by Mingshen Sun on 21/1/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import passKit
class PGPKeyUrlTableViewController: 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
pgpPrivateKeyURLTextField.text = Defaults.pgpPrivateKeyURL?.absoluteString
}
@IBAction func save(_ sender: Any) {
savePassphraseDialog()
}
}
extension PGPKeyUrlTableViewController: PGPKeyImporter {
static let keySource = PGPKeySource.url
static let label = "DownloadFromUrl".localize()
func isReadyToUse() -> Bool {
return validate(pgpKeyUrl: pgpPublicKeyURLTextField.text)
&& validate(pgpKeyUrl: pgpPrivateKeyURLTextField.text)
}
func importKeys() throws {
Defaults.pgpKeySource = Self.keySource
Defaults.pgpPrivateKeyURL = URL(string: pgpPrivateKeyURLTextField.text!.trimmed)
Defaults.pgpPublicKeyURL = URL(string: pgpPublicKeyURLTextField.text!.trimmed)
try KeyFileManager.PublicPgp.importKey(from: Defaults.pgpPublicKeyURL!)
try KeyFileManager.PrivatePgp.importKey(from: Defaults.pgpPrivateKeyURL!)
}
func doAfterImport() {
Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromServer.".localize(), controller: self, completion: nil)
}
func saveImportedKeys() {
self.performSegue(withIdentifier: "savePGPKeySegue", sender: self)
}
private func validate(pgpKeyUrl: String?) -> Bool {
guard let pgpKeyUrl = pgpKeyUrl, let url = URL(string: pgpKeyUrl), let scheme = url.scheme else {
Utils.alert(title: "CannotSavePgpKey".localize(), message: "SetPgpKeyUrlFirst.".localize(), controller: self, completion: nil)
return false
}
guard scheme == "https" else {
Utils.alert(title: "CannotSavePgpKey".localize(), message: "UseHttps.".localize(), controller: self, completion: nil)
return false
}
return true
}
}

View file

@ -26,74 +26,28 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
}
@IBAction func savePGPKey(segue: UIStoryboardSegue) {
if let controller = segue.source as? PGPKeySettingTableViewController {
Defaults.pgpPrivateKeyURL = URL(string: controller.pgpPrivateKeyURLTextField.text!.trimmed)
Defaults.pgpPublicKeyURL = URL(string: controller.pgpPublicKeyURLTextField.text!.trimmed)
Defaults.pgpKeySource = "url"
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "FetchingPgpKey".localize())
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do {
try KeyFileManager.PublicPgp.importKey(from: Defaults.pgpPublicKeyURL!)
try KeyFileManager.PrivatePgp.importKey(from: Defaults.pgpPrivateKeyURL!)
try PGPAgent.shared.initKeys()
DispatchQueue.main.async {
self.setPGPKeyTableViewCellDetailText()
SVProgressHUD.showSuccess(withStatus: "Success".localize())
SVProgressHUD.dismiss(withDelay: 1)
Utils.alert(title: "RememberToRemoveKey".localize(), message: "RememberToRemoveKeyFromServer.".localize(), controller: self, completion: nil)
}
} catch {
DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = "NotSet".localize()
Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
}
}
}
} else if let controller = segue.source as? PGPKeyArmorSettingTableViewController {
Defaults.pgpKeySource = "armor"
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "FetchingPgpKey".localize())
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do {
try KeyFileManager.PublicPgp.importKey(from: controller.armorPublicKeyTextView.text ?? "")
try KeyFileManager.PrivatePgp.importKey(from: controller.armorPrivateKeyTextView.text ?? "")
try PGPAgent.shared.initKeys()
DispatchQueue.main.async {
self.setPGPKeyTableViewCellDetailText()
SVProgressHUD.showSuccess(withStatus: "Success".localize())
SVProgressHUD.dismiss(withDelay: 1)
}
} catch {
DispatchQueue.main.async {
self.pgpKeyTableViewCell.detailTextLabel?.text = "NotSet".localize()
Utils.alert(title: "Error".localize(), message: error.localizedDescription, controller: self, completion: nil)
}
}
}
guard let sourceController = segue.source as? PGPKeyImporter else {
return
}
savePGPKey(using: sourceController)
}
private func saveImportedPGPKey() {
// load keys
Defaults.pgpKeySource = "file"
private func savePGPKey(using keyImporter: PGPKeyImporter) {
guard keyImporter.isReadyToUse() else {
return
}
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.light)
SVProgressHUD.show(withStatus: "FetchingPgpKey".localize())
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do {
try KeyFileManager.PublicPgp.importKeyFromFileSharing()
try KeyFileManager.PrivatePgp.importKeyFromFileSharing()
try keyImporter.importKeys()
try PGPAgent.shared.initKeys()
DispatchQueue.main.async {
self.setPGPKeyTableViewCellDetailText()
SVProgressHUD.showSuccess(withStatus: "Imported".localize())
SVProgressHUD.showSuccess(withStatus: "Success".localize())
SVProgressHUD.dismiss(withDelay: 1)
keyImporter.doAfterImport()
}
} catch {
DispatchQueue.main.async {
@ -108,10 +62,6 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults.gitURL.host
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return super.tableView(tableView, numberOfRowsInSection: section)
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: .passwordStoreErased, object: nil)
@ -182,67 +132,25 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
func showPGPKeyActionSheet() {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var urlActionTitle = "DownloadFromUrl".localize()
var armorActionTitle = "AsciiArmorEncryptedKey".localize()
var fileActionTitle = "ITunesFileSharing".localize()
if Defaults.pgpKeySource == "url" {
urlActionTitle = "\(urlActionTitle)"
} else if Defaults.pgpKeySource == "armor" {
armorActionTitle = "\(armorActionTitle)"
} else if Defaults.pgpKeySource == "file" {
fileActionTitle = "\(fileActionTitle)"
}
let urlAction = UIAlertAction(title: urlActionTitle, style: .default) { _ in
optionMenu.addAction(UIAlertAction(title: PGPKeyUrlTableViewController.menuLabel, style: .default) { _ in
self.performSegue(withIdentifier: "setPGPKeyByURLSegue", sender: self)
}
let armorAction = UIAlertAction(title: armorActionTitle, style: .default) { _ in
})
optionMenu.addAction(UIAlertAction(title: PGPKeyArmorSettingTableViewController.menuLabel, style: .default) { _ in
self.performSegue(withIdentifier: "setPGPKeyByASCIISegue", sender: self)
}
let cancelAction = UIAlertAction(title: "Cancel".localize(), style: .cancel, handler: nil)
optionMenu.addAction(urlAction)
optionMenu.addAction(armorAction)
})
if KeyFileManager.PublicPgp.doesKeyFileExist() && KeyFileManager.PrivatePgp.doesKeyFileExist() {
fileActionTitle.append(" (\("Import".localize()))")
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
// passphrase related
let savePassphraseAlert = UIAlertController(title: "Passphrase".localize(), message: "WantToSavePassphrase?".localize(), preferredStyle: UIAlertController.Style.alert)
// no
savePassphraseAlert.addAction(UIAlertAction(title: "No".localize(), style: UIAlertAction.Style.default) { _ in
self.keychain.removeContent(for: Globals.pgpKeyPassphrase)
Defaults.isRememberPGPPassphraseOn = false
self.saveImportedPGPKey()
})
// yes
savePassphraseAlert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive) {_ in
// ask for the passphrase
let alert = UIAlertController(title: "Passphrase".localize(), message: "FillInPgpPassphrase.".localize(), preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok".localize(), style: UIAlertAction.Style.default, handler: {_ in
self.keychain.add(string: alert.textFields?.first?.text, for: Globals.pgpKeyPassphrase)
Defaults.isRememberPGPPassphraseOn = true
self.saveImportedPGPKey()
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = ""
textField.isSecureTextEntry = true
})
self.present(alert, animated: true, completion: nil)
})
self.present(savePassphraseAlert, animated: true, completion: nil)
}
optionMenu.addAction(fileAction)
if isReadyToUse() {
optionMenu.addAction(UIAlertAction(title: "\(Self.menuLabel) (\("Import".localize()))", style: .default) { _ in
self.savePassphraseDialog()
})
} else {
fileActionTitle.append(" (\("Tips".localize()))")
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
optionMenu.addAction(UIAlertAction(title: "\(Self.menuLabel) (\("Tips".localize()))", style: .default) { _ in
let title = "Tips".localize()
let message = "PgpCopyPublicAndPrivateKeyToPass.".localize()
Utils.alert(title: title, message: message, controller: self)
}
optionMenu.addAction(fileAction)
})
}
if Defaults.pgpKeySource != nil {
let deleteAction = UIAlertAction(title: "RemovePgpKeys".localize(), style: .destructive) { _ in
self.keychain.removeContent(for: PgpKey.PUBLIC.getKeychainKey())
@ -252,7 +160,7 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
}
optionMenu.addAction(deleteAction)
}
optionMenu.addAction(cancelAction)
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)
@ -330,3 +238,28 @@ class SettingsTableViewController: UITableViewController, UITabBarControllerDele
self.present(setPasscodeLockAlert!, animated: true, completion: nil)
}
}
extension SettingsTableViewController: PGPKeyImporter {
static let keySource = PGPKeySource.itunes
static let label = "ITunesFileSharing".localize()
func isReadyToUse() -> Bool {
return KeyFileManager.PublicPgp.doesKeyFileExist() && KeyFileManager.PrivatePgp.doesKeyFileExist()
}
func importKeys() throws {
Defaults.pgpKeySource = Self.keySource
try KeyFileManager.PublicPgp.importKeyFromFileSharing()
try KeyFileManager.PrivatePgp.importKeyFromFileSharing()
}
func doAfterImport() {
}
func saveImportedKeys() {
self.savePGPKey(using: self)
}
}

View file

@ -65,7 +65,7 @@
"FileNotFoundError." = "Die Datei '%@' kann nicht gelesen werden.";
"PasswordDuplicatedError." = "Passwort kann nicht hinzugefügt werden; es existiert bereits.";
"GitResetError." = "Der zuletzt synchronisierte Commit kann nicht identifiziert werden.";
"WrongPasswordFilename." = "Schreiben der Passwort-Datei nicht möglich .";
"WrongPasswordFilenameError." = "Schreiben der Passwort-Datei nicht möglich .";
"DecryptionError." = "Passwort kann nicht entschlüsselt werden.";
"EncodingError." = "Schlüssel ist nicht in ASCII kodiert.";
"UnknownError." = "Ein unbekannter Fehler ist aufgetreten.";