Reject make_join for empty rooms (#1439)

* Sanity-check room version on RS event input

* Update gomatrixserverlib

* Reject make_join when no room members are left

* Revert some changes from wrong branch

* Distinguish between room not existing and room being abandoned on this server

* nolint
main
Neil Alexander 2020-09-24 16:18:13 +01:00 committed by GitHub
parent a6700331ce
commit 3013ade84f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 139 additions and 0 deletions

View File

@ -29,6 +29,7 @@ import (
) )
// MakeJoin implements the /make_join API // MakeJoin implements the /make_join API
// nolint:gocyclo
func MakeJoin( func MakeJoin(
httpReq *http.Request, httpReq *http.Request,
request *gomatrixserverlib.FederationRequest, request *gomatrixserverlib.FederationRequest,
@ -79,6 +80,29 @@ 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.RoomExists {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound(fmt.Sprintf("Room ID %q was not found on this server", roomID)),
}
}
if !inRoomRes.IsInRoom {
return util.JSONResponse{
Code: http.StatusNotFound,
JSON: jsonerror.NotFound(fmt.Sprintf("Room ID %q has no remaining users on this server", roomID)),
}
}
// Try building an event for the server // Try building an event for the server
builder := gomatrixserverlib.EventBuilder{ builder := gomatrixserverlib.EventBuilder{
Sender: userID, Sender: userID,

View File

@ -199,6 +199,15 @@ func (t *testRoomserverAPI) QueryMembershipsForRoom(
return fmt.Errorf("not implemented") 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 // Query whether a server is allowed to see an event
func (t *testRoomserverAPI) QueryServerAllowedToSeeEvent( func (t *testRoomserverAPI) QueryServerAllowedToSeeEvent(
ctx context.Context, ctx context.Context,

View File

@ -89,6 +89,13 @@ type RoomserverInternalAPI interface {
response *QueryMembershipsForRoomResponse, response *QueryMembershipsForRoomResponse,
) error ) 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 // Query whether a server is allowed to see an event
QueryServerAllowedToSeeEvent( QueryServerAllowedToSeeEvent(
ctx context.Context, ctx context.Context,

View File

@ -134,6 +134,16 @@ func (t *RoomserverInternalAPITrace) QueryMembershipsForRoom(
return err 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( func (t *RoomserverInternalAPITrace) QueryServerAllowedToSeeEvent(
ctx context.Context, ctx context.Context,
req *QueryServerAllowedToSeeEventRequest, req *QueryServerAllowedToSeeEventRequest,

View File

@ -140,6 +140,22 @@ type QueryMembershipsForRoomResponse struct {
HasBeenInRoom bool `json:"has_been_in_room"` 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 the room exists on the server
RoomExists bool `json:"room_exists"`
// True if we still believe that we are participating in the room
IsInRoom bool `json:"is_in_room"`
}
// QueryServerAllowedToSeeEventRequest is a request to QueryServerAllowedToSeeEvent // QueryServerAllowedToSeeEventRequest is a request to QueryServerAllowedToSeeEvent
type QueryServerAllowedToSeeEventRequest struct { type QueryServerAllowedToSeeEventRequest struct {
// The event ID to look up invites in. // The event ID to look up invites in.

View File

@ -227,6 +227,50 @@ func (r *Queryer) QueryMembershipsForRoom(
return nil 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 || info.IsStub {
return nil
}
response.RoomExists = true
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 // QueryServerAllowedToSeeEvent implements api.RoomserverInternalAPI
func (r *Queryer) QueryServerAllowedToSeeEvent( func (r *Queryer) QueryServerAllowedToSeeEvent(
ctx context.Context, ctx context.Context,

View File

@ -38,6 +38,7 @@ const (
RoomserverQueryEventsByIDPath = "/roomserver/queryEventsByID" RoomserverQueryEventsByIDPath = "/roomserver/queryEventsByID"
RoomserverQueryMembershipForUserPath = "/roomserver/queryMembershipForUser" RoomserverQueryMembershipForUserPath = "/roomserver/queryMembershipForUser"
RoomserverQueryMembershipsForRoomPath = "/roomserver/queryMembershipsForRoom" RoomserverQueryMembershipsForRoomPath = "/roomserver/queryMembershipsForRoom"
RoomserverQueryServerJoinedToRoomPath = "/roomserver/queryServerJoinedToRoomPath"
RoomserverQueryServerAllowedToSeeEventPath = "/roomserver/queryServerAllowedToSeeEvent" RoomserverQueryServerAllowedToSeeEventPath = "/roomserver/queryServerAllowedToSeeEvent"
RoomserverQueryMissingEventsPath = "/roomserver/queryMissingEvents" RoomserverQueryMissingEventsPath = "/roomserver/queryMissingEvents"
RoomserverQueryStateAndAuthChainPath = "/roomserver/queryStateAndAuthChain" RoomserverQueryStateAndAuthChainPath = "/roomserver/queryStateAndAuthChain"
@ -312,6 +313,19 @@ func (h *httpRoomserverInternalAPI) QueryMembershipsForRoom(
return httputil.PostJSON(ctx, span, h.httpClient, apiURL, request, response) 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 // QueryServerAllowedToSeeEvent implements RoomserverQueryAPI
func (h *httpRoomserverInternalAPI) QueryServerAllowedToSeeEvent( func (h *httpRoomserverInternalAPI) QueryServerAllowedToSeeEvent(
ctx context.Context, ctx context.Context,

View File

@ -167,6 +167,20 @@ func AddRoutes(r api.RoomserverInternalAPI, internalAPIMux *mux.Router) {
return util.JSONResponse{Code: http.StatusOK, JSON: &response} 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( internalAPIMux.Handle(
RoomserverQueryServerAllowedToSeeEventPath, RoomserverQueryServerAllowedToSeeEventPath,
httputil.MakeInternalAPI("queryServerAllowedToSeeEvent", func(req *http.Request) util.JSONResponse { httputil.MakeInternalAPI("queryServerAllowedToSeeEvent", func(req *http.Request) util.JSONResponse {

View File

@ -477,3 +477,4 @@ Inbound federation correctly soft fails events
Inbound federation accepts a second soft-failed event 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 POST request
Federation key API can act as a notary server via a GET 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