Skip to content

Commit

Permalink
Recording id of who remotely deleted a message
Browse files Browse the repository at this point in the history
  • Loading branch information
oxtoacart committed Jun 8, 2021
1 parent 3976c36 commit 3fe6986
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,6 @@ import io.lantern.messaging.tassis.websocket.WSListener
import io.lantern.messaging.tassis.websocket.WebSocketTransportFactory
import io.lantern.messaging.time.minutesToMillis
import io.lantern.messaging.time.secondsToMillis
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import mu.KotlinLogging
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okio.ByteString
import org.junit.Test
import org.whispersystems.libsignal.util.KeyHelper
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream
import org.whispersystems.signalservice.internal.util.Util
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
Expand All @@ -36,6 +24,18 @@ import kotlin.test.fail
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
import kotlin.time.seconds
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import mu.KotlinLogging
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okio.ByteString
import org.junit.Test
import org.whispersystems.libsignal.util.KeyHelper
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream
import org.whispersystems.signalservice.internal.util.Util

private val logger = KotlinLogging.logger {}

Expand Down Expand Up @@ -631,6 +631,18 @@ class MessagingTest : BaseMessagingTest() {
smileyFace
)

// try to remotely delete message with someone other than sender
var bogusContactId = "blahblah".directContactId
try {
dog.deleteLocally(
replyMsgs.received.dbPath,
remotelyDeletedBy = bogusContactId
)
fail("deletion by anyone other than sender of message should not have been allowed") // ktlint-disable max-line-length
} catch (e: IllegalArgumentException) {
// okay
}

// globally delete cat's reply
cat.deleteGlobally(replyMsgs.sent.dbPath)
assertNull(
Expand Down Expand Up @@ -666,8 +678,9 @@ class MessagingTest : BaseMessagingTest() {
replyMsgs.received.dbPath,
"message should have been marked deleted for dog too"
) {
it.deletedBySenderAt > 0
it.remotelyDeletedAt > 0
}
assertEquals(deletedMessage.contactId, deletedMessage.remotelyDeletedBy)
assertEquals("", deletedMessage.text)
assertEquals(0, deletedMessage.thumbnailsCount)
assertEquals(0, deletedMessage.attachmentsCount)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ internal class CryptoWorker(
senderId.storedMessagePath(transferMsg.deleteMessageId.base32),
// We keep metadata so that the recipient's UI still has an empty placeholder for the deleted message.
// Once the recipient chooses to delete this message locally, the metadata will be deleted.
keepMetadata = true
remotelyDeletedBy = senderId.directContactId,
)
Model.TransferMessage.ContentCase.DISAPPEARSETTINGS -> storeDisappearSettings(
tx,
Expand Down
37 changes: 24 additions & 13 deletions messaging/src/main/java/io/lantern/messaging/Messaging.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ import io.lantern.messaging.tassis.TransportFactory
import io.lantern.messaging.tassis.byteString
import io.lantern.messaging.time.hoursToMillis
import io.lantern.messaging.time.secondsToMillis
import mu.KotlinLogging
import org.whispersystems.libsignal.DeviceId
import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream
import org.whispersystems.signalservice.internal.util.Util
import java.io.ByteArrayInputStream
import java.io.Closeable
import java.io.File
Expand All @@ -29,6 +24,11 @@ import java.util.UUID
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import kotlin.collections.HashSet
import mu.KotlinLogging
import org.whispersystems.libsignal.DeviceId
import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream
import org.whispersystems.signalservice.internal.util.Util

/**
* This exception indicates that a message was received from an unknown sender (i.e. someone not in
Expand Down Expand Up @@ -488,19 +488,23 @@ class Messaging(
* Deletes the message locally only.
*
* @param msgPath the path identifying the message to delete.
* @param keepMetadata if true, we retain a record of the message and basic metadata, but delete
* everything else
* @param remotelyDeletedBy if this deletion was remotely requested, set the id of the requester
* here. When remotely requested, we retain a record of the message and
* basic metadata, but delete everything else.
*/
fun deleteLocally(msgPath: String, keepMetadata: Boolean = false): Model.StoredMessage? {
fun deleteLocally(
msgPath: String,
remotelyDeletedBy: Model.ContactId? = null
): Model.StoredMessage? {
return db.mutate { tx ->
doDeleteLocally(tx, msgPath, keepMetadata)
doDeleteLocally(tx, msgPath, remotelyDeletedBy)
}
}

private fun doDeleteLocally(
tx: Transaction,
msgPath: String,
keepMetadata: Boolean = false
remotelyDeletedBy: Model.ContactId? = null
): Model.StoredMessage? {
return db.get<Model.StoredMessage>(msgPath)?.let { msg ->
// Delete attachments on disk
Expand All @@ -513,20 +517,27 @@ class Messaging(
}
}

if (keepMetadata) {
remotelyDeletedBy?.let {
if (remotelyDeletedBy != msg.contactId) {
throw IllegalArgumentException(
"Messages can only be remotely deleted by the original sender"
)
}

// don't actually physically delete the message yet, just clear the message content
// and mark it as deleted
tx.put(
msg.dbPath,
msg.toBuilder()
.setDeletedBySenderAt(now)
.setRemotelyDeletedBy(remotelyDeletedBy)
.setRemotelyDeletedAt(now)
.clearText()
.clearThumbnails()
.clearAttachments()
.clearReactions()
.build()
)
} else {
} ?: run {
// Delete the message
tx.delete(msgPath)

Expand Down
3 changes: 2 additions & 1 deletion messaging/src/main/protos/Model.proto
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ message StoredMessage {
DeliveryStatus status = 12; // for outgoing messages, the status of its sending and delivery
int64 firstViewedAt = 13; // the unix timestamp in milliseconds of when this message was first viewed on the current device
int64 disappearAt = 14; // the unix timestamp in milliseconds of when this message will automatically disappear
int64 deletedBySenderAt = 16; // the unix timestamp in milliseconds of when the sender remotely deleted this message (will be 0 if the message hasn't been remotely deleted)
ContactId remotelyDeletedBy = 16; // the id of the contact who remotely deleted this message
int64 remotelyDeletedAt = 17; // the unix timestamp in milliseconds of when an authorized party remotely deleted this message (will be 0 if the message hasn't been remotely deleted)
}

// A reaction to a message
Expand Down

0 comments on commit 3fe6986

Please sign in to comment.