aboutsummaryrefslogtreecommitdiff
path: root/federationapi
diff options
context:
space:
mode:
authordevonh <devon.dmytro@gmail.com>2023-05-31 16:33:49 +0000
committerGitHub <noreply@github.com>2023-05-31 16:33:49 +0000
commitea6b368ad424a3d2e05135afb7fd0c0801b3609b (patch)
tree416132c5bdc525b9ad93911a40813c0dc3fb4439 /federationapi
parentcbdc601f1b6d1c2a648b69ff44b3a49916f4d31a (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.go8
-rw-r--r--federationapi/internal/perform.go47
-rw-r--r--federationapi/routing/invite.go237
-rw-r--r--federationapi/routing/join.go21
-rw-r--r--federationapi/routing/routing.go20
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,
)
},