diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 91a1588cb..5dc6d7db9 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -149,6 +149,31 @@ func Setup( return GetEvent(req, device, vars["roomID"], vars["eventID"], cfg, queryAPI, federation, keyRing) }), ).Methods(http.MethodGet, http.MethodOptions) + + r0mux.Handle("/rooms/{roomID}/state", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + vars, err := common.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return OnIncomingStateRequest(req.Context(), queryAPI, vars["roomID"]) + })).Methods(http.MethodGet, http.MethodOptions) + + r0mux.Handle("/rooms/{roomID}/state/{type}", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + vars, err := common.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return OnIncomingStateTypeRequest(req.Context(), queryAPI, vars["roomID"], vars["type"], "") + })).Methods(http.MethodGet, http.MethodOptions) + + r0mux.Handle("/rooms/{roomID}/state/{type}/{stateKey}", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + vars, err := common.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + return OnIncomingStateTypeRequest(req.Context(), queryAPI, vars["roomID"], vars["type"], vars["stateKey"]) + })).Methods(http.MethodGet, http.MethodOptions) + r0mux.Handle("/rooms/{roomID}/state/{eventType:[^/]+/?}", common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { vars, err := common.URLDecodeMapValues(mux.Vars(req)) @@ -164,6 +189,7 @@ func Setup( return SendEvent(req, device, vars["roomID"], eventType, nil, &emptyString, cfg, queryAPI, producer, nil) }), ).Methods(http.MethodPut, http.MethodOptions) + r0mux.Handle("/rooms/{roomID}/state/{eventType}/{stateKey}", common.MakeAuthAPI("send_message", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { vars, err := common.URLDecodeMapValues(mux.Vars(req)) diff --git a/syncapi/routing/state.go b/clientapi/routing/state.go similarity index 69% rename from syncapi/routing/state.go rename to clientapi/routing/state.go index 87b6396ac..c243eec0f 100644 --- a/syncapi/routing/state.go +++ b/clientapi/routing/state.go @@ -15,11 +15,12 @@ package routing import ( + "context" "encoding/json" "net/http" "github.com/matrix-org/dendrite/clientapi/jsonerror" - "github.com/matrix-org/dendrite/syncapi/storage" + "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/syncapi/types" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" @@ -39,22 +40,29 @@ type stateEventInStateResp struct { // TODO: Check if the user is in the room. If not, check if the room's history // is publicly visible. Current behaviour is returning an empty array if the // user cannot see the room's history. -func OnIncomingStateRequest(req *http.Request, db storage.Database, roomID string) util.JSONResponse { +func OnIncomingStateRequest(ctx context.Context, queryAPI api.RoomserverQueryAPI, roomID string) util.JSONResponse { // TODO(#287): Auth request and handle the case where the user has left (where // we should return the state at the poin they left) + stateReq := api.QueryLatestEventsAndStateRequest{ + RoomID: roomID, + } + stateRes := api.QueryLatestEventsAndStateResponse{} - stateFilter := gomatrixserverlib.DefaultStateFilter() - // TODO: stateFilter should not limit the number of state events (or only limits abusive number of events) - - stateEvents, err := db.GetStateEventsForRoom(req.Context(), roomID, &stateFilter) - if err != nil { - util.GetLogger(req.Context()).WithError(err).Error("db.GetStateEventsForRoom failed") + if err := queryAPI.QueryLatestEventsAndState(ctx, &stateReq, &stateRes); err != nil { + util.GetLogger(ctx).WithError(err).Error("queryAPI.QueryLatestEventsAndState failed") return jsonerror.InternalServerError() } + if len(stateRes.StateEvents) == 0 { + return util.JSONResponse{ + Code: http.StatusNotFound, + JSON: jsonerror.NotFound("cannot find state"), + } + } + resp := []stateEventInStateResp{} // Fill the prev_content and replaces_state keys if necessary - for _, event := range stateEvents { + for _, event := range stateRes.StateEvents { stateEvent := stateEventInStateResp{ ClientEvent: gomatrixserverlib.HeaderedToClientEvents( []gomatrixserverlib.HeaderedEvent{event}, gomatrixserverlib.FormatAll, @@ -63,7 +71,7 @@ func OnIncomingStateRequest(req *http.Request, db storage.Database, roomID strin var prevEventRef types.PrevEventRef if len(event.Unsigned()) > 0 { if err := json.Unmarshal(event.Unsigned(), &prevEventRef); err != nil { - util.GetLogger(req.Context()).WithError(err).Error("json.Unmarshal failed") + util.GetLogger(ctx).WithError(err).Error("json.Unmarshal failed") return jsonerror.InternalServerError() } // Fills the previous state event ID if the state event replaces another @@ -90,24 +98,32 @@ func OnIncomingStateRequest(req *http.Request, db storage.Database, roomID strin // /rooms/{roomID}/state/{type}/{statekey} request. It will look in current // state to see if there is an event with that type and state key, if there // is then (by default) we return the content, otherwise a 404. -func OnIncomingStateTypeRequest(req *http.Request, db storage.Database, roomID string, evType, stateKey string) util.JSONResponse { +func OnIncomingStateTypeRequest(ctx context.Context, queryAPI api.RoomserverQueryAPI, roomID string, evType, stateKey string) util.JSONResponse { // TODO(#287): Auth request and handle the case where the user has left (where // we should return the state at the poin they left) - - logger := util.GetLogger(req.Context()) - logger.WithFields(log.Fields{ + util.GetLogger(ctx).WithFields(log.Fields{ "roomID": roomID, "evType": evType, "stateKey": stateKey, }).Info("Fetching state") - event, err := db.GetStateEvent(req.Context(), roomID, evType, stateKey) - if err != nil { - util.GetLogger(req.Context()).WithError(err).Error("db.GetStateEvent failed") + stateReq := api.QueryLatestEventsAndStateRequest{ + RoomID: roomID, + StateToFetch: []gomatrixserverlib.StateKeyTuple{ + gomatrixserverlib.StateKeyTuple{ + EventType: evType, + StateKey: stateKey, + }, + }, + } + stateRes := api.QueryLatestEventsAndStateResponse{} + + if err := queryAPI.QueryLatestEventsAndState(ctx, &stateReq, &stateRes); err != nil { + util.GetLogger(ctx).WithError(err).Error("queryAPI.QueryLatestEventsAndState failed") return jsonerror.InternalServerError() } - if event == nil { + if len(stateRes.StateEvents) == 0 { return util.JSONResponse{ Code: http.StatusNotFound, JSON: jsonerror.NotFound("cannot find state"), @@ -115,7 +131,7 @@ func OnIncomingStateTypeRequest(req *http.Request, db storage.Database, roomID s } stateEvent := stateEventInStateResp{ - ClientEvent: gomatrixserverlib.HeaderedToClientEvent(*event, gomatrixserverlib.FormatAll), + ClientEvent: gomatrixserverlib.HeaderedToClientEvent(stateRes.StateEvents[0], gomatrixserverlib.FormatAll), } return util.JSONResponse{ diff --git a/common/events.go b/common/events.go index adbdf3389..556b7b671 100644 --- a/common/events.go +++ b/common/events.go @@ -73,6 +73,10 @@ func AddPrevEventsToEvent( return fmt.Errorf("gomatrixserverlib.StateNeededForEventBuilder: %w", err) } + if len(eventsNeeded.Tuples()) == 0 { + return errors.New("expecting state tuples for event builder, got none") + } + // Ask the roomserver for information about this room queryReq := api.QueryLatestEventsAndStateRequest{ RoomID: builder.RoomID, diff --git a/federationapi/routing/threepid.go b/federationapi/routing/threepid.go index da7174730..f93d934ed 100644 --- a/federationapi/routing/threepid.go +++ b/federationapi/routing/threepid.go @@ -271,6 +271,10 @@ func buildMembershipEvent( return nil, err } + if len(eventsNeeded.Tuples()) == 0 { + return nil, errors.New("expecting state tuples for event builder, got none") + } + // Ask the roomserver for information about this room queryReq := roomserverAPI.QueryLatestEventsAndStateRequest{ RoomID: builder.RoomID, diff --git a/roomserver/alias/alias.go b/roomserver/alias/alias.go index 032b8da1c..eb606e5cd 100644 --- a/roomserver/alias/alias.go +++ b/roomserver/alias/alias.go @@ -17,6 +17,7 @@ package alias import ( "context" "encoding/json" + "errors" "net/http" "time" @@ -218,6 +219,9 @@ func (r *RoomserverAliasAPI) sendUpdatedAliasesEvent( if err != nil { return err } + if len(eventsNeeded.Tuples()) == 0 { + return errors.New("expecting state tuples for event builder, got none") + } req := roomserverAPI.QueryLatestEventsAndStateRequest{ RoomID: roomID, StateToFetch: eventsNeeded.Tuples(), diff --git a/roomserver/query/query.go b/roomserver/query/query.go index 7e05fe36f..12d8436ef 100644 --- a/roomserver/query/query.go +++ b/roomserver/query/query.go @@ -132,10 +132,18 @@ func (r *RoomserverQueryAPI) QueryLatestEventsAndState( return err } - // Look up the current state for the requested tuples. - stateEntries, err := roomState.LoadStateAtSnapshotForStringTuples( - ctx, currentStateSnapshotNID, request.StateToFetch, - ) + var stateEntries []types.StateEntry + if len(request.StateToFetch) == 0 { + // Look up all room state. + stateEntries, err = roomState.LoadStateAtSnapshot( + ctx, currentStateSnapshotNID, + ) + } else { + // Look up the current state for the requested tuples. + stateEntries, err = roomState.LoadStateAtSnapshotForStringTuples( + ctx, currentStateSnapshotNID, request.StateToFetch, + ) + } if err != nil { return err } diff --git a/syncapi/routing/routing.go b/syncapi/routing/routing.go index be90e0a07..9078b87ff 100644 --- a/syncapi/routing/routing.go +++ b/syncapi/routing/routing.go @@ -56,30 +56,6 @@ func Setup( return srp.OnIncomingSyncRequest(req, device) })).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/rooms/{roomID}/state", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { - vars, err := common.URLDecodeMapValues(mux.Vars(req)) - if err != nil { - return util.ErrorResponse(err) - } - return OnIncomingStateRequest(req, syncDB, vars["roomID"]) - })).Methods(http.MethodGet, http.MethodOptions) - - r0mux.Handle("/rooms/{roomID}/state/{type}", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { - vars, err := common.URLDecodeMapValues(mux.Vars(req)) - if err != nil { - return util.ErrorResponse(err) - } - return OnIncomingStateTypeRequest(req, syncDB, vars["roomID"], vars["type"], "") - })).Methods(http.MethodGet, http.MethodOptions) - - r0mux.Handle("/rooms/{roomID}/state/{type}/{stateKey}", common.MakeAuthAPI("room_state", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { - vars, err := common.URLDecodeMapValues(mux.Vars(req)) - if err != nil { - return util.ErrorResponse(err) - } - return OnIncomingStateTypeRequest(req, syncDB, vars["roomID"], vars["type"], vars["stateKey"]) - })).Methods(http.MethodGet, http.MethodOptions) - r0mux.Handle("/rooms/{roomID}/messages", common.MakeAuthAPI("room_messages", authData, func(req *http.Request, device *authtypes.Device) util.JSONResponse { vars, err := common.URLDecodeMapValues(mux.Vars(req)) if err != nil { diff --git a/sytest-blacklist b/sytest-blacklist index da0667a1d..caad25455 100644 --- a/sytest-blacklist +++ b/sytest-blacklist @@ -35,3 +35,7 @@ Inbound federation rejects invites which are not signed by the sender # Blacklisted because we don't support ignores yet Ignore invite in incremental sync + +# Blacklisted because this test calls /r0/events which we don't implement +New room members see their own join event +Existing members see new members' join events diff --git a/sytest-whitelist b/sytest-whitelist index 97ded9bb8..3277cc59d 100644 --- a/sytest-whitelist +++ b/sytest-whitelist @@ -55,8 +55,9 @@ Request to logout with invalid an access token is rejected Request to logout without an access token is rejected Room creation reports m.room.create to myself Room creation reports m.room.member to myself -New room members see their own join event -Existing members see new members' join events +# Blacklisted because these tests call /r0/events which we don't implement +# New room members see their own join event +# Existing members see new members' join events setting 'm.room.power_levels' respects room powerlevel Unprivileged users can set m.room.topic if it only needs level 0 Users cannot set ban powerlevel higher than their own @@ -245,3 +246,4 @@ Remote user can backfill in a room with version 4 Outbound federation can send invites via v2 API User can invite local user to room with version 3 User can invite local user to room with version 4 +A pair of servers can establish a join in a v2 room