1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414import logging
15+ from typing import List
1516from unittest import TestCase
1617
1718from synapse .api .constants import EventTypes
2223from synapse .logging .context import LoggingContext , run_in_background
2324from synapse .rest import admin
2425from synapse .rest .client .v1 import login , room
26+ from synapse .util .stringutils import random_string
2527
2628from tests import unittest
2729
@@ -39,6 +41,8 @@ def make_homeserver(self, reactor, clock):
3941 hs = self .setup_test_homeserver (federation_http_client = None )
4042 self .handler = hs .get_federation_handler ()
4143 self .store = hs .get_datastore ()
44+ self .state_store = hs .get_storage ().state
45+ self ._event_auth_handler = hs .get_event_auth_handler ()
4246 return hs
4347
4448 def test_exchange_revoked_invite (self ):
@@ -190,6 +194,133 @@ def test_rejected_state_event_state(self):
190194
191195 self .assertEqual (sg , sg2 )
192196
197+ def test_backfill_floating_outlier_membership_auth (self ):
198+ """
199+ As the local homeserver, check that we can properly process a federated
200+ event from the OTHER_SERVER with auth_events that include a floating
201+ membership event from the OTHER_SERVER.
202+
203+ Regression test, see #10439.
204+ """
205+ OTHER_SERVER = "otherserver"
206+ OTHER_USER = "@otheruser:" + OTHER_SERVER
207+
208+ # create the room
209+ user_id = self .register_user ("kermit" , "test" )
210+ tok = self .login ("kermit" , "test" )
211+ room_id = self .helper .create_room_as (
212+ room_creator = user_id ,
213+ is_public = True ,
214+ tok = tok ,
215+ extra_content = {
216+ "preset" : "public_chat" ,
217+ },
218+ )
219+ room_version = self .get_success (self .store .get_room_version (room_id ))
220+
221+ prev_event_ids = self .get_success (self .store .get_prev_events_for_room (room_id ))
222+ (
223+ most_recent_prev_event_id ,
224+ most_recent_prev_event_depth ,
225+ ) = self .get_success (self .store .get_max_depth_of (prev_event_ids ))
226+ # mapping from (type, state_key) -> state_event_id
227+ prev_state_map = self .get_success (
228+ self .state_store .get_state_ids_for_event (most_recent_prev_event_id )
229+ )
230+ # List of state event ID's
231+ prev_state_ids = list (prev_state_map .values ())
232+ auth_event_ids = prev_state_ids
233+ auth_events = list (
234+ self .get_success (self .store .get_events (auth_event_ids )).values ()
235+ )
236+
237+ # build a floating outlier member state event
238+ fake_prev_event_id = "$" + random_string (43 )
239+ member_event_dict = {
240+ "type" : EventTypes .Member ,
241+ "content" : {
242+ "membership" : "join" ,
243+ },
244+ "state_key" : OTHER_USER ,
245+ "room_id" : room_id ,
246+ "sender" : OTHER_USER ,
247+ "depth" : most_recent_prev_event_depth ,
248+ "prev_events" : [fake_prev_event_id ],
249+ "origin_server_ts" : self .clock .time_msec (),
250+ "signatures" : {OTHER_SERVER : {"ed25519:key_version" : "SomeSignatureHere" }},
251+ }
252+ builder = self .hs .get_event_builder_factory ().for_room_version (
253+ room_version , member_event_dict
254+ )
255+ member_event = self .get_success (
256+ builder .build (
257+ prev_event_ids = member_event_dict ["prev_events" ],
258+ auth_event_ids = self ._event_auth_handler .compute_auth_events (
259+ builder ,
260+ prev_state_map ,
261+ for_verification = False ,
262+ ),
263+ depth = member_event_dict ["depth" ],
264+ )
265+ )
266+ # Override the signature added from "test" homeserver that we created the event with
267+ member_event .signatures = member_event_dict ["signatures" ]
268+
269+ # Add the new member_event to the StateMap
270+ prev_state_map [
271+ (member_event .type , member_event .state_key )
272+ ] = member_event .event_id
273+ auth_events .append (member_event )
274+
275+ # build and send an event authed based on the member event
276+ message_event_dict = {
277+ "type" : EventTypes .Message ,
278+ "content" : {},
279+ "room_id" : room_id ,
280+ "sender" : OTHER_USER ,
281+ "depth" : most_recent_prev_event_depth ,
282+ "prev_events" : prev_event_ids .copy (),
283+ "origin_server_ts" : self .clock .time_msec (),
284+ "signatures" : {OTHER_SERVER : {"ed25519:key_version" : "SomeSignatureHere" }},
285+ }
286+ builder = self .hs .get_event_builder_factory ().for_room_version (
287+ room_version , message_event_dict
288+ )
289+ message_event = self .get_success (
290+ builder .build (
291+ prev_event_ids = message_event_dict ["prev_events" ],
292+ auth_event_ids = self ._event_auth_handler .compute_auth_events (
293+ builder ,
294+ prev_state_map ,
295+ for_verification = False ,
296+ ),
297+ depth = message_event_dict ["depth" ],
298+ )
299+ )
300+ # Override the signature added from "test" homeserver that we created the event with
301+ message_event .signatures = message_event_dict ["signatures" ]
302+
303+ # Stub the /event_auth response from the OTHER_SERVER
304+ async def get_event_auth (
305+ destination : str , room_id : str , event_id : str
306+ ) -> List [EventBase ]:
307+ return auth_events
308+
309+ self .handler .federation_client .get_event_auth = get_event_auth
310+
311+ with LoggingContext ("receive_pdu" ):
312+ # Fake the OTHER_SERVER federating the message event over to our local homeserver
313+ d = run_in_background (
314+ self .handler .on_receive_pdu , OTHER_SERVER , message_event
315+ )
316+ self .get_success (d )
317+
318+ # Now try and get the events on our local homeserver
319+ stored_event = self .get_success (
320+ self .store .get_event (message_event .event_id , allow_none = True )
321+ )
322+ self .assertTrue (stored_event is not None )
323+
193324 @unittest .override_config (
194325 {"rc_invites" : {"per_user" : {"per_second" : 0.5 , "burst_count" : 3 }}}
195326 )
0 commit comments