@@ -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