Refactor GitCredential to simplify it and to add tests

This commit is contained in:
Danny Moesch 2020-08-23 01:15:23 +02:00 committed by Mingshen Sun
parent 56b7b24fce
commit 6044098278
11 changed files with 295 additions and 225 deletions

View file

@ -10,7 +10,7 @@ import passKit
import SVProgressHUD
import UIKit
class GitRepositorySettingsTableViewController: UITableViewController {
class GitRepositorySettingsTableViewController: UITableViewController, PasswordAlertPresenter {
// MARK: - View Outlet
@IBOutlet var gitURLTextField: UITextField!
@ -25,6 +25,14 @@ class GitRepositorySettingsTableViewController: UITableViewController {
private var sshLabel: UILabel?
private let passwordStore = PasswordStore.shared
private let keychain = AppKeychain.shared
private var gitCredential: GitCredential {
GitCredential.from(
authenticationMethod: Defaults.gitAuthenticationMethod,
userName: Defaults.gitUsername,
keyStore: keychain
)
}
private var gitAuthenticationMethod: GitAuthenticationMethod {
get { Defaults.gitAuthenticationMethod }
set {
@ -48,16 +56,6 @@ class GitRepositorySettingsTableViewController: UITableViewController {
set { Defaults.gitUsername = newValue }
}
private var gitCredential: GitCredential {
switch Defaults.gitAuthenticationMethod {
case .password:
return GitCredential(credential: .http(userName: Defaults.gitUsername))
case .key:
let privateKey: String = AppKeychain.shared.get(for: SshKey.PRIVATE.getKeychainKey()) ?? ""
return GitCredential(credential: .ssh(userName: Defaults.gitUsername, privateKey: privateKey))
}
}
// MARK: - View Controller Lifecycle
override func viewDidLoad() {
@ -72,7 +70,7 @@ class GitRepositorySettingsTableViewController: UITableViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Grey out ssh option if ssh_key is not present.
sshLabel?.isEnabled = AppKeychain.shared.contains(key: SshKey.PRIVATE.getKeychainKey())
sshLabel?.isEnabled = keychain.contains(key: SshKey.PRIVATE.getKeychainKey())
updateAuthenticationMethodCheckView(for: gitAuthenticationMethod)
}
@ -97,7 +95,7 @@ class GitRepositorySettingsTableViewController: UITableViewController {
if cell == authPasswordCell {
gitAuthenticationMethod = .password
} else if cell == authSSHKeyCell {
if !AppKeychain.shared.contains(key: SshKey.PRIVATE.getKeychainKey()) {
if !keychain.contains(key: SshKey.PRIVATE.getKeychainKey()) {
Utils.alert(title: "CannotSelectSshKey".localize(), message: "PleaseSetupSshKeyFirst.".localize(), controller: self)
gitAuthenticationMethod = .password
} else {
@ -177,11 +175,12 @@ class GitRepositorySettingsTableViewController: UITableViewController {
SVProgressHUD.showProgress(progress, status: "CheckingOutBranch".localize(self.gitBranchName))
}
let options = self.gitCredential.getCredentialOptions(passwordProvider: self.present)
try self.passwordStore.cloneRepository(
remoteRepoURL: self.gitUrl,
credential: self.gitCredential,
branchName: self.gitBranchName,
requestCredentialPassword: self.requestCredentialPassword,
options: options,
transferProgressBlock: transferProgressBlock,
checkoutProgressBlock: checkoutProgressBlock
)
@ -301,10 +300,6 @@ class GitRepositorySettingsTableViewController: UITableViewController {
present(optionMenu, animated: true)
}
private func requestCredentialPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? {
requestGitCredentialPassword(credential: credential, lastPassword: lastPassword, controller: self)
}
private func updateAuthenticationMethodCheckView(for method: GitAuthenticationMethod) {
let passwordCheckView = authPasswordCell.viewWithTag(1001)
let sshKeyCheckView = authSSHKeyCell.viewWithTag(1001)

View file

@ -10,7 +10,7 @@ import passKit
import SVProgressHUD
import UIKit
class PasswordsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITabBarControllerDelegate, UISearchBarDelegate {
class PasswordsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITabBarControllerDelegate, UISearchBarDelegate, PasswordAlertPresenter {
// Arbitrary threshold to decide whether to show folders or not for only a few entries.
private static let hideSectionHeaderThreshold = 6
@ -19,6 +19,13 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
private var parentPasswordEntity: PasswordEntity?
private let passwordStore = PasswordStore.shared
private let keychain = AppKeychain.shared
private var gitCredential: GitCredential {
GitCredential.from(
authenticationMethod: Defaults.gitAuthenticationMethod,
userName: Defaults.gitUsername,
keyStore: keychain
)
}
private var tapTabBarTime: TimeInterval = 0
private var tapNavigationBarGestureRecognizer: UITapGestureRecognizer!
@ -30,16 +37,6 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
case unsynced
}
private var gitCredential: GitCredential {
switch Defaults.gitAuthenticationMethod {
case .password:
return GitCredential(credential: .http(userName: Defaults.gitUsername))
case .key:
let privateKey: String = AppKeychain.shared.get(for: SshKey.PRIVATE.getKeychainKey()) ?? ""
return GitCredential(credential: .ssh(userName: Defaults.gitUsername, privateKey: privateKey))
}
}
private lazy var searchController: UISearchController = {
let uiSearchController = UISearchController(searchResultsController: nil)
uiSearchController.searchResultsUpdater = self
@ -193,13 +190,15 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
do {
try self.passwordStore.pullRepository(credential: self.gitCredential, requestCredentialPassword: self.requestCredentialPassword) { git_transfer_progress, _ in
let pullOptions = self.gitCredential.getCredentialOptions(passwordProvider: self.present)
try self.passwordStore.pullRepository(options: pullOptions) { git_transfer_progress, _ in
DispatchQueue.main.async {
SVProgressHUD.showProgress(Float(git_transfer_progress.pointee.received_objects) / Float(git_transfer_progress.pointee.total_objects), status: "PullingFromRemoteRepository".localize())
}
}
if self.passwordStore.numberOfLocalCommits > 0 {
try self.passwordStore.pushRepository(credential: self.gitCredential, requestCredentialPassword: self.requestCredentialPassword) { current, total, _, _ in
let pushOptions = self.gitCredential.getCredentialOptions(passwordProvider: self.present)
try self.passwordStore.pushRepository(options: pushOptions) { current, total, _, _ in
DispatchQueue.main.async {
SVProgressHUD.showProgress(Float(current) / Float(total), status: "PushingToRemoteRepository".localize())
}
@ -212,6 +211,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
self.syncControl.endRefreshing()
}
} catch {
self.gitCredential.delete()
DispatchQueue.main.async {
SVProgressHUD.dismiss()
self.syncControl.endRefreshing()
@ -699,10 +699,6 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
updateSearchResults(for: searchController)
return true
}
private func requestCredentialPassword(credential: GitCredential.Credential, lastPassword: String?) -> String? {
requestGitCredentialPassword(credential: credential, lastPassword: lastPassword, controller: self)
}
}
extension PasswordsViewController: UISearchResultsUpdating {

View file

@ -1,53 +0,0 @@
//
// GitCredentialPassword.swift
// pass
//
// Created by Sun, Mingshen on 11/30/19.
// Copyright © 2019 Bob Sun. All rights reserved.
//
import Foundation
import passKit
import SVProgressHUD
public func requestGitCredentialPassword(
credential: GitCredential.Credential,
lastPassword: String?,
controller: UIViewController
) -> String? {
let sem = DispatchSemaphore(value: 0)
var password: String?
let message: String = {
switch credential {
case .http:
return "FillInGitAccountPassword.".localize()
case .ssh:
return "FillInSshKeyPassphrase.".localize()
}
}()
DispatchQueue.main.async {
SVProgressHUD.dismiss()
let alert = UIAlertController(title: "Password".localize(), message: message, preferredStyle: .alert)
alert.addTextField {
$0.text = lastPassword ?? ""
$0.isSecureTextEntry = true
}
alert.addAction(
UIAlertAction.ok { _ in
password = alert.textFields?.first?.text
sem.signal()
}
)
alert.addAction(
UIAlertAction.cancel { _ in
password = nil
sem.signal()
}
)
controller.present(alert, animated: true)
}
_ = sem.wait(timeout: .distantFuture)
return password
}

View file

@ -0,0 +1,45 @@
//
// PasswordAlertPresenter.swift
// pass
//
// Created by Danny Moesch on 23.08.20.
// Copyright © 2020 Bob Sun. All rights reserved.
//
import SVProgressHUD
protocol PasswordAlertPresenter {
func present(message: String, lastPassword: String?) -> String?
}
extension PasswordAlertPresenter where Self: UIViewController {
func present(message: String, lastPassword: String?) -> String? {
let sem = DispatchSemaphore(value: 0)
var password: String?
DispatchQueue.main.async {
SVProgressHUD.dismiss()
let alert = UIAlertController(title: "Password".localize(), message: message, preferredStyle: .alert)
alert.addTextField {
$0.text = lastPassword ?? ""
$0.isSecureTextEntry = true
}
alert.addAction(
.ok { _ in
password = alert.textFields?.first?.text
sem.signal()
}
)
alert.addAction(
.cancel { _ in
password = nil
sem.signal()
}
)
self.present(alert, animated: true)
}
_ = sem.wait(timeout: .distantFuture)
return password
}
}