From 6cdfaa2b6b9c7ee5a4213431ab3de0da95ae3e9f Mon Sep 17 00:00:00 2001 From: Till Faelligen <2353100+S7evinK@users.noreply.github.com> Date: Wed, 24 May 2023 19:18:18 +0200 Subject: [PATCH] Move send_leave to GMSL --- federationapi/routing/leave.go | 184 +++++------------------------ roomserver/api/api.go | 1 + roomserver/internal/query/query.go | 24 ++++ 3 files changed, 55 insertions(+), 154 deletions(-) diff --git a/federationapi/routing/leave.go b/federationapi/routing/leave.go index 3e576e09c..aa7f69360 100644 --- a/federationapi/routing/leave.go +++ b/federationapi/routing/leave.go @@ -165,165 +165,41 @@ func SendLeave( } } - verImpl, err := gomatrixserverlib.GetRoomVersion(roomVersion) - if err != nil { - return util.JSONResponse{ - Code: http.StatusInternalServerError, - JSON: spec.UnsupportedRoomVersion( - fmt.Sprintf("QueryRoomVersionForRoom returned unknown version: %s", roomVersion), - ), - } - } + event, err := gomatrixserverlib.HandleSendLeave( + httpReq.Context(), request.Content(), request.Origin(), roomVersion, eventID, roomID, rsAPI, keys, + ) - // Decode the event JSON from the request. - event, err := verImpl.NewEventFromUntrustedJSON(request.Content()) - switch err.(type) { - case gomatrixserverlib.BadJSONError: - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON(err.Error()), - } + switch e := err.(type) { case nil: + case spec.InternalServerError: + util.GetLogger(httpReq.Context()).WithError(err).Error("failed to handle send_leave request") + return util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.InternalServerError{}, + } + case spec.MatrixError: + util.GetLogger(httpReq.Context()).WithError(err).Error("failed to handle send_leave request") + code := http.StatusInternalServerError + switch e.ErrCode { + case spec.ErrorForbidden: + code = http.StatusForbidden + case spec.ErrorNotFound: + code = http.StatusNotFound + case spec.ErrorUnsupportedRoomVersion: + code = http.StatusInternalServerError + case spec.ErrorBadJSON: + code = http.StatusBadRequest + } + + return util.JSONResponse{ + Code: code, + JSON: e, + } default: + util.GetLogger(httpReq.Context()).WithError(err).Error("failed to handle send_leave request") return util.JSONResponse{ Code: http.StatusBadRequest, - JSON: spec.NotJSON("The request body could not be decoded into valid JSON. " + err.Error()), - } - } - - // Check that the room ID is correct. - if event.RoomID() != roomID { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON("The room ID in the request path must match the room ID in the leave event JSON"), - } - } - - // Check that the event ID is correct. - if event.EventID() != eventID { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON("The event ID in the request path must match the event ID in the leave event JSON"), - } - } - - if event.StateKey() == nil || event.StateKeyEquals("") { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON("No state key was provided in the leave event."), - } - } - if !event.StateKeyEquals(event.Sender()) { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON("Event state key must match the event sender."), - } - } - - // Check that the sender belongs to the server that is sending us - // the request. By this point we've already asserted that the sender - // and the state key are equal so we don't need to check both. - var serverName spec.ServerName - if _, serverName, err = gomatrixserverlib.SplitID('@', event.Sender()); err != nil { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: spec.Forbidden("The sender of the join is invalid"), - } - } else if serverName != request.Origin() { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: spec.Forbidden("The sender does not match the server that originated the request"), - } - } - - // Check if the user has already left. If so, no-op! - queryReq := &api.QueryLatestEventsAndStateRequest{ - RoomID: roomID, - StateToFetch: []gomatrixserverlib.StateKeyTuple{ - { - EventType: spec.MRoomMember, - StateKey: *event.StateKey(), - }, - }, - } - queryRes := &api.QueryLatestEventsAndStateResponse{} - err = rsAPI.QueryLatestEventsAndState(httpReq.Context(), queryReq, queryRes) - if err != nil { - util.GetLogger(httpReq.Context()).WithError(err).Error("rsAPI.QueryLatestEventsAndState failed") - return util.JSONResponse{ - Code: http.StatusInternalServerError, - JSON: spec.InternalServerError{}, - } - } - // The room doesn't exist or we weren't ever joined to it. Might as well - // no-op here. - if !queryRes.RoomExists || len(queryRes.StateEvents) == 0 { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: struct{}{}, - } - } - // Check if we're recycling a previous leave event. - if event.EventID() == queryRes.StateEvents[0].EventID() { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: struct{}{}, - } - } - // We are/were joined/invited/banned or something. Check if - // we can no-op here. - if len(queryRes.StateEvents) == 1 { - if mem, merr := queryRes.StateEvents[0].Membership(); merr == nil && mem == spec.Leave { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: struct{}{}, - } - } - } - - // Check that the event is signed by the server sending the request. - redacted, err := verImpl.RedactEventJSON(event.JSON()) - if err != nil { - logrus.WithError(err).Errorf("XXX: leave.go") - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON("The event JSON could not be redacted"), - } - } - verifyRequests := []gomatrixserverlib.VerifyJSONRequest{{ - ServerName: serverName, - Message: redacted, - AtTS: event.OriginServerTS(), - StrictValidityChecking: true, - }} - verifyResults, err := keys.VerifyJSONs(httpReq.Context(), verifyRequests) - if err != nil { - util.GetLogger(httpReq.Context()).WithError(err).Error("keys.VerifyJSONs failed") - return util.JSONResponse{ - Code: http.StatusInternalServerError, - JSON: spec.InternalServerError{}, - } - } - if verifyResults[0].Error != nil { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: spec.Forbidden("The leave must be signed by the server it originated on"), - } - } - - // check membership is set to leave - mem, err := event.Membership() - if err != nil { - util.GetLogger(httpReq.Context()).WithError(err).Error("event.Membership failed") - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON("missing content.membership key"), - } - } - if mem != spec.Leave { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON("The membership in the event content must be set to leave"), + JSON: spec.Unknown("unknown error"), } } diff --git a/roomserver/api/api.go b/roomserver/api/api.go index 213e16e5d..0627770f5 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -197,6 +197,7 @@ type UserRoomserverAPI interface { } type FederationRoomserverAPI interface { + gomatrixserverlib.LatestStateQuerier InputRoomEventsAPI QueryLatestEventsAndStateAPI QueryBulkStateContentAPI diff --git a/roomserver/internal/query/query.go b/roomserver/internal/query/query.go index effcc90d7..c81692ce1 100644 --- a/roomserver/internal/query/query.go +++ b/roomserver/internal/query/query.go @@ -55,6 +55,30 @@ func (r *Queryer) QueryLatestEventsAndState( return helpers.QueryLatestEventsAndState(ctx, r.DB, request, response) } +func (r *Queryer) LatestState( + ctx context.Context, roomID spec.RoomID, userID spec.UserID, +) ([]gomatrixserverlib.PDU, error) { + request := &api.QueryLatestEventsAndStateRequest{ + RoomID: roomID.String(), + StateToFetch: []gomatrixserverlib.StateKeyTuple{ + { + EventType: spec.MRoomMember, + StateKey: userID.String(), + }, + }, + } + response := &api.QueryLatestEventsAndStateResponse{} + err := helpers.QueryLatestEventsAndState(ctx, r.DB, request, response) + if err != nil { + return nil, err + } + res := make([]gomatrixserverlib.PDU, 0, len(response.StateEvents)) + for _, ev := range response.StateEvents { + res = append(res, ev.PDU) + } + return res, nil +} + // QueryStateAfterEvents implements api.RoomserverInternalAPI func (r *Queryer) QueryStateAfterEvents( ctx context.Context,