aboutsummaryrefslogtreecommitdiff
path: root/syncapi/routing
diff options
context:
space:
mode:
authorTill <2353100+S7evinK@users.noreply.github.com>2022-08-11 18:23:35 +0200
committerGitHub <noreply@github.com>2022-08-11 18:23:35 +0200
commit05cafbd197c99c0e116c9b61447e70ba5af992a3 (patch)
treef96dbf70e30b2a255f2b19574188115dda8e6145 /syncapi/routing
parent371336c6b5ffd510802d06b193a48b01a5e78d0c (diff)
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>
Diffstat (limited to 'syncapi/routing')
-rw-r--r--syncapi/routing/context.go99
-rw-r--r--syncapi/routing/messages.go100
2 files changed, 92 insertions, 107 deletions
diff --git a/syncapi/routing/context.go b/syncapi/routing/context.go
index f6b4d15e..13c4e9d8 100644
--- a/syncapi/routing/context.go
+++ b/syncapi/routing/context.go
@@ -21,10 +21,12 @@ import (
"fmt"
"net/http"
"strconv"
+ "time"
"github.com/matrix-org/dendrite/clientapi/jsonerror"
"github.com/matrix-org/dendrite/internal/caching"
roomserver "github.com/matrix-org/dendrite/roomserver/api"
+ "github.com/matrix-org/dendrite/syncapi/internal"
"github.com/matrix-org/dendrite/syncapi/storage"
"github.com/matrix-org/dendrite/syncapi/types"
userapi "github.com/matrix-org/dendrite/userapi/api"
@@ -95,24 +97,6 @@ func Context(
ContainsURL: filter.ContainsURL,
}
- // TODO: Get the actual state at the last event returned by SelectContextAfterEvent
- state, _ := syncDB.CurrentState(ctx, roomID, &stateFilter, nil)
- // verify the user is allowed to see the context for this room/event
- for _, x := range state {
- var hisVis gomatrixserverlib.HistoryVisibility
- hisVis, err = x.HistoryVisibility()
- if err != nil {
- continue
- }
- allowed := hisVis == gomatrixserverlib.WorldReadable || membershipRes.Membership == gomatrixserverlib.Join
- if !allowed {
- return util.JSONResponse{
- Code: http.StatusForbidden,
- JSON: jsonerror.Forbidden("User is not allowed to query context"),
- }
- }
- }
-
id, requestedEvent, err := syncDB.SelectContextEvent(ctx, roomID, eventID)
if err != nil {
if err == sql.ErrNoRows {
@@ -125,6 +109,24 @@ func Context(
return jsonerror.InternalServerError()
}
+ // verify the user is allowed to see the context for this room/event
+ startTime := time.Now()
+ filteredEvents, err := internal.ApplyHistoryVisibilityFilter(ctx, syncDB, rsAPI, []*gomatrixserverlib.HeaderedEvent{&requestedEvent}, nil, device.UserID, "context")
+ if err != nil {
+ logrus.WithError(err).Error("unable to apply history visibility filter")
+ return jsonerror.InternalServerError()
+ }
+ logrus.WithFields(logrus.Fields{
+ "duration": time.Since(startTime),
+ "room_id": roomID,
+ }).Debug("applied history visibility (context)")
+ if len(filteredEvents) == 0 {
+ return util.JSONResponse{
+ Code: http.StatusForbidden,
+ JSON: jsonerror.Forbidden("User is not allowed to query context"),
+ }
+ }
+
eventsBefore, err := syncDB.SelectContextBeforeEvent(ctx, id, roomID, filter)
if err != nil && err != sql.ErrNoRows {
logrus.WithError(err).Error("unable to fetch before events")
@@ -137,8 +139,27 @@ func Context(
return jsonerror.InternalServerError()
}
- eventsBeforeClient := gomatrixserverlib.HeaderedToClientEvents(eventsBefore, gomatrixserverlib.FormatAll)
- eventsAfterClient := gomatrixserverlib.HeaderedToClientEvents(eventsAfter, gomatrixserverlib.FormatAll)
+ startTime = time.Now()
+ eventsBeforeFiltered, eventsAfterFiltered, err := applyHistoryVisibilityOnContextEvents(ctx, syncDB, rsAPI, eventsBefore, eventsAfter, device.UserID)
+ if err != nil {
+ logrus.WithError(err).Error("unable to apply history visibility filter")
+ return jsonerror.InternalServerError()
+ }
+
+ logrus.WithFields(logrus.Fields{
+ "duration": time.Since(startTime),
+ "room_id": roomID,
+ }).Debug("applied history visibility (context eventsBefore/eventsAfter)")
+
+ // TODO: Get the actual state at the last event returned by SelectContextAfterEvent
+ state, err := syncDB.CurrentState(ctx, roomID, &stateFilter, nil)
+ if err != nil {
+ logrus.WithError(err).Error("unable to fetch current room state")
+ return jsonerror.InternalServerError()
+ }
+
+ eventsBeforeClient := gomatrixserverlib.HeaderedToClientEvents(eventsBeforeFiltered, gomatrixserverlib.FormatAll)
+ eventsAfterClient := gomatrixserverlib.HeaderedToClientEvents(eventsAfterFiltered, gomatrixserverlib.FormatAll)
newState := applyLazyLoadMembers(device, filter, eventsAfterClient, eventsBeforeClient, state, lazyLoadCache)
response := ContextRespsonse{
@@ -162,6 +183,44 @@ func Context(
}
}
+// applyHistoryVisibilityOnContextEvents is a helper function to avoid roundtrips to the roomserver
+// by combining the events before and after the context event. Returns the filtered events,
+// and an error, if any.
+func applyHistoryVisibilityOnContextEvents(
+ ctx context.Context, syncDB storage.Database, rsAPI roomserver.SyncRoomserverAPI,
+ eventsBefore, eventsAfter []*gomatrixserverlib.HeaderedEvent,
+ userID string,
+) (filteredBefore, filteredAfter []*gomatrixserverlib.HeaderedEvent, err error) {
+ eventIDsBefore := make(map[string]struct{}, len(eventsBefore))
+ eventIDsAfter := make(map[string]struct{}, len(eventsAfter))
+
+ // Remember before/after eventIDs, so we can restore them
+ // after applying history visibility checks
+ for _, ev := range eventsBefore {
+ eventIDsBefore[ev.EventID()] = struct{}{}
+ }
+ for _, ev := range eventsAfter {
+ eventIDsAfter[ev.EventID()] = struct{}{}
+ }
+
+ allEvents := append(eventsBefore, eventsAfter...)
+ filteredEvents, err := internal.ApplyHistoryVisibilityFilter(ctx, syncDB, rsAPI, allEvents, nil, userID, "context")
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // "Restore" events in the correct context
+ for _, ev := range filteredEvents {
+ if _, ok := eventIDsBefore[ev.EventID()]; ok {
+ filteredBefore = append(filteredBefore, ev)
+ }
+ if _, ok := eventIDsAfter[ev.EventID()]; ok {
+ filteredAfter = append(filteredAfter, ev)
+ }
+ }
+ return filteredBefore, filteredAfter, nil
+}
+
func getStartEnd(ctx context.Context, syncDB storage.Database, startEvents, endEvents []*gomatrixserverlib.HeaderedEvent) (start, end types.TopologyToken, err error) {
if len(startEvents) > 0 {
start, err = syncDB.EventPositionInTopology(ctx, startEvents[0].EventID())
diff --git a/syncapi/routing/messages.go b/syncapi/routing/messages.go
index b4c9a542..9db3d8e1 100644
--- a/syncapi/routing/messages.go
+++ b/syncapi/routing/messages.go
@@ -19,6 +19,7 @@ import (
"fmt"
"net/http"
"sort"
+ "time"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
@@ -28,6 +29,7 @@ import (
"github.com/matrix-org/dendrite/internal/caching"
"github.com/matrix-org/dendrite/roomserver/api"
"github.com/matrix-org/dendrite/setup/config"
+ "github.com/matrix-org/dendrite/syncapi/internal"
"github.com/matrix-org/dendrite/syncapi/storage"
"github.com/matrix-org/dendrite/syncapi/sync"
"github.com/matrix-org/dendrite/syncapi/types"
@@ -324,6 +326,9 @@ func (r *messagesReq) retrieveEvents() (
// reliable way to define it), it would be easier and less troublesome to
// only have to change it in one place, i.e. the database.
start, end, err = r.getStartEnd(events)
+ if err != nil {
+ return []gomatrixserverlib.ClientEvent{}, *r.from, *r.to, err
+ }
// Sort the events to ensure we send them in the right order.
if r.backwardOrdering {
@@ -337,97 +342,18 @@ func (r *messagesReq) retrieveEvents() (
}
events = reversed(events)
}
- events = r.filterHistoryVisible(events)
if len(events) == 0 {
return []gomatrixserverlib.ClientEvent{}, *r.from, *r.to, nil
}
- // Convert all of the events into client events.
- clientEvents = gomatrixserverlib.HeaderedToClientEvents(events, gomatrixserverlib.FormatAll)
- return clientEvents, start, end, err
-}
-
-func (r *messagesReq) filterHistoryVisible(events []*gomatrixserverlib.HeaderedEvent) []*gomatrixserverlib.HeaderedEvent {
- // TODO FIXME: We don't fully implement history visibility yet. To avoid leaking events which the
- // user shouldn't see, we check the recent events and remove any prior to the join event of the user
- // which is equiv to history_visibility: joined
- joinEventIndex := -1
- for i, ev := range events {
- if ev.Type() == gomatrixserverlib.MRoomMember && ev.StateKeyEquals(r.device.UserID) {
- membership, _ := ev.Membership()
- if membership == "join" {
- joinEventIndex = i
- break
- }
- }
- }
-
- var result []*gomatrixserverlib.HeaderedEvent
- var eventsToCheck []*gomatrixserverlib.HeaderedEvent
- if joinEventIndex != -1 {
- if r.backwardOrdering {
- result = events[:joinEventIndex+1]
- eventsToCheck = append(eventsToCheck, result[0])
- } else {
- result = events[joinEventIndex:]
- eventsToCheck = append(eventsToCheck, result[len(result)-1])
- }
- } else {
- eventsToCheck = []*gomatrixserverlib.HeaderedEvent{events[0], events[len(events)-1]}
- result = events
- }
- // make sure the user was in the room for both the earliest and latest events, we need this because
- // some backpagination results will not have the join event (e.g if they hit /messages at the join event itself)
- wasJoined := true
- for _, ev := range eventsToCheck {
- var queryRes api.QueryStateAfterEventsResponse
- err := r.rsAPI.QueryStateAfterEvents(r.ctx, &api.QueryStateAfterEventsRequest{
- RoomID: ev.RoomID(),
- PrevEventIDs: ev.PrevEventIDs(),
- StateToFetch: []gomatrixserverlib.StateKeyTuple{
- {EventType: gomatrixserverlib.MRoomMember, StateKey: r.device.UserID},
- {EventType: gomatrixserverlib.MRoomHistoryVisibility, StateKey: ""},
- },
- }, &queryRes)
- if err != nil {
- wasJoined = false
- break
- }
- var hisVisEvent, membershipEvent *gomatrixserverlib.HeaderedEvent
- for i := range queryRes.StateEvents {
- switch queryRes.StateEvents[i].Type() {
- case gomatrixserverlib.MRoomMember:
- membershipEvent = queryRes.StateEvents[i]
- case gomatrixserverlib.MRoomHistoryVisibility:
- hisVisEvent = queryRes.StateEvents[i]
- }
- }
- if hisVisEvent == nil {
- return events // apply no filtering as it defaults to Shared.
- }
- hisVis, _ := hisVisEvent.HistoryVisibility()
- if hisVis == "shared" || hisVis == "world_readable" {
- return events // apply no filtering
- }
- if membershipEvent == nil {
- wasJoined = false
- break
- }
- membership, err := membershipEvent.Membership()
- if err != nil {
- wasJoined = false
- break
- }
- if membership != "join" {
- wasJoined = false
- break
- }
- }
- if !wasJoined {
- util.GetLogger(r.ctx).WithField("num_events", len(events)).Warnf("%s was not joined to room during these events, omitting them", r.device.UserID)
- return []*gomatrixserverlib.HeaderedEvent{}
- }
- return result
+ // Apply room history visibility filter
+ startTime := time.Now()
+ filteredEvents, err := internal.ApplyHistoryVisibilityFilter(r.ctx, r.db, r.rsAPI, events, nil, r.device.UserID, "messages")
+ logrus.WithFields(logrus.Fields{
+ "duration": time.Since(startTime),
+ "room_id": r.roomID,
+ }).Debug("applied history visibility (messages)")
+ return gomatrixserverlib.HeaderedToClientEvents(filteredEvents, gomatrixserverlib.FormatAll), start, end, err
}
func (r *messagesReq) getStartEnd(events []*gomatrixserverlib.HeaderedEvent) (start, end types.TopologyToken, err error) {