Prevent outgoing calls started from various places unless have been seen

e.g. from contacts app

// FREEBIE
This commit is contained in:
Michael Kirk 2017-05-31 18:12:18 -07:00
parent 130aa132a0
commit 5b12f4afae
9 changed files with 125 additions and 103 deletions

View File

@ -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

View File

@ -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()
}
init(thread: TSThread) {
self.thread = thread
public class func run(recipientId: String) {
MarkIdentityAsSeenJob(recipientIds: [recipientId]).run()
}
init(recipientIds: [String]) {
self.recipientIds = recipientIds
}
public func run() {
for recipientId in self.thread.recipientIdentifiers {
for recipientId in self.recipientIds {
markAsSeenIfNecessary(recipientId: recipientId)
}
}

View File

@ -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")

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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: