@@ -12,6 +12,7 @@ import (
1212 "github.com/matrix-org/gomatrixserverlib/spec"
1313 "github.com/tidwall/gjson"
1414
15+ "github.com/matrix-org/complement/b"
1516 "github.com/matrix-org/complement/client"
1617 "github.com/matrix-org/complement/helpers"
1718 "github.com/matrix-org/complement/match"
@@ -23,6 +24,7 @@ func TestFederationRoomsInvite(t *testing.T) {
2324 defer deployment .Destroy (t )
2425
2526 alice := deployment .Register (t , "hs1" , helpers.RegistrationOpts {LocalpartSuffix : "alice" })
27+ alice2 := deployment .Register (t , "hs1" , helpers.RegistrationOpts {LocalpartSuffix : "alice2" })
2628 bob := deployment .Register (t , "hs2" , helpers.RegistrationOpts {LocalpartSuffix : "bob" })
2729 bob2 := deployment .Register (t , "hs2" , helpers.RegistrationOpts {LocalpartSuffix : "bob2" })
2830
@@ -149,6 +151,73 @@ func TestFederationRoomsInvite(t *testing.T) {
149151 alice .MustSyncUntil (t , client.SyncReq {Filter : includeLeaveSyncFilter }, client .SyncLeftFrom (bob2 .UserID , roomID ))
150152 })
151153
154+ t .Run ("Inviter user can rescind invite over federation" , func (t * testing.T ) {
155+ t .Parallel ()
156+ roomID := alice .MustCreateRoom (t , map [string ]interface {}{
157+ "preset" : "private_chat" ,
158+ "invite" : []string {bob .UserID },
159+ })
160+ bob .MustSyncUntil (t , client.SyncReq {}, client .SyncInvitedTo (bob .UserID , roomID ))
161+ alice .MustDo (t , "POST" , []string {"_matrix" , "client" , "v3" , "rooms" , roomID , "kick" },
162+ client .WithJSONBody (t , map [string ]interface {}{
163+ "user_id" : bob .UserID ,
164+ "reason" : "testing" ,
165+ }),
166+ )
167+
168+ bob .MustSyncUntil (t , client.SyncReq {Filter : includeLeaveSyncFilter }, client .SyncLeftFrom (bob .UserID , roomID ))
169+ })
170+
171+ t .Run ("Non-invitee user cannot rescind invite over federation" , func (t * testing.T ) {
172+ t .Parallel ()
173+
174+ // First create a room that Bob is in. This is so that later we can
175+ // send a message to test that Bob doesn't see the rescission.
176+ roomID1 := alice .MustCreateRoom (t , map [string ]interface {}{
177+ "preset" : "private_chat" ,
178+ "invite" : []string {bob .UserID },
179+ })
180+ since := bob .MustSyncUntil (t , client.SyncReq {}, client .SyncInvitedTo (bob .UserID , roomID1 ))
181+ bob .MustJoinRoom (t , roomID1 , []spec.ServerName {})
182+
183+ // Second room which Alice and Alice2 join, Alice2 invites Bob and
184+ // then Alice kicks him.
185+ roomID2 := alice .MustCreateRoom (t , map [string ]interface {}{
186+ "preset" : "private_chat" ,
187+ "invite" : []string {alice2 .UserID },
188+ })
189+ alice2 .MustSyncUntil (t , client.SyncReq {}, client .SyncInvitedTo (alice2 .UserID , roomID2 ))
190+ alice2 .MustJoinRoom (t , roomID2 , []spec.ServerName {})
191+
192+ alice2 .MustInviteRoom (t , roomID2 , bob .UserID )
193+ bob .MustSyncUntil (t , client.SyncReq {}, client .SyncInvitedTo (bob .UserID , roomID2 ))
194+
195+ // Alice, not the original inviter, kicks bob. This does not result
196+ // in bob seeing the rescission.
197+ alice .MustDo (t , "POST" , []string {"_matrix" , "client" , "v3" , "rooms" , roomID2 , "kick" },
198+ client .WithJSONBody (t , map [string ]interface {}{
199+ "user_id" : bob .UserID ,
200+ "reason" : "testing" ,
201+ }),
202+ )
203+
204+ // Check bob *doesn't* see the rescission. We do this by sending a
205+ // message in room1 and checking that once Bob receives that message
206+ // he still hasn't seen the leave.
207+ eventID := alice .SendEventSynced (t , roomID1 , b.Event {
208+ Type : "m.room.message" ,
209+ Content : map [string ]interface {}{
210+ "body" : "1" ,
211+ "msgtype" : "m.text" ,
212+ },
213+ Sender : alice .UserID ,
214+ })
215+ bob .MustSyncUntil (t , client.SyncReq {Since : since }, client .SyncTimelineHasEventID (roomID1 , eventID ))
216+
217+ // Check bob is still invited by doing an initial sync
218+ bob .MustSyncUntil (t , client.SyncReq {}, client .SyncInvitedTo (bob .UserID , roomID2 ))
219+ })
220+
152221 t .Run ("Invited user has 'is_direct' flag in prev_content after joining" , func (t * testing.T ) {
153222 roomID := alice .MustCreateRoom (t , map [string ]interface {}{
154223 "preset" : "private_chat" ,
0 commit comments