@@ -18,7 +18,7 @@ import (
1818 "github.com/matrix-org/complement/internal/must"
1919)
2020
21- func failJoinRoom (t * testing.T , c * client.CSAPI , roomIDOrAlias string , serverName string ) {
21+ func failJoinRoom (t * testing.T , c * client.CSAPI , roomIDOrAlias string , serverName string , expectedErrorCode int ) {
2222 t .Helper ()
2323
2424 // This is copied from Client.JoinRoom to test a join failure.
@@ -31,7 +31,7 @@ func failJoinRoom(t *testing.T, c *client.CSAPI, roomIDOrAlias string, serverNam
3131 client .WithQueries (query ),
3232 )
3333 must .MatchResponse (t , res , match.HTTPResponse {
34- StatusCode : 403 ,
34+ StatusCode : expectedErrorCode ,
3535 })
3636}
3737
@@ -85,7 +85,7 @@ func setupRestrictedRoom(t *testing.T, deployment *docker.Deployment) (*client.C
8585func checkRestrictedRoom (t * testing.T , alice * client.CSAPI , bob * client.CSAPI , space string , room string ) {
8686 t .Helper ()
8787
88- failJoinRoom (t , bob , room , "hs1" )
88+ failJoinRoom (t , bob , room , "hs1" , 403 )
8989
9090 // Join the space, attempt to join the room again, which now should succeed.
9191 bob .JoinRoom (t , space , []string {"hs1" })
@@ -109,7 +109,7 @@ func checkRestrictedRoom(t *testing.T, alice *client.CSAPI, bob *client.CSAPI, s
109109 return ev .Get ("content" ).Get ("membership" ).Str == "leave"
110110 })
111111
112- failJoinRoom (t , bob , room , "hs1" )
112+ failJoinRoom (t , bob , room , "hs1" , 403 )
113113
114114 // Invite the user and joining should work.
115115 alice .InviteRoom (t , room , bob .UserID )
@@ -136,7 +136,7 @@ func checkRestrictedRoom(t *testing.T, alice *client.CSAPI, bob *client.CSAPI, s
136136 },
137137 )
138138 // Fails since invalid values get filtered out of allow.
139- failJoinRoom (t , bob , room , "hs1" )
139+ failJoinRoom (t , bob , room , "hs1" , 403 )
140140
141141 alice .SendEventSynced (
142142 t ,
@@ -152,7 +152,7 @@ func checkRestrictedRoom(t *testing.T, alice *client.CSAPI, bob *client.CSAPI, s
152152 },
153153 )
154154 // Fails since a fully invalid allow key requires an invite.
155- failJoinRoom (t , bob , room , "hs1" )
155+ failJoinRoom (t , bob , room , "hs1" , 403 )
156156}
157157
158158// Test joining a room with join rules restricted to membership in a space.
@@ -251,7 +251,7 @@ func TestRestrictedRoomsRemoteJoinLocalUser(t *testing.T) {
251251 })
252252
253253 // Bob cannot join the room.
254- failJoinRoom (t , bob , room , "hs1" )
254+ failJoinRoom (t , bob , room , "hs1" , 403 )
255255
256256 // Join the space via hs2.
257257 bob .JoinRoom (t , space , []string {"hs2" })
@@ -310,6 +310,150 @@ func TestRestrictedRoomsRemoteJoinLocalUser(t *testing.T) {
310310 bob .JoinRoom (t , room , []string {"hs1" })
311311}
312312
313+ // A server will request a failover if asked to /make_join and it does not have
314+ // the appropriate authorisation to complete the request.
315+ //
316+ // Setup 3 homeservers:
317+ // * hs1 creates the space/room.
318+ // * hs2 joins the room
319+ // * hs3 attempts to join via hs2 (should fail) and hs1 (should work)
320+ func TestRestrictedRoomsRemoteJoinFailOver (t * testing.T ) {
321+ deployment := Deploy (t , b.Blueprint {
322+ Name : "federation_three_homeservers" ,
323+ Homeservers : []b.Homeserver {
324+ {
325+ Name : "hs1" ,
326+ Users : []b.User {
327+ {
328+ Localpart : "alice" ,
329+ DisplayName : "Alice" ,
330+ },
331+ },
332+ },
333+ {
334+ Name : "hs2" ,
335+ Users : []b.User {
336+ {
337+ Localpart : "bob" ,
338+ DisplayName : "Bob" ,
339+ },
340+ },
341+ },
342+ {
343+ Name : "hs3" ,
344+ Users : []b.User {
345+ {
346+ Localpart : "charlie" ,
347+ DisplayName : "Charlie" ,
348+ },
349+ },
350+ },
351+ },
352+ })
353+ defer deployment .Destroy (t )
354+
355+ // Setup the user, space, and restricted room.
356+ alice , space , room := setupRestrictedRoom (t , deployment )
357+
358+ // Raise the power level so that only alice can invite.
359+ state_key := ""
360+ alice .SendEventSynced (t , room , b.Event {
361+ Type : "m.room.power_levels" ,
362+ StateKey : & state_key ,
363+ Content : map [string ]interface {}{
364+ "invite" : 100 ,
365+ "users" : map [string ]interface {}{
366+ alice .UserID : 100 ,
367+ },
368+ },
369+ })
370+
371+ // Create a second user on a different homeserver.
372+ bob := deployment .Client (t , "hs2" , "@bob:hs2" )
373+
374+ // Bob joins the room and space.
375+ bob .JoinRoom (t , space , []string {"hs1" })
376+ bob .JoinRoom (t , room , []string {"hs1" })
377+
378+ // Charlie should join the space (which gives access to the room).
379+ charlie := deployment .Client (t , "hs3" , "@charlie:hs3" )
380+ charlie .JoinRoom (t , space , []string {"hs1" })
381+
382+ // hs2 doesn't have anyone to invite from, so the join fails.
383+ failJoinRoom (t , charlie , room , "hs2" , 502 )
384+
385+ // Including hs1 (and failing over to it) allows the join to succeed.
386+ charlie .JoinRoom (t , room , []string {"hs2" , "hs1" })
387+
388+ // Double check that the join was authorised via hs1.
389+ bob .SyncUntilTimelineHas (
390+ t ,
391+ room ,
392+ func (ev gjson.Result ) bool {
393+ if ev .Get ("type" ).Str != "m.room.member" || ev .Get ("state_key" ).Str != charlie .UserID {
394+ return false
395+ }
396+ must .EqualStr (t , ev .Get ("content" ).Get ("membership" ).Str , "join" , "Charlie failed to join the room" )
397+ must .EqualStr (t , ev .Get ("content" ).Get ("join_authorised_via_users_server" ).Str , alice .UserID , "Join authorised via incorrect server" )
398+
399+ return true
400+ },
401+ )
402+
403+ // Bump the power-level of bob.
404+ alice .SendEventSynced (t , room , b.Event {
405+ Type : "m.room.power_levels" ,
406+ StateKey : & state_key ,
407+ Content : map [string ]interface {}{
408+ "invite" : 100 ,
409+ "users" : map [string ]interface {}{
410+ alice .UserID : 100 ,
411+ bob .UserID : 100 ,
412+ },
413+ },
414+ })
415+
416+ // Charlie leaves the room (so they can rejoin).
417+ charlie .LeaveRoom (t , room )
418+
419+ // Ensure the events have synced to hs2.
420+ bob .SyncUntilTimelineHas (
421+ t ,
422+ room ,
423+ func (ev gjson.Result ) bool {
424+ if ev .Get ("type" ).Str != "m.room.member" || ev .Get ("state_key" ).Str != charlie .UserID {
425+ return false
426+ }
427+ return ev .Get ("content" ).Get ("membership" ).Str == "leave"
428+ },
429+ )
430+
431+ // Bob leaves the space so that hs2 doesn't know if Charlie is in the space or not.
432+ bob .LeaveRoom (t , space )
433+
434+ // hs2 cannot complete the join since they do not know if Charlie meets the
435+ // requirements (since it is no longer in the space).
436+ failJoinRoom (t , charlie , room , "hs2" , 502 )
437+
438+ // Including hs1 (and failing over to it) allows the join to succeed.
439+ charlie .JoinRoom (t , room , []string {"hs2" , "hs1" })
440+
441+ // Double check that the join was authorised via hs1.
442+ bob .SyncUntilTimelineHas (
443+ t ,
444+ room ,
445+ func (ev gjson.Result ) bool {
446+ if ev .Get ("type" ).Str != "m.room.member" || ev .Get ("state_key" ).Str != charlie .UserID {
447+ return false
448+ }
449+ must .EqualStr (t , ev .Get ("content" ).Get ("membership" ).Str , "join" , "Charlie failed to join the room" )
450+ must .EqualStr (t , ev .Get ("content" ).Get ("join_authorised_via_users_server" ).Str , alice .UserID , "Join authorised via incorrect server" )
451+
452+ return true
453+ },
454+ )
455+ }
456+
313457// Request the room summary and ensure the expected rooms are in the response.
314458func requestAndAssertSummary (t * testing.T , user * client.CSAPI , space string , expected_rooms []interface {}) {
315459 t .Helper ()
0 commit comments