@@ -2476,6 +2476,265 @@ func TestPartialStateJoin(t *testing.T) {
2476
2476
mustSyncUntilDeviceListsHas (t , alice , syncToken , "left" , server .UserID ("elsie" ))
2477
2477
mustQueryKeysWithFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2478
2478
})
2479
+
2480
+ // setupUserIncorrectlyInRoom tricks the homeserver under test into thinking that @elsie is
2481
+ // in the room when they have really been kicked. Once the partial state join completes,
2482
+ // @elsie will be discovered to be no longer in the room.
2483
+ setupUserIncorrectlyInRoom := func (
2484
+ t * testing.T , deployment * docker.Deployment , alice * client.CSAPI ,
2485
+ server * federation.Server , room * federation.ServerRoom ,
2486
+ ) (syncToken string , psjResult partialStateJoinResult ) {
2487
+ charlie := server .UserID ("charlie" )
2488
+ derek := server .UserID ("derek" )
2489
+ elsie := server .UserID ("elsie" )
2490
+ fred := server .UserID ("fred" )
2491
+
2492
+ // The room starts with @charlie and @derek in it.
2493
+ // @charlie makes @fred an admin.
2494
+ // @charlie makes @derek a moderator.
2495
+ var powerLevelsContent map [string ]interface {}
2496
+ json .Unmarshal (room .CurrentState ("m.room.power_levels" , "" ).Content (), & powerLevelsContent )
2497
+ powerLevelsContent ["users" ].(map [string ]interface {})[derek ] = 50
2498
+ powerLevelsContent ["users" ].(map [string ]interface {})[fred ] = 100
2499
+ room .AddEvent (server .MustCreateEvent (t , room , b.Event {
2500
+ Type : "m.room.power_levels" ,
2501
+ StateKey : b .Ptr ("" ),
2502
+ Sender : charlie ,
2503
+ Content : powerLevelsContent ,
2504
+ }))
2505
+
2506
+ // @fred joins and leaves the room.
2507
+ fredJoinEvent := createJoinEvent (t , server , room , fred )
2508
+ room .AddEvent (fredJoinEvent )
2509
+ fredLeaveEvent := createLeaveEvent (t , server , room , fred )
2510
+ room .AddEvent (fredLeaveEvent )
2511
+
2512
+ // @alice:hs1 joins the room.
2513
+ psjResult = beginPartialStateJoin (t , server , room , alice )
2514
+
2515
+ // @elsie joins the room.
2516
+ joinEvent := createJoinEvent (t , server , room , elsie )
2517
+ room .AddEvent (joinEvent )
2518
+ server .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {joinEvent .JSON ()}, nil )
2519
+ syncToken = awaitEventViaSync (t , alice , room .RoomID , joinEvent .EventID (), "" )
2520
+
2521
+ // @fred "bans" @derek.
2522
+ // This is incorrectly accepted, since the homeserver under test does not know whether
2523
+ // @fred is really in the room.
2524
+ // This event has to be a ban, rather than a kick, otherwise state resolution can bring
2525
+ // @derek back into the room and ruin the test setup.
2526
+ badKickEvent := server .MustCreateEvent (t , room , b.Event {
2527
+ Type : "m.room.member" ,
2528
+ StateKey : b .Ptr (derek ),
2529
+ Sender : fred ,
2530
+ Content : map [string ]interface {}{"membership" : "ban" },
2531
+ AuthEvents : room .EventIDsOrReferences ([]* gomatrixserverlib.Event {
2532
+ room .CurrentState ("m.room.create" , "" ),
2533
+ room .CurrentState ("m.room.power_levels" , "" ),
2534
+ fredJoinEvent ,
2535
+ }),
2536
+ })
2537
+ room .Timeline = append (room .Timeline , badKickEvent )
2538
+ room .Depth = badKickEvent .Depth ()
2539
+ room .ForwardExtremities = []string {badKickEvent .EventID ()}
2540
+ server .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {badKickEvent .JSON ()}, nil )
2541
+ syncToken = awaitEventViaSync (t , alice , room .RoomID , badKickEvent .EventID (), syncToken )
2542
+
2543
+ // @derek kicks @elsie.
2544
+ // This is incorrectly rejected since the homeserver under test incorrectly thinks
2545
+ // @derek had been kicked from the room.
2546
+ kickEvent := server .MustCreateEvent (t , room , b.Event {
2547
+ Type : "m.room.member" ,
2548
+ StateKey : b .Ptr (elsie ),
2549
+ Sender : derek ,
2550
+ Content : map [string ]interface {}{"membership" : "leave" },
2551
+ })
2552
+ room .AddEvent (kickEvent )
2553
+ server .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {kickEvent .JSON ()}, nil )
2554
+
2555
+ // Ensure that the kick event has been persisted.
2556
+ sentinelEvent := psjResult .CreateMessageEvent (t , "charlie" , nil )
2557
+ room .AddEvent (sentinelEvent )
2558
+ server .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {sentinelEvent .JSON ()}, nil )
2559
+ syncToken = awaitEventViaSync (t , alice , room .RoomID , sentinelEvent .EventID (), syncToken )
2560
+
2561
+ // Check that the last kick was incorrectly rejected.
2562
+ must .MatchResponse (t ,
2563
+ alice .DoFunc (t , "GET" , []string {"_matrix" , "client" , "r0" , "rooms" , room .RoomID , "event" , kickEvent .EventID ()}),
2564
+ match.HTTPResponse {
2565
+ StatusCode : 404 ,
2566
+ JSON : []match.JSON {
2567
+ match .JSONKeyEqual ("errcode" , "M_NOT_FOUND" ),
2568
+ },
2569
+ },
2570
+ )
2571
+
2572
+ return syncToken , psjResult
2573
+ }
2574
+
2575
+ // test that device lists stop being tracked when it is discovered that a remote user is not
2576
+ // in a room once a partial state join completes.
2577
+ t .Run ("Device list no longer tracked for user incorrectly believed to be in room" , func (t * testing.T ) {
2578
+ alice , server , userDevicesChannel , room , _ , cleanup := setupDeviceListCachingTest (t , deployment , "t36alice" )
2579
+ defer cleanup ()
2580
+
2581
+ // The room starts with @charlie and @derek in it.
2582
+ // @charlie leaves the room.
2583
+ // @t36alice:hs1 joins the room.
2584
+ // @elsie joins the room.
2585
+ // @charlie "kicks" @derek, which the homeserver under test incorrectly accepts.
2586
+ // @derek kicks @elsie, which the homeserver under test incorrectly rejects.
2587
+ _ , psjResult := setupUserIncorrectlyInRoom (t , deployment , alice , server , room )
2588
+ defer psjResult .Destroy ()
2589
+ // @elsie is now incorrectly believed to be in the room.
2590
+
2591
+ // The homeserver under test incorrectly thinks it is subscribed to @elsie's device list updates.
2592
+ mustQueryKeysWithFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2593
+ mustQueryKeysWithoutFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2594
+
2595
+ // Finish the partial state join.
2596
+ // The homeserver under test will discover that @elsie was actually not in the room.
2597
+ psjResult .FinishStateRequest ()
2598
+ awaitPartialStateJoinCompletion (t , room , alice )
2599
+
2600
+ // @elsie's device list ought to no longer be cached.
2601
+ // `device_lists.left` is not working yet: https://github.com/matrix-org/synapse/issues/13886
2602
+ // mustSyncUntilDeviceListsHas(t, alice, syncToken, "left", server.UserID("elsie"))
2603
+ mustQueryKeysWithFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2604
+ })
2605
+
2606
+ // test that cached device lists are flushed when it is discovered that a remote user was
2607
+ // not in a room the whole time once a partial state join completes.
2608
+ t .Run ("Device list tracking for user incorrectly believed to be in room when they rejoin before partial state join completes" , func (t * testing.T ) {
2609
+ // Tracked in https://github.com/matrix-org/synapse/issues/13887.
2610
+ t .Skip ("This edge case is being ignored for now." )
2611
+
2612
+ alice , server , userDevicesChannel , room , _ , cleanup := setupDeviceListCachingTest (t , deployment , "t37alice" )
2613
+ defer cleanup ()
2614
+
2615
+ // The room starts with @charlie and @derek in it.
2616
+ // @charlie leaves the room.
2617
+ // @t37alice:hs1 joins the room.
2618
+ // @elsie joins the room.
2619
+ // @charlie "kicks" @derek, which the homeserver under test incorrectly accepts.
2620
+ // @derek kicks @elsie, which the homeserver under test incorrectly rejects.
2621
+ syncToken , psjResult := setupUserIncorrectlyInRoom (t , deployment , alice , server , room )
2622
+ defer psjResult .Destroy ()
2623
+ // @elsie is now incorrectly believed to be in the room.
2624
+
2625
+ // The homeserver under test incorrectly thinks it is subscribed to @elsie's device list updates.
2626
+ mustQueryKeysWithFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2627
+ mustQueryKeysWithoutFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2628
+
2629
+ // @elsie rejoins the room.
2630
+ joinEvent := createJoinEvent (t , server , room , server .UserID ("elsie" ))
2631
+ room .AddEvent (joinEvent )
2632
+ server .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {joinEvent .JSON ()}, nil )
2633
+ awaitEventViaSync (t , alice , room .RoomID , joinEvent .EventID (), syncToken )
2634
+
2635
+ // @elsie's device list is still cached.
2636
+ mustQueryKeysWithoutFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2637
+
2638
+ // Finish the partial state join.
2639
+ // The homeserver under test will discover that there was a period where @elsie was
2640
+ // actually not in the room.
2641
+ psjResult .FinishStateRequest ()
2642
+ awaitPartialStateJoinCompletion (t , room , alice )
2643
+
2644
+ // @elsie's device list ought to have been flushed from the cache.
2645
+ mustQueryKeysWithFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2646
+ })
2647
+
2648
+ // test that device lists stop being tracked when it is discovered that a remote user is not
2649
+ // in a room once a partial state join completes.
2650
+ // Similar to a previous test, except @elsie rejoins the room after the partial state join
2651
+ // completes, so that their device list is being tracked again at the time we test the
2652
+ // device list cache.
2653
+ t .Run ("Device list tracking for user incorrectly believed to be in room when they rejoin after partial state join completes" , func (t * testing.T ) {
2654
+ alice , server , userDevicesChannel , room , _ , cleanup := setupDeviceListCachingTest (t , deployment , "t38alice" )
2655
+ defer cleanup ()
2656
+
2657
+ // The room starts with @charlie and @derek in it.
2658
+ // @charlie leaves the room.
2659
+ // @t38alice:hs1 joins the room.
2660
+ // @elsie joins the room.
2661
+ // @charlie "kicks" @derek, which the homeserver under test incorrectly accepts.
2662
+ // @derek kicks @elsie, which the homeserver under test incorrectly rejects.
2663
+ syncToken , psjResult := setupUserIncorrectlyInRoom (t , deployment , alice , server , room )
2664
+ defer psjResult .Destroy ()
2665
+ // @elsie is now incorrectly believed to be in the room.
2666
+
2667
+ // The homeserver under test incorrectly thinks it is subscribed to @elsie's device list updates.
2668
+ mustQueryKeysWithFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2669
+ mustQueryKeysWithoutFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2670
+
2671
+ // Finish the partial state join.
2672
+ // The homeserver under test will discover that @elsie was actually not in the room.
2673
+ psjResult .FinishStateRequest ()
2674
+ awaitPartialStateJoinCompletion (t , room , alice )
2675
+ // `device_lists.left` is not working yet: https://github.com/matrix-org/synapse/issues/13886
2676
+ // mustSyncUntilDeviceListsHas(t, alice, syncToken, "left", server.UserID("elsie"))
2677
+
2678
+ // @elsie rejoins the room.
2679
+ joinEvent := createJoinEvent (t , server , room , server .UserID ("elsie" ))
2680
+ room .AddEvent (joinEvent )
2681
+ server .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {joinEvent .JSON ()}, nil )
2682
+ awaitEventViaSync (t , alice , room .RoomID , joinEvent .EventID (), syncToken )
2683
+
2684
+ // @elsie's device list ought to have been flushed from the cache.
2685
+ mustQueryKeysWithFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2686
+ })
2687
+
2688
+ // test that cached device lists are flushed when it is discovered that a remote user did
2689
+ // not share a room the whole time once a partial state join completes.
2690
+ t .Run ("Device list tracking for user incorrectly believed to be in room when they join another shared room before partial state join completes" , func (t * testing.T ) {
2691
+ // Tracked in https://github.com/matrix-org/synapse/issues/13887.
2692
+ t .Skip ("This edge case is being ignored for now." )
2693
+
2694
+ alice , server , userDevicesChannel , room , _ , cleanup := setupDeviceListCachingTest (t , deployment , "t39alice" )
2695
+ defer cleanup ()
2696
+
2697
+ // The room starts with @charlie and @derek in it.
2698
+ // @charlie leaves the room.
2699
+ // @t39alice:hs1 joins the room.
2700
+ // @elsie joins the room.
2701
+ // @charlie "kicks" @derek, which the homeserver under test incorrectly accepts.
2702
+ // @derek kicks @elsie, which the homeserver under test incorrectly rejects.
2703
+ syncToken , psjResult := setupUserIncorrectlyInRoom (t , deployment , alice , server , room )
2704
+ defer psjResult .Destroy ()
2705
+ // @elsie is now incorrectly believed to be in the room.
2706
+
2707
+ // The homeserver under test incorrectly thinks it is subscribed to @elsie's device list updates.
2708
+ mustQueryKeysWithFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2709
+ mustQueryKeysWithoutFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2710
+
2711
+ // @t39alice:hs1 creates a public room.
2712
+ otherRoomID := alice .CreateRoom (t , map [string ]interface {}{"preset" : "public_chat" })
2713
+
2714
+ // @elsie joins the room.
2715
+ // The homeserver under test is now subscribed to @elsie's device list updates.
2716
+ server .MustJoinRoom (t , deployment , "hs1" , otherRoomID , server .UserID ("elsie" ))
2717
+ alice .MustSyncUntil (t ,
2718
+ client.SyncReq {
2719
+ Since : syncToken ,
2720
+ Filter : buildLazyLoadingSyncFilter (nil ),
2721
+ },
2722
+ client .SyncJoinedTo (server .UserID ("elsie" ), otherRoomID ),
2723
+ )
2724
+
2725
+ // The cache device list for @elsie is stale, but the homeserver does not know that yet.
2726
+ mustQueryKeysWithoutFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2727
+
2728
+ // Finish the partial state join.
2729
+ // The homeserver under test will discover that @elsie was actually not in the room, and
2730
+ // so did not share a room the whole time.
2731
+ psjResult .FinishStateRequest ()
2732
+ awaitPartialStateJoinCompletion (t , room , alice )
2733
+
2734
+ // @elsie's device list ought to be evicted from the cache.
2735
+ mustSyncUntilDeviceListsHas (t , alice , syncToken , "changed" , server .UserID ("elsie" ))
2736
+ mustQueryKeysWithFederationRequest (t , alice , userDevicesChannel , server .UserID ("elsie" ))
2737
+ })
2479
2738
})
2480
2739
}
2481
2740
0 commit comments