diff options
author | devonh <devon.dmytro@gmail.com> | 2023-05-31 16:33:49 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-31 16:33:49 +0000 |
commit | ea6b368ad424a3d2e05135afb7fd0c0801b3609b (patch) | |
tree | 416132c5bdc525b9ad93911a40813c0dc3fb4439 /federationapi | |
parent | cbdc601f1b6d1c2a648b69ff44b3a49916f4d31a (diff) |
Move Invite logic to GMSL (#3086)
This is both the federation receiving & sending side logic (which were
previously entangeld in a single function)
Diffstat (limited to 'federationapi')
-rw-r--r-- | federationapi/api/api.go | 8 | ||||
-rw-r--r-- | federationapi/internal/perform.go | 47 | ||||
-rw-r--r-- | federationapi/routing/invite.go | 237 | ||||
-rw-r--r-- | federationapi/routing/join.go | 21 | ||||
-rw-r--r-- | federationapi/routing/routing.go | 20 |
5 files changed, 158 insertions, 175 deletions
diff --git a/federationapi/api/api.go b/federationapi/api/api.go index b53ec3dd..5b49e509 100644 --- a/federationapi/api/api.go +++ b/federationapi/api/api.go @@ -62,7 +62,7 @@ type RoomserverFederationAPI interface { // Handle an instruction to make_leave & send_leave with a remote server. PerformLeave(ctx context.Context, request *PerformLeaveRequest, response *PerformLeaveResponse) error // Handle sending an invite to a remote server. - PerformInvite(ctx context.Context, request *PerformInviteRequest, response *PerformInviteResponse) error + SendInvite(ctx context.Context, event gomatrixserverlib.PDU, strippedState []gomatrixserverlib.InviteStrippedState) (gomatrixserverlib.PDU, error) // Handle an instruction to peek a room on a remote server. PerformOutboundPeek(ctx context.Context, request *PerformOutboundPeekRequest, response *PerformOutboundPeekResponse) error // Query the server names of the joined hosts in a room. @@ -190,9 +190,9 @@ type PerformLeaveResponse struct { } type PerformInviteRequest struct { - RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"` - Event *rstypes.HeaderedEvent `json:"event"` - InviteRoomState []fclient.InviteV2StrippedState `json:"invite_room_state"` + RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"` + Event *rstypes.HeaderedEvent `json:"event"` + InviteRoomState []gomatrixserverlib.InviteStrippedState `json:"invite_room_state"` } type PerformInviteResponse struct { diff --git a/federationapi/internal/perform.go b/federationapi/internal/perform.go index 99943d86..ed800d03 100644 --- a/federationapi/internal/perform.go +++ b/federationapi/internal/perform.go @@ -503,60 +503,59 @@ func (r *FederationInternalAPI) PerformLeave( ) } -// PerformLeaveRequest implements api.FederationInternalAPI -func (r *FederationInternalAPI) PerformInvite( +// SendInvite implements api.FederationInternalAPI +func (r *FederationInternalAPI) SendInvite( ctx context.Context, - request *api.PerformInviteRequest, - response *api.PerformInviteResponse, -) (err error) { - _, origin, err := r.cfg.Matrix.SplitLocalID('@', request.Event.Sender()) + event gomatrixserverlib.PDU, + strippedState []gomatrixserverlib.InviteStrippedState, +) (gomatrixserverlib.PDU, error) { + _, origin, err := r.cfg.Matrix.SplitLocalID('@', event.Sender()) if err != nil { - return err + return nil, err } - if request.Event.StateKey() == nil { - return errors.New("invite must be a state event") + if event.StateKey() == nil { + return nil, errors.New("invite must be a state event") } - _, destination, err := gomatrixserverlib.SplitID('@', *request.Event.StateKey()) + _, destination, err := gomatrixserverlib.SplitID('@', *event.StateKey()) if err != nil { - return fmt.Errorf("gomatrixserverlib.SplitID: %w", err) + return nil, fmt.Errorf("gomatrixserverlib.SplitID: %w", err) } // TODO (devon): This should be allowed via a relay. Currently only transactions // can be sent to relays. Would need to extend relays to handle invites. if !r.shouldAttemptDirectFederation(destination) { - return fmt.Errorf("relay servers have no meaningful response for invite.") + return nil, fmt.Errorf("relay servers have no meaningful response for invite.") } logrus.WithFields(logrus.Fields{ - "event_id": request.Event.EventID(), - "user_id": *request.Event.StateKey(), - "room_id": request.Event.RoomID(), - "room_version": request.RoomVersion, + "event_id": event.EventID(), + "user_id": *event.StateKey(), + "room_id": event.RoomID(), + "room_version": event.Version(), "destination": destination, }).Info("Sending invite") - inviteReq, err := fclient.NewInviteV2Request(request.Event.PDU, request.InviteRoomState) + inviteReq, err := fclient.NewInviteV2Request(event, strippedState) if err != nil { - return fmt.Errorf("gomatrixserverlib.NewInviteV2Request: %w", err) + return nil, fmt.Errorf("gomatrixserverlib.NewInviteV2Request: %w", err) } inviteRes, err := r.federation.SendInviteV2(ctx, origin, destination, inviteReq) if err != nil { - return fmt.Errorf("r.federation.SendInviteV2: failed to send invite: %w", err) + return nil, fmt.Errorf("r.federation.SendInviteV2: failed to send invite: %w", err) } - verImpl, err := gomatrixserverlib.GetRoomVersion(request.RoomVersion) + verImpl, err := gomatrixserverlib.GetRoomVersion(event.Version()) if err != nil { - return err + return nil, err } inviteEvent, err := verImpl.NewEventFromUntrustedJSON(inviteRes.Event) if err != nil { - return fmt.Errorf("r.federation.SendInviteV2 failed to decode event response: %w", err) + return nil, fmt.Errorf("r.federation.SendInviteV2 failed to decode event response: %w", err) } - response.Event = &types.HeaderedEvent{PDU: inviteEvent} - return nil + return inviteEvent, nil } // PerformServersAlive implements api.FederationInternalAPI diff --git a/federationapi/routing/invite.go b/federationapi/routing/invite.go index 993d4046..78a09d94 100644 --- a/federationapi/routing/invite.go +++ b/federationapi/routing/invite.go @@ -20,7 +20,6 @@ import ( "fmt" "net/http" - "github.com/getsentry/sentry-go" "github.com/matrix-org/dendrite/roomserver/api" "github.com/matrix-org/dendrite/roomserver/types" "github.com/matrix-org/dendrite/setup/config" @@ -34,7 +33,7 @@ import ( func InviteV2( httpReq *http.Request, request *fclient.FederationRequest, - roomID string, + roomID spec.RoomID, eventID string, cfg *config.FederationAPI, rsAPI api.FederationRoomserverAPI, @@ -56,9 +55,55 @@ func InviteV2( JSON: spec.BadJSON(err.Error()), } case nil: - return processInvite( - httpReq.Context(), true, inviteReq.Event(), inviteReq.RoomVersion(), inviteReq.InviteRoomState(), roomID, eventID, cfg, rsAPI, keys, - ) + if inviteReq.Event().StateKey() == nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.BadJSON("The invite event has no state key"), + } + } + + invitedUser, userErr := spec.NewUserID(*inviteReq.Event().StateKey(), true) + if userErr != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.InvalidParam("The user ID is invalid"), + } + } + if !cfg.Matrix.IsLocalServerName(invitedUser.Domain()) { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.InvalidParam("The invited user domain does not belong to this server"), + } + } + + if inviteReq.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 invite event JSON"), + } + } + + input := gomatrixserverlib.HandleInviteInput{ + RoomVersion: inviteReq.RoomVersion(), + RoomID: roomID, + InvitedUser: *invitedUser, + KeyID: cfg.Matrix.KeyID, + PrivateKey: cfg.Matrix.PrivateKey, + Verifier: keys, + RoomQuerier: rsAPI, + MembershipQuerier: &api.MembershipQuerier{Roomserver: rsAPI}, + StateQuerier: rsAPI.StateQuerier(), + InviteEvent: inviteReq.Event(), + StrippedState: inviteReq.InviteRoomState(), + } + event, jsonErr := handleInvite(httpReq.Context(), input, rsAPI) + if jsonErr != nil { + return *jsonErr + } + return util.JSONResponse{ + Code: http.StatusOK, + JSON: fclient.RespInviteV2{Event: event.JSON()}, + } default: return util.JSONResponse{ Code: http.StatusBadRequest, @@ -71,7 +116,7 @@ func InviteV2( func InviteV1( httpReq *http.Request, request *fclient.FederationRequest, - roomID string, + roomID spec.RoomID, eventID string, cfg *config.FederationAPI, rsAPI api.FederationRoomserverAPI, @@ -94,55 +139,11 @@ func InviteV1( JSON: spec.NotJSON("The request body could not be decoded into an invite v1 request. " + err.Error()), } } - var strippedState []fclient.InviteV2StrippedState - if err := json.Unmarshal(event.Unsigned(), &strippedState); err != nil { + var strippedState []gomatrixserverlib.InviteStrippedState + if jsonErr := json.Unmarshal(event.Unsigned(), &strippedState); jsonErr != nil { // just warn, they may not have added any. util.GetLogger(httpReq.Context()).Warnf("failed to extract stripped state from invite event") } - return processInvite( - httpReq.Context(), false, event, roomVer, strippedState, roomID, eventID, cfg, rsAPI, keys, - ) -} - -func processInvite( - ctx context.Context, - isInviteV2 bool, - event gomatrixserverlib.PDU, - roomVer gomatrixserverlib.RoomVersion, - strippedState []fclient.InviteV2StrippedState, - roomID string, - eventID string, - cfg *config.FederationAPI, - rsAPI api.FederationRoomserverAPI, - keys gomatrixserverlib.JSONVerifier, -) util.JSONResponse { - - // Check that we can accept invites for this room version. - verImpl, err := gomatrixserverlib.GetRoomVersion(roomVer) - if err != nil { - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.UnsupportedRoomVersion( - fmt.Sprintf("Room version %q is not supported by this server.", roomVer), - ), - } - } - - // 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 invite 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 invite event JSON"), - } - } if event.StateKey() == nil { return util.JSONResponse{ @@ -151,105 +152,91 @@ func processInvite( } } - _, domain, err := cfg.Matrix.SplitLocalID('@', *event.StateKey()) + invitedUser, err := spec.NewUserID(*event.StateKey(), true) if err != nil { return util.JSONResponse{ Code: http.StatusBadRequest, - JSON: spec.InvalidParam(fmt.Sprintf("The user ID is invalid or domain %q does not belong to this server", domain)), + JSON: spec.InvalidParam("The user ID is invalid"), } } - - // Check that the event is signed by the server sending the request. - redacted, err := verImpl.RedactEventJSON(event.JSON()) - if err != nil { + if !cfg.Matrix.IsLocalServerName(invitedUser.Domain()) { return util.JSONResponse{ Code: http.StatusBadRequest, - JSON: spec.BadJSON("The event JSON could not be redacted"), + JSON: spec.InvalidParam("The invited user domain does not belong to this server"), } } - _, serverName, err := gomatrixserverlib.SplitID('@', event.Sender()) - if err != nil { + + if event.EventID() != eventID { return util.JSONResponse{ Code: http.StatusBadRequest, - JSON: spec.BadJSON("The event JSON contains an invalid sender"), + JSON: spec.BadJSON("The event ID in the request path must match the event ID in the invite event JSON"), } } - verifyRequests := []gomatrixserverlib.VerifyJSONRequest{{ - ServerName: serverName, - Message: redacted, - AtTS: event.OriginServerTS(), - StrictValidityChecking: true, - }} - verifyResults, err := keys.VerifyJSONs(ctx, verifyRequests) - if err != nil { - util.GetLogger(ctx).WithError(err).Error("keys.VerifyJSONs failed") - return util.JSONResponse{ - Code: http.StatusInternalServerError, - JSON: spec.InternalServerError{}, - } + + input := gomatrixserverlib.HandleInviteInput{ + RoomVersion: roomVer, + RoomID: roomID, + InvitedUser: *invitedUser, + KeyID: cfg.Matrix.KeyID, + PrivateKey: cfg.Matrix.PrivateKey, + Verifier: keys, + RoomQuerier: rsAPI, + MembershipQuerier: &api.MembershipQuerier{Roomserver: rsAPI}, + StateQuerier: rsAPI.StateQuerier(), + InviteEvent: event, + StrippedState: strippedState, } - if verifyResults[0].Error != nil { - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: spec.Forbidden("The invite must be signed by the server it originated on"), - } + event, jsonErr := handleInvite(httpReq.Context(), input, rsAPI) + if jsonErr != nil { + return *jsonErr } - - // Sign the event so that other servers will know that we have received the invite. - signedEvent := event.Sign( - string(domain), cfg.Matrix.KeyID, cfg.Matrix.PrivateKey, - ) - - // Add the invite event to the roomserver. - inviteEvent := &types.HeaderedEvent{PDU: signedEvent} - request := &api.PerformInviteRequest{ - Event: inviteEvent, - InviteRoomState: strippedState, - RoomVersion: inviteEvent.Version(), - SendAsServer: string(api.DoNotSendToOtherServers), - TransactionID: nil, + return util.JSONResponse{ + Code: http.StatusOK, + JSON: fclient.RespInvite{Event: event.JSON()}, } +} - if err = rsAPI.PerformInvite(ctx, request); err != nil { - util.GetLogger(ctx).WithError(err).Error("PerformInvite failed") - return util.JSONResponse{ +func handleInvite(ctx context.Context, input gomatrixserverlib.HandleInviteInput, rsAPI api.FederationRoomserverAPI) (gomatrixserverlib.PDU, *util.JSONResponse) { + inviteEvent, err := gomatrixserverlib.HandleInvite(ctx, input) + switch e := err.(type) { + case nil: + case spec.InternalServerError: + util.GetLogger(ctx).WithError(err) + return nil, &util.JSONResponse{ Code: http.StatusInternalServerError, JSON: spec.InternalServerError{}, } - } - - switch e := err.(type) { - case api.ErrInvalidID: - return util.JSONResponse{ - Code: http.StatusBadRequest, - JSON: spec.Unknown(e.Error()), + case spec.MatrixError: + util.GetLogger(ctx).WithError(err) + code := http.StatusInternalServerError + switch e.ErrCode { + case spec.ErrorForbidden: + code = http.StatusForbidden + case spec.ErrorUnsupportedRoomVersion: + fallthrough // http.StatusBadRequest + case spec.ErrorBadJSON: + code = http.StatusBadRequest } - case api.ErrNotAllowed: - return util.JSONResponse{ - Code: http.StatusForbidden, - JSON: spec.Forbidden(e.Error()), + + return nil, &util.JSONResponse{ + Code: code, + JSON: e, } - case nil: default: - util.GetLogger(ctx).WithError(err).Error("PerformInvite failed") - sentry.CaptureException(err) - return util.JSONResponse{ - Code: http.StatusInternalServerError, - JSON: spec.InternalServerError{}, + util.GetLogger(ctx).WithError(err) + return nil, &util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.Unknown("unknown error"), } } - // Return the signed event to the originating server, it should then tell - // the other servers in the room that we have been invited. - if isInviteV2 { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: fclient.RespInviteV2{Event: signedEvent.JSON()}, - } - } else { - return util.JSONResponse{ - Code: http.StatusOK, - JSON: fclient.RespInvite{Event: signedEvent.JSON()}, + headeredInvite := &types.HeaderedEvent{PDU: inviteEvent} + if err = rsAPI.HandleInvite(ctx, headeredInvite); err != nil { + util.GetLogger(ctx).WithError(err).Error("HandleInvite failed") + return nil, &util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: spec.InternalServerError{}, } } + return inviteEvent, nil } diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index 03d3309a..c6f96375 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -216,25 +216,6 @@ func MakeJoin( } } -type MembershipQuerier struct { - roomserver api.FederationRoomserverAPI -} - -func (mq *MembershipQuerier) CurrentMembership(ctx context.Context, roomID spec.RoomID, userID spec.UserID) (string, error) { - req := api.QueryMembershipForUserRequest{ - RoomID: roomID.String(), - UserID: userID.String(), - } - res := api.QueryMembershipForUserResponse{} - err := mq.roomserver.QueryMembershipForUser(ctx, &req, &res) - - membership := "" - if err == nil { - membership = res.Membership - } - return membership, err -} - // SendJoin implements the /send_join API // The make-join send-join dance makes much more sense as a single // flow so the cyclomatic complexity is high: @@ -268,7 +249,7 @@ func SendJoin( KeyID: cfg.Matrix.KeyID, PrivateKey: cfg.Matrix.PrivateKey, Verifier: keys, - MembershipQuerier: &MembershipQuerier{roomserver: rsAPI}, + MembershipQuerier: &api.MembershipQuerier{Roomserver: rsAPI}, } response, joinErr := gomatrixserverlib.HandleSendJoin(input) switch e := joinErr.(type) { diff --git a/federationapi/routing/routing.go b/federationapi/routing/routing.go index fad06c1c..8865022f 100644 --- a/federationapi/routing/routing.go +++ b/federationapi/routing/routing.go @@ -152,8 +152,16 @@ func Setup( JSON: spec.Forbidden("Forbidden by server ACLs"), } } + + roomID, err := spec.NewRoomID(vars["roomID"]) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.InvalidParam("Invalid RoomID"), + } + } return InviteV1( - httpReq, request, vars["roomID"], vars["eventID"], + httpReq, request, *roomID, vars["eventID"], cfg, rsAPI, keys, ) }, @@ -168,8 +176,16 @@ func Setup( JSON: spec.Forbidden("Forbidden by server ACLs"), } } + + roomID, err := spec.NewRoomID(vars["roomID"]) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.InvalidParam("Invalid RoomID"), + } + } return InviteV2( - httpReq, request, vars["roomID"], vars["eventID"], + httpReq, request, *roomID, vars["eventID"], cfg, rsAPI, keys, ) }, |