Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions tests/federation_acl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/matrix-org/complement/runtime"
"github.com/matrix-org/complement/should"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/tidwall/gjson"
)

// Test for https://github.com/matrix-org/dendrite/issues/3004
Expand Down Expand Up @@ -120,3 +121,115 @@ func TestACLs(t *testing.T) {
)
}
}

// MSC4163: TestACLsForEDUs checks that ACLs are applied to EDUs (typing notifications and read receipts)
func TestACLsForEDUs(t *testing.T) {
runtime.SkipIf(t, runtime.Dendrite)
deployment := complement.Deploy(t, 3)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment from the other TestACLs still relevant?

Suggested change
deployment := complement.Deploy(t, 3)
// 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.
deployment := complement.Deploy(t, 3)

(both places should be wrapped like this)

defer deployment.Destroy(t)

alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
bob := deployment.Register(t, "hs2", helpers.RegistrationOpts{})
charlie := deployment.Register(t, "hs3", helpers.RegistrationOpts{})

// Create a room where hs2 will be blocked by ACL
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Create a room where hs2 will be blocked by ACL
// Create a room where hs2 will be blocked by `m.room.server_acl` later on

roomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
aliceSince := alice.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(alice.UserID, roomID))

bob.MustJoinRoom(t, roomID, []spec.ServerName{
deployment.GetFullyQualifiedHomeserverName(t, "hs1"),
})
aliceSince = alice.MustSyncUntil(t, client.SyncReq{Since: aliceSince}, client.SyncJoinedTo(bob.UserID, roomID))
bobSince := bob.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(bob.UserID, roomID))

// Create a sentinel room without ACLs to confirm federation is working
sentinelRoom := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
aliceSince = alice.MustSyncUntil(t, client.SyncReq{Since: aliceSince}, client.SyncJoinedTo(alice.UserID, sentinelRoom))
bob.MustJoinRoom(t, sentinelRoom, []spec.ServerName{
deployment.GetFullyQualifiedHomeserverName(t, "hs1"),
})
charlie.MustJoinRoom(t, sentinelRoom, []spec.ServerName{
deployment.GetFullyQualifiedHomeserverName(t, "hs1"),
})
aliceSince = alice.MustSyncUntil(t, client.SyncReq{Since: aliceSince},
client.SyncJoinedTo(bob.UserID, sentinelRoom),
client.SyncJoinedTo(charlie.UserID, sentinelRoom),
)

// Block hs2 from participating in room with id roomID
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Block hs2 from participating in room with id roomID
// Block hs2 from participating in the roomID via `m.room.server_acl` rules

stateKey := ""
aclEventID := alice.SendEventSynced(t, roomID, b.Event{
Type: "m.room.server_acl",
Sender: alice.UserID,
StateKey: &stateKey,
Content: map[string]interface{}{
"allow": []string{"*"},
"allow_ip_literals": true,
"deny": []string{
string(deployment.GetFullyQualifiedHomeserverName(t, "hs2")),
},
},
})
// Wait for the ACL to reach hs2 before sending EDUs
bob.MustSyncUntil(t, client.SyncReq{Since: bobSince}, client.SyncTimelineHasEventID(roomID, aclEventID))

// Join charlie to roomID after the ACL is set up
charlie.MustJoinRoom(t, roomID, []spec.ServerName{
deployment.GetFullyQualifiedHomeserverName(t, "hs1"),
})
aliceSince = alice.MustSyncUntil(t, client.SyncReq{Since: aliceSince}, client.SyncJoinedTo(charlie.UserID, roomID))
charlieSince := charlie.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(charlie.UserID, roomID))

// Bob starts typing in both rooms; typing in roomID should be dropped by ACLs
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Bob starts typing in both rooms; typing in roomID should be dropped by ACLs
// Bob starts typing (kind of EDU) in both rooms; typing in roomID should be dropped by ACLs

bob.SendTyping(t, roomID, true, 10000)
bob.SendTyping(t, sentinelRoom, true, 10000)

// Bob sets read receipts on both rooms; receipts in roomID should be dropped by ACLs
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Bob sets read receipts on both rooms; receipts in roomID should be dropped by ACLs
// Bob sets read receipts (another kind of EDU) on both rooms; receipts in roomID should be dropped by ACLs

bob.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "read_markers"},
client.WithJSONBody(t, map[string]interface{}{"m.read": aclEventID}))

// Send a sentinel message to sentinelRoom to use as a receipt anchor
sentinelEventID := bob.SendEventSynced(t, sentinelRoom, b.Event{
Type: "m.room.message",
Sender: bob.UserID,
Content: map[string]interface{}{
"msgtype": "m.text",
"body": "sentinel",
},
})
bob.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", sentinelRoom, "read_markers"},
client.WithJSONBody(t, map[string]interface{}{"m.read": sentinelEventID}))

// Wait for the sentinel message and EDUs in sentinelRoom to arrive, proving the federation
// transaction containing hs2's EDUs was processed
alice.MustSyncUntil(t, client.SyncReq{Since: aliceSince},
client.SyncTimelineHasEventID(sentinelRoom, sentinelEventID),
client.SyncEphemeralHas(sentinelRoom, func(result gjson.Result) bool {
return result.Get("type").Str == "m.receipt"
}),
client.SyncEphemeralHas(sentinelRoom, func(result gjson.Result) bool {
return result.Get("type").Str == "m.typing"
}),
)
charlie.MustSyncUntil(t, client.SyncReq{Since: charlieSince},
client.SyncTimelineHasEventID(sentinelRoom, sentinelEventID),
client.SyncEphemeralHas(sentinelRoom, func(result gjson.Result) bool {
return result.Get("type").Str == "m.receipt"
}),
client.SyncEphemeralHas(sentinelRoom, func(result gjson.Result) bool {
return result.Get("type").Str == "m.typing"
}),
)

for _, user := range []*client.CSAPI{alice, charlie} {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for _, user := range []*client.CSAPI{alice, charlie} {
// Verify with alice (hs1) and charlie (hs3) that we never received EDU's from hs2
for _, user := range []*client.CSAPI{alice, charlie} {

syncResp, _ := user.MustSync(t, client.SyncReq{})

// No typing or read receipts from the blocked server should appear in room with id roomID
ephemerals := syncResp.Get("rooms.join." + client.GjsonEscape(roomID) + ".ephemeral")
must.MatchGJSON(t, ephemerals, match.JSONKeyArrayOfSize("events", 0))

// sentinelRoom should have received the read receipt and typing notification
ephemerals = syncResp.Get("rooms.join." + client.GjsonEscape(sentinelRoom) + ".ephemeral")
must.MatchGJSON(t, ephemerals, match.JSONKeyArrayOfSize("events", 2))
}
}
Loading