Skip to content

Commit

Permalink
feat: Include legal hold flag when sending/receiving messages (WPB-54…
Browse files Browse the repository at this point in the history
…42) (#2279)

* feat: add legal hold status to conversation table

* feat: add legal hold status to conversation table

* feat: add missing params of legal hold

* chore: unit test

* feat: include legal hold flag when sending/receiving messages

* chore: detekt

* chore: revert changes

* chore: revert changes

* feat: legal hold status for ephemeral messages

---------

Co-authored-by: Yamil Medina <[email protected]>
Co-authored-by: Mojtaba Chenani <[email protected]>
  • Loading branch information
3 people authored Dec 7, 2023
1 parent 8a56c77 commit edee554
Show file tree
Hide file tree
Showing 14 changed files with 297 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import com.wire.kalium.persistence.dao.asset.AssetMessageEntity
import com.wire.kalium.persistence.dao.message.MessageEntity
import com.wire.kalium.persistence.dao.message.MessageEntityContent
import com.wire.kalium.protobuf.messages.Asset
import com.wire.kalium.protobuf.messages.LegalHoldStatus
import com.wire.kalium.util.DateTimeUtil
import com.wire.kalium.util.KaliumDispatcher
import com.wire.kalium.util.KaliumDispatcherImpl
Expand All @@ -52,7 +53,11 @@ interface AssetMapper {
fun fromUserAssetToDaoModel(assetId: String, assetDomain: String?, dataPath: Path, dataSize: Long): AssetEntity
fun fromAssetEntityToAssetContent(assetContentEntity: MessageEntityContent.Asset): AssetContent
fun fromProtoAssetMessageToAssetContent(protoAssetMessage: Asset): AssetContent
fun fromAssetContentToProtoAssetMessage(messageContent: MessageContent.Asset, expectsReadConfirmation: Boolean): Asset
fun fromAssetContentToProtoAssetMessage(
messageContent: MessageContent.Asset,
expectsReadConfirmation: Boolean,
legalHoldStatus: LegalHoldStatus
): Asset
}

class AssetMapperImpl(
Expand Down Expand Up @@ -196,8 +201,11 @@ class AssetMapperImpl(
}
}

override fun fromAssetContentToProtoAssetMessage(messageContent: MessageContent.Asset, expectsReadConfirmation: Boolean): Asset =
with(messageContent.value) {
override fun fromAssetContentToProtoAssetMessage(
messageContent: MessageContent.Asset,
expectsReadConfirmation: Boolean,
legalHoldStatus: LegalHoldStatus
): Asset = with(messageContent.value) {
Asset(
original = Asset.Original(
mimeType = mimeType,
Expand Down Expand Up @@ -231,7 +239,8 @@ class AssetMapperImpl(
encryption = encryptionAlgorithmMapper.toProtoBufModel(remoteData.encryptionAlgorithm)
)
),
expectsReadConfirmation = expectsReadConfirmation
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = legalHoldStatus
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ data class Conversation(
enum class TypingIndicatorMode { STARTED, STOPPED }

enum class VerificationStatus { VERIFIED, NOT_VERIFIED, DEGRADED }
enum class LegalHoldStatus { ENABLED, DISABLED, DEGRADED }
enum class LegalHoldStatus { ENABLED, DISABLED, DEGRADED, UNKNOWN }

@Suppress("MagicNumber")
enum class CipherSuite(val tag: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package com.wire.kalium.logic.data.message

import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.message.ProtoContent.ExternalMessageInstructions
import com.wire.kalium.persistence.dao.conversation.ConversationEntity.ProtocolInfo.Proteus
import com.wire.kalium.protobuf.messages.GenericMessage
Expand All @@ -38,6 +39,7 @@ sealed interface ProtoContent {
override val messageUid: String,
val messageContent: MessageContent.FromProto,
val expectsReadConfirmation: Boolean,
val legalHoldStatus: Conversation.LegalHoldStatus,
val expiresAfterMillis: Long? = null
) : ProtoContent

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package com.wire.kalium.logic.data.message

import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.data.asset.AssetMapper
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.IdMapper
import com.wire.kalium.logic.data.message.mention.MessageMentionMapper
Expand All @@ -43,6 +44,7 @@ import com.wire.kalium.protobuf.messages.External
import com.wire.kalium.protobuf.messages.GenericMessage
import com.wire.kalium.protobuf.messages.Knock
import com.wire.kalium.protobuf.messages.LastRead
import com.wire.kalium.protobuf.messages.LegalHoldStatus
import com.wire.kalium.protobuf.messages.MessageDelete
import com.wire.kalium.protobuf.messages.MessageEdit
import com.wire.kalium.protobuf.messages.MessageHide
Expand Down Expand Up @@ -84,26 +86,29 @@ class ProtoContentMapperImpl(
mapEphemeralContent(
protoContent.messageContent,
protoContent.expiresAfterMillis,
protoContent.expectsReadConfirmation
protoContent.expectsReadConfirmation,
protoContent.legalHoldStatus
)
} else {
mapNormalContent(
protoContent.messageContent,
protoContent.expectsReadConfirmation
protoContent.expectsReadConfirmation,
protoContent.legalHoldStatus
)
}
}

@Suppress("ComplexMethod")
private fun mapNormalContent(
readableContent: MessageContent.FromProto,
expectsReadConfirmation: Boolean
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content<out Any> {
return when (readableContent) {
is MessageContent.Text -> packText(readableContent, expectsReadConfirmation)
is MessageContent.Text -> packText(readableContent, expectsReadConfirmation, legalHoldStatus)
is MessageContent.Calling -> packCalling(readableContent)
is MessageContent.Asset -> packAsset(readableContent, expectsReadConfirmation)
is MessageContent.Knock -> GenericMessage.Content.Knock(Knock(hotKnock = readableContent.hotKnock))
is MessageContent.Asset -> packAsset(readableContent, expectsReadConfirmation, legalHoldStatus)
is MessageContent.Knock -> packKnock(readableContent, legalHoldStatus)
is MessageContent.DeleteMessage -> GenericMessage.Content.Deleted(MessageDelete(messageId = readableContent.messageId))
is MessageContent.DeleteForMe -> packHidden(readableContent)
is MessageContent.Availability -> GenericMessage.Content.Availability(
Expand All @@ -114,7 +119,7 @@ class ProtoContentMapperImpl(

is MessageContent.LastRead -> packLastRead(readableContent)
is MessageContent.Cleared -> packCleared(readableContent)
is MessageContent.Reaction -> packReaction(readableContent)
is MessageContent.Reaction -> packReaction(readableContent, legalHoldStatus)
is MessageContent.Receipt -> packReceipt(readableContent)
is MessageContent.ClientAction -> packClientAction()
is MessageContent.TextEdited -> packEdited(readableContent)
Expand All @@ -123,7 +128,7 @@ class ProtoContentMapperImpl(
"Unexpected message content type: ${readableContent.getType()}"
)

is MessageContent.Composite -> packComposite(readableContent, expectsReadConfirmation)
is MessageContent.Composite -> packComposite(readableContent, expectsReadConfirmation, legalHoldStatus)
is MessageContent.ButtonAction -> packButtonAction(readableContent)

is MessageContent.ButtonActionConfirmation -> TODO()
Expand All @@ -142,12 +147,13 @@ class ProtoContentMapperImpl(

private fun packComposite(
readableContent: MessageContent.Composite,
expectsReadConfirmation: Boolean
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Composite {
val items: MutableList<Composite.Item> = mutableListOf()

readableContent.textContent?.let {
val text = packText(it, expectsReadConfirmation)
val text = packText(it, expectsReadConfirmation, legalHoldStatus)
Composite.Item.Content.Text(text.value).also {
items.add(Composite.Item(it))
}
Expand All @@ -159,7 +165,8 @@ class ProtoContentMapperImpl(
val composite = GenericMessage.Content.Composite(
Composite(
items = items,
expectsReadConfirmation = expectsReadConfirmation
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
)
)
return composite
Expand All @@ -168,25 +175,26 @@ class ProtoContentMapperImpl(
private fun mapEphemeralContent(
readableContent: MessageContent.FromProto,
expireAfterMillis: Long,
expectsReadConfirmation: Boolean
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content<out Any> {
val ephemeralContent = when (readableContent) {
is MessageContent.Text -> {
val text = packText(readableContent, expectsReadConfirmation)
val text = packText(readableContent, expectsReadConfirmation, legalHoldStatus)
Ephemeral.Content.Text(
text.value
)
}

is MessageContent.Asset -> {
val asset = packAsset(readableContent, expectsReadConfirmation)
val asset = packAsset(readableContent, expectsReadConfirmation, legalHoldStatus)
Ephemeral.Content.Asset(
asset.value
)
}

is MessageContent.Knock -> {
val knock = GenericMessage.Content.Knock(Knock(hotKnock = readableContent.hotKnock))
val knock = packKnock(readableContent, legalHoldStatus)
Ephemeral.Content.Knock(
knock.value
)
Expand Down Expand Up @@ -236,6 +244,7 @@ class ProtoContentMapperImpl(
is GenericMessage.Content.Asset -> content.value.expectsReadConfirmation ?: false
else -> false
}
val legalHoldStatus = getLegalHoldStatusFromProtoContent(genericMessage)
val expiresAfterMillis: Long? = when (val content = genericMessage.content) {
is GenericMessage.Content.Ephemeral -> content.value.expireAfterMillis
else -> null
Expand All @@ -244,11 +253,32 @@ class ProtoContentMapperImpl(
messageUid = genericMessage.messageId,
messageContent = getReadableContent(genericMessage, encodedContent),
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = fromProtoLegalHoldStatus(legalHoldStatus),
expiresAfterMillis = expiresAfterMillis
)
}
}

private fun getLegalHoldStatusFromProtoContent(genericMessage: GenericMessage) =
when (val content = genericMessage.content) {
is GenericMessage.Content.Text -> content.value.legalHoldStatus
is GenericMessage.Content.Asset -> content.value.legalHoldStatus
is GenericMessage.Content.Knock -> content.value.legalHoldStatus
is GenericMessage.Content.Location -> content.value.legalHoldStatus
is GenericMessage.Content.Reaction -> content.value.legalHoldStatus
is GenericMessage.Content.Composite -> content.value.legalHoldStatus
else -> null
}

private fun fromProtoLegalHoldStatus(legalHoldStatus: LegalHoldStatus?): Conversation.LegalHoldStatus =
legalHoldStatus?.let {
when (legalHoldStatus) {
is LegalHoldStatus.ENABLED -> Conversation.LegalHoldStatus.ENABLED
is LegalHoldStatus.DISABLED -> Conversation.LegalHoldStatus.DISABLED
else -> Conversation.LegalHoldStatus.UNKNOWN
}
} ?: run { Conversation.LegalHoldStatus.UNKNOWN }

@Suppress("ComplexMethod", "LongMethod")
private fun getReadableContent(
genericMessage: GenericMessage,
Expand Down Expand Up @@ -335,12 +365,20 @@ class ProtoContentMapperImpl(
)
} ?: MessageContent.Ignored

private fun packReaction(readableContent: MessageContent.Reaction) =
GenericMessage.Content.Reaction(Reaction(emoji = readableContent.emojiSet.map { it.trim() }.filter { it.isNotBlank() }
.joinToString(separator = ",") { it },
messageId = readableContent.messageId
)
private fun packReaction(
readableContent: MessageContent.Reaction,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Reaction {
val protoLegalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
return GenericMessage.Content.Reaction(
Reaction(
emoji = readableContent.emojiSet.map { it.trim() }.filter { it.isNotBlank() }
.joinToString(separator = ",") { it },
messageId = readableContent.messageId,
legalHoldStatus = protoLegalHoldStatus
)
)
}

private fun packClientAction() = GenericMessage.Content.ClientAction(ClientAction.RESET_SESSION)

Expand Down Expand Up @@ -461,17 +499,30 @@ class ProtoContentMapperImpl(
time = Instant.fromEpochMilliseconds(protoContent.value.clearedTimestamp)
)

private fun packText(readableContent: MessageContent.Text, expectsReadConfirmation: Boolean): GenericMessage.Content.Text {
private fun toProtoLegalHoldStatus(legalHoldStatus: Conversation.LegalHoldStatus): LegalHoldStatus =
when (legalHoldStatus) {
Conversation.LegalHoldStatus.ENABLED -> LegalHoldStatus.ENABLED
Conversation.LegalHoldStatus.DISABLED -> LegalHoldStatus.DISABLED
else -> LegalHoldStatus.UNKNOWN
}

private fun packText(
readableContent: MessageContent.Text,
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Text {
val mentions = readableContent.mentions.map { messageMentionMapper.fromModelToProto(it) }
val quote = readableContent.quotedMessageReference?.let {
Quote(it.quotedMessageId, it.quotedMessageSha256?.let { hash -> ByteArr(hash) })
}
val protoLegalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
return GenericMessage.Content.Text(
Text(
content = readableContent.value,
mentions = mentions,
quote = quote,
expectsReadConfirmation = expectsReadConfirmation
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = protoLegalHoldStatus
)
)
}
Expand Down Expand Up @@ -509,11 +560,30 @@ class ProtoContentMapperImpl(
}
}

private fun packAsset(readableContent: MessageContent.Asset, expectsReadConfirmation: Boolean): GenericMessage.Content.Asset {
private fun packKnock(
readableContent: MessageContent.Knock,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Knock {
val protoLegalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
return GenericMessage.Content.Knock(
Knock(
hotKnock = readableContent.hotKnock,
legalHoldStatus = protoLegalHoldStatus
)
)
}

private fun packAsset(
readableContent: MessageContent.Asset,
expectsReadConfirmation: Boolean,
legalHoldStatus: Conversation.LegalHoldStatus
): GenericMessage.Content.Asset {
val protoLegalHoldStatus = toProtoLegalHoldStatus(legalHoldStatus)
return GenericMessage.Content.Asset(
asset = assetMapper.fromAssetContentToProtoAssetMessage(
readableContent,
expectsReadConfirmation
expectsReadConfirmation,
protoLegalHoldStatus
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ fun WebEventContent.toMigratedMessage(selfUserDomain: String): MigratedMessage?
MessageContent.Text(
data.text,
),
data.expectsReadConfirmation ?: false
data.expectsReadConfirmation ?: false,
legalHoldStatus = if (data.legalHoldStatus == 2) Conversation.LegalHoldStatus.ENABLED
else Conversation.LegalHoldStatus.DISABLED
),
encryptedProto = null,
null,
Expand Down Expand Up @@ -110,7 +112,9 @@ fun WebEventContent.toMigratedMessage(selfUserDomain: String): MigratedMessage?
downloadStatus = Message.DownloadStatus.NOT_DOWNLOADED
),
),
data.expectsReadConfirmation
data.expectsReadConfirmation,
legalHoldStatus = if (data.legalHoldStatus == 2) Conversation.LegalHoldStatus.ENABLED
else Conversation.LegalHoldStatus.DISABLED
),
encryptedProto = null,
null,
Expand Down Expand Up @@ -172,7 +176,7 @@ fun WebConversationContent.toConversation(selfUserId: UserId): Conversation? {
archivedDateTime = conversationArchivedTimestamp,
mlsVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED,
proteusVerificationStatus = Conversation.VerificationStatus.NOT_VERIFIED,
legalHoldStatus = if (legalHoldStatus == 1) Conversation.LegalHoldStatus.ENABLED
legalHoldStatus = if (legalHoldStatus == 2) Conversation.LegalHoldStatus.ENABLED
else Conversation.LegalHoldStatus.DISABLED
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package com.wire.kalium.logic.feature.message

import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.data.client.MLSClientProvider
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.id.GroupID
import com.wire.kalium.logic.data.id.IdMapper
import com.wire.kalium.logic.data.message.Message
Expand Down Expand Up @@ -59,11 +60,18 @@ class MLSMessageCreatorImpl(
else -> false
}

// TODO(legalhold) - Get correct legal hold status
val legalHoldStatus = when (message) {
is Message.Regular -> Conversation.LegalHoldStatus.DISABLED
else -> Conversation.LegalHoldStatus.DISABLED
}

val content = protoContentMapper.encodeToProtobuf(
protoContent = ProtoContent.Readable(
messageUid = message.id,
messageContent = message.content,
expectsReadConfirmation = expectsReadConfirmation
expectsReadConfirmation = expectsReadConfirmation,
legalHoldStatus = legalHoldStatus
)
)
wrapMLSRequest { MLSMessageApi.Message(mlsClient.encryptMessage(idMapper.toCryptoModel(groupId), content.data)) }
Expand Down
Loading

0 comments on commit edee554

Please sign in to comment.