diff --git a/federationapi/routing/leave.go b/federationapi/routing/leave.go index 7c86ba69bd..2612f61013 100644 --- a/federationapi/routing/leave.go +++ b/federationapi/routing/leave.go @@ -176,173 +176,72 @@ func SendLeave( keys gomatrixserverlib.JSONVerifier, roomID, eventID string, ) util.JSONResponse { - roomVersion, err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), roomID) - if err != nil { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.UnsupportedRoomVersion(err.Error()), - } - } - - verImpl, err := gomatrixserverlib.GetRoomVersion(roomVersion) + parsedRoomID, err := spec.NewRoomID(roomID) if err != nil { return util.JSONResponse{ Code: http.StatusInternalServerError, - JSON: spec.UnsupportedRoomVersion( - fmt.Sprintf("QueryRoomVersionForRoom returned unknown version: %s", roomVersion), - ), - } - } - - // 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()), - } - case nil: - default: - 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().String() != 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(string(event.SenderID())) { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON("Event state key must match the event sender."), + JSON: spec.InternalServerError{}, } } - - // 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. - sender, err := rsAPI.QueryUserIDForSender(httpReq.Context(), event.RoomID(), event.SenderID()) + roomInfo, err := rsAPI.QueryRoomInfo(httpReq.Context(), *parsedRoomID) if err != nil { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: spec.Forbidden("The sender of the join is invalid"), - } - } else if sender.Domain() != 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 { + // Either the room does not exist or does not + // have any events. + if roomInfo == nil || roomInfo.IsStub() { 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: sender.Domain(), - Message: redacted, - AtTS: event.OriginServerTS(), - ValidityCheckingFunc: gomatrixserverlib.StrictValiditySignatureCheck, - }} - verifyResults, err := keys.VerifyJSONs(httpReq.Context(), verifyRequests) - if err != nil { - util.GetLogger(httpReq.Context()).WithError(err).Error("keys.VerifyJSONs failed") + leaveEvent, err := gomatrixserverlib.HandleSendLeave( + httpReq.Context(), request.Content(), request.Origin(), roomInfo.RoomVersion, eventID, roomID, rsAPI, keys) + + 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{}, } - } - 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"), + 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 } - } - // 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: 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.BadJSON("missing content.membership key"), + JSON: spec.Unknown("unknown error"), } } - if mem != spec.Leave { + + // The user was never joined to the room, has already left, + // or we already processed this leave event, so this is a no-op. + if leaveEvent == nil { return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.BadJSON("The membership in the event content must be set to leave"), + Code: http.StatusOK, + JSON: struct{}{}, } } @@ -354,7 +253,7 @@ func SendLeave( InputRoomEvents: []api.InputRoomEvent{ { Kind: api.KindNew, - Event: &types.HeaderedEvent{PDU: event}, + Event: &types.HeaderedEvent{PDU: leaveEvent}, SendAsServer: string(cfg.Matrix.ServerName), TransactionID: nil, }, diff --git a/go.mod b/go.mod index 8dad4ebf02..fc666f9a2a 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 - github.com/matrix-org/gomatrixserverlib v0.0.0-20231024124730-58af9a2712ca + github.com/matrix-org/gomatrixserverlib v0.0.0-20231123125815-185a49a8c72d github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7 github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 github.com/mattn/go-sqlite3 v1.14.17 diff --git a/go.sum b/go.sum index b173f6b851..4d2aea84ce 100644 --- a/go.sum +++ b/go.sum @@ -208,8 +208,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 h1:s7fexw github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= -github.com/matrix-org/gomatrixserverlib v0.0.0-20231024124730-58af9a2712ca h1:JCP72vU4Vcmur2071RwYVOSoekR+ZjbC03wZD5lAAK0= -github.com/matrix-org/gomatrixserverlib v0.0.0-20231024124730-58af9a2712ca/go.mod h1:M8m7seOroO5ePlgxA7AFZymnG90Cnh94rYQyngSrZkk= +github.com/matrix-org/gomatrixserverlib v0.0.0-20231123125815-185a49a8c72d h1:gdXFfDL6daF/mf6PihcArRITjbT5HJGApkaHR9Z3KWs= +github.com/matrix-org/gomatrixserverlib v0.0.0-20231123125815-185a49a8c72d/go.mod h1:M8m7seOroO5ePlgxA7AFZymnG90Cnh94rYQyngSrZkk= github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7 h1:6t8kJr8i1/1I5nNttw6nn1ryQJgzVlBmSGgPiiaTdw4= github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7/go.mod h1:ReWMS/LoVnOiRAdq9sNUC2NZnd1mZkMNB52QhpTRWjg= github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y=