15
15
from typing import Any
16
16
from unittest .mock import patch
17
17
18
+ from parameterized import parameterized
19
+
18
20
from twisted .test .proto_helpers import MemoryReactor
19
21
20
22
from synapse .api .constants import EventContentFields
@@ -48,35 +50,84 @@ def prepare(
48
50
self .requester = create_requester (self .alice )
49
51
50
52
self .room_id = self .helper .create_room_as (
51
- self .alice , room_version = RoomVersions .V9 .identifier , tok = self .token
53
+ # This is deliberately set to V9, because we want to test the logic which
54
+ # handles stringy power levels. Stringy power levels were outlawed in V10.
55
+ self .alice ,
56
+ room_version = RoomVersions .V9 .identifier ,
57
+ tok = self .token ,
52
58
)
53
59
54
60
self .event_creation_handler = self .hs .get_event_creation_handler ()
55
61
56
- def test_action_for_event_by_user_handles_noninteger_power_levels (self ) -> None :
57
- """We should convert floats and strings to integers before passing to Rust.
62
+ @parameterized .expand (
63
+ [
64
+ # The historically-permitted bad values. Alice's notification should be
65
+ # allowed if this threshold is at or below her power level (60)
66
+ ("100" , False ),
67
+ ("0" , True ),
68
+ (12.34 , True ),
69
+ (60.0 , True ),
70
+ (67.89 , False ),
71
+ # Values that int(...) would not successfully cast should be ignored.
72
+ # The room notification level should then default to 50, per the spec, so
73
+ # Alice's notification is allowed.
74
+ (None , True ),
75
+ # We haven't seen `"room": []` or `"room": {}` in the wild (yet), but
76
+ # let's check them for paranoia's sake.
77
+ ([], True ),
78
+ ({}, True ),
79
+ ]
80
+ )
81
+ def test_action_for_event_by_user_handles_noninteger_room_power_levels (
82
+ self , bad_room_level : object , should_permit : bool
83
+ ) -> None :
84
+ """We should convert strings in `room` to integers before passing to Rust.
85
+
86
+ Test this as follows:
87
+ - Create a room as Alice and invite two other users Bob and Charlie.
88
+ - Set PLs so that Alice has PL 60 and `notifications.room` is set to a bad value.
89
+ - Have Alice create a message notifying @room.
90
+ - Evaluate notification actions for that message. This should not raise.
91
+ - Look in the DB to see if that message triggered a highlight for Bob.
92
+
93
+ The test is parameterised with two arguments:
94
+ - the bad power level value for "room", before JSON serisalistion
95
+ - whether Bob should expect the message to be highlighted
58
96
59
97
Reproduces #14060.
60
98
61
99
A lack of validation: the gift that keeps on giving.
62
100
"""
63
-
64
- # Alter the power levels in that room to include stringy and floaty levels.
65
- # We need to suppress the validation logic or else it will reject these dodgy
66
- # values. (Presumably this validation was not always present.)
101
+ # Join another user to the room, so that there is someone to see Alice's
102
+ # @room notification.
103
+ bob = self .register_user ("bob" , "pass" )
104
+ bob_token = self .login (bob , "pass" )
105
+ self .helper .join (self .room_id , bob , tok = bob_token )
106
+
107
+ # Alter the power levels in that room to include the bad @room notification
108
+ # level. We need to suppress
109
+ #
110
+ # - canonicaljson validation, because canonicaljson forbids floats;
111
+ # - the event jsonschema validation, because it will forbid bad values; and
112
+ # - the auth rules checks, because they stop us from creating power levels
113
+ # with `"room": null`. (We want to test this case, because we have seen it
114
+ # in the wild.)
115
+ #
116
+ # We have seen stringy and null values for "room" in the wild, so presumably
117
+ # some of this validation was missing in the past.
67
118
with patch ("synapse.events.validator.validate_canonicaljson" ), patch (
68
119
"synapse.events.validator.jsonschema.validate"
69
- ):
70
- self .helper .send_state (
120
+ ), patch ( "synapse.handlers.event_auth.check_state_dependent_auth_rules" ) :
121
+ pl_event_id = self .helper .send_state (
71
122
self .room_id ,
72
123
"m.room.power_levels" ,
73
124
{
74
- "users" : {self .alice : "100" }, # stringy
75
- "notifications" : {"room" : 100.0 }, # float
125
+ "users" : {self .alice : 60 },
126
+ "notifications" : {"room" : bad_room_level },
76
127
},
77
128
self .token ,
78
129
state_key = "" ,
79
- )
130
+ )[ "event_id" ]
80
131
81
132
# Create a new message event, and try to evaluate it under the dodgy
82
133
# power level event.
@@ -88,17 +139,33 @@ def test_action_for_event_by_user_handles_noninteger_power_levels(self) -> None:
88
139
"room_id" : self .room_id ,
89
140
"content" : {
90
141
"msgtype" : "m.text" ,
91
- "body" : "helo" ,
142
+ "body" : "helo @room " ,
92
143
},
93
144
"sender" : self .alice ,
94
145
},
146
+ prev_event_ids = [pl_event_id ],
95
147
)
96
148
)
97
149
98
150
bulk_evaluator = BulkPushRuleEvaluator (self .hs )
99
151
# should not raise
100
152
self .get_success (bulk_evaluator .action_for_events_by_user ([(event , context )]))
101
153
154
+ # Did Bob see Alice's @room notification?
155
+ highlighted_actions = self .get_success (
156
+ self .hs .get_datastores ().main .db_pool .simple_select_list (
157
+ table = "event_push_actions_staging" ,
158
+ keyvalues = {
159
+ "event_id" : event .event_id ,
160
+ "user_id" : bob ,
161
+ "highlight" : 1 ,
162
+ },
163
+ retcols = ("*" ,),
164
+ desc = "get_event_push_actions_staging" ,
165
+ )
166
+ )
167
+ self .assertEqual (len (highlighted_actions ), int (should_permit ))
168
+
102
169
@override_config ({"push" : {"enabled" : False }})
103
170
def test_action_for_event_by_user_disabled_by_config (self ) -> None :
104
171
"""Ensure that push rules are not calculated when disabled in the config"""
0 commit comments