Skip to content
This repository has been archived by the owner on Jun 2, 2019. It is now read-only.

Add generic passwork checker before sending transaction #867

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
55c1d9e
Add generic passwork checker before sending transaction
taraspasichnyk Jul 30, 2018
644a594
Use AuthenticateUserCoordinator
taraspasichnyk Jul 31, 2018
a323fef
Add properly in user defaults. Implemented functionality of checking …
taraspasichnyk Jul 31, 2018
863d444
Add protocol for view model in LockEnterPasscodeViewController
taraspasichnyk Jul 31, 2018
3f990bc
Add new files in folder hierarchy
taraspasichnyk Jul 31, 2018
ea7bf52
Add localized string for lock.authenticate.enter.passcode.view.model.…
taraspasichnyk Jul 31, 2018
947a158
Add localized strings for settings.lockTransactions.label.title
taraspasichnyk Jul 31, 2018
4263885
Merge branch 'master' into feature/830-require-password-on-sending-tr…
taraspasichnyk Aug 2, 2018
86c17c3
Check if user needs to confirm with a password on Coordinator level
taraspasichnyk Aug 2, 2018
15f678b
Use R.string.localizable. Fix issue with Localizable files
taraspasichnyk Aug 2, 2018
ef83945
Change LockCreatePasscodeViewModel to use R.string.localizable
taraspasichnyk Aug 2, 2018
f1d62af
Remove duplication
taraspasichnyk Aug 2, 2018
bef4c80
Passcode protect transaction option only visible if passcode lock is set
taraspasichnyk Aug 2, 2018
9faf26b
Fix localizable string. Remove 'self'. Add general reference to passc…
taraspasichnyk Aug 2, 2018
18dbc31
Merge branch 'master' into feature/830-require-password-on-sending-tr…
taraspasichnyk Aug 3, 2018
d453f52
Change user defaults to keychain
taraspasichnyk Aug 3, 2018
14121c6
Removed localized strings
taraspasichnyk Aug 3, 2018
ee2dc8a
Updated fake lock protocol
taraspasichnyk Aug 3, 2018
b5c6cde
Merge branch 'master' into feature/830-require-password-on-sending-tr…
taraspasichnyk Aug 6, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Trust.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@
61FC5ECF1FCFBAE500CCB12A /* EtherNumberFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FC5ECE1FCFBAE500CCB12A /* EtherNumberFormatter.swift */; };
61FC5ED11FCFBDEB00CCB12A /* EtherNumberFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61FC5ED01FCFBDEB00CCB12A /* EtherNumberFormatterTests.swift */; };
664D11A12007D59F0041A0B0 /* EstimateGasRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664D11A02007D59F0041A0B0 /* EstimateGasRequest.swift */; };
71684E5521105E4200D8FD4B /* AuthenticateEnterPasscodeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71684E5421105E4200D8FD4B /* AuthenticateEnterPasscodeViewModel.swift */; };
71684E5721105F3E00D8FD4B /* EnterPasscodeViewModelInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71684E5621105F3E00D8FD4B /* EnterPasscodeViewModelInterface.swift */; };
71A433702113860900985ADC /* ConfirmPaymentDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71A4336F2113860900985ADC /* ConfirmPaymentDetailsViewModelTests.swift */; };
7301BA9220A3117000E1AFE5 /* AutoLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7301BA9120A3117000E1AFE5 /* AutoLock.swift */; };
7301BA9620AB1E5600E1AFE5 /* CookiesStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7301BA9520AB1E5600E1AFE5 /* CookiesStore.swift */; };
Expand Down Expand Up @@ -746,6 +748,8 @@
61FC5ED01FCFBDEB00CCB12A /* EtherNumberFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EtherNumberFormatterTests.swift; sourceTree = "<group>"; };
646C8C822C986358D7388602 /* Pods_Trust.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Trust.framework; sourceTree = BUILT_PRODUCTS_DIR; };
664D11A02007D59F0041A0B0 /* EstimateGasRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EstimateGasRequest.swift; sourceTree = "<group>"; };
71684E5421105E4200D8FD4B /* AuthenticateEnterPasscodeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticateEnterPasscodeViewModel.swift; sourceTree = "<group>"; };
71684E5621105F3E00D8FD4B /* EnterPasscodeViewModelInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterPasscodeViewModelInterface.swift; sourceTree = "<group>"; };
71A4336F2113860900985ADC /* ConfirmPaymentDetailsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmPaymentDetailsViewModelTests.swift; sourceTree = "<group>"; };
7301BA9120A3117000E1AFE5 /* AutoLock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoLock.swift; sourceTree = "<group>"; };
7301BA9520AB1E5600E1AFE5 /* CookiesStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookiesStore.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2102,8 +2106,8 @@
73ACEEF720163B4E003DD71D /* Coordinators */ = {
isa = PBXGroup;
children = (
73ACEF0420163F46003DD71D /* LockEnterPasscodeCoordinator.swift */,
73C41C74201B65AD00243C6C /* LockCreatePasscodeCoordinator.swift */,
73ACEF0420163F46003DD71D /* LockEnterPasscodeCoordinator.swift */,
772C6DBA210A842200FCE4F1 /* AuthenticateUserCoordinator.swift */,
);
path = Coordinators;
Expand All @@ -2123,8 +2127,10 @@
isa = PBXGroup;
children = (
73ACEF0020163ED4003DD71D /* LockViewModel.swift */,
73C41C70201B46AD00243C6C /* LockEnterPasscodeViewModel.swift */,
73C41C72201B5EFF00243C6C /* LockCreatePasscodeViewModel.swift */,
73C41C70201B46AD00243C6C /* LockEnterPasscodeViewModel.swift */,
71684E5421105E4200D8FD4B /* AuthenticateEnterPasscodeViewModel.swift */,
71684E5621105F3E00D8FD4B /* EnterPasscodeViewModelInterface.swift */,
);
path = ViewModels;
sourceTree = "<group>";
Expand Down Expand Up @@ -2922,6 +2928,7 @@
29E14FDB1F7F4F3D00185568 /* Transaction.swift in Sources */,
7712437A20FD97730097296E /* WalletAccountViewModel.swift in Sources */,
BBF4F9B72029D0B3009E04C0 /* GasViewModel.swift in Sources */,
71684E5721105F3E00D8FD4B /* EnterPasscodeViewModelInterface.swift in Sources */,
73C41C75201B65AD00243C6C /* LockCreatePasscodeCoordinator.swift in Sources */,
7301BA9220A3117000E1AFE5 /* AutoLock.swift in Sources */,
296106C81F7646590006164B /* TokensViewModel.swift in Sources */,
Expand Down Expand Up @@ -3017,6 +3024,7 @@
90DDF48520518AE50016E6D4 /* BookmarkViewModel.swift in Sources */,
BBB61E122050993A00428BBD /* BrowserErrorView.swift in Sources */,
29C80D4B1FB51C460037B1E0 /* Decimal.swift in Sources */,
71684E5521105E4200D8FD4B /* AuthenticateEnterPasscodeViewModel.swift in Sources */,
297800521F71FDCF003185C1 /* FormAppearance.swift in Sources */,
77B3BF3C201908ED00EEC15A /* ConfirmCoordinator.swift in Sources */,
7704837020FFD2F700837735 /* TransactionsViewController.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions Trust/Localization/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"settings.feedback.email.title" = "Trust Feedback";
"settings.navigation.title" = "Settings";
"settings.network.button.title" = "Network";
"settings.lockTransactions.label.title" = "Authenticate Transactions";
"settings.openSourceDevelopment.label.title" = "Open Source Development";
"settings.privacyPolicy.button.title" = "Privacy Policy";
"settings.pushNotifications.button.title" = "Push Notifications";
Expand Down Expand Up @@ -142,6 +143,7 @@
"lock.enter.passcode.view.model.incorrect.passcode" = "Incorrect passcode. You have %li attempts left.";
"lock.enter.passcode.view.model.initial" = "Enter your passcode.";
"lock.enter.passcode.view.model.touch.id" = "Logging in with Touch ID";
"lock.authenticate.enter.passcode.view.model.touch.id" = "Authenticating with Touch ID";
"lock.enter.passcode.view.model.try.after.one.minute" = "Try after 1 minute.";
"Name" = "Name";
"send.action.copy.transaction.title" = "Copy Transaction ID";
Expand Down
16 changes: 11 additions & 5 deletions Trust/Lock/Coordinators/AuthenticateUserCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,39 @@ import Foundation
final class AuthenticateUserCoordinator: Coordinator {

var coordinators: [Coordinator] = []
private var waitForUnlockResult: UnlockResult?
let navigationController: NavigationController
private let model: LockEnterPasscodeViewModel
private let model: AuthenticateEnterPasscodeViewModel
private let lock: LockInterface
private lazy var lockEnterPasscodeViewController: LockEnterPasscodeViewController = {
return LockEnterPasscodeViewController(model: model)
}()

init(
navigationController: NavigationController,
model: LockEnterPasscodeViewModel = LockEnterPasscodeViewModel(),
model: AuthenticateEnterPasscodeViewModel = AuthenticateEnterPasscodeViewModel(),
lock: LockInterface = Lock()
) {
self.navigationController = navigationController
self.model = model
self.lock = lock

lockEnterPasscodeViewController.unlockWithResult = { [weak self] (state, bioUnlock) in
if state {
lockEnterPasscodeViewController.unlockWithResult = { [weak self] (success, bioUnlock) in
self?.waitForUnlockResult?(success, bioUnlock)
if success {
self?.stop()
}
}
}

func start() {
func start(unlockResult: UnlockResult? = nil) {
guard lock.shouldShowProtection() else { return }

navigationController.present(lockEnterPasscodeViewController, animated: true)

if let unlockResult = unlockResult {
lockEnterPasscodeViewController.unlockWithResult = unlockResult
}
}

func showAuthentication() {
Expand Down
5 changes: 3 additions & 2 deletions Trust/Lock/Coordinators/LockEnterPasscodeCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ final class LockEnterPasscodeCoordinator: Coordinator {
self.window.windowLevel = UIWindowLevelStatusBar + 1.0
self.model = model
self.lock = lock
lockEnterPasscodeViewController.unlockWithResult = { [weak self] (state, bioUnlock) in
if state {
lockEnterPasscodeViewController.unlockWithResult = { [weak self] (success, bioUnlock) in
if success {
self?.stop()
}
}
Expand All @@ -27,6 +27,7 @@ final class LockEnterPasscodeCoordinator: Coordinator {
window.rootViewController = lockEnterPasscodeViewController
window.makeKeyAndVisible()
}

//This method should be refactored!!!
func showAuthentication() {
guard window.isKeyWindow, lock.isPasscodeSet() else {
Expand Down
18 changes: 18 additions & 0 deletions Trust/Lock/Lock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import KeychainSwift
protocol LockInterface {
func isPasscodeSet() -> Bool
func shouldShowProtection() -> Bool
func shouldAuthorizeTransactions() -> Bool
}

final class Lock: LockInterface {
Expand All @@ -20,12 +21,17 @@ final class Lock: LockInterface {
private let maxAttemptTime = "maxAttemptTime"
private let autoLockType = "autoLockType"
private let autoLockTime = "autoLockTime"
private let authorizeTransactions = "authorizeTransactions"
private let keychain = KeychainSwift(keyPrefix: Constants.keychainKeyPrefix)

func shouldShowProtection() -> Bool {
return isPasscodeSet() && autoLockTriggered()
}

func shouldAuthorizeTransactions() -> Bool {
return isPasscodeSet() && authorizeTransactionsValue()
}

func isPasscodeSet() -> Bool {
return currentPasscode() != nil
}
Expand All @@ -34,6 +40,14 @@ final class Lock: LockInterface {
return SAMKeychain.password(forService: Keys.service, account: Keys.account)
}

func authorizeTransactionsValue() -> Bool {
if let value = keychain.getBool(authorizeTransactions) {
return value
}
setShouldAuthorizeTransactions(enabled: false)
return false
}

func isPasscodeValid(passcode: String) -> Bool {
return passcode == currentPasscode()
}
Expand Down Expand Up @@ -67,6 +81,10 @@ final class Lock: LockInterface {
SAMKeychain.setPassword(passcode, forService: Keys.service, account: Keys.account)
}

func setShouldAuthorizeTransactions(enabled: Bool) {
keychain.set(enabled, forKey: authorizeTransactions)
}

func deletePasscode() {
SAMKeychain.deletePassword(forService: Keys.service, account: Keys.account)
resetPasscodeAttemptHistory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import UIKit
import LocalAuthentication

typealias UnlockResult = ((_ success: Bool, _ bioUnlock: Bool) -> Void)

final class LockEnterPasscodeViewController: LockPasscodeViewController {
private lazy var lockEnterPasscodeViewModel: LockEnterPasscodeViewModel = {
return self.model as! LockEnterPasscodeViewModel
private lazy var lockEnterPasscodeViewModel: EnterPasscodeViewModelInterface = {
return self.model as! EnterPasscodeViewModelInterface
}()
var unlockWithResult: ((_ success: Bool, _ bioUnlock: Bool) -> Void)?
var unlockWithResult: UnlockResult?
private var context: LAContext!
override func viewDidLoad() {
super.viewDidLoad()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright DApps Platform Inc. All rights reserved.

import Foundation

final class AuthenticateEnterPasscodeViewModel: LockViewModel, EnterPasscodeViewModelInterface {
var loginReason: String {
return R.string.localizable.lockAuthenticateEnterPasscodeViewModelTouchId()
}
}
16 changes: 16 additions & 0 deletions Trust/Lock/ViewModels/EnterPasscodeViewModelInterface.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright DApps Platform Inc. All rights reserved.

import Foundation

protocol EnterPasscodeViewModelInterface: class {
var loginReason: String { get }
}

extension EnterPasscodeViewModelInterface {
var initialLabelText: String {
return R.string.localizable.lockEnterPasscodeViewModelInitial()
}
var tryAfterOneMinute: String {
return R.string.localizable.lockEnterPasscodeViewModelTryAfterOneMinute()
}
}
6 changes: 3 additions & 3 deletions Trust/Lock/ViewModels/LockCreatePasscodeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import UIKit

final class LockCreatePasscodeViewModel: LockViewModel {
let title = NSLocalizedString("lock.create.passcode.view.model.title", value: "Set Passcode", comment: "")
let initialLabelText = NSLocalizedString("lock.create.passcode.view.model.initial", value: "Enter a new passcode", comment: "")
let confirmLabelText = NSLocalizedString("lock.create.passcode.view.model.confirm", value: "Please re-enter your passcode", comment: "")
let title = R.string.localizable.lockCreatePasscodeViewModelTitle()
let initialLabelText = R.string.localizable.lockCreatePasscodeViewModelInitial()
let confirmLabelText = R.string.localizable.lockCreatePasscodeViewModelConfirm()
}
8 changes: 4 additions & 4 deletions Trust/Lock/ViewModels/LockEnterPasscodeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import UIKit

final class LockEnterPasscodeViewModel: LockViewModel {
let initialLabelText = NSLocalizedString("lock.enter.passcode.view.model.initial", value: "Enter your passcode.", comment: "")
let tryAfterOneMinute = NSLocalizedString("lock.enter.passcode.view.model.try.after.one.minute", value: "Try after 1 minute.", comment: "")
let loginReason = NSLocalizedString("lock.enter.passcode.view.model.touch.id", value: "Logging in with Touch ID", comment: "")
final class LockEnterPasscodeViewModel: LockViewModel, EnterPasscodeViewModelInterface {
baophucct marked this conversation as resolved.
Show resolved Hide resolved
var loginReason: String {
return R.string.localizable.lockEnterPasscodeViewModelTouchId()
}
}
35 changes: 33 additions & 2 deletions Trust/Settings/ViewControllers/SettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ final class SettingsViewController: FormViewController, Coordinator {
static let currencyPopularKey = "0"
static let currencyAllKey = "1"
static let passcodeRow = "PasscodeRow"
static let passcodeTransactionLockRow = "PasscodeTransactionLockRow"
}

private var config = Config()
Expand All @@ -28,6 +29,10 @@ final class SettingsViewController: FormViewController, Coordinator {
return lock.isPasscodeSet()
}

var passcodeRow: SwitchRow? {
return form.rowBy(tag: Values.passcodeRow) as? SwitchRow
}

lazy var viewModel: SettingsViewModel = {
return SettingsViewModel(isDebug: isDebug)
}()
Expand All @@ -44,8 +49,8 @@ final class SettingsViewController: FormViewController, Coordinator {
$0.displayValueFor = { value in
return value?.displayName
}
$0.hidden = Condition.function([Values.passcodeRow], { form in
return !((form.rowBy(tag: Values.passcodeRow) as? SwitchRow)?.value ?? false)
$0.hidden = Condition.function([Values.passcodeRow], { [weak self] _ in
return !(self?.passcodeRow?.value ?? false)
})
}.onChange { [weak self] row in
let autoLockType = row.value ?? AutoLock.immediate
Expand All @@ -58,6 +63,23 @@ final class SettingsViewController: FormViewController, Coordinator {
}
}()

lazy var passcodeTransactionLockRow: SwitchRow = {
return SwitchRow(Values.passcodeTransactionLockRow) { [weak self] in
$0.title = self?.viewModel.verifyTransactionsWithPasscodeTitle
$0.value = self?.lock.shouldAuthorizeTransactions()
$0.hidden = Condition.function([Values.passcodeRow], { [weak self] _ in
return !(self?.passcodeRow?.value ?? false)
})
}.onChange { [unowned self] row in
if let value = row.value {
self.lock.setShouldAuthorizeTransactions(enabled: value)
row.updateCell()
}
}.cellSetup { cell, _ in
cell.imageView?.image = R.image.settings_colorful_security()
}
}()

let session: WalletSession
let keystore: Keystore

Expand Down Expand Up @@ -91,11 +113,14 @@ final class SettingsViewController: FormViewController, Coordinator {
} else {
self.lock.deletePasscode()
self.updateAutoLockRow(with: AutoLock.immediate)
self.updatePasscodeTransactionLockRow(enabled: false)
}
}.cellSetup { cell, _ in
cell.imageView?.image = R.image.settings_colorful_security()
}

<<< passcodeTransactionLockRow

<<< autoLockRow

<<< AppFormAppearance.button { [weak self] row in
Expand Down Expand Up @@ -306,6 +331,12 @@ final class SettingsViewController: FormViewController, Coordinator {
self.autoLockRow.reload()
}

private func updatePasscodeTransactionLockRow(enabled: Bool) {
lock.setShouldAuthorizeTransactions(enabled: enabled)
passcodeTransactionLockRow.value = enabled
passcodeTransactionLockRow.reload()
}

func run(action: SettingsAction) {
delegate?.didAction(action: action, in: self)
}
Expand Down
4 changes: 4 additions & 0 deletions Trust/Settings/ViewModels/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ struct SettingsViewModel {
}
}

var verifyTransactionsWithPasscodeTitle: String {
return R.string.localizable.settingsLockTransactionsLabelTitle()
}

var networkTitle: String {
return NSLocalizedString("settings.network.button.title", value: "Network", comment: "")
}
Expand Down
23 changes: 22 additions & 1 deletion Trust/Transfer/Coordinators/ConfirmCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ final class ConfirmCoordinator: RootCoordinator {
return controller
}

private lazy var authenticateUserCoordinator: AuthenticateUserCoordinator = {
return AuthenticateUserCoordinator(navigationController: navigationController)
}()

private lazy var controller: ConfirmPaymentViewController = {
return ConfirmPaymentViewController(
session: session,
Expand Down Expand Up @@ -55,7 +59,8 @@ final class ConfirmCoordinator: RootCoordinator {
self.type = type
self.server = server

controller.didCompleted = { [weak self] result in
controller.delegate = self
controller.didComplete = { [weak self] result in
guard let `self` = self else { return }
switch result {
case .success(let data):
Expand All @@ -76,3 +81,19 @@ final class ConfirmCoordinator: RootCoordinator {
delegate?.didCancel(in: self)
}
}

extension ConfirmCoordinator: ConfirmPaymentAuthenticationDelegate {
func confirmPaymentControllerNeedsAuthentication(_ controller: ConfirmPaymentViewController) {
let needsPasscodeCheck = Lock().shouldAuthorizeTransactions()
if needsPasscodeCheck {
authenticateUserCoordinator.start { [weak self] (success, _) in
if success {
self?.controller.sendTransaction()
self?.authenticateUserCoordinator.stop()
}
}
} else {
self.controller.sendTransaction()
}
}
}
Loading