Handle unpair request flag.

This commit is contained in:
Mikunj 2019-11-21 12:43:33 +11:00
parent c66786e0f1
commit 3a79e1f215
5 changed files with 108 additions and 48 deletions

View File

@ -331,11 +331,21 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
SignalServiceDataMessage message = content.getDataMessage().get(); SignalServiceDataMessage message = content.getDataMessage().get();
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent(); boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
if (message.isUnpairingRequest()) {
// Make sure we got the request from our primary device
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
if (ourPrimaryDevice != null && ourPrimaryDevice.equals(content.getSender())) {
MultiDeviceUtilities.checkForRevocation(context);
}
} else {
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId); if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId); else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
else if (message.isExpirationUpdate()) handleExpirationUpdate(content, message, smsMessageId); else if (message.isExpirationUpdate())
else if (isMediaMessage) handleMediaMessage(content, message, smsMessageId, Optional.absent()); handleExpirationUpdate(content, message, smsMessageId);
else if (message.getBody().isPresent()) handleTextMessage(content, message, smsMessageId, Optional.absent()); else if (isMediaMessage)
handleMediaMessage(content, message, smsMessageId, Optional.absent());
else if (message.getBody().isPresent())
handleTextMessage(content, message, smsMessageId, Optional.absent());
if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) { if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get().getGroupId(), false))) {
handleUnknownGroupMessage(content, message.getGroupInfo().get()); handleUnknownGroupMessage(content, message.getGroupInfo().get());
@ -352,6 +362,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
// Loki - Handle friend request logic if needed // Loki - Handle friend request logic if needed
updateFriendRequestStatusIfNeeded(envelope, content, message); updateFriendRequestStatusIfNeeded(envelope, content, message);
}
} else if (content.getSyncMessage().isPresent()) { } else if (content.getSyncMessage().isPresent()) {
TextSecurePreferences.setMultiDevice(context, true); TextSecurePreferences.setMultiDevice(context, true);

View File

@ -8,6 +8,7 @@ import org.thoughtcrime.securesms.*
import org.thoughtcrime.securesms.util.DynamicTheme import org.thoughtcrime.securesms.util.DynamicTheme
import org.thoughtcrime.securesms.util.DynamicLanguage import org.thoughtcrime.securesms.util.DynamicLanguage
import network.loki.messenger.R import network.loki.messenger.R
import nl.komponents.kovenant.then
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.sms.MessageSender import org.thoughtcrime.securesms.sms.MessageSender
import org.thoughtcrime.securesms.util.TextSecurePreferences import org.thoughtcrime.securesms.util.TextSecurePreferences
@ -44,9 +45,10 @@ class LinkedDevicesActivity : PassphraseRequiredActionBarActivity(), DeviceLinki
val database = DatabaseFactory.getLokiAPIDatabase(this) val database = DatabaseFactory.getLokiAPIDatabase(this)
database.removePairingAuthorisation(ourPublicKey, devicePublicKey) database.removePairingAuthorisation(ourPublicKey, devicePublicKey)
// Update mapping on the file server // Update mapping on the file server
LokiStorageAPI.shared.updateUserDeviceMappings() LokiStorageAPI.shared.updateUserDeviceMappings().success {
// Send a background message to let the device know that it has been revoked // Send an unpair request to let the device know that it has been revoked
MessageSender.sendBackgroundMessage(this, devicePublicKey) MessageSender.sendUnpairRequest(this, devicePublicKey)
}
// Refresh the list // Refresh the list
this.deviceListFragment.refresh() this.deviceListFragment.refresh()
Toast.makeText(this, R.string.DeviceListActivity_unlinked_device, Toast.LENGTH_LONG).show() Toast.makeText(this, R.string.DeviceListActivity_unlinked_device, Toast.LENGTH_LONG).show()

View File

@ -6,6 +6,7 @@ import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.all import nl.komponents.kovenant.all
import nl.komponents.kovenant.functional.bind import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map import nl.komponents.kovenant.functional.map
import nl.komponents.kovenant.then
import nl.komponents.kovenant.toFailVoid import nl.komponents.kovenant.toFailVoid
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
@ -27,6 +28,23 @@ import org.whispersystems.signalservice.loki.utilities.retryIfNeeded
import java.util.* import java.util.*
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
fun checkForRevocation(context: Context) {
val primaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) ?: return
val ourDevice = TextSecurePreferences.getLocalNumber(context)
LokiStorageAPI.shared.fetchDeviceMappings(primaryDevice).bind { mappings ->
val ourMapping = mappings.find { it.secondaryDevicePublicKey == ourDevice }
if (ourMapping != null) throw Error("Device has not been revoked")
// remove pairing auths for our device
DatabaseFactory.getLokiAPIDatabase(context).removePairingAuthorisations(ourDevice)
LokiStorageAPI.shared.updateUserDeviceMappings()
}.success {
// TODO: Revoke here
}.fail { error ->
Log.d("Loki", "Revocation check failed: $error")
}
}
fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> { fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise<Map<String, LokiThreadFriendRequestStatus>, Exception> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context) val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
return LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).map { keys -> return LokiStorageAPI.shared.getAllDevicePublicKeys(hexEncodedPublicKey).map { keys ->

View File

@ -11,39 +11,58 @@ import org.whispersystems.libsignal.util.guava.Optional
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
import org.whispersystems.signalservice.api.push.SignalServiceAddress import org.whispersystems.signalservice.api.push.SignalServiceAddress
import org.whispersystems.signalservice.internal.util.JsonUtil
import java.io.IOException import java.io.IOException
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
data class BackgroundMessage private constructor(val recipient: String, val body: String?, val friendRequest: Boolean, val unpairingRequest: Boolean) {
companion object {
@JvmStatic
fun create(recipient: String) = BackgroundMessage(recipient, null, false, false)
@JvmStatic
fun createFriendRequest(recipient: String, messageBody: String) = BackgroundMessage(recipient, messageBody, true, false)
@JvmStatic
fun createUnpairingRequest(recipient: String) = BackgroundMessage(recipient, null, false, true)
internal fun parse(serialized: String): BackgroundMessage {
val node = JsonUtil.fromJson(serialized)
val recipient = node.get("recipient").asText()
val body = if (node.hasNonNull("body")) node.get("body").asText() else null
val friendRequest = node.get("friendRequest").asBoolean(false)
val unpairingRequest = node.get("unpairingRequest").asBoolean(false)
return BackgroundMessage(recipient, body, friendRequest, unpairingRequest)
}
}
fun serialize(): String {
val map = mapOf("recipient" to recipient, "body" to body, "friendRequest" to friendRequest, "unpairingRequest" to unpairingRequest)
return JsonUtil.toJson(map)
}
}
class PushBackgroundMessageSendJob private constructor( class PushBackgroundMessageSendJob private constructor(
parameters: Parameters, parameters: Parameters,
private val recipient: String, private val message: BackgroundMessage
private val messageBody: String?,
private val friendRequest: Boolean
) : BaseJob(parameters) { ) : BaseJob(parameters) {
companion object { companion object {
const val KEY = "PushBackgroundMessageSendJob" const val KEY = "PushBackgroundMessageSendJob"
private val TAG = PushBackgroundMessageSendJob::class.java.simpleName private val TAG = PushBackgroundMessageSendJob::class.java.simpleName
private val KEY_RECIPIENT = "recipient" private val KEY_MESSAGE = "message"
private val KEY_MESSAGE_BODY = "message_body"
private val KEY_FRIEND_REQUEST = "asFriendRequest"
} }
constructor(recipient: String): this(recipient, null, false) constructor(message: BackgroundMessage) : this(Parameters.Builder()
constructor(recipient: String, messageBody: String?, friendRequest: Boolean) : this(Parameters.Builder()
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setQueue(KEY) .setQueue(KEY)
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(1) .setMaxAttempts(1)
.build(), .build(),
recipient, messageBody, friendRequest) message)
override fun serialize(): Data { override fun serialize(): Data {
return Data.Builder() return Data.Builder()
.putString(KEY_RECIPIENT, recipient) .putString(KEY_MESSAGE, message.serialize())
.putString(KEY_MESSAGE_BODY, messageBody)
.putBoolean(KEY_FRIEND_REQUEST, friendRequest)
.build() .build()
} }
@ -52,22 +71,24 @@ class PushBackgroundMessageSendJob private constructor(
} }
public override fun onRun() { public override fun onRun() {
val message = SignalServiceDataMessage.newBuilder() val dataMessage = SignalServiceDataMessage.newBuilder()
.withTimestamp(System.currentTimeMillis()) .withTimestamp(System.currentTimeMillis())
.withBody(messageBody) .withBody(message.body)
if (friendRequest) { if (message.friendRequest) {
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient) val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(message.recipient)
message.withPreKeyBundle(bundle) dataMessage.withPreKeyBundle(bundle)
.asFriendRequest(true) .asFriendRequest(true)
} else if (message.unpairingRequest) {
dataMessage.asUnpairingRequest(true)
} }
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
val address = SignalServiceAddress(recipient) val address = SignalServiceAddress(message.recipient)
try { try {
messageSender.sendMessage(-1, address, Optional.absent<UnidentifiedAccessPair>(), message.build()) // The message ID doesn't matter messageSender.sendMessage(-1, address, Optional.absent<UnidentifiedAccessPair>(), dataMessage.build()) // The message ID doesn't matter
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to send background message to: $recipient.") Log.d("Loki", "Failed to send background message to: ${message.recipient}.")
throw e throw e
} }
} }
@ -82,10 +103,8 @@ class PushBackgroundMessageSendJob private constructor(
class Factory : Job.Factory<PushBackgroundMessageSendJob> { class Factory : Job.Factory<PushBackgroundMessageSendJob> {
override fun create(parameters: Parameters, data: Data): PushBackgroundMessageSendJob { override fun create(parameters: Parameters, data: Data): PushBackgroundMessageSendJob {
try { try {
val recipient = data.getString(KEY_RECIPIENT) val messageJSON = data.getString(KEY_MESSAGE)
val messageBody = if (data.hasString(KEY_MESSAGE_BODY)) data.getString(KEY_MESSAGE_BODY) else null return PushBackgroundMessageSendJob(parameters, BackgroundMessage.parse(messageJSON))
val friendRequest = data.getBooleanOrDefault(KEY_FRIEND_REQUEST, false)
return PushBackgroundMessageSendJob(parameters, recipient, messageBody, friendRequest)
} catch (e: IOException) { } catch (e: IOException) {
throw AssertionError(e) throw AssertionError(e)
} }

View File

@ -17,6 +17,7 @@
package org.thoughtcrime.securesms.sms; package org.thoughtcrime.securesms.sms;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.ApplicationContext;
@ -44,6 +45,7 @@ import org.thoughtcrime.securesms.jobs.SmsSendJob;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil;
import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.loki.BackgroundMessage;
import org.thoughtcrime.securesms.loki.FriendRequestHandler; import org.thoughtcrime.securesms.loki.FriendRequestHandler;
import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt; import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt;
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
@ -58,7 +60,11 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.Util;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.push.ContactTokenDetails; import org.whispersystems.signalservice.api.push.ContactTokenDetails;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.loki.api.LokiStorageAPI; import org.whispersystems.signalservice.loki.api.LokiStorageAPI;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus;
import org.whispersystems.signalservice.loki.utilities.PromiseUtil; import org.whispersystems.signalservice.loki.utilities.PromiseUtil;
@ -123,11 +129,15 @@ public class MessageSender {
// We don't call the message sender here directly and instead we just opt to create a specific job for the send // We don't call the message sender here directly and instead we just opt to create a specific job for the send
// This is because calling message sender directly would cause the application to freeze in some cases as it was blocking the thread when waiting for a response from the send // This is because calling message sender directly would cause the application to freeze in some cases as it was blocking the thread when waiting for a response from the send
public static void sendBackgroundMessage(Context context, String contactHexEncodedPublicKey) { public static void sendBackgroundMessage(Context context, String contactHexEncodedPublicKey) {
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(contactHexEncodedPublicKey)); ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.create(contactHexEncodedPublicKey)));
} }
public static void sendBackgroundFriendRequest(Context context, String contactHexEncodedPublicKey, String messageBody) { public static void sendBackgroundFriendRequest(Context context, String contactHexEncodedPublicKey, String messageBody) {
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(contactHexEncodedPublicKey, messageBody, true)); ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createFriendRequest(contactHexEncodedPublicKey, messageBody)));
}
public static void sendUnpairRequest(Context context, String contactHexEncodedPublicKey) {
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createUnpairingRequest(contactHexEncodedPublicKey)));
} }
// endregion // endregion