mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-12-18 07:11:58 +03:00
Merge pull request #8170 from vector-im/feature/fre/apply_push_rules_after_decryption
Reapply push rules on the decrypted event source (PSG-1146)
This commit is contained in:
commit
39c702f41b
7 changed files with 122 additions and 16 deletions
1
changelog.d/8170.bugfix
Normal file
1
changelog.d/8170.bugfix
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Reapply local push rules after event decryption
|
|
@ -39,8 +39,17 @@ class EventMatchCondition(
|
||||||
override fun technicalDescription() = "'$key' matches '$pattern'"
|
override fun technicalDescription() = "'$key' matches '$pattern'"
|
||||||
|
|
||||||
fun isSatisfied(event: Event): Boolean {
|
fun isSatisfied(event: Event): Boolean {
|
||||||
// TODO encrypted events?
|
val rawJson: Map<*, *> = (MoshiProvider.providesMoshi().adapter(Event::class.java).toJsonValue(event) as? Map<*, *>)
|
||||||
val rawJson = MoshiProvider.providesMoshi().adapter(Event::class.java).toJsonValue(event) as? Map<*, *>
|
?.let { rawJson ->
|
||||||
|
val decryptedRawJson = event.mxDecryptionResult?.payload
|
||||||
|
if (decryptedRawJson != null) {
|
||||||
|
rawJson
|
||||||
|
.toMutableMap()
|
||||||
|
.apply { putAll(decryptedRawJson) }
|
||||||
|
} else {
|
||||||
|
rawJson
|
||||||
|
}
|
||||||
|
}
|
||||||
?: return false
|
?: return false
|
||||||
val value = extractField(rawJson, key) ?: return false
|
val value = extractField(rawJson, key) ?: return false
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,10 @@ internal class EventDecryptor @Inject constructor(
|
||||||
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
|
* @param timeline the id of the timeline where the event is decrypted. It is used to prevent replay attack.
|
||||||
*/
|
*/
|
||||||
suspend fun decryptEventAndSaveResult(event: Event, timeline: String) {
|
suspend fun decryptEventAndSaveResult(event: Event, timeline: String) {
|
||||||
tryOrNull(message = "Unable to decrypt the event") {
|
// event is not encrypted or already decrypted
|
||||||
|
if (event.getClearType() != EventType.ENCRYPTED) return
|
||||||
|
|
||||||
|
tryOrNull(message = "decryptEventAndSaveResult | Unable to decrypt the event") {
|
||||||
decryptEvent(event, timeline)
|
decryptEvent(event, timeline)
|
||||||
}
|
}
|
||||||
?.let { result ->
|
?.let { result ->
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.session.events.model.isInvitation
|
||||||
import org.matrix.android.sdk.api.session.pushrules.PushEvents
|
import org.matrix.android.sdk.api.session.pushrules.PushEvents
|
||||||
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
|
import org.matrix.android.sdk.api.session.pushrules.rest.PushRule
|
||||||
import org.matrix.android.sdk.api.session.sync.model.RoomsSyncResponse
|
import org.matrix.android.sdk.api.session.sync.model.RoomsSyncResponse
|
||||||
|
import org.matrix.android.sdk.internal.crypto.EventDecryptor
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -36,21 +37,22 @@ internal interface ProcessEventForPushTask : Task<ProcessEventForPushTask.Params
|
||||||
internal class DefaultProcessEventForPushTask @Inject constructor(
|
internal class DefaultProcessEventForPushTask @Inject constructor(
|
||||||
private val defaultPushRuleService: DefaultPushRuleService,
|
private val defaultPushRuleService: DefaultPushRuleService,
|
||||||
private val pushRuleFinder: PushRuleFinder,
|
private val pushRuleFinder: PushRuleFinder,
|
||||||
@UserId private val userId: String
|
@UserId private val userId: String,
|
||||||
|
private val eventDecryptor: EventDecryptor,
|
||||||
) : ProcessEventForPushTask {
|
) : ProcessEventForPushTask {
|
||||||
|
|
||||||
override suspend fun execute(params: ProcessEventForPushTask.Params) {
|
override suspend fun execute(params: ProcessEventForPushTask.Params) {
|
||||||
val newJoinEvents = params.syncResponse.join
|
val newJoinEvents = params.syncResponse.join
|
||||||
.mapNotNull { (key, value) ->
|
.mapNotNull { (key, value) ->
|
||||||
value.timeline?.events?.mapNotNull {
|
value.timeline?.events?.mapNotNull {
|
||||||
it.takeIf { !it.isInvitation() }?.copy(roomId = key)
|
it.takeIf { !it.isInvitation() }?.copyAll(roomId = key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|
||||||
val inviteEvents = params.syncResponse.invite
|
val inviteEvents = params.syncResponse.invite
|
||||||
.mapNotNull { (key, value) ->
|
.mapNotNull { (key, value) ->
|
||||||
value.inviteState?.events?.map { it.copy(roomId = key) }
|
value.inviteState?.events?.map { it.copyAll(roomId = key) }
|
||||||
}
|
}
|
||||||
.flatten()
|
.flatten()
|
||||||
|
|
||||||
|
|
|
@ -437,6 +437,10 @@ internal class RoomSyncHandler @Inject constructor(
|
||||||
if (event.isEncrypted() && !isInitialSync) {
|
if (event.isEncrypted() && !isInitialSync) {
|
||||||
try {
|
try {
|
||||||
decryptIfNeeded(event, roomId)
|
decryptIfNeeded(event, roomId)
|
||||||
|
// share the decryption result with the rawEvent because the decryption is done on a copy containing the roomId, see previous comment
|
||||||
|
rawEvent.mxDecryptionResult = event.mxDecryptionResult
|
||||||
|
rawEvent.mCryptoError = event.mCryptoError
|
||||||
|
rawEvent.mCryptoErrorReason = event.mCryptoErrorReason
|
||||||
} catch (e: InterruptedException) {
|
} catch (e: InterruptedException) {
|
||||||
Timber.i("Decryption got interrupted")
|
Timber.i("Decryption got interrupted")
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,11 @@ import org.junit.Assert.assertFalse
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.android.sdk.MatrixTest
|
import org.matrix.android.sdk.MatrixTest
|
||||||
|
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
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.content.EncryptedEventContent
|
||||||
import org.matrix.android.sdk.api.session.events.model.toContent
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
import org.matrix.android.sdk.api.session.room.Room
|
import org.matrix.android.sdk.api.session.room.Room
|
||||||
import org.matrix.android.sdk.api.session.room.members.MembershipService
|
import org.matrix.android.sdk.api.session.room.members.MembershipService
|
||||||
|
@ -38,15 +42,40 @@ class PushRulesConditionTest : MatrixTest {
|
||||||
* Test EventMatchCondition
|
* Test EventMatchCondition
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
||||||
|
private fun createFakeEncryptedEvent() = Event(
|
||||||
|
type = EventType.ENCRYPTED,
|
||||||
|
eventId = "mx0",
|
||||||
|
roomId = "!fakeRoom",
|
||||||
|
content = EncryptedEventContent(
|
||||||
|
algorithm = MXCRYPTO_ALGORITHM_MEGOLM,
|
||||||
|
ciphertext = "AwgBEpACQEKOkd4Gp0+gSXG4M+btcrnPgsF23xs/lUmS2I4YjmqF...",
|
||||||
|
sessionId = "TO2G4u2HlnhtbIJk",
|
||||||
|
senderKey = "5e3EIqg3JfooZnLQ2qHIcBarbassQ4qXblai0",
|
||||||
|
deviceId = "FAKEE"
|
||||||
|
).toContent()
|
||||||
|
)
|
||||||
|
|
||||||
private fun createSimpleTextEvent(text: String): Event {
|
private fun createSimpleTextEvent(text: String): Event {
|
||||||
return Event(
|
return Event(
|
||||||
type = "m.room.message",
|
type = EventType.MESSAGE,
|
||||||
eventId = "mx0",
|
eventId = "mx0",
|
||||||
content = MessageTextContent("m.text", text).toContent(),
|
content = MessageTextContent("m.text", text).toContent(),
|
||||||
originServerTs = 0
|
originServerTs = 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createSimpleTextEventEncrypted(text: String): Event {
|
||||||
|
return createFakeEncryptedEvent().apply {
|
||||||
|
mxDecryptionResult = OlmDecryptionResult(
|
||||||
|
payload = mapOf(
|
||||||
|
"type" to EventType.MESSAGE,
|
||||||
|
"content" to MessageTextContent("m.text", text).toContent(),
|
||||||
|
),
|
||||||
|
senderKey = "the_real_sender_key",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_eventmatch_type_condition() {
|
fun test_eventmatch_type_condition() {
|
||||||
val condition = EventMatchCondition("type", "m.room.message")
|
val condition = EventMatchCondition("type", "m.room.message")
|
||||||
|
@ -70,6 +99,26 @@ class PushRulesConditionTest : MatrixTest {
|
||||||
assertFalse(condition.isSatisfied(simpleRoomMemberEvent))
|
assertFalse(condition.isSatisfied(simpleRoomMemberEvent))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_decrypted_eventmatch_type_condition() {
|
||||||
|
val condition = EventMatchCondition("type", "m.room.message")
|
||||||
|
|
||||||
|
val simpleDecryptedTextEvent = createSimpleTextEventEncrypted("Yo wtf?")
|
||||||
|
|
||||||
|
val encryptedDummyEvent = createFakeEncryptedEvent().apply {
|
||||||
|
mxDecryptionResult = OlmDecryptionResult(
|
||||||
|
payload = mapOf(
|
||||||
|
"type" to EventType.DUMMY,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val encryptedEvent = createFakeEncryptedEvent()
|
||||||
|
|
||||||
|
assert(condition.isSatisfied(simpleDecryptedTextEvent))
|
||||||
|
assertFalse(condition.isSatisfied(encryptedDummyEvent))
|
||||||
|
assertFalse(condition.isSatisfied(encryptedEvent))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_eventmatch_path_condition() {
|
fun test_eventmatch_path_condition() {
|
||||||
val condition = EventMatchCondition("content.msgtype", "m.text")
|
val condition = EventMatchCondition("content.msgtype", "m.text")
|
||||||
|
@ -125,6 +174,22 @@ class PushRulesConditionTest : MatrixTest {
|
||||||
assert(condition.isSatisfied(createSimpleTextEvent("BEN")))
|
assert(condition.isSatisfied(createSimpleTextEvent("BEN")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_encrypted_eventmatch_words_only_condition() {
|
||||||
|
val condition = EventMatchCondition("content.body", "ben")
|
||||||
|
|
||||||
|
assertFalse(condition.isSatisfied(createSimpleTextEventEncrypted("benoit")))
|
||||||
|
assertFalse(condition.isSatisfied(createSimpleTextEventEncrypted("Hello benoit")))
|
||||||
|
assertFalse(condition.isSatisfied(createSimpleTextEventEncrypted("superben")))
|
||||||
|
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("ben")))
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("hello ben")))
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("ben is there")))
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("hello ben!")))
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("hello Ben!")))
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("BEN")))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_eventmatch_at_room_condition() {
|
fun test_eventmatch_at_room_condition() {
|
||||||
val condition = EventMatchCondition("content.body", "@room")
|
val condition = EventMatchCondition("content.body", "@room")
|
||||||
|
@ -140,6 +205,21 @@ class PushRulesConditionTest : MatrixTest {
|
||||||
assert(condition.isSatisfied(createSimpleTextEvent("Don't ping @room!")))
|
assert(condition.isSatisfied(createSimpleTextEvent("Don't ping @room!")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_encrypted_eventmatch_at_room_condition() {
|
||||||
|
val condition = EventMatchCondition("content.body", "@room")
|
||||||
|
|
||||||
|
assertFalse(condition.isSatisfied(createSimpleTextEventEncrypted("@roomba")))
|
||||||
|
assertFalse(condition.isSatisfied(createSimpleTextEventEncrypted("room benoit")))
|
||||||
|
assertFalse(condition.isSatisfied(createSimpleTextEventEncrypted("abc@roomba")))
|
||||||
|
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("@room")))
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("@room, ben")))
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("@ROOM")))
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("Use:@room")))
|
||||||
|
assert(condition.isSatisfied(createSimpleTextEventEncrypted("Don't ping @room!")))
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_notice_condition() {
|
fun test_notice_condition() {
|
||||||
val conditionEqual = EventMatchCondition("content.msgtype", "m.notice")
|
val conditionEqual = EventMatchCondition("content.msgtype", "m.notice")
|
||||||
|
@ -155,6 +235,17 @@ class PushRulesConditionTest : MatrixTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_eventmatch_encrypted_type_condition() {
|
||||||
|
val condition = EventMatchCondition("type", "m.room.encrypted")
|
||||||
|
|
||||||
|
val simpleDecryptedTextEvent = createSimpleTextEventEncrypted("Yo wtf?")
|
||||||
|
val encryptedEvent = createFakeEncryptedEvent()
|
||||||
|
|
||||||
|
assertFalse(condition.isSatisfied(simpleDecryptedTextEvent))
|
||||||
|
assert(condition.isSatisfied(encryptedEvent))
|
||||||
|
}
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* Test RoomMemberCountCondition
|
* Test RoomMemberCountCondition
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
|
@ -66,9 +66,6 @@ class NotifiableEventResolver @Inject constructor(
|
||||||
private val buildMeta: BuildMeta,
|
private val buildMeta: BuildMeta,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val nonEncryptedNotifiableEventTypes: List<String> =
|
|
||||||
listOf(EventType.MESSAGE) + EventType.POLL_START.values + EventType.POLL_END.values + EventType.STATE_ROOM_BEACON_INFO.values
|
|
||||||
|
|
||||||
suspend fun resolveEvent(event: Event, session: Session, isNoisy: Boolean): NotifiableEvent? {
|
suspend fun resolveEvent(event: Event, session: Session, isNoisy: Boolean): NotifiableEvent? {
|
||||||
val roomID = event.roomId ?: return null
|
val roomID = event.roomId ?: return null
|
||||||
val eventId = event.eventId ?: return null
|
val eventId = event.eventId ?: return null
|
||||||
|
@ -76,9 +73,8 @@ class NotifiableEventResolver @Inject constructor(
|
||||||
return resolveStateRoomEvent(event, session, canBeReplaced = false, isNoisy = isNoisy)
|
return resolveStateRoomEvent(event, session, canBeReplaced = false, isNoisy = isNoisy)
|
||||||
}
|
}
|
||||||
val timelineEvent = session.getRoom(roomID)?.getTimelineEvent(eventId) ?: return null
|
val timelineEvent = session.getRoom(roomID)?.getTimelineEvent(eventId) ?: return null
|
||||||
return when (event.getClearType()) {
|
return when {
|
||||||
in nonEncryptedNotifiableEventTypes,
|
event.supportsNotification() || event.type == EventType.ENCRYPTED -> {
|
||||||
EventType.ENCRYPTED -> {
|
|
||||||
resolveMessageEvent(timelineEvent, session, canBeReplaced = false, isNoisy = isNoisy)
|
resolveMessageEvent(timelineEvent, session, canBeReplaced = false, isNoisy = isNoisy)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -163,8 +159,8 @@ class NotifiableEventResolver @Inject constructor(
|
||||||
} else {
|
} else {
|
||||||
event.attemptToDecryptIfNeeded(session)
|
event.attemptToDecryptIfNeeded(session)
|
||||||
// only convert encrypted messages to NotifiableMessageEvents
|
// only convert encrypted messages to NotifiableMessageEvents
|
||||||
when (event.root.getClearType()) {
|
when {
|
||||||
in nonEncryptedNotifiableEventTypes -> {
|
event.root.supportsNotification() -> {
|
||||||
val body = displayableEventFormatter.format(event, isDm = room.roomSummary()?.isDirect.orFalse(), appendAuthor = false).toString()
|
val body = displayableEventFormatter.format(event, isDm = room.roomSummary()?.isDirect.orFalse(), appendAuthor = false).toString()
|
||||||
val roomName = room.roomSummary()?.displayName ?: ""
|
val roomName = room.roomSummary()?.displayName ?: ""
|
||||||
val senderDisplayName = event.senderInfo.disambiguatedDisplayName
|
val senderDisplayName = event.senderInfo.disambiguatedDisplayName
|
||||||
|
|
Loading…
Reference in a new issue