Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85602948
D333.1784755652.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
13 KB
Referenced Files
None
Subscribers
None
D333.1784755652.diff
View Options
diff --git a/src/client/notification-handler.cpp b/src/client/notification-handler.cpp
--- a/src/client/notification-handler.cpp
+++ b/src/client/notification-handler.cpp
@@ -42,8 +42,11 @@
PushAction NotificationHandler::handleNotification(const Event &e) const
{
- auto roomId = e.raw().get().contains("room_id")
- ? e.raw().get()["room_id"].template get<std::string>()
+ // https://spec.matrix.org/v1.19/client-server-api/#receiving-notifications
+ // "Clients should process push rules for each incoming event after decrypting them."
+ // room_id is always in the outer cleartext event
+ auto roomId = e.originalJson().get().contains("room_id")
+ ? e.originalJson().get()["room_id"].template get<std::string>()
: std::string();
auto room = m_d->client[&ClientModel::roomList][&RoomListModel::rooms][roomId][lager::lenses::or_default].make().get();
return m_d->pushRulesDesc.get().handle(e, room);
diff --git a/src/client/push-rules-desc.cpp b/src/client/push-rules-desc.cpp
--- a/src/client/push-rules-desc.cpp
+++ b/src/client/push-rules-desc.cpp
@@ -232,21 +232,24 @@
}
if (ruleSetName == "override" || ruleSetName == "underride") {
+ // If the event is not decrypted, use the cleartext event json
+ // (the event can also be unencrypted, which only has cleartext)
+ auto eventJson = e.decrypted() ? e.raw().get() : e.originalJson().get();
return std::all_of(
rule.at("conditions").begin(),
rule.at("conditions").end(),
- [&e, &room](const auto &condition) mutable {
+ [&eventJson, &room](const auto &condition) mutable {
if (condition.is_object()) {
if (condition.at("kind") == "event_property_is") {
auto path = splitPath(condition.at("key"));
- auto valueInEvent = getInJson(e.originalJson().get(), path);
+ auto valueInEvent = getInJson(eventJson, path);
if (!valueInEvent.has_value() || !isNonCompoundCanonicalJsonValue(valueInEvent.value())) {
return false;
}
return valueInEvent.value() == condition.at("value");
} else if (condition.at("kind") == "event_match") {
auto path = splitPath(condition.at("key"));
- auto valueInEvent = getInJson(e.originalJson().get(), path);
+ auto valueInEvent = getInJson(eventJson, path);
if (!valueInEvent.has_value() || !valueInEvent.value().is_string()) {
return false;
}
@@ -255,7 +258,7 @@
return matchGlob(valueStr, patternStr);
} else if (condition.at("kind") == "event_property_contains") {
auto path = splitPath(condition.at("key"));
- auto valueInEvent = getInJson(e.originalJson().get(), path);
+ auto valueInEvent = getInJson(eventJson, path);
if (!valueInEvent.has_value() || !valueInEvent.value().is_array()) {
return false;
}
diff --git a/src/tests/client/notification-handler-test.cpp b/src/tests/client/notification-handler-test.cpp
--- a/src/tests/client/notification-handler-test.cpp
+++ b/src/tests/client/notification-handler-test.cpp
@@ -10,6 +10,7 @@
#include <notification-handler.hpp>
#include <asio-promise-handler.hpp>
#include <factory.hpp>
+#include <room/room-model.hpp>
#include "push-rules-test-util.hpp"
#include "client-test-util.hpp"
@@ -17,6 +18,14 @@
using namespace Kazv;
using namespace Kazv::Factory;
+static Event makePushRulesEvent(const json &pushRulesContent)
+{
+ return makeEvent(
+ withEventContent(pushRulesContent)
+ | withEventType("m.push_rules")
+ );
+}
+
TEST_CASE("NotificationHandler::handleNotification()", "[client][push-rules]")
{
auto pushRulesEvent = makeEvent(
@@ -44,3 +53,143 @@
REQUIRE(res.shouldNotify);
}
+
+TEST_CASE("NotificationHandler resolves room for encrypted and undecryptable events", "[client][push-rules][notification]")
+{
+ auto rulesContent = emptyRulesContent;
+ rulesContent["global"]["override"].push_back(suppressNoticesRule);
+ rulesContent["global"]["override"].push_back(encryptedOneToOneRule);
+ rulesContent["global"]["underride"].push_back(messageRule);
+
+ auto pushRulesEvent = makePushRulesEvent(rulesContent);
+
+ auto room = makeRoom();
+ room.joinedMemberCount = 2;
+
+ auto clientModel = makeClient(
+ withRoom(room)
+ | withAccountData({pushRulesEvent})
+ );
+
+ auto io = boost::asio::io_context();
+ auto ph = AsioPromiseHandler(io.get_executor());
+ auto store = createTestClientStoreFrom(clientModel, ph);
+
+ auto client = Client(
+ store.reader().map([](auto c) { return SdkModel{c}; }),
+ store,
+ std::nullopt
+ );
+
+ auto nh = client.notificationHandler();
+
+ SECTION("unencrypted event finds room and matches .m.rule.message")
+ {
+ auto unencryptedEvt = makeEvent(
+ withEventKV("/room_id"_json_pointer, room.roomId)
+ );
+
+ auto result = nh.handleNotification(unencryptedEvt);
+ REQUIRE(result.shouldNotify);
+ }
+
+ SECTION("encrypted event in one-to-one room matches .m.rule.encrypted_room_one_to_one")
+ {
+ auto encryptedEvt = makeEvent(
+ withEventType("m.room.encrypted")
+ | withEventKV("/room_id"_json_pointer, room.roomId)
+ | withEventKV("/content/session_id"_json_pointer, "some-session-id")
+ );
+
+ auto result = nh.handleNotification(encryptedEvt);
+ REQUIRE(result.shouldNotify);
+ REQUIRE(result.sound.has_value());
+ REQUIRE(result.sound.value() == "default");
+ }
+
+ SECTION("undecryptable event in one-to-one room matches .m.rule.encrypted_room_one_to_one")
+ {
+ auto undecryptableEvt = makeEvent(
+ withEventType("m.room.encrypted")
+ | withEventKV("/room_id"_json_pointer, room.roomId)
+ | withEventKV("/content/session_id"_json_pointer, "some-session-id")
+ );
+ undecryptableEvt = undecryptableEvt.setDecryptedJson(
+ json{
+ {"type", "m.room.message"},
+ {"content", {
+ {"msgtype", "moe.kazv.mxc.cannot.decrypt"},
+ {"body", "**This message cannot be decrypted due to some reason.**"},
+ {"moe.kazv.mxc.error", "some reason"},
+ {"moe.kazv.mxc.errcode", "MOE.KAZV.MXC_DECRYPT_ERROR"},
+ {"moe.kazv.mxc.raw", json(nullptr)},
+ }},
+ },
+ Event::NotDecrypted
+ );
+
+ auto result = nh.handleNotification(undecryptableEvt);
+ REQUIRE(result.shouldNotify);
+ REQUIRE(result.sound.has_value());
+ REQUIRE(result.sound.value() == "default");
+ }
+}
+
+TEST_CASE("AddLocalNotificationsAction adds notifications for encrypted events", "[client][push-rules][room]")
+{
+ auto rulesContent = emptyRulesContent;
+ rulesContent["global"]["underride"].push_back(encryptedRule);
+ auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
+
+ auto encryptedEvt = makeEvent(
+ withEventType("m.room.encrypted")
+ | withEventKV("/content/session_id"_json_pointer, "some-session-id")
+ | withEventSenderId("@someone:example.com")
+ );
+
+ auto room = makeRoom(
+ withRoomEncrypted(true)
+ | withRoomTimeline({encryptedEvt})
+ );
+
+ auto updatedRoom = RoomModel::update(
+ room,
+ AddLocalNotificationsAction{
+ immer::flex_vector<Event>{encryptedEvt},
+ pushRules,
+ "@bob:example.com",
+ }
+ );
+
+ REQUIRE(!updatedRoom.unreadNotificationEventIds.empty());
+ REQUIRE(updatedRoom.unreadNotificationEventIds[0] == encryptedEvt.id());
+}
+
+TEST_CASE("AddLocalNotificationsAction does not add notifications for own events", "[client][push-rules][room]")
+{
+ auto rulesContent = emptyRulesContent;
+ rulesContent["global"]["underride"].push_back(encryptedRule);
+ auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
+
+ auto ownEncryptedEvt = makeEvent(
+ withEventType("m.room.encrypted")
+ | withEventKV("/content/session_id"_json_pointer, "some-session-id")
+ | withEventSenderId("@bob:example.com")
+ );
+
+ auto room = makeRoom(
+ withRoomEncrypted(true)
+ | withRoomTimeline({ownEncryptedEvt})
+ );
+
+ auto updatedRoom = RoomModel::update(
+ room,
+ AddLocalNotificationsAction{
+ immer::flex_vector<Event>{ownEncryptedEvt},
+ pushRules,
+ "@bob:example.com",
+ }
+ );
+
+ REQUIRE(updatedRoom.unreadNotificationEventIds.empty());
+}
diff --git a/src/tests/client/push-rules-desc-test.cpp b/src/tests/client/push-rules-desc-test.cpp
--- a/src/tests/client/push-rules-desc-test.cpp
+++ b/src/tests/client/push-rules-desc-test.cpp
@@ -551,3 +551,105 @@
REQUIRE(PushRulesDesc(makePushRulesEvent(rulesContent)).handle(makeEvent(), room).shouldHighlight);
}
}
+
+TEST_CASE("PushRulesDesc handles encrypted events", "[client][push-rules]")
+{
+ auto rulesContent = emptyRulesContent;
+ rulesContent["global"]["underride"].push_back(encryptedRule);
+ auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
+ auto room = makeRoom();
+
+ auto encryptedEvt = makeEvent(
+ withEventType("m.room.encrypted")
+ | withEventKV("/content/session_id"_json_pointer, "some-session-id")
+ );
+
+ REQUIRE(pushRules.handle(encryptedEvt, room).shouldNotify);
+}
+
+TEST_CASE("PushRulesDesc handles undecryptable events", "[client][push-rules]")
+{
+ auto rulesContent = emptyRulesContent;
+ rulesContent["global"]["underride"].push_back(encryptedRule);
+ auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
+ auto room = makeRoom();
+
+ auto undecryptableEvt = makeEvent(
+ withEventType("m.room.encrypted")
+ | withEventKV("/content/session_id"_json_pointer, "some-session-id")
+ );
+ undecryptableEvt = undecryptableEvt.setDecryptedJson(
+ json{
+ {"type", "m.room.message"},
+ {"content", {
+ {"msgtype", "moe.kazv.mxc.cannot.decrypt"},
+ {"body", "**This message cannot be decrypted due to some reason.**"},
+ {"moe.kazv.mxc.error", "some reason"},
+ {"moe.kazv.mxc.errcode", "MOE.KAZV.MXC_DECRYPT_ERROR"},
+ {"moe.kazv.mxc.raw", json(nullptr)},
+ }},
+ },
+ Event::NotDecrypted
+ );
+
+ REQUIRE(pushRules.handle(undecryptableEvt, room).shouldNotify);
+}
+
+TEST_CASE("PushRulesDesc uses correct type for decrypted vs undecryptable events", "[client][push-rules]")
+{
+ auto rulesContent = emptyRulesContent;
+ auto room = makeRoom();
+
+ SECTION("decrypted event should use decrypted type")
+ {
+ rulesContent["global"]["underride"].push_back(messageRule);
+ auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
+
+ auto decryptedEvt = makeEvent(
+ withEventType("m.room.encrypted")
+ | withEventKV("/content/session_id"_json_pointer, "some-session-id")
+ );
+ decryptedEvt = decryptedEvt.setDecryptedJson(
+ json{
+ {"type", "m.room.message"},
+ {"content", {
+ {"msgtype", "m.text"},
+ {"body", "hello"},
+ }},
+ },
+ Event::Decrypted
+ );
+
+ REQUIRE(decryptedEvt.decrypted());
+ REQUIRE(decryptedEvt.raw().get()["type"] == "m.room.message");
+ REQUIRE(decryptedEvt.originalJson().get()["type"] == "m.room.encrypted");
+ REQUIRE(pushRules.handle(decryptedEvt, room).shouldNotify);
+ }
+
+ SECTION("undecryptable event should use originalJson() with m.room.encrypted type")
+ {
+ rulesContent["global"]["underride"].push_back(encryptedRule);
+ auto pushRules = PushRulesDesc(makePushRulesEvent(rulesContent));
+
+ auto undecryptableEvt = makeEvent(
+ withEventType("m.room.encrypted")
+ | withEventKV("/content/session_id"_json_pointer, "some-session-id")
+ );
+ undecryptableEvt = undecryptableEvt.setDecryptedJson(
+ json{
+ {"type", "m.room.message"},
+ {"content", {
+ {"msgtype", "moe.kazv.mxc.cannot.decrypt"},
+ {"body", "**Cannot decrypt**"},
+ }},
+ },
+ Event::NotDecrypted
+ );
+
+ REQUIRE(!undecryptableEvt.decrypted());
+ REQUIRE(undecryptableEvt.originalJson().get()["type"] == "m.room.encrypted");
+ // Undecryptable events are not decrypted, so they should use
+ // originalJson() with type m.room.encrypted to match .m.rule.encrypted.
+ REQUIRE(pushRules.handle(undecryptableEvt, room).shouldNotify);
+ }
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Jul 22, 2:27 PM (21 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1701929
Default Alt Text
D333.1784755652.diff (13 KB)
Attached To
Mode
D333: Fix undecrytable events not generating notifications
Attached
Detach File
Event Timeline
Log In to Comment