@@ -1719,6 +1719,248 @@ func TestPartialStateJoin(t *testing.T) {
17191719 mustNotReceiveDeviceListUpdate (t , deviceListUpdateChannel2 , "@elsie received device list update unexpectedly." )
17201720 t .Log ("@charlie and @derek received device list update." )
17211721 })
1722+
1723+ // setupIncorrectlyAcceptedKick joins the homeserver under test to a room, then joins
1724+ // @elsie:server2 and sends an invalid event to kick @elsie:server2 from the room.
1725+ // As a side effect, @derek is promoted to admin and leaves the room before the homeserver
1726+ // under test joins.
1727+ setupIncorrectlyAcceptedKick := func (
1728+ t * testing.T , deployment * docker.Deployment , alice * client.CSAPI ,
1729+ server1 * federation.Server , server2 * federation.Server ,
1730+ deviceListUpdateChannel1 chan gomatrixserverlib.DeviceListUpdateEvent ,
1731+ deviceListUpdateChannel2 chan gomatrixserverlib.DeviceListUpdateEvent ,
1732+ room * federation.ServerRoom ,
1733+ ) (syncToken string , psjResult partialStateJoinResult ) {
1734+ derek := server1 .UserID ("derek" )
1735+ elsie := server2 .UserID ("elsie" )
1736+
1737+ // The room starts with @charlie:server1 and @derek:server1 in it.
1738+ // @derek:server1 becomes an admin.
1739+ var powerLevelsContent map [string ]interface {}
1740+ json .Unmarshal (room .CurrentState ("m.room.power_levels" , "" ).Content (), & powerLevelsContent )
1741+ powerLevelsContent ["users" ].(map [string ]interface {})[derek ] = 100
1742+ room .AddEvent (server1 .MustCreateEvent (t , room , b.Event {
1743+ Type : "m.room.power_levels" ,
1744+ StateKey : b .Ptr ("" ),
1745+ Sender : server1 .UserID ("charlie" ),
1746+ Content : powerLevelsContent ,
1747+ }))
1748+
1749+ // @derek:server1 leaves the room.
1750+ derekJoinEvent := room .CurrentState ("m.room.member" , derek )
1751+ derekLeaveEvent := createLeaveEvent (t , server1 , room , derek )
1752+ room .AddEvent (derekLeaveEvent )
1753+
1754+ // @alice:hs1 joins the room.
1755+ psjResult = beginPartialStateJoin (t , server1 , room , alice )
1756+
1757+ // @elsie:server2 joins the room.
1758+ // Make server1 send the event to the homeserver, since server2's rooms list isn't set
1759+ // up right and it can't answer queries about events in the room.
1760+ joinEvent := createJoinEvent (t , server2 , room , elsie )
1761+ room .AddEvent (joinEvent )
1762+ server1 .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {joinEvent .JSON ()}, nil )
1763+ syncToken = awaitEventViaSync (t , alice , room .RoomID , joinEvent .EventID (), "" )
1764+
1765+ // Both servers should receive device list updates.
1766+ renameDevice (t , alice , "A new device name 1" )
1767+ mustReceiveDeviceListUpdate (t , deviceListUpdateChannel1 , "@charlie and @derek did not receive device list update." )
1768+ mustReceiveDeviceListUpdate (t , deviceListUpdateChannel2 , "@elsie did not receive device list update." )
1769+ t .Log ("@charlie, @derek and @elsie received device list update." )
1770+
1771+ // @derek:server1 "kicks" @elsie:server2.
1772+ badKickEvent := server1 .MustCreateEvent (t , room , b.Event {
1773+ Type : "m.room.member" ,
1774+ StateKey : b .Ptr (elsie ),
1775+ Sender : derek ,
1776+ Content : map [string ]interface {}{"membership" : "leave" },
1777+ AuthEvents : room .EventIDsOrReferences ([]* gomatrixserverlib.Event {
1778+ room .CurrentState ("m.room.create" , "" ),
1779+ room .CurrentState ("m.room.power_levels" , "" ),
1780+ derekJoinEvent ,
1781+ }),
1782+ })
1783+ room .Timeline = append (room .Timeline , badKickEvent )
1784+ room .Depth = badKickEvent .Depth ()
1785+ room .ForwardExtremities = []string {badKickEvent .EventID ()}
1786+ server1 .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {badKickEvent .JSON ()}, nil )
1787+ awaitEventViaSync (t , alice , room .RoomID , badKickEvent .EventID (), syncToken )
1788+
1789+ return syncToken , psjResult
1790+ }
1791+
1792+ // setupAnotherSharedRoomThenLeave has @alice:hs1 create a public room, @elsie:server2 join
1793+ // the public room, then leave the partial state room.
1794+ // Returns @alice:hs1's sync token after @elsie:server2 has left the partial state room.
1795+ setupAnotherSharedRoomThenLeave := func (
1796+ t * testing.T , deployment * docker.Deployment , alice * client.CSAPI ,
1797+ server1 * federation.Server , server2 * federation.Server ,
1798+ partialStateRoom * federation.ServerRoom , syncToken string ,
1799+ ) string {
1800+ elsie := server2 .UserID ("elsie" )
1801+
1802+ // @alice:hs1 creates a public room.
1803+ roomID := alice .CreateRoom (t , map [string ]interface {}{"preset" : "public_chat" })
1804+
1805+ // @elsie:server2 joins the room.
1806+ server2 .MustJoinRoom (t , deployment , "hs1" , roomID , elsie )
1807+ alice .MustSyncUntil (t ,
1808+ client.SyncReq {
1809+ Since : syncToken ,
1810+ Filter : buildLazyLoadingSyncFilter (nil ),
1811+ },
1812+ client .SyncJoinedTo (elsie , roomID ),
1813+ )
1814+
1815+ // @elsie:server2 leaves the room.
1816+ // Make server1 send the event to the homeserver, since server2's rooms list isn't set
1817+ // up right and it can't answer queries about events in the room.
1818+ leaveEvent := createLeaveEvent (t , server2 , partialStateRoom , elsie )
1819+ partialStateRoom .AddEvent (leaveEvent )
1820+ server1 .MustSendTransaction (t , deployment , "hs1" , []json.RawMessage {leaveEvent .JSON ()}, nil )
1821+ syncToken = awaitEventViaSync (t , alice , partialStateRoom .RoomID , leaveEvent .EventID (), syncToken )
1822+
1823+ return syncToken
1824+ }
1825+
1826+ // testMissedDeviceListUpdateSentOncePartialJoinCompletes takes a room where hs1 incorrectly
1827+ // believes @elsie:server2 not to be present and tests that server2 receives missed device
1828+ // list updates once hs1's partial state join has completed.
1829+ testMissedDeviceListUpdateSentOncePartialJoinCompletes := func (
1830+ t * testing.T , deployment * docker.Deployment , alice * client.CSAPI ,
1831+ server1 * federation.Server , server2 * federation.Server ,
1832+ deviceListUpdateChannel1 chan gomatrixserverlib.DeviceListUpdateEvent ,
1833+ deviceListUpdateChannel2 chan gomatrixserverlib.DeviceListUpdateEvent ,
1834+ room * federation.ServerRoom , psjResult partialStateJoinResult , syncToken string ,
1835+ withLeave bool ,
1836+ ) {
1837+ // The homeserver under test incorrectly believes @elsie:server2 is not in the room.
1838+ // @elsie:server2 should miss device list updates.
1839+ renameDevice (t , alice , "A new device name 2" )
1840+ mustReceiveDeviceListUpdate (t , deviceListUpdateChannel1 , "@charlie and @derek did not receive device list update." )
1841+ mustNotReceiveDeviceListUpdate (t , deviceListUpdateChannel2 , "@elsie received device list update unexpectedly." )
1842+ t .Log ("@charlie and @derek received device list update." )
1843+
1844+ if withLeave {
1845+ // @elsie:server2 joins a room shared with @alice:hs1 and leaves the partial state room.
1846+ // The homeserver under test cannot simply use the current state of the room to
1847+ // determine which device list updates it must send out once the partial state join
1848+ // completes.
1849+ setupAnotherSharedRoomThenLeave (t , deployment , alice , server1 , server2 , room , syncToken )
1850+ }
1851+
1852+ // Finish the partial state join.
1853+ psjResult .FinishStateRequest ()
1854+ awaitPartialStateJoinCompletion (t , room , alice )
1855+
1856+ // @elsie:server2 must receive missed device list updates.
1857+ mustReceiveDeviceListUpdate (t , deviceListUpdateChannel2 , "@elsie did not receive missed device list update." )
1858+ t .Log ("@elsie received missed device list update." )
1859+
1860+ // Both homeservers should receive device list updates again.
1861+ renameDevice (t , alice , "A new device name 3" )
1862+ mustReceiveDeviceListUpdate (t , deviceListUpdateChannel1 , "@charlie and @derek did not receive device list update." )
1863+ mustReceiveDeviceListUpdate (t , deviceListUpdateChannel2 , "@elsie did not receive device list update." )
1864+ t .Log ("@charlie, @derek and @elsie received device list update." )
1865+ }
1866+
1867+ // test that device list updates are sent to remote homeservers incorrectly believed not to
1868+ // be in a room with partial state once the partial state join completes.
1869+ t .Run ("Device list updates reach incorrectly kicked servers once partial state join completes" , func (t * testing.T ) {
1870+ alice , server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room , cleanup := setupOutgoingDeviceListUpdateTest (t , deployment , "t26alice" )
1871+ defer cleanup ()
1872+
1873+ // The room starts with @charlie:server1 and @derek:server1 in it.
1874+ // @t26alice:hs1 joins the room, followed by @elsie:server2.
1875+ // @elsie:server2 is kicked with an invalid event.
1876+ syncToken , psjResult := setupIncorrectlyAcceptedKick (t , deployment , alice , server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room )
1877+ defer psjResult .Destroy ()
1878+
1879+ // @t26alice:hs1 sends out a device list update which is missed by @elsie:server2.
1880+ // @elsie:server2 must receive missed device list updates once the partial state join finishes.
1881+ testMissedDeviceListUpdateSentOncePartialJoinCompletes (t , deployment , alice ,
1882+ server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room ,
1883+ psjResult , syncToken , false ,
1884+ )
1885+ })
1886+
1887+ // test that device list updates are sent to remote homeservers incorrectly believed not to
1888+ // be in a room with partial state once the partial state join completes, even if the remote
1889+ // homeserver leaves the room beforehand.
1890+ t .Run ("Device list updates reach incorrectly kicked servers once partial state join completes even though remote server left room" , func (t * testing.T ) {
1891+ alice , server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room , cleanup := setupOutgoingDeviceListUpdateTest (t , deployment , "t27alice" )
1892+ defer cleanup ()
1893+
1894+ // The room starts with @charlie:server1 and @derek:server1 in it.
1895+ // @t27alice:hs1 joins the room, followed by @elsie:server2.
1896+ // @elsie:server2 is kicked with an invalid event.
1897+ syncToken , psjResult := setupIncorrectlyAcceptedKick (t , deployment , alice , server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room )
1898+ defer psjResult .Destroy ()
1899+
1900+ // @t27alice:hs1 sends out a device list update which is missed by @elsie:server2.
1901+ // @elsie:server2 joins another room shared with @t27alice:hs1 and leaves the partial state room.
1902+ // @elsie:server2 must receive missed device list updates once the partial state join finishes.
1903+ testMissedDeviceListUpdateSentOncePartialJoinCompletes (t , deployment , alice ,
1904+ server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room ,
1905+ psjResult , syncToken , true ,
1906+ )
1907+ })
1908+
1909+ // handleSendJoinRequestsWithIncompleteServersInRoom responds to `/send_join` requests with a minimal `servers_in_room` list.
1910+ handleSendJoinRequestsWithIncompleteServersInRoom := func (server * federation.Server ) {
1911+ server .Mux ().Handle ("/_matrix/federation/v2/send_join/{roomID}/{eventID}" , http .HandlerFunc (func (w http.ResponseWriter , req * http.Request ) {
1912+ // Tell the joining server there are no other servers in the room.
1913+ federation .SendJoinRequestsHandler (server , w , req , true , true )
1914+ })).Methods ("PUT" )
1915+ }
1916+
1917+ // test that device list updates are sent to remote homeservers incorrectly omitted from the
1918+ // `/send_join` response once the partial state join completes.
1919+ t .Run ("Device list updates reach incorrectly absent servers once partial state join completes" , func (t * testing.T ) {
1920+ alice , server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room , cleanup := setupOutgoingDeviceListUpdateTest (
1921+ t , deployment , "t28alice" , handleSendJoinRequestsWithIncompleteServersInRoom ,
1922+ )
1923+ defer cleanup ()
1924+
1925+ // The room starts with @charlie:server1 and @derek:server1 in it.
1926+ // @elsie:server2 joins the room, followed by @t28alice:hs1.
1927+ // server1 does not tell hs1 that server2 is in the room.
1928+ room .AddEvent (createJoinEvent (t , server2 , room , server2 .UserID ("elsie" )))
1929+ psjResult := beginPartialStateJoin (t , server1 , room , alice )
1930+ defer psjResult .Destroy ()
1931+
1932+ // @t28alice:hs1 sends out a device list update which is missed by @elsie:server2.
1933+ // @elsie:server2 must receive missed device list updates once the partial state join finishes.
1934+ testMissedDeviceListUpdateSentOncePartialJoinCompletes (t , deployment , alice ,
1935+ server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room ,
1936+ psjResult , "" , false ,
1937+ )
1938+ })
1939+
1940+ // test that device list updates are sent to remote homeservers incorrectly omitted from the
1941+ // `/send_join` response once the partial state join completes, even if the remote
1942+ // homeserver leaves the room beforehand.
1943+ t .Run ("Device list updates reach incorrectly absent servers once partial state join completes even though remote server left room" , func (t * testing.T ) {
1944+ alice , server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room , cleanup := setupOutgoingDeviceListUpdateTest (
1945+ t , deployment , "t29alice" , handleSendJoinRequestsWithIncompleteServersInRoom ,
1946+ )
1947+ defer cleanup ()
1948+
1949+ // The room starts with @charlie:server1 and @derek:server1 in it.
1950+ // @elsie:server2 joins the room, followed by @t29alice:hs1.
1951+ // server1 does not tell hs1 that server2 is in the room.
1952+ room .AddEvent (createJoinEvent (t , server2 , room , server2 .UserID ("elsie" )))
1953+ psjResult := beginPartialStateJoin (t , server1 , room , alice )
1954+ defer psjResult .Destroy ()
1955+
1956+ // @t29alice:hs1 sends out a device list update which is missed by @elsie:server2.
1957+ // @elsie:server2 joins another room shared with @t29alice:hs1 and leaves the partial state room.
1958+ // @elsie:server2 must receive missed device list updates once the partial state join finishes.
1959+ testMissedDeviceListUpdateSentOncePartialJoinCompletes (t , deployment , alice ,
1960+ server1 , server2 , deviceListUpdateChannel1 , deviceListUpdateChannel2 , room ,
1961+ psjResult , "" , true ,
1962+ )
1963+ })
17221964 })
17231965}
17241966
0 commit comments