mirror of
https://github.com/element-hq/dendrite.git
synced 2025-09-13 12:52:24 +03:00
Implement history visibility on /messages
, /context
, /sync
(#2511)
* Add possibility to set history_visibility and user AccountType * Add new DB queries * Add actual history_visibility changes for /messages * Add passing tests * Extract check function * Cleanup * Cleanup * Fix build on 386 * Move ApplyHistoryVisibilityFilter to internal * Move queries to topology table * Add filtering to /sync and /context Some cleanup * Add passing tests; Remove failing tests :( * Re-add passing tests * Move filtering to own function to avoid duplication * Re-add passing test * Use newly added GMSL HistoryVisibility * Update gomatrixserverlib * Set the visibility when creating events * Default to shared history visibility * Remove unused query * Update history visibility checks to use gmsl Update tests * Remove unused statement * Update migrations to set "correct" history visibility * Add method to fetch the membership at a given event * Tweaks and logging * Use actual internal rsAPI, default to shared visibility in tests * Revert "Move queries to topology table" This reverts commit 4f0d41be9c194a46379796435ce73e79203edbd6. * Remove noise/unneeded code * More cleanup * Try to optimize database requests * Fix imports * PR peview fixes/changes * Move setting history visibility to own migration, be more restrictive * Fix unit tests * Lint * Fix missing entries * Tweaks for incremental syncs * Adapt generic changes Co-authored-by: Neil Alexander <neilalexander@users.noreply.github.com> Co-authored-by: kegsay <kegan@matrix.org>
This commit is contained in:
parent
371336c6b5
commit
05cafbd197
31 changed files with 1043 additions and 224 deletions
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
"github.com/matrix-org/dendrite/clientapi/producers"
|
||||
keyapi "github.com/matrix-org/dendrite/keyserver/api"
|
||||
"github.com/matrix-org/dendrite/roomserver"
|
||||
"github.com/matrix-org/dendrite/roomserver/api"
|
||||
rsapi "github.com/matrix-org/dendrite/roomserver/api"
|
||||
"github.com/matrix-org/dendrite/setup/base"
|
||||
|
@ -54,6 +55,16 @@ func (s *syncRoomserverAPI) QueryBulkStateContent(ctx context.Context, req *rsap
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *syncRoomserverAPI) QueryMembershipForUser(ctx context.Context, req *rsapi.QueryMembershipForUserRequest, res *rsapi.QueryMembershipForUserResponse) error {
|
||||
res.IsRoomForgotten = false
|
||||
res.RoomExists = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *syncRoomserverAPI) QueryMembershipAtEvent(ctx context.Context, req *rsapi.QueryMembershipAtEventRequest, res *rsapi.QueryMembershipAtEventResponse) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type syncUserAPI struct {
|
||||
userapi.SyncUserAPI
|
||||
accounts []userapi.Device
|
||||
|
@ -107,7 +118,7 @@ func testSyncAccessTokens(t *testing.T, dbType test.DBType) {
|
|||
|
||||
jsctx, _ := base.NATS.Prepare(base.ProcessContext, &base.Cfg.Global.JetStream)
|
||||
defer jetstream.DeleteAllStreams(jsctx, &base.Cfg.Global.JetStream)
|
||||
msgs := toNATSMsgs(t, base, room.Events())
|
||||
msgs := toNATSMsgs(t, base, room.Events()...)
|
||||
AddPublicRoutes(base, &syncUserAPI{accounts: []userapi.Device{alice}}, &syncRoomserverAPI{rooms: []*test.Room{room}}, &syncKeyAPI{})
|
||||
testrig.MustPublishMsgs(t, jsctx, msgs...)
|
||||
|
||||
|
@ -200,7 +211,7 @@ func testSyncAPICreateRoomSyncEarly(t *testing.T, dbType test.DBType) {
|
|||
// m.room.power_levels
|
||||
// m.room.join_rules
|
||||
// m.room.history_visibility
|
||||
msgs := toNATSMsgs(t, base, room.Events())
|
||||
msgs := toNATSMsgs(t, base, room.Events()...)
|
||||
sinceTokens := make([]string, len(msgs))
|
||||
AddPublicRoutes(base, &syncUserAPI{accounts: []userapi.Device{alice}}, &syncRoomserverAPI{rooms: []*test.Room{room}}, &syncKeyAPI{})
|
||||
for i, msg := range msgs {
|
||||
|
@ -315,6 +326,174 @@ func testSyncAPIUpdatePresenceImmediately(t *testing.T, dbType test.DBType) {
|
|||
|
||||
}
|
||||
|
||||
// This is mainly what Sytest is doing in "test_history_visibility"
|
||||
func TestMessageHistoryVisibility(t *testing.T) {
|
||||
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
|
||||
testHistoryVisibility(t, dbType)
|
||||
})
|
||||
}
|
||||
|
||||
func testHistoryVisibility(t *testing.T, dbType test.DBType) {
|
||||
type result struct {
|
||||
seeWithoutJoin bool
|
||||
seeBeforeJoin bool
|
||||
seeAfterInvite bool
|
||||
}
|
||||
|
||||
// create the users
|
||||
alice := test.NewUser(t)
|
||||
bob := test.NewUser(t)
|
||||
|
||||
bobDev := userapi.Device{
|
||||
ID: "BOBID",
|
||||
UserID: bob.ID,
|
||||
AccessToken: "BOD_BEARER_TOKEN",
|
||||
DisplayName: "BOB",
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
// check guest and normal user accounts
|
||||
for _, accType := range []userapi.AccountType{userapi.AccountTypeGuest, userapi.AccountTypeUser} {
|
||||
testCases := []struct {
|
||||
historyVisibility gomatrixserverlib.HistoryVisibility
|
||||
wantResult result
|
||||
}{
|
||||
{
|
||||
historyVisibility: gomatrixserverlib.HistoryVisibilityWorldReadable,
|
||||
wantResult: result{
|
||||
seeWithoutJoin: true,
|
||||
seeBeforeJoin: true,
|
||||
seeAfterInvite: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
historyVisibility: gomatrixserverlib.HistoryVisibilityShared,
|
||||
wantResult: result{
|
||||
seeWithoutJoin: false,
|
||||
seeBeforeJoin: true,
|
||||
seeAfterInvite: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
historyVisibility: gomatrixserverlib.HistoryVisibilityInvited,
|
||||
wantResult: result{
|
||||
seeWithoutJoin: false,
|
||||
seeBeforeJoin: false,
|
||||
seeAfterInvite: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
historyVisibility: gomatrixserverlib.HistoryVisibilityJoined,
|
||||
wantResult: result{
|
||||
seeWithoutJoin: false,
|
||||
seeBeforeJoin: false,
|
||||
seeAfterInvite: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
bobDev.AccountType = accType
|
||||
userType := "guest"
|
||||
if accType == userapi.AccountTypeUser {
|
||||
userType = "real user"
|
||||
}
|
||||
|
||||
base, close := testrig.CreateBaseDendrite(t, dbType)
|
||||
defer close()
|
||||
|
||||
jsctx, _ := base.NATS.Prepare(base.ProcessContext, &base.Cfg.Global.JetStream)
|
||||
defer jetstream.DeleteAllStreams(jsctx, &base.Cfg.Global.JetStream)
|
||||
|
||||
// Use the actual internal roomserver API
|
||||
rsAPI := roomserver.NewInternalAPI(base)
|
||||
rsAPI.SetFederationAPI(nil, nil)
|
||||
|
||||
AddPublicRoutes(base, &syncUserAPI{accounts: []userapi.Device{bobDev}}, rsAPI, &syncKeyAPI{})
|
||||
|
||||
for _, tc := range testCases {
|
||||
testname := fmt.Sprintf("%s - %s", tc.historyVisibility, userType)
|
||||
t.Run(testname, func(t *testing.T) {
|
||||
// create a room with the given visibility
|
||||
room := test.NewRoom(t, alice, test.RoomHistoryVisibility(tc.historyVisibility))
|
||||
|
||||
// send the events/messages to NATS to create the rooms
|
||||
beforeJoinEv := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": fmt.Sprintf("Before invite in a %s room", tc.historyVisibility)})
|
||||
eventsToSend := append(room.Events(), beforeJoinEv)
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, eventsToSend, "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
// There is only one event, we expect only to be able to see this, if the room is world_readable
|
||||
w := httptest.NewRecorder()
|
||||
base.PublicClientAPIMux.ServeHTTP(w, test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/messages", room.ID), test.WithQueryParams(map[string]string{
|
||||
"access_token": bobDev.AccessToken,
|
||||
"dir": "b",
|
||||
})))
|
||||
if w.Code != 200 {
|
||||
t.Logf("%s", w.Body.String())
|
||||
t.Fatalf("got HTTP %d want %d", w.Code, 200)
|
||||
}
|
||||
// We only care about the returned events at this point
|
||||
var res struct {
|
||||
Chunk []gomatrixserverlib.ClientEvent `json:"chunk"`
|
||||
}
|
||||
if err := json.NewDecoder(w.Body).Decode(&res); err != nil {
|
||||
t.Errorf("failed to decode response body: %s", err)
|
||||
}
|
||||
|
||||
verifyEventVisible(t, tc.wantResult.seeWithoutJoin, beforeJoinEv, res.Chunk)
|
||||
|
||||
// Create invite, a message, join the room and create another message.
|
||||
inviteEv := room.CreateAndInsert(t, alice, "m.room.member", map[string]interface{}{"membership": "invite"}, test.WithStateKey(bob.ID))
|
||||
afterInviteEv := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": fmt.Sprintf("After invite in a %s room", tc.historyVisibility)})
|
||||
joinEv := room.CreateAndInsert(t, bob, "m.room.member", map[string]interface{}{"membership": "join"}, test.WithStateKey(bob.ID))
|
||||
msgEv := room.CreateAndInsert(t, alice, "m.room.message", map[string]interface{}{"body": fmt.Sprintf("After join in a %s room", tc.historyVisibility)})
|
||||
|
||||
eventsToSend = append([]*gomatrixserverlib.HeaderedEvent{}, inviteEv, afterInviteEv, joinEv, msgEv)
|
||||
|
||||
if err := api.SendEvents(ctx, rsAPI, api.KindNew, eventsToSend, "test", "test", nil, false); err != nil {
|
||||
t.Fatalf("failed to send events: %v", err)
|
||||
}
|
||||
|
||||
// Verify the messages after/before invite are visible or not
|
||||
w = httptest.NewRecorder()
|
||||
base.PublicClientAPIMux.ServeHTTP(w, test.NewRequest(t, "GET", fmt.Sprintf("/_matrix/client/v3/rooms/%s/messages", room.ID), test.WithQueryParams(map[string]string{
|
||||
"access_token": bobDev.AccessToken,
|
||||
"dir": "b",
|
||||
})))
|
||||
if w.Code != 200 {
|
||||
t.Logf("%s", w.Body.String())
|
||||
t.Fatalf("got HTTP %d want %d", w.Code, 200)
|
||||
}
|
||||
if err := json.NewDecoder(w.Body).Decode(&res); err != nil {
|
||||
t.Errorf("failed to decode response body: %s", err)
|
||||
}
|
||||
// verify results
|
||||
verifyEventVisible(t, tc.wantResult.seeBeforeJoin, beforeJoinEv, res.Chunk)
|
||||
verifyEventVisible(t, tc.wantResult.seeAfterInvite, afterInviteEv, res.Chunk)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyEventVisible(t *testing.T, wantVisible bool, wantVisibleEvent *gomatrixserverlib.HeaderedEvent, chunk []gomatrixserverlib.ClientEvent) {
|
||||
t.Helper()
|
||||
if wantVisible {
|
||||
for _, ev := range chunk {
|
||||
if ev.EventID == wantVisibleEvent.EventID() {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatalf("expected to see event %s but didn't: %+v", wantVisibleEvent.EventID(), chunk)
|
||||
} else {
|
||||
for _, ev := range chunk {
|
||||
if ev.EventID == wantVisibleEvent.EventID() {
|
||||
t.Fatalf("expected not to see event %s: %+v", wantVisibleEvent.EventID(), string(ev.Content))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendToDevice(t *testing.T) {
|
||||
test.WithAllDatabases(t, testSendToDevice)
|
||||
}
|
||||
|
@ -448,7 +627,7 @@ func testSendToDevice(t *testing.T, dbType test.DBType) {
|
|||
}
|
||||
}
|
||||
|
||||
func toNATSMsgs(t *testing.T, base *base.BaseDendrite, input []*gomatrixserverlib.HeaderedEvent) []*nats.Msg {
|
||||
func toNATSMsgs(t *testing.T, base *base.BaseDendrite, input ...*gomatrixserverlib.HeaderedEvent) []*nats.Msg {
|
||||
result := make([]*nats.Msg, len(input))
|
||||
for i, ev := range input {
|
||||
var addsStateIDs []string
|
||||
|
@ -460,6 +639,7 @@ func toNATSMsgs(t *testing.T, base *base.BaseDendrite, input []*gomatrixserverli
|
|||
NewRoomEvent: &rsapi.OutputNewRoomEvent{
|
||||
Event: ev,
|
||||
AddsStateEventIDs: addsStateIDs,
|
||||
HistoryVisibility: ev.Visibility,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue