Replace usage of new UnableToDecryptEventEntity by usage of existing EventInsertEntity

This commit is contained in:
Maxime NATUREL 2023-01-06 15:19:33 +01:00
parent fd58875c31
commit 8b051c5b86
21 changed files with 252 additions and 603 deletions

View file

@ -62,9 +62,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.CreateUnableToDecryptEventEntityTask
import org.matrix.android.sdk.internal.crypto.tasks.DefaultClaimOneTimeKeysForUsersDevice
import org.matrix.android.sdk.internal.crypto.tasks.DefaultCreateUnableToDecryptEventEntityTask
import org.matrix.android.sdk.internal.crypto.tasks.DefaultDeleteDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.DefaultDownloadKeysForUsers
import org.matrix.android.sdk.internal.crypto.tasks.DefaultEncryptEventTask
@ -255,7 +253,4 @@ internal abstract class CryptoModule {
@Binds
abstract fun bindSendEventTask(task: DefaultSendEventTask): SendEventTask
@Binds
abstract fun bindCreateUnableToDecryptEventEntityTask(task: DefaultCreateUnableToDecryptEventEntityTask): CreateUnableToDecryptEventEntityTask
}

View file

@ -38,7 +38,6 @@ import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.tasks.CreateUnableToDecryptEventEntityTask
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.extensions.foldToCallback
import org.matrix.android.sdk.internal.session.SessionScope
@ -61,7 +60,6 @@ internal class EventDecryptor @Inject constructor(
private val deviceListManager: DeviceListManager,
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val cryptoStore: IMXCryptoStore,
private val createUnableToDecryptEventEntityTask: CreateUnableToDecryptEventEntityTask,
) {
/**
@ -138,7 +136,6 @@ internal class EventDecryptor @Inject constructor(
val eventContent = event.content
if (eventContent == null) {
Timber.tag(loggerTag.value).e("decryptEvent : empty event content")
createUnableToDecryptEventEntity(event.eventId)
throw MXCryptoError.Base(MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE, MXCryptoError.BAD_ENCRYPTED_MESSAGE_REASON)
} else if (event.isRedacted()) {
// we shouldn't attempt to decrypt a redacted event because the content is cleared and decryption will fail because of null algorithm
@ -156,7 +153,6 @@ internal class EventDecryptor @Inject constructor(
if (alg == null) {
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, algorithm)
Timber.tag(loggerTag.value).e("decryptEvent() : $reason")
createUnableToDecryptEventEntity(event.eventId)
throw MXCryptoError.Base(MXCryptoError.ErrorType.UNABLE_TO_DECRYPT, reason)
} else {
try {
@ -175,20 +171,12 @@ internal class EventDecryptor @Inject constructor(
}
}
}
createUnableToDecryptEventEntity(event.eventId)
throw mxCryptoError
}
}
}
}
private suspend fun createUnableToDecryptEventEntity(eventId: String?) {
eventId?.let {
val params = CreateUnableToDecryptEventEntityTask.Params(eventId = it)
createUnableToDecryptEventEntityTask.execute(params)
}
}
private suspend fun markOlmSessionForUnwedging(senderId: String, senderKey: String) {
wedgedMutex.withLock {
val info = WedgedDeviceInfo(senderId, senderKey)

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto.tasks
import io.realm.RealmConfiguration
import org.matrix.android.sdk.internal.crypto.store.db.doRealmTransactionAsync
import org.matrix.android.sdk.internal.database.model.UnableToDecryptEventEntity
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.task.Task
import javax.inject.Inject
/**
* This task create a dedicated entity for UTD events so that it can be processed later.
*/
internal interface CreateUnableToDecryptEventEntityTask : Task<CreateUnableToDecryptEventEntityTask.Params, Unit> {
data class Params(
val eventId: String,
)
}
internal class DefaultCreateUnableToDecryptEventEntityTask @Inject constructor(
@SessionDatabase val realmConfiguration: RealmConfiguration,
) : CreateUnableToDecryptEventEntityTask {
override suspend fun execute(params: CreateUnableToDecryptEventEntityTask.Params) {
val utdEventEntity = UnableToDecryptEventEntity(eventId = params.eventId)
doRealmTransactionAsync(realmConfiguration) { realm ->
realm.insert(utdEventEntity)
}
}
}

View file

@ -22,6 +22,7 @@ import io.realm.RealmResults
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventInsertEntity
@ -34,7 +35,7 @@ import javax.inject.Inject
internal class EventInsertLiveObserver @Inject constructor(
@SessionDatabase realmConfiguration: RealmConfiguration,
private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>
private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>,
) :
RealmLiveEntityObserver<EventInsertEntity>(realmConfiguration) {
@ -51,6 +52,7 @@ internal class EventInsertLiveObserver @Inject constructor(
return@withLock
}
val idsToDeleteAfterProcess = ArrayList<String>()
val idsOfEncryptedEvents = ArrayList<String>()
val filteredEvents = ArrayList<EventInsertEntity>(results.size)
Timber.v("EventInsertEntity updated with ${results.size} results in db")
results.forEach {
@ -64,8 +66,12 @@ internal class EventInsertLiveObserver @Inject constructor(
}
filteredEvents.add(copiedEvent)
}
if (it.eventType == EventType.ENCRYPTED) {
idsOfEncryptedEvents.add(it.eventId)
} else {
idsToDeleteAfterProcess.add(it.eventId)
}
}
awaitTransaction(realmConfiguration) { realm ->
Timber.v("##Transaction: There are ${filteredEvents.size} events to process ")
filteredEvents.forEach { eventInsert ->
@ -86,6 +92,12 @@ internal class EventInsertLiveObserver @Inject constructor(
.`in`(EventInsertEntityFields.EVENT_ID, idsToDeleteAfterProcess.toTypedArray())
.findAll()
.deleteAllFromRealm()
// make the encrypted events not processable: they will be processed again after decryption
realm.where(EventInsertEntity::class.java)
.`in`(EventInsertEntityFields.EVENT_ID, idsOfEncryptedEvents.toTypedArray())
.findAll()
.forEach { it.canBeProcessed = false }
}
processors.forEach { it.onPostProcess() }
}

View file

@ -1,83 +0,0 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database
import com.zhuinden.monarchy.Monarchy
import io.realm.RealmConfiguration
import io.realm.RealmResults
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.matrix.android.sdk.internal.database.mapper.asDomain
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.UnableToDecryptEventEntity
import org.matrix.android.sdk.internal.database.model.UnableToDecryptEventEntityFields
import org.matrix.android.sdk.internal.database.query.where
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.UnableToDecryptEventLiveProcessor
import timber.log.Timber
import javax.inject.Inject
internal class UnableToDecryptEventLiveObserver @Inject constructor(
@SessionDatabase realmConfiguration: RealmConfiguration,
private val processors: Set<@JvmSuppressWildcards UnableToDecryptEventLiveProcessor>
) :
RealmLiveEntityObserver<UnableToDecryptEventEntity>(realmConfiguration) {
private val lock = Mutex()
override val query = Monarchy.Query {
it.where(UnableToDecryptEventEntity::class.java)
}
override fun onChange(results: RealmResults<UnableToDecryptEventEntity>) {
observerScope.launch {
lock.withLock {
if (!results.isLoaded || results.isEmpty()) {
return@withLock
}
val copiedEvents = ArrayList<UnableToDecryptEventEntity>(results.size)
Timber.v("UnableToDecryptEventEntity updated with ${results.size} results in db")
results.forEach {
// don't use copy from realm over there
val copiedEvent = UnableToDecryptEventEntity(eventId = it.eventId)
copiedEvents.add(copiedEvent)
}
awaitTransaction(realmConfiguration) { realm ->
Timber.v("##Transaction: There are ${copiedEvents.size} events to process ")
copiedEvents.forEach { utdEvent ->
val eventId = utdEvent.eventId
val event = EventEntity.where(realm, eventId).findFirst()
if (event == null) {
Timber.v("Event $eventId not found")
return@forEach
}
val domainEvent = event.asDomain()
processors.forEach {
it.process(realm, domainEvent)
}
}
realm.where(UnableToDecryptEventEntity::class.java)
.`in`(UnableToDecryptEventEntityFields.EVENT_ID, copiedEvents.map { it.eventId }.toTypedArray())
.findAll()
.deleteAllFromRealm()
}
processors.forEach { it.onPostProcess() }
}
}
}
}

View file

@ -18,21 +18,15 @@ package org.matrix.android.sdk.internal.database.migration
import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.UnableToDecryptEventEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator
/**
* Adding a new field in poll summary to keep track of non decrypted related events.
* Adding a new entity UnableToDecryptEventEntity.
*/
internal class MigrateSessionTo048(realm: DynamicRealm) : RealmMigrator(realm, 47) {
internal class MigrateSessionTo048(realm: DynamicRealm) : RealmMigrator(realm, 48) {
override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("PollResponseAggregatedSummaryEntity")
?.addRealmListField(PollResponseAggregatedSummaryEntityFields.ENCRYPTED_RELATED_EVENT_IDS.`$`, String::class.java)
realm.schema.create("UnableToDecryptEventEntity")
?.addField(UnableToDecryptEventEntityFields.EVENT_ID, String::class.java)
?.setRequired(UnableToDecryptEventEntityFields.EVENT_ID, true)
}
}

View file

@ -27,7 +27,7 @@ internal open class EventInsertEntity(
var eventType: String = "",
/**
* This flag will be used to filter EventInsertEntity in EventInsertLiveObserver.
* Currently it's set to false when the event content is encrypted.
* Currently it's set to false after an event with encrypted content has been processed.
*/
var canBeProcessed: Boolean = true
) : RealmObject() {

View file

@ -73,7 +73,6 @@ import org.matrix.android.sdk.internal.database.model.threads.ThreadSummaryEntit
UserPresenceEntity::class,
ThreadSummaryEntity::class,
ThreadListPageEntity::class,
UnableToDecryptEventEntity::class,
]
)
internal class SessionRealmModule

View file

@ -20,7 +20,6 @@ import io.realm.Realm
import io.realm.RealmList
import io.realm.RealmQuery
import io.realm.kotlin.where
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.internal.database.model.EventEntity
import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.EventInsertEntity
@ -32,10 +31,9 @@ internal fun EventEntity.copyToRealmOrIgnore(realm: Realm, insertType: EventInse
.equalTo(EventEntityFields.ROOM_ID, roomId)
.findFirst()
return if (eventEntity == null) {
val canBeProcessed = type != EventType.ENCRYPTED || decryptionResultJson != null
val insertEntity = EventInsertEntity(eventId = eventId, eventType = type, canBeProcessed = canBeProcessed).apply {
this.insertType = insertType
}
val insertEntity = EventInsertEntity(eventId = eventId, eventType = type, canBeProcessed = true)
insertEntity.insertType = insertType
realm.insert(insertEntity)
// copy this event entity and return it
realm.copyToRealm(this)

View file

@ -50,7 +50,6 @@ import org.matrix.android.sdk.internal.crypto.tasks.RedactEventTask
import org.matrix.android.sdk.internal.database.EventInsertLiveObserver
import org.matrix.android.sdk.internal.database.RealmSessionProvider
import org.matrix.android.sdk.internal.database.SessionRealmConfigurationFactory
import org.matrix.android.sdk.internal.database.UnableToDecryptEventLiveObserver
import org.matrix.android.sdk.internal.di.Authenticated
import org.matrix.android.sdk.internal.di.CacheDirectory
import org.matrix.android.sdk.internal.di.DeviceId
@ -85,7 +84,6 @@ import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService
import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService
import org.matrix.android.sdk.internal.session.room.EncryptedEventRelationsAggregationProcessor
import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor
import org.matrix.android.sdk.internal.session.room.aggregation.poll.DefaultPollAggregationProcessor
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor
@ -348,10 +346,6 @@ internal abstract class SessionModule {
@IntoSet
abstract fun bindEventInsertObserver(observer: EventInsertLiveObserver): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindUnableToDecryptEventObserver(observer: UnableToDecryptEventLiveObserver): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindIntegrationManager(manager: IntegrationManager): SessionLifecycleObserver
@ -411,8 +405,4 @@ internal abstract class SessionModule {
@Binds
abstract fun bindPollAggregationProcessor(processor: DefaultPollAggregationProcessor): PollAggregationProcessor
@Binds
@IntoSet
abstract fun bindEncryptedEventRelationsAggregationProcessor(processor: EncryptedEventRelationsAggregationProcessor): UnableToDecryptEventLiveProcessor
}

View file

@ -1,39 +0,0 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session
import io.realm.Realm
import org.matrix.android.sdk.api.session.events.model.Event
internal interface UnableToDecryptEventLiveProcessor {
/**
* Process the given event.
* @param realm a realm instance
* @param event the event to be processed
* @return true if it has been processed, false if it was ignored.
*/
fun process(realm: Realm, event: Event): Boolean
/**
* Called after transaction.
* Maybe you prefer to process the events outside of the realm transaction.
*/
suspend fun onPostProcess() {
// Noop by default
}
}

View file

@ -1,95 +0,0 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.room
import io.realm.Realm
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.internal.session.UnableToDecryptEventLiveProcessor
import org.matrix.android.sdk.internal.session.room.aggregation.utd.EncryptedReferenceAggregationProcessor
import timber.log.Timber
import javax.inject.Inject
internal class EncryptedEventRelationsAggregationProcessor @Inject constructor(
private val encryptedReferenceAggregationProcessor: EncryptedReferenceAggregationProcessor,
) : UnableToDecryptEventLiveProcessor {
override fun process(realm: Realm, event: Event): Boolean {
val roomId = event.roomId
return if (roomId == null) {
Timber.w("Event has no room id ${event.eventId}")
false
} else {
val isLocalEcho = LocalEcho.isLocalEchoId(event.eventId ?: "")
return when (event.getClearType()) {
EventType.ENCRYPTED -> {
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
processEncryptedContent(
encryptedEventContent = encryptedEventContent,
realm = realm,
event = event,
roomId = roomId,
isLocalEcho = isLocalEcho,
)
}
else -> false
}
}
}
private fun processEncryptedContent(
encryptedEventContent: EncryptedEventContent?,
realm: Realm,
event: Event,
roomId: String,
isLocalEcho: Boolean,
): Boolean {
return when (encryptedEventContent?.relatesTo?.type) {
RelationType.REPLACE -> {
Timber.w("## UTD replace in room $roomId for event ${event.eventId}")
false
}
RelationType.RESPONSE -> {
// can we / should we do we something for UTD response??
Timber.w("## UTD response in room $roomId related to ${encryptedEventContent.relatesTo.eventId}")
false
}
RelationType.REFERENCE -> {
// can we / should we do we something for UTD reference??
Timber.w("## UTD reference in room $roomId related to ${encryptedEventContent.relatesTo.eventId}")
val result = encryptedReferenceAggregationProcessor.handle(
realm = realm,
event = event,
isLocalEcho = isLocalEcho,
relatedEventId = encryptedEventContent.relatesTo.eventId,
)
result
}
RelationType.ANNOTATION -> {
// can we / should we do we something for UTD annotation??
Timber.w("## UTD annotation in room $roomId related to ${encryptedEventContent.relatesTo.eventId}")
false
}
else -> false
}
}
}

View file

@ -23,6 +23,7 @@ import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.LocalEcho
import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.getRelationContent
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel
@ -60,6 +61,7 @@ import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor
import org.matrix.android.sdk.internal.session.room.aggregation.utd.EncryptedReferenceAggregationProcessor
import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
import org.matrix.android.sdk.internal.util.time.Clock
import timber.log.Timber
@ -72,6 +74,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
private val sessionManager: SessionManager,
private val liveLocationAggregationProcessor: LiveLocationAggregationProcessor,
private val pollAggregationProcessor: PollAggregationProcessor,
private val encryptedReferenceAggregationProcessor: EncryptedReferenceAggregationProcessor,
private val editValidator: EventEditValidator,
private val clock: Clock,
) : EventInsertLiveProcessor {
@ -139,6 +142,16 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
Timber.v("###REACTION in room $roomId , reaction eventID ${event.eventId}")
handleReaction(realm, event, roomId, isLocalEcho)
}
EventType.ENCRYPTED -> {
val encryptedEventContent = event.content.toModel<EncryptedEventContent>()
processEncryptedContent(
encryptedEventContent = encryptedEventContent,
realm = realm,
event = event,
roomId = roomId,
isLocalEcho = isLocalEcho,
)
}
EventType.MESSAGE -> {
if (event.unsignedData?.relations?.annotations != null) {
Timber.v("###REACTION Aggregation in room $roomId for event ${event.eventId}")
@ -223,6 +236,36 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
}
}
private fun processEncryptedContent(
encryptedEventContent: EncryptedEventContent?,
realm: Realm,
event: Event,
roomId: String,
isLocalEcho: Boolean,
) {
when (encryptedEventContent?.relatesTo?.type) {
RelationType.REPLACE -> {
Timber.w("## UTD replace in room $roomId for event ${event.eventId}")
}
RelationType.RESPONSE -> {
Timber.w("## UTD response in room $roomId related to ${encryptedEventContent.relatesTo.eventId}")
}
RelationType.REFERENCE -> {
Timber.w("## UTD reference in room $roomId related to ${encryptedEventContent.relatesTo.eventId}")
encryptedReferenceAggregationProcessor.handle(
realm = realm,
event = event,
isLocalEcho = isLocalEcho,
relatedEventId = encryptedEventContent.relatesTo.eventId,
)
}
RelationType.ANNOTATION -> {
Timber.w("## UTD annotation in room $roomId related to ${encryptedEventContent.relatesTo.eventId}")
}
else -> Unit
}
}
// OPT OUT serer aggregation until API mature enough
private val SHOULD_HANDLE_SERVER_AGREGGATION = false // should be true to work with e2e

View file

@ -1,65 +0,0 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto.tasks
import io.mockk.unmockkAll
import io.mockk.verify
import io.realm.RealmModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Test
import org.matrix.android.sdk.internal.database.model.UnableToDecryptEventEntity
import org.matrix.android.sdk.test.fakes.FakeRealm
import org.matrix.android.sdk.test.fakes.FakeRealmConfiguration
@OptIn(ExperimentalCoroutinesApi::class)
internal class DefaultCreateUnableToDecryptEventEntityTaskTest {
private val fakeRealmConfiguration = FakeRealmConfiguration()
private val defaultCreateUnableToDecryptEventEntityTask = DefaultCreateUnableToDecryptEventEntityTask(
realmConfiguration = fakeRealmConfiguration.instance,
)
@After
fun tearDown() {
unmockkAll()
}
@Test
fun `given an event id when execute then insert entity into database`() = runTest {
// Given
val anEventId = "event-id"
val params = CreateUnableToDecryptEventEntityTask.Params(
eventId = anEventId,
)
val fakeRealm = FakeRealm()
fakeRealm.givenExecuteTransactionAsync()
fakeRealmConfiguration.givenGetRealmInstance(fakeRealm.instance)
// When
defaultCreateUnableToDecryptEventEntityTask.execute(params)
// Then
verify {
fakeRealm.instance.insert(match<RealmModel> {
it is UnableToDecryptEventEntity && it.eventId == anEventId
})
}
}
}

View file

@ -1,209 +0,0 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.room
import io.mockk.every
import io.mockk.mockk
import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldBeFalse
import org.junit.Test
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
import org.matrix.android.sdk.test.fakes.FakeRealm
import org.matrix.android.sdk.test.fakes.internal.session.room.aggregation.utd.FakeEncryptedReferenceAggregationProcessor
class EncryptedEventRelationsAggregationProcessorTest {
private val fakeEncryptedReferenceAggregationProcessor = FakeEncryptedReferenceAggregationProcessor()
private val fakeRealm = FakeRealm()
private val encryptedEventRelationsAggregationProcessor = EncryptedEventRelationsAggregationProcessor(
encryptedReferenceAggregationProcessor = fakeEncryptedReferenceAggregationProcessor.instance,
)
@Test
fun `given no room Id when process then result is false`() {
// Given
val anEvent = givenAnEvent(
eventId = "event-id",
roomId = null,
eventType = EventType.ENCRYPTED,
)
// When
val result = encryptedEventRelationsAggregationProcessor.process(
realm = fakeRealm.instance,
event = anEvent,
)
// Then
result.shouldBeFalse()
}
@Test
fun `given an encrypted reference event when process then reference is processed`() {
// Given
val anEvent = givenAnEvent(
eventId = "event-id",
roomId = "room-id",
eventType = EventType.ENCRYPTED,
)
val relatedEventId = "related-event-id"
val encryptedEventContent = givenEncryptedEventContent(
relationType = RelationType.REFERENCE,
relatedEventId = relatedEventId,
)
every { anEvent.content } returns encryptedEventContent.toContent()
val resultOfReferenceProcess = false
fakeEncryptedReferenceAggregationProcessor.givenHandleReturns(resultOfReferenceProcess)
// When
val result = encryptedEventRelationsAggregationProcessor.process(
realm = fakeRealm.instance,
event = anEvent,
)
// Then
result shouldBeEqualTo resultOfReferenceProcess
fakeEncryptedReferenceAggregationProcessor.verifyHandle(
realm = fakeRealm.instance,
event = anEvent,
isLocalEcho = false,
relatedEventId = relatedEventId,
)
}
@Test
fun `given an encrypted replace event when process then result is false`() {
// Given
val anEvent = givenAnEvent(
eventId = "event-id",
roomId = "room-id",
eventType = EventType.ENCRYPTED,
)
val relatedEventId = "related-event-id"
val encryptedEventContent = givenEncryptedEventContent(
relationType = RelationType.REPLACE,
relatedEventId = relatedEventId,
)
every { anEvent.content } returns encryptedEventContent.toContent()
// When
val result = encryptedEventRelationsAggregationProcessor.process(
realm = fakeRealm.instance,
event = anEvent,
)
// Then
result.shouldBeFalse()
}
@Test
fun `given an encrypted response event when process then result is false`() {
// Given
val anEvent = givenAnEvent(
eventId = "event-id",
roomId = "room-id",
eventType = EventType.ENCRYPTED,
)
val relatedEventId = "related-event-id"
val encryptedEventContent = givenEncryptedEventContent(
relationType = RelationType.RESPONSE,
relatedEventId = relatedEventId,
)
every { anEvent.content } returns encryptedEventContent.toContent()
// When
val result = encryptedEventRelationsAggregationProcessor.process(
realm = fakeRealm.instance,
event = anEvent,
)
// Then
result.shouldBeFalse()
}
@Test
fun `given an encrypted annotation event when process then result is false`() {
// Given
val anEvent = givenAnEvent(
eventId = "event-id",
roomId = "room-id",
eventType = EventType.ENCRYPTED,
)
val relatedEventId = "related-event-id"
val encryptedEventContent = givenEncryptedEventContent(
relationType = RelationType.ANNOTATION,
relatedEventId = relatedEventId,
)
every { anEvent.content } returns encryptedEventContent.toContent()
// When
val result = encryptedEventRelationsAggregationProcessor.process(
realm = fakeRealm.instance,
event = anEvent,
)
// Then
result.shouldBeFalse()
}
@Test
fun `given a non encrypted event when process then result is false`() {
// Given
val anEvent = givenAnEvent(
eventId = "event-id",
roomId = "room-id",
eventType = EventType.MESSAGE,
)
// When
val result = encryptedEventRelationsAggregationProcessor.process(
realm = fakeRealm.instance,
event = anEvent,
)
// Then
result.shouldBeFalse()
}
private fun givenAnEvent(
eventId: String,
roomId: String?,
eventType: String,
): Event {
return mockk<Event>().also {
every { it.eventId } returns eventId
every { it.roomId } returns roomId
every { it.getClearType() } returns eventType
}
}
private fun givenEncryptedEventContent(relationType: String, relatedEventId: String): EncryptedEventContent {
val relationContent = RelationDefaultContent(
eventId = relatedEventId,
type = relationType,
)
return EncryptedEventContent(
relatesTo = relationContent,
)
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.room
import io.mockk.every
import io.mockk.mockk
import org.junit.Test
import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields
import org.matrix.android.sdk.test.fakes.FakeClock
import org.matrix.android.sdk.test.fakes.FakeRealm
import org.matrix.android.sdk.test.fakes.FakeStateEventDataSource
import org.matrix.android.sdk.test.fakes.givenEqualTo
import org.matrix.android.sdk.test.fakes.givenFindFirst
import org.matrix.android.sdk.test.fakes.internal.FakeEventEditValidator
import org.matrix.android.sdk.test.fakes.internal.FakeLiveLocationAggregationProcessor
import org.matrix.android.sdk.test.fakes.internal.FakePollAggregationProcessor
import org.matrix.android.sdk.test.fakes.internal.FakeSessionManager
import org.matrix.android.sdk.test.fakes.internal.session.room.aggregation.utd.FakeEncryptedReferenceAggregationProcessor
private const val A_ROOM_ID = "room-id"
private const val AN_EVENT_ID = "event-id"
internal class EventRelationsAggregationProcessorTest {
private val fakeStateEventDataSource = FakeStateEventDataSource()
private val fakeSessionManager = FakeSessionManager()
private val fakeLiveLocationAggregationProcessor = FakeLiveLocationAggregationProcessor()
private val fakePollAggregationProcessor = FakePollAggregationProcessor()
private val fakeEncryptedReferenceAggregationProcessor = FakeEncryptedReferenceAggregationProcessor()
private val fakeEventEditValidator = FakeEventEditValidator()
private val fakeClock = FakeClock()
private val fakeRealm = FakeRealm()
private val encryptedEventRelationsAggregationProcessor = EventRelationsAggregationProcessor(
userId = "userId",
stateEventDataSource = fakeStateEventDataSource.instance,
sessionId = "sessionId",
sessionManager = fakeSessionManager.instance,
liveLocationAggregationProcessor = fakeLiveLocationAggregationProcessor.instance,
pollAggregationProcessor = fakePollAggregationProcessor.instance,
encryptedReferenceAggregationProcessor = fakeEncryptedReferenceAggregationProcessor.instance,
editValidator = fakeEventEditValidator.instance,
clock = fakeClock,
)
@Test
fun `given an encrypted reference event when process then reference is processed`() {
// Given
val anEvent = givenAnEvent(
eventId = AN_EVENT_ID,
roomId = A_ROOM_ID,
eventType = EventType.ENCRYPTED,
)
val relatedEventId = "related-event-id"
val encryptedEventContent = givenEncryptedEventContent(
relationType = RelationType.REFERENCE,
relatedEventId = relatedEventId,
)
every { anEvent.content } returns encryptedEventContent.toContent()
val resultOfReferenceProcess = false
fakeEncryptedReferenceAggregationProcessor.givenHandleReturns(resultOfReferenceProcess)
givenEventAnnotationsSummary(roomId = A_ROOM_ID, eventId = AN_EVENT_ID, annotationsSummary = null)
// When
encryptedEventRelationsAggregationProcessor.process(
realm = fakeRealm.instance,
event = anEvent,
)
// Then
fakeEncryptedReferenceAggregationProcessor.verifyHandle(
realm = fakeRealm.instance,
event = anEvent,
isLocalEcho = false,
relatedEventId = relatedEventId,
)
}
private fun givenAnEvent(
eventId: String,
roomId: String?,
eventType: String,
): Event {
return mockk<Event>().also {
every { it.eventId } returns eventId
every { it.roomId } returns roomId
every { it.getClearType() } returns eventType
}
}
private fun givenEncryptedEventContent(relationType: String, relatedEventId: String): EncryptedEventContent {
val relationContent = RelationDefaultContent(
eventId = relatedEventId,
type = relationType,
)
return EncryptedEventContent(
relatesTo = relationContent,
)
}
private fun givenEventAnnotationsSummary(
roomId: String,
eventId: String,
annotationsSummary: EventAnnotationsSummaryEntity?
) {
fakeRealm.givenWhere<EventAnnotationsSummaryEntity>()
.givenEqualTo(EventAnnotationsSummaryEntityFields.ROOM_ID, roomId)
.givenEqualTo(EventAnnotationsSummaryEntityFields.EVENT_ID, eventId)
.givenFindFirst(annotationsSummary)
}
}

View file

@ -23,7 +23,6 @@ import io.mockk.mockk
import io.mockk.runs
import io.mockk.verify
import io.realm.Realm
import io.realm.Realm.Transaction
import io.realm.RealmModel
import io.realm.RealmObject
import io.realm.RealmQuery
@ -43,13 +42,6 @@ internal class FakeRealm {
inline fun <reified T : RealmModel> verifyInsertOrUpdate(crossinline verification: MockKVerificationScope.() -> T) {
verify { instance.insertOrUpdate(verification()) }
}
fun givenExecuteTransactionAsync() {
every { instance.executeTransactionAsync(any()) } answers {
firstArg<Transaction>().execute(instance)
mockk()
}
}
}
inline fun <reified T : RealmModel> RealmQuery<T>.givenFindFirst(

View file

@ -17,7 +17,6 @@
package org.matrix.android.sdk.test.fakes
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.realm.Realm
@ -37,9 +36,4 @@ internal class FakeRealmConfiguration {
secondArg<(Realm) -> T>().invoke(realm)
}
}
fun givenGetRealmInstance(realm: Realm) {
mockkStatic(Realm::class)
every { Realm.getInstance(instance) } returns realm
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
* Copyright (c) 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,14 +14,12 @@
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database.model
package org.matrix.android.sdk.test.fakes.internal
import io.realm.RealmObject
import io.mockk.mockk
import org.matrix.android.sdk.internal.session.room.EventEditValidator
/**
* This class is used to get notification on new UTD events. Since these events cannot be processed
* in EventInsertEntity, we should introduce a dedicated entity for that.
*/
internal open class UnableToDecryptEventEntity(
var eventId: String = "",
) : RealmObject()
internal class FakeEventEditValidator {
val instance: EventEditValidator = mockk()
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.test.fakes.internal
import io.mockk.mockk
import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
internal class FakeLiveLocationAggregationProcessor {
val instance: LiveLocationAggregationProcessor = mockk()
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2023 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.test.fakes.internal
import io.mockk.mockk
import org.matrix.android.sdk.internal.session.room.aggregation.poll.PollAggregationProcessor
internal class FakePollAggregationProcessor {
val instance: PollAggregationProcessor = mockk()
}