From 252830b2b0968315295759d8acfb5ae65e9c82f9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Thu, 24 Sep 2020 15:24:02 +0100 Subject: [PATCH] Reject make_join when no room members are left --- federationapi/routing/join.go | 17 ++++++++++++ federationapi/routing/send_test.go | 9 +++++++ go.mod | 2 +- go.sum | 4 +-- roomserver/api/api.go | 7 +++++ roomserver/api/api_trace.go | 10 +++++++ roomserver/api/query.go | 14 ++++++++++ roomserver/internal/query/query.go | 43 ++++++++++++++++++++++++++++++ roomserver/inthttp/client.go | 14 ++++++++++ roomserver/inthttp/server.go | 14 ++++++++++ sytest-whitelist | 1 + 11 files changed, 132 insertions(+), 3 deletions(-) diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index 36afe30ab..ef03f7a74 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -79,6 +79,23 @@ func MakeJoin( } } + // Check if we think we are still joined to the room + inRoomReq := &api.QueryServerJoinedToRoomRequest{ + ServerName: cfg.Matrix.ServerName, + RoomID: roomID, + } + inRoomRes := &api.QueryServerJoinedToRoomResponse{} + if err = rsAPI.QueryServerJoinedToRoom(httpReq.Context(), inRoomReq, inRoomRes); err != nil { + util.GetLogger(httpReq.Context()).WithError(err).Error("rsAPI.QueryServerJoinedToRoom failed") + return jsonerror.InternalServerError() + } + if !inRoomRes.IsInRoom { + return util.JSONResponse{ + Code: http.StatusNotFound, + JSON: jsonerror.NotFound("There are no remaining users in this room"), + } + } + // Try building an event for the server builder := gomatrixserverlib.EventBuilder{ Sender: userID, diff --git a/federationapi/routing/send_test.go b/federationapi/routing/send_test.go index 4f447f372..e1211ffe9 100644 --- a/federationapi/routing/send_test.go +++ b/federationapi/routing/send_test.go @@ -199,6 +199,15 @@ func (t *testRoomserverAPI) QueryMembershipsForRoom( return fmt.Errorf("not implemented") } +// Query if a server is joined to a room +func (t *testRoomserverAPI) QueryServerJoinedToRoom( + ctx context.Context, + request *api.QueryServerJoinedToRoomRequest, + response *api.QueryServerJoinedToRoomResponse, +) error { + return fmt.Errorf("not implemented") +} + // Query whether a server is allowed to see an event func (t *testRoomserverAPI) QueryServerAllowedToSeeEvent( ctx context.Context, diff --git a/go.mod b/go.mod index 52b64bbf9..971937d1c 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/matrix-org/go-http-js-libp2p v0.0.0-20200518170932-783164aeeda4 github.com/matrix-org/go-sqlite3-js v0.0.0-20200522092705-bc8506ccbcf3 github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd - github.com/matrix-org/gomatrixserverlib v0.0.0-20200924092535-0404770f91c7 + github.com/matrix-org/gomatrixserverlib v0.0.0-20200923114637-d0bf7a3c8b02 github.com/matrix-org/naffka v0.0.0-20200901083833-bcdd62999a91 github.com/matrix-org/util v0.0.0-20200807132607-55161520e1d4 github.com/mattn/go-sqlite3 v1.14.2 diff --git a/go.sum b/go.sum index ff15c92b2..5cd72b690 100644 --- a/go.sum +++ b/go.sum @@ -569,8 +569,8 @@ github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26 h1:Hr3zjRsq2bh github.com/matrix-org/gomatrix v0.0.0-20190528120928-7df988a63f26/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0= github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd h1:xVrqJK3xHREMNjwjljkAUaadalWc0rRbmVuQatzmgwg= github.com/matrix-org/gomatrix v0.0.0-20200827122206-7dd5e2a05bcd/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= -github.com/matrix-org/gomatrixserverlib v0.0.0-20200924092535-0404770f91c7 h1:bTPIIp0PEOq09pLj4diAXPgW8dCbXLE5gxJ/ex9MqEc= -github.com/matrix-org/gomatrixserverlib v0.0.0-20200924092535-0404770f91c7/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU= +github.com/matrix-org/gomatrixserverlib v0.0.0-20200923114637-d0bf7a3c8b02 h1:oos5KSWybuqmDKsiedQYBPFTzLLYaI3m2iisL0wB4yw= +github.com/matrix-org/gomatrixserverlib v0.0.0-20200923114637-d0bf7a3c8b02/go.mod h1:JsAzE1Ll3+gDWS9JSUHPJiiyAksvOOnGWF2nXdg4ZzU= github.com/matrix-org/naffka v0.0.0-20200901083833-bcdd62999a91 h1:HJ6U3S3ljJqNffYMcIeAncp5qT/i+ZMiJ2JC2F0aXP4= github.com/matrix-org/naffka v0.0.0-20200901083833-bcdd62999a91/go.mod h1:sjyPyRxKM5uw1nD2cJ6O2OxI6GOqyVBfNXqKjBZTBZE= github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7 h1:ntrLa/8xVzeSs8vHFHK25k0C+NV74sYMJnNSg5NoSRo= diff --git a/roomserver/api/api.go b/roomserver/api/api.go index 2495157a6..159c18299 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -89,6 +89,13 @@ type RoomserverInternalAPI interface { response *QueryMembershipsForRoomResponse, ) error + // Query if we think we're still in a room. + QueryServerJoinedToRoom( + ctx context.Context, + request *QueryServerJoinedToRoomRequest, + response *QueryServerJoinedToRoomResponse, + ) error + // Query whether a server is allowed to see an event QueryServerAllowedToSeeEvent( ctx context.Context, diff --git a/roomserver/api/api_trace.go b/roomserver/api/api_trace.go index b7accb9a8..5fabbc21d 100644 --- a/roomserver/api/api_trace.go +++ b/roomserver/api/api_trace.go @@ -134,6 +134,16 @@ func (t *RoomserverInternalAPITrace) QueryMembershipsForRoom( return err } +func (t *RoomserverInternalAPITrace) QueryServerJoinedToRoom( + ctx context.Context, + req *QueryServerJoinedToRoomRequest, + res *QueryServerJoinedToRoomResponse, +) error { + err := t.Impl.QueryServerJoinedToRoom(ctx, req, res) + util.GetLogger(ctx).WithError(err).Infof("QueryServerJoinedToRoom req=%+v res=%+v", js(req), js(res)) + return err +} + func (t *RoomserverInternalAPITrace) QueryServerAllowedToSeeEvent( ctx context.Context, req *QueryServerAllowedToSeeEventRequest, diff --git a/roomserver/api/query.go b/roomserver/api/query.go index 67a217c82..4b784a21f 100644 --- a/roomserver/api/query.go +++ b/roomserver/api/query.go @@ -140,6 +140,20 @@ type QueryMembershipsForRoomResponse struct { HasBeenInRoom bool `json:"has_been_in_room"` } +// QueryServerJoinedToRoomRequest is a request to QueryServerJoinedToRoom +type QueryServerJoinedToRoomRequest struct { + // Server name of the server to find + ServerName gomatrixserverlib.ServerName `json:"server_name"` + // ID of the room to see if we are still joined to + RoomID string `json:"room_id"` +} + +// QueryMembershipsForRoomResponse is a response to QueryServerJoinedToRoom +type QueryServerJoinedToRoomResponse struct { + // True if we still believe that we are participating in the room + IsInRoom bool `json:"is_in_room"` +} + // QueryServerAllowedToSeeEventRequest is a request to QueryServerAllowedToSeeEvent type QueryServerAllowedToSeeEventRequest struct { // The event ID to look up invites in. diff --git a/roomserver/internal/query/query.go b/roomserver/internal/query/query.go index fb981447f..ec89df749 100644 --- a/roomserver/internal/query/query.go +++ b/roomserver/internal/query/query.go @@ -227,6 +227,49 @@ func (r *Queryer) QueryMembershipsForRoom( return nil } +// QueryServerJoinedToRoom implements api.RoomserverInternalAPI +func (r *Queryer) QueryServerJoinedToRoom( + ctx context.Context, + request *api.QueryServerJoinedToRoomRequest, + response *api.QueryServerJoinedToRoomResponse, +) error { + info, err := r.DB.RoomInfo(ctx, request.RoomID) + if err != nil { + return fmt.Errorf("r.DB.RoomInfo: %w", err) + } + if info == nil { + return nil + } + + eventNIDs, err := r.DB.GetMembershipEventNIDsForRoom(ctx, info.RoomNID, true, false) + if err != nil { + return fmt.Errorf("r.DB.GetMembershipEventNIDsForRoom: %w", err) + } + if len(eventNIDs) == 0 { + return nil + } + + events, err := r.DB.Events(ctx, eventNIDs) + if err != nil { + return fmt.Errorf("r.DB.Events: %w", err) + } + + for _, e := range events { + if e.Type() == gomatrixserverlib.MRoomMember && e.StateKey() != nil { + _, serverName, err := gomatrixserverlib.SplitID('@', *e.StateKey()) + if err != nil { + continue + } + if serverName == request.ServerName { + response.IsInRoom = true + break + } + } + } + + return nil +} + // QueryServerAllowedToSeeEvent implements api.RoomserverInternalAPI func (r *Queryer) QueryServerAllowedToSeeEvent( ctx context.Context, diff --git a/roomserver/inthttp/client.go b/roomserver/inthttp/client.go index f2510c753..3dd3edaff 100644 --- a/roomserver/inthttp/client.go +++ b/roomserver/inthttp/client.go @@ -38,6 +38,7 @@ const ( RoomserverQueryEventsByIDPath = "/roomserver/queryEventsByID" RoomserverQueryMembershipForUserPath = "/roomserver/queryMembershipForUser" RoomserverQueryMembershipsForRoomPath = "/roomserver/queryMembershipsForRoom" + RoomserverQueryServerJoinedToRoomPath = "/roomserver/queryServerJoinedToRoomPath" RoomserverQueryServerAllowedToSeeEventPath = "/roomserver/queryServerAllowedToSeeEvent" RoomserverQueryMissingEventsPath = "/roomserver/queryMissingEvents" RoomserverQueryStateAndAuthChainPath = "/roomserver/queryStateAndAuthChain" @@ -312,6 +313,19 @@ func (h *httpRoomserverInternalAPI) QueryMembershipsForRoom( return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response) } +// QueryMembershipsForRoom implements RoomserverQueryAPI +func (h *httpRoomserverInternalAPI) QueryServerJoinedToRoom( + ctx context.Context, + request *api.QueryServerJoinedToRoomRequest, + response *api.QueryServerJoinedToRoomResponse, +) error { + span, ctx := opentracing.StartSpanFromContext(ctx, "QueryServerJoinedToRoom") + defer span.Finish() + + apiURL := h.roomserverURL + RoomserverQueryServerJoinedToRoomPath + return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response) +} + // QueryServerAllowedToSeeEvent implements RoomserverQueryAPI func (h *httpRoomserverInternalAPI) QueryServerAllowedToSeeEvent( ctx context.Context, diff --git a/roomserver/inthttp/server.go b/roomserver/inthttp/server.go index 8ffa9cf9f..c7e541dd6 100644 --- a/roomserver/inthttp/server.go +++ b/roomserver/inthttp/server.go @@ -167,6 +167,20 @@ func AddRoutes(r api.RoomserverInternalAPI, internalAPIMux *mux.Router) { return util.JSONResponse{Code: http.StatusOK, JSON: &response} }), ) + internalAPIMux.Handle( + RoomserverQueryServerJoinedToRoomPath, + httputil.MakeInternalAPI("queryServerJoinedToRoom", func(req *http.Request) util.JSONResponse { + var request api.QueryServerJoinedToRoomRequest + var response api.QueryServerJoinedToRoomResponse + if err := json.NewDecoder(req.Body).Decode(&request); err != nil { + return util.ErrorResponse(err) + } + if err := r.QueryServerJoinedToRoom(req.Context(), &request, &response); err != nil { + return util.ErrorResponse(err) + } + return util.JSONResponse{Code: http.StatusOK, JSON: &response} + }), + ) internalAPIMux.Handle( RoomserverQueryServerAllowedToSeeEventPath, httputil.MakeInternalAPI("queryServerAllowedToSeeEvent", func(req *http.Request) util.JSONResponse { diff --git a/sytest-whitelist b/sytest-whitelist index 84706b6c4..f609dd6af 100644 --- a/sytest-whitelist +++ b/sytest-whitelist @@ -477,3 +477,4 @@ Inbound federation correctly soft fails events Inbound federation accepts a second soft-failed event Federation key API can act as a notary server via a POST request Federation key API can act as a notary server via a GET request +Inbound /make_join rejects attempts to join rooms where all users have left