From 5b12f4afae711d7a047125ca98090fd7b364fde7 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 31 May 2017 18:12:18 -0700 Subject: [PATCH] Prevent outgoing calls started from various places unless have been seen e.g. from contacts app // FREEBIE --- Podfile.lock | 2 +- Signal/src/Jobs/MarkIdentityAsSeenJob.swift | 16 +++-- Signal/src/UserInterface/Strings.swift | 2 + .../AddToGroupViewController.m | 48 +++++++-------- .../ViewControllers/MessagesViewController.m | 26 ++++---- .../ViewControllers/NewGroupViewController.m | 60 +++++++++---------- .../SafetyNumberConfirmationAlert.swift | 60 ++++++++++--------- Signal/src/call/CallService.swift | 2 +- Signal/src/call/OutboundCallInitiator.swift | 12 +++- 9 files changed, 125 insertions(+), 103 deletions(-) diff --git a/Podfile.lock b/Podfile.lock index 9888d0801..1344e8d38 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -137,7 +137,7 @@ CHECKOUT OPTIONS: :commit: 7054e4b13ee5bcd6d524adb6dc9a726e8c466308 :git: https://github.com/WhisperSystems/JSQMessagesViewController.git SignalServiceKit: - :commit: e10cc0c1803c598a7518b6c8b28195e20f0eb12e + :commit: ebd4800e21a1dd3410bfa498f986706c5c71aa4c :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :commit: 877ac7438be3ad0b45ef5ca3969574e4b97112bf diff --git a/Signal/src/Jobs/MarkIdentityAsSeenJob.swift b/Signal/src/Jobs/MarkIdentityAsSeenJob.swift index 3efd7c4d4..329f0b78b 100644 --- a/Signal/src/Jobs/MarkIdentityAsSeenJob.swift +++ b/Signal/src/Jobs/MarkIdentityAsSeenJob.swift @@ -8,18 +8,24 @@ import Foundation class MarkIdentityAsSeenJob: NSObject { let TAG = "[MarkIdentityAsSeenJob]" - private let thread: TSThread + private let recipientIds: [String] public class func run(thread: TSThread) { - MarkIdentityAsSeenJob(thread: thread).run() + let recipientIds = thread.recipientIdentifiers + + MarkIdentityAsSeenJob(recipientIds: recipientIds).run() + } + + public class func run(recipientId: String) { + MarkIdentityAsSeenJob(recipientIds: [recipientId]).run() } - init(thread: TSThread) { - self.thread = thread + init(recipientIds: [String]) { + self.recipientIds = recipientIds } public func run() { - for recipientId in self.thread.recipientIdentifiers { + for recipientId in self.recipientIds { markAsSeenIfNecessary(recipientId: recipientId) } } diff --git a/Signal/src/UserInterface/Strings.swift b/Signal/src/UserInterface/Strings.swift index 73da726e3..36af12bdf 100644 --- a/Signal/src/UserInterface/Strings.swift +++ b/Signal/src/UserInterface/Strings.swift @@ -11,6 +11,8 @@ import Foundation static let callStatusFormat = NSLocalizedString("CALL_STATUS_FORMAT", comment: "embeds {{Call Status}} in call screen label. For ongoing calls, {{Call Status}} is a seconds timer like 01:23, otherwise {{Call Status}} is a short text like 'Ringing', 'Busy', or 'Failed Call'") + static let confirmAndCallButtonTitle = NSLocalizedString("SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION", comment: "alert button text to confirm placing an outgoing call after the recipients Safety Number has changed.") + // MARK: Notification actions static let callBackButtonTitle = NSLocalizedString("CALLBACK_BUTTON_TITLE", comment: "notification action") static let confirmIdentityAndCallBackButtonTitle = NSLocalizedString("CONFIRM_IDENTITY_AND_CALLBACK_BUTTON_TITLE", comment: "notification action, confirming that it's OK to proceed calling after a caller's Safety Number has changed") diff --git a/Signal/src/ViewControllers/AddToGroupViewController.m b/Signal/src/ViewControllers/AddToGroupViewController.m index e4b85f952..217140871 100644 --- a/Signal/src/ViewControllers/AddToGroupViewController.m +++ b/Signal/src/ViewControllers/AddToGroupViewController.m @@ -67,18 +67,18 @@ NS_ASSUME_NONNULL_BEGIN } BOOL didShowSNAlert = [SafetyNumberConfirmationAlert - presentAlertIfNecessaryFromViewController:self - recipientId:phoneNumber - confirmationText: - NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION", - @"button title to confirm adding a recipient to a group when their safety " - @"number has recently changed") - contactsManager:helper.contactsManager - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf addToGroup:phoneNumber]; - } - }]; + presentAlertIfNecessaryWithRecipientId:phoneNumber + confirmationText: + NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION", + @"button title to confirm adding a recipient to a group when their safety " + @"number has recently changed") + contactsManager:helper.contactsManager + verifySeen:YES + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf addToGroup:phoneNumber]; + } + }]; if (didShowSNAlert) { return; } @@ -119,18 +119,18 @@ NS_ASSUME_NONNULL_BEGIN } BOOL didShowSNAlert = [SafetyNumberConfirmationAlert - presentAlertIfNecessaryFromViewController:self - recipientId:signalAccount.recipientId - confirmationText: - NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION", - @"button title to confirm adding a recipient to a group when their safety " - @"number has recently changed") - contactsManager:helper.contactsManager - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf addToGroup:signalAccount.recipientId]; - } - }]; + presentAlertIfNecessaryWithRecipientId:signalAccount.recipientId + confirmationText: + NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_ADD_TO_GROUP_ACTION", + @"button title to confirm adding a recipient to a group when their safety " + @"number has recently changed") + contactsManager:helper.contactsManager + verifySeen:YES + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf addToGroup:signalAccount.recipientId]; + } + }]; if (didShowSNAlert) { return; } diff --git a/Signal/src/ViewControllers/MessagesViewController.m b/Signal/src/ViewControllers/MessagesViewController.m index 7e80ab698..a79095e05 100644 --- a/Signal/src/ViewControllers/MessagesViewController.m +++ b/Signal/src/ViewControllers/MessagesViewController.m @@ -1638,11 +1638,11 @@ typedef enum : NSUInteger { completion: (void (^)(BOOL didConfirmedIdentity))completionHandler { - return [SafetyNumberConfirmationAlert presentAlertIfNecessaryFromViewController:self - recipientIds:self.thread.recipientIdentifiers - confirmationText:confirmationText - contactsManager:self.contactsManager - completion:completionHandler]; + return [SafetyNumberConfirmationAlert presentAlertIfNecessaryWithRecipientIds:self.thread.recipientIdentifiers + confirmationText:confirmationText + contactsManager:self.contactsManager + verifySeen:NO + completion:completionHandler]; } - (void)showFingerprintWithTheirIdentityKey:(NSData *)theirIdentityKey theirSignalId:(NSString *)theirSignalId @@ -1694,15 +1694,13 @@ typedef enum : NSUInteger { return; } - BOOL didShowSNAlert = [self showSafetyNumberConfirmationIfNecessaryWithConfirmationText: - NSLocalizedString(@"SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION", - @"button title to confirm calling a recipient whose safety " - @"number recently changed") - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf callAction:sender]; - } - }]; + BOOL didShowSNAlert = + [self showSafetyNumberConfirmationIfNecessaryWithConfirmationText:[CallStrings confirmAndCallButtonTitle] + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf callAction:sender]; + } + }]; if (didShowSNAlert) { return; } diff --git a/Signal/src/ViewControllers/NewGroupViewController.m b/Signal/src/ViewControllers/NewGroupViewController.m index 9b7dd114f..2deb7ff2f 100644 --- a/Signal/src/ViewControllers/NewGroupViewController.m +++ b/Signal/src/ViewControllers/NewGroupViewController.m @@ -252,21 +252,21 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68; } else { BOOL didShowSNAlert = [SafetyNumberConfirmationAlert - presentAlertIfNecessaryFromViewController:self - recipientId:recipientId - confirmationText:NSLocalizedString( - @"SAFETY_NUMBER_CHANGED_CONFIRM_" - @"ADD_TO_GROUP_ACTION", - @"button title to confirm adding " - @"a recipient to a group when " - @"their safety " - @"number has recently changed") - contactsManager:contactsViewHelper.contactsManager - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf addRecipientId:recipientId]; - } - }]; + presentAlertIfNecessaryWithRecipientId:recipientId + confirmationText:NSLocalizedString( + @"SAFETY_NUMBER_CHANGED_CONFIRM_" + @"ADD_TO_GROUP_ACTION", + @"button title to confirm adding " + @"a recipient to a group when " + @"their safety " + @"number has recently changed") + contactsManager:contactsViewHelper.contactsManager + verifySeen:YES + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf addRecipientId:recipientId]; + } + }]; if (didShowSNAlert) { return; } @@ -342,21 +342,21 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68; }]; } else { BOOL didShowSNAlert = [SafetyNumberConfirmationAlert - presentAlertIfNecessaryFromViewController:self - recipientId:signalAccount.recipientId - confirmationText:NSLocalizedString( - @"SAFETY_NUMBER_CHANGED_CONFIRM_" - @"ADD_TO_GROUP_ACTION", - @"button title to confirm adding " - @"a recipient to a group when " - @"their safety " - @"number has recently changed") - contactsManager:contactsViewHelper.contactsManager - completion:^(BOOL didConfirmIdentity) { - if (didConfirmIdentity) { - [weakSelf addRecipientId:recipientId]; - } - }]; + presentAlertIfNecessaryWithRecipientId:signalAccount.recipientId + confirmationText:NSLocalizedString( + @"SAFETY_NUMBER_CHANGED_CONFIRM_" + @"ADD_TO_GROUP_ACTION", + @"button title to confirm adding " + @"a recipient to a group when " + @"their safety " + @"number has recently changed") + contactsManager:contactsViewHelper.contactsManager + verifySeen:YES + completion:^(BOOL didConfirmIdentity) { + if (didConfirmIdentity) { + [weakSelf addRecipientId:recipientId]; + } + }]; if (didShowSNAlert) { return; } diff --git a/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift b/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift index f9dae9b40..6f5515d4f 100644 --- a/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift +++ b/Signal/src/ViewControllers/SafetyNumberConfirmationAlert.swift @@ -16,14 +16,14 @@ class SafetyNumberConfirmationAlert: NSObject { self.storageManager = TSStorageManager.shared() } - public class func presentAlertIfNecessary(fromViewController: UIViewController, recipientId: String, confirmationText: String, contactsManager: OWSContactsManager, completion: @escaping (Bool) -> Void) -> Bool { - return self.presentAlertIfNecessary(fromViewController: fromViewController, recipientIds: [recipientId], confirmationText: confirmationText, contactsManager: contactsManager, completion: completion) + public class func presentAlertIfNecessary(recipientId: String, confirmationText: String, contactsManager: OWSContactsManager, verifySeen: Bool, completion: @escaping (Bool) -> Void) -> Bool { + return self.presentAlertIfNecessary(recipientIds: [recipientId], confirmationText: confirmationText, contactsManager: contactsManager, verifySeen: verifySeen, completion: completion) } - public class func presentAlertIfNecessary(fromViewController: UIViewController, recipientIds: [String], confirmationText: String, contactsManager: OWSContactsManager, completion: @escaping (Bool) -> Void) -> Bool { - return SafetyNumberConfirmationAlert(contactsManager: contactsManager).presentIfNecessary(fromViewController: fromViewController, - recipientIds: recipientIds, + public class func presentAlertIfNecessary(recipientIds: [String], confirmationText: String, contactsManager: OWSContactsManager, verifySeen: Bool, completion: @escaping (Bool) -> Void) -> Bool { + return SafetyNumberConfirmationAlert(contactsManager: contactsManager).presentIfNecessary(recipientIds: recipientIds, confirmationText: confirmationText, + verifySeen: verifySeen, completion: completion) } @@ -33,18 +33,25 @@ class SafetyNumberConfirmationAlert: NSObject { * @returns true if an alert was shown * false if there were no unconfirmed identities */ - public func presentIfNecessary(fromViewController: UIViewController, recipientIds: [String], confirmationText: String, completion: @escaping (Bool) -> Void) -> Bool { + public func presentIfNecessary(recipientIds: [String], confirmationText: String, verifySeen: Bool, completion: @escaping (Bool) -> Void) -> Bool { - guard let unconfirmedIdentity = self.unconfirmedIdentityThatShouldBlockSending(recipientIds: recipientIds) else { + let unconfirmedIdentity = unconfirmedIdentities(recipientIds: recipientIds).first + + var unseenIdentity: OWSRecipientIdentity? + if verifySeen { + unseenIdentity = unseenIdentities(recipientIds: recipientIds).first + } + + guard let untrustedIdentity = [unseenIdentity, unconfirmedIdentity].flatMap({ $0 }).first else { // No identities to confirm, no alert to present. return false } let displayName: String = { - if let signalAccount = contactsManager.signalAccountMap[unconfirmedIdentity.recipientId] { + if let signalAccount = contactsManager.signalAccountMap[untrustedIdentity.recipientId] { return contactsManager.displayName(for: signalAccount) } else { - return contactsManager.displayName(forPhoneIdentifier: unconfirmedIdentity.recipientId) + return contactsManager.displayName(forPhoneIdentifier: untrustedIdentity.recipientId) } }() @@ -59,12 +66,14 @@ class SafetyNumberConfirmationAlert: NSObject { let actionSheetController = UIAlertController(title: title, message:body, preferredStyle: .actionSheet) let confirmAction = UIAlertAction(title: confirmationText, style: .default) { _ in - Logger.info("\(self.TAG) Confirmed identity: \(unconfirmedIdentity)") + Logger.info("\(self.TAG) Confirmed identity: \(untrustedIdentity)") + OWSDispatch.sessionStoreQueue().async { - self.storageManager.saveRemoteIdentity(unconfirmedIdentity.identityKey, - recipientId: unconfirmedIdentity.recipientId, + self.storageManager.saveRemoteIdentity(untrustedIdentity.identityKey, + recipientId: untrustedIdentity.recipientId, approvedForBlockingUse: true, approvedForNonBlockingUse: true) + MarkIdentityAsSeenJob.run(recipientId: untrustedIdentity.recipientId) DispatchQueue.main.async { completion(true) } @@ -73,11 +82,10 @@ class SafetyNumberConfirmationAlert: NSObject { actionSheetController.addAction(confirmAction) let showSafetyNumberAction = UIAlertAction(title: NSLocalizedString("VERIFY_PRIVACY", comment: "Action sheet item"), style: .default) { _ in - Logger.info("\(self.TAG) Opted to show Safety Number for identity: \(unconfirmedIdentity)") + Logger.info("\(self.TAG) Opted to show Safety Number for identity: \(untrustedIdentity)") - self.presentSafetyNumberViewController(fromViewController: fromViewController, - theirIdentityKey: unconfirmedIdentity.identityKey, - theirRecipientId: unconfirmedIdentity.recipientId, + self.presentSafetyNumberViewController(theirIdentityKey: untrustedIdentity.identityKey, + theirRecipientId: untrustedIdentity.recipientId, theirDisplayName: displayName, completion: { completion(false) }) @@ -87,11 +95,11 @@ class SafetyNumberConfirmationAlert: NSObject { let dismissAction = UIAlertAction(title: NSLocalizedString("TXT_CANCEL_TITLE", comment: "generic cancel text"), style: .cancel) actionSheetController.addAction(dismissAction) - fromViewController.present(actionSheetController, animated: true) + UIApplication.shared.frontmostViewController?.present(actionSheetController, animated: true) return true } - public func presentSafetyNumberViewController(fromViewController: UIViewController, theirIdentityKey: Data, theirRecipientId: String, theirDisplayName: String, completion: (() -> Void)? = nil) { + public func presentSafetyNumberViewController(theirIdentityKey: Data, theirRecipientId: String, theirDisplayName: String, completion: (() -> Void)? = nil) { let fingerprintViewController = UIStoryboard.instantiateFingerprintViewController() let fingerprintBuilder = OWSFingerprintBuilder(storageManager: self.storageManager, contactsManager: self.contactsManager) @@ -99,21 +107,19 @@ class SafetyNumberConfirmationAlert: NSObject { fingerprintViewController.configure(fingerprint: fingerprint, contactName: theirDisplayName) - fromViewController.present(fingerprintViewController, animated: true, completion: completion) + UIApplication.shared.frontmostViewController?.present(fingerprintViewController, animated: true, completion: completion) } - private func unconfirmedIdentitiesThatShouldBlockSending(recipientIds: [String]) -> [OWSRecipientIdentity] { + private func unconfirmedIdentities(recipientIds: [String]) -> [OWSRecipientIdentity] { return recipientIds.flatMap { - return self.storageManager.unconfirmedIdentityThatShouldBlockSending(forRecipientId: $0) + self.storageManager.unconfirmedIdentityThatShouldBlockSending(forRecipientId: $0) } } - private func unconfirmedIdentityThatShouldBlockSending(recipientIds: [String]) -> OWSRecipientIdentity? { - return unconfirmedIdentitiesThatShouldBlockSending(recipientIds: recipientIds).first - } - - private func shouldShow(recipientIds: [String]) -> Bool { - return !unconfirmedIdentitiesThatShouldBlockSending(recipientIds: recipientIds).isEmpty + private func unseenIdentities(recipientIds: [String]) -> [OWSRecipientIdentity] { + return recipientIds.flatMap { + self.storageManager.unseenIdentityChange(forRecipientId: $0) + } } } diff --git a/Signal/src/call/CallService.swift b/Signal/src/call/CallService.swift index d73b9a4d4..07229e4ad 100644 --- a/Signal/src/call/CallService.swift +++ b/Signal/src/call/CallService.swift @@ -470,7 +470,7 @@ protocol CallServiceObserver: class { let newCall = SignalCall.incomingCall(localId: UUID(), remotePhoneNumber: thread.contactIdentifier(), signalingId: callId) - guard !self.storageManager.hasUnseenIdentityChange(forRecipientId: thread.contactIdentifier()) else { + guard self.storageManager.unseenIdentityChange(forRecipientId: thread.contactIdentifier()) == nil else { let callerName = self.contactsManager.displayName(forPhoneIdentifier: thread.contactIdentifier()) self.notificationsAdapter.presentRejectedCallWithUnseenIdentityChange(newCall, callerName: callerName) return diff --git a/Signal/src/call/OutboundCallInitiator.swift b/Signal/src/call/OutboundCallInitiator.swift index b88b363ee..144fbc1af 100644 --- a/Signal/src/call/OutboundCallInitiator.swift +++ b/Signal/src/call/OutboundCallInitiator.swift @@ -46,7 +46,17 @@ import Foundation return false } - // TODO possible to get here when. e.g. dialing from contacts/recent calls. Should verify seen latest SN. + let showedAlert = SafetyNumberConfirmationAlert.presentAlertIfNecessary(recipientId: recipientId, + confirmationText: CallStrings.confirmAndCallButtonTitle, + contactsManager: self.contactsManager, + verifySeen: true) { didConfirmIdentity in + if didConfirmIdentity { + _ = self.initiateCall(recipientId: recipientId) + } + } + guard !showedAlert else { + return false + } // Check for microphone permissions // Alternative way without prompting for permissions: