Skip to content

Commit 8067142

Browse files
MSC4163: Apply server ACLs to receipt and typing EDUs (#862)
Synapse PR: element-hq/synapse#18475 For [MSC4163](matrix-org/matrix-spec-proposals#4163) Supercedes #783
1 parent ad46e29 commit 8067142

1 file changed

Lines changed: 122 additions & 2 deletions

File tree

tests/federation_acl_test.go

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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
1819
func 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

Comments
 (0)