@@ -12,12 +12,15 @@ import (
1212 "github.com/matrix-org/complement/runtime"
1313 "github.com/matrix-org/complement/should"
1414 "github.com/matrix-org/gomatrixserverlib/spec"
15+ "github.com/tidwall/gjson"
1516)
1617
1718// Test for https://github.com/matrix-org/dendrite/issues/3004
1819func TestACLs (t * testing.T ) {
1920 runtime .SkipIf (t , runtime .Dendrite ) // needs https://github.com/matrix-org/dendrite/pull/3008
20- // 1. Prepare 3 or more servers. 1st will be room host, 2nd will be blocked with m.room.server_acl and 3rd server will be affected by this issue. 1st and 2nd servers don't have to be powered by dendrite.
21+ // 1. Prepare 3 or more servers. 1st will be room host, 2nd will be blocked with
22+ // m.room.server_acl and 3rd server will be affected by this issue. 1st and 2nd
23+ // servers don't have to be powered by dendrite.
2124 deployment := complement .Deploy (t , 3 )
2225 defer deployment .Destroy (t )
2326
@@ -74,7 +77,8 @@ func TestACLs(t *testing.T) {
7477 aliceSince = alice .MustSyncUntil (t , client.SyncReq {Since : aliceSince }, client .SyncJoinedTo (charlie .UserID , roomID ))
7578 charlieSince := charlie .MustSyncUntil (t , client.SyncReq {}, client .SyncJoinedTo (charlie .UserID , roomID ))
7679
77- // 6. Any events sent by 2nd server will not appear for users from 1st server, but will be visible for users from 3rd server
80+ // 6. Any events sent by 2nd server will not appear for users from 1st or 3rd server, as
81+ // m.room.server_acl blocks everyone from accepting hs2's events
7882 eventID = bob .SendEventSynced (t , roomID , b.Event {
7983 Type : "m.room.message" ,
8084 Sender : bob .UserID ,
@@ -120,3 +124,119 @@ func TestACLs(t *testing.T) {
120124 )
121125 }
122126}
127+
128+ // MSC4163: TestACLsForEDUs checks that ACLs are applied to EDUs (typing notifications and read receipts)
129+ func TestACLsForEDUs (t * testing.T ) {
130+ runtime .SkipIf (t , runtime .Dendrite )
131+ // 1. Prepare 3 or more servers. 1st will be room host, 2nd will be blocked with
132+ // m.room.server_acl and 3rd server will be affected by this issue. 1st and 2nd
133+ // servers don't have to be powered by dendrite.
134+ deployment := complement .Deploy (t , 3 )
135+ defer deployment .Destroy (t )
136+
137+ alice := deployment .Register (t , "hs1" , helpers.RegistrationOpts {})
138+ bob := deployment .Register (t , "hs2" , helpers.RegistrationOpts {})
139+ charlie := deployment .Register (t , "hs3" , helpers.RegistrationOpts {})
140+
141+ // Create a room where hs2 will be blocked by `m.room.server_acl` later on
142+ roomID := alice .MustCreateRoom (t , map [string ]interface {}{"preset" : "public_chat" })
143+ aliceSince := alice .MustSyncUntil (t , client.SyncReq {}, client .SyncJoinedTo (alice .UserID , roomID ))
144+
145+ bob .MustJoinRoom (t , roomID , []spec.ServerName {
146+ deployment .GetFullyQualifiedHomeserverName (t , "hs1" ),
147+ })
148+ aliceSince = alice .MustSyncUntil (t , client.SyncReq {Since : aliceSince }, client .SyncJoinedTo (bob .UserID , roomID ))
149+ bobSince := bob .MustSyncUntil (t , client.SyncReq {}, client .SyncJoinedTo (bob .UserID , roomID ))
150+
151+ // Create a sentinel room without ACLs to confirm federation is working
152+ sentinelRoom := alice .MustCreateRoom (t , map [string ]interface {}{"preset" : "public_chat" })
153+ aliceSince = alice .MustSyncUntil (t , client.SyncReq {Since : aliceSince }, client .SyncJoinedTo (alice .UserID , sentinelRoom ))
154+ bob .MustJoinRoom (t , sentinelRoom , []spec.ServerName {
155+ deployment .GetFullyQualifiedHomeserverName (t , "hs1" ),
156+ })
157+ charlie .MustJoinRoom (t , sentinelRoom , []spec.ServerName {
158+ deployment .GetFullyQualifiedHomeserverName (t , "hs1" ),
159+ })
160+ aliceSince = alice .MustSyncUntil (t , client.SyncReq {Since : aliceSince },
161+ client .SyncJoinedTo (bob .UserID , sentinelRoom ),
162+ client .SyncJoinedTo (charlie .UserID , sentinelRoom ),
163+ )
164+
165+ // Block hs2 from participating in the roomID via `m.room.server_acl` rules
166+ stateKey := ""
167+ aclEventID := alice .SendEventSynced (t , roomID , b.Event {
168+ Type : "m.room.server_acl" ,
169+ Sender : alice .UserID ,
170+ StateKey : & stateKey ,
171+ Content : map [string ]interface {}{
172+ "allow" : []string {"*" },
173+ "allow_ip_literals" : true ,
174+ "deny" : []string {
175+ string (deployment .GetFullyQualifiedHomeserverName (t , "hs2" )),
176+ },
177+ },
178+ })
179+ // Wait for the ACL to reach hs2 before sending EDUs
180+ bob .MustSyncUntil (t , client.SyncReq {Since : bobSince }, client .SyncTimelineHasEventID (roomID , aclEventID ))
181+
182+ // Join charlie to roomID after the ACL is set up
183+ charlie .MustJoinRoom (t , roomID , []spec.ServerName {
184+ deployment .GetFullyQualifiedHomeserverName (t , "hs1" ),
185+ })
186+ aliceSince = alice .MustSyncUntil (t , client.SyncReq {Since : aliceSince }, client .SyncJoinedTo (charlie .UserID , roomID ))
187+ charlieSince := charlie .MustSyncUntil (t , client.SyncReq {}, client .SyncJoinedTo (charlie .UserID , roomID ))
188+
189+ // Bob starts typing (kind of EDU) in both rooms; typing in roomID should be dropped by ACLs
190+ bob .SendTyping (t , roomID , true , 10000 )
191+ bob .SendTyping (t , sentinelRoom , true , 10000 )
192+
193+ // Bob sets read receipts (another kind of EDU) on both rooms; receipts in roomID should be dropped by ACLs
194+ bob .MustDo (t , "POST" , []string {"_matrix" , "client" , "v3" , "rooms" , roomID , "read_markers" },
195+ client .WithJSONBody (t , map [string ]interface {}{"m.read" : aclEventID }))
196+
197+ // Send a sentinel message to sentinelRoom to use as a receipt anchor
198+ sentinelEventID := bob .SendEventSynced (t , sentinelRoom , b.Event {
199+ Type : "m.room.message" ,
200+ Sender : bob .UserID ,
201+ Content : map [string ]interface {}{
202+ "msgtype" : "m.text" ,
203+ "body" : "sentinel" ,
204+ },
205+ })
206+ bob .MustDo (t , "POST" , []string {"_matrix" , "client" , "v3" , "rooms" , sentinelRoom , "read_markers" },
207+ client .WithJSONBody (t , map [string ]interface {}{"m.read" : sentinelEventID }))
208+
209+ // Wait for the sentinel message and EDUs in sentinelRoom to arrive, proving the federation
210+ // transaction containing hs2's EDUs was processed
211+ alice .MustSyncUntil (t , client.SyncReq {Since : aliceSince },
212+ client .SyncTimelineHasEventID (sentinelRoom , sentinelEventID ),
213+ client .SyncEphemeralHas (sentinelRoom , func (result gjson.Result ) bool {
214+ return result .Get ("type" ).Str == "m.receipt"
215+ }),
216+ client .SyncEphemeralHas (sentinelRoom , func (result gjson.Result ) bool {
217+ return result .Get ("type" ).Str == "m.typing"
218+ }),
219+ )
220+ charlie .MustSyncUntil (t , client.SyncReq {Since : charlieSince },
221+ client .SyncTimelineHasEventID (sentinelRoom , sentinelEventID ),
222+ client .SyncEphemeralHas (sentinelRoom , func (result gjson.Result ) bool {
223+ return result .Get ("type" ).Str == "m.receipt"
224+ }),
225+ client .SyncEphemeralHas (sentinelRoom , func (result gjson.Result ) bool {
226+ return result .Get ("type" ).Str == "m.typing"
227+ }),
228+ )
229+
230+ // Verify with alice (hs1) and charlie (hs3) that we never received EDU's from hs2
231+ for _ , user := range []* client.CSAPI {alice , charlie } {
232+ syncResp , _ := user .MustSync (t , client.SyncReq {})
233+
234+ // No typing or read receipts from the blocked server should appear in room with id roomID
235+ ephemerals := syncResp .Get ("rooms.join." + client .GjsonEscape (roomID ) + ".ephemeral" )
236+ must .MatchGJSON (t , ephemerals , match .JSONKeyArrayOfSize ("events" , 0 ))
237+
238+ // sentinelRoom should have received the read receipt and typing notification
239+ ephemerals = syncResp .Get ("rooms.join." + client .GjsonEscape (sentinelRoom ) + ".ephemeral" )
240+ must .MatchGJSON (t , ephemerals , match .JSONKeyArrayOfSize ("events" , 2 ))
241+ }
242+ }
0 commit comments