diff options
author | devonh <devon.dmytro@gmail.com> | 2023-07-06 15:15:24 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-06 15:15:24 +0000 |
commit | d507c5fc9534f2d9e994ce8706f5d51ff192dfdf (patch) | |
tree | d357b40f29b89b0b5a4466a3915f1b26ba0cb080 /federationapi | |
parent | fea946d9148338f75ca3b4eb7ef224a6ea4d0e5b (diff) |
Add pseudoID compatibility to Invites (#3126)
Diffstat (limited to 'federationapi')
-rw-r--r-- | federationapi/api/api.go | 2 | ||||
-rw-r--r-- | federationapi/internal/api.go | 5 | ||||
-rw-r--r-- | federationapi/internal/perform.go | 52 | ||||
-rw-r--r-- | federationapi/internal/perform_test.go | 27 | ||||
-rw-r--r-- | federationapi/routing/invite.go | 78 | ||||
-rw-r--r-- | federationapi/routing/join.go | 3 | ||||
-rw-r--r-- | federationapi/routing/routing.go | 32 |
7 files changed, 192 insertions, 7 deletions
diff --git a/federationapi/api/api.go b/federationapi/api/api.go index 5b49e509..756f9bc1 100644 --- a/federationapi/api/api.go +++ b/federationapi/api/api.go @@ -63,6 +63,8 @@ type RoomserverFederationAPI interface { PerformLeave(ctx context.Context, request *PerformLeaveRequest, response *PerformLeaveResponse) error // Handle sending an invite to a remote server. SendInvite(ctx context.Context, event gomatrixserverlib.PDU, strippedState []gomatrixserverlib.InviteStrippedState) (gomatrixserverlib.PDU, error) + // Handle sending an invite to a remote server. + SendInviteV3(ctx context.Context, event gomatrixserverlib.ProtoEvent, invitee spec.UserID, version gomatrixserverlib.RoomVersion, 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. diff --git a/federationapi/internal/api.go b/federationapi/internal/api.go index aa501f63..3e6f3956 100644 --- a/federationapi/internal/api.go +++ b/federationapi/internal/api.go @@ -54,11 +54,14 @@ func NewFederationInternalAPI( KeyDatabase: serverKeyDB, } + pubKey := cfg.Matrix.PrivateKey.Public().(ed25519.PublicKey) addDirectFetcher := func() { keyRing.KeyFetchers = append( keyRing.KeyFetchers, &gomatrixserverlib.DirectKeyFetcher{ - Client: federation, + Client: federation, + IsLocalServerName: cfg.Matrix.IsLocalServerName, + LocalPublicKey: []byte(pubKey), }, ) } diff --git a/federationapi/internal/perform.go b/federationapi/internal/perform.go index 515b3377..ff00305b 100644 --- a/federationapi/internal/perform.go +++ b/federationapi/internal/perform.go @@ -599,6 +599,58 @@ func (r *FederationInternalAPI) SendInvite( return inviteEvent, nil } +// SendInviteV3 implements api.FederationInternalAPI +func (r *FederationInternalAPI) SendInviteV3( + ctx context.Context, + event gomatrixserverlib.ProtoEvent, + invitee spec.UserID, + version gomatrixserverlib.RoomVersion, + strippedState []gomatrixserverlib.InviteStrippedState, +) (gomatrixserverlib.PDU, error) { + validRoomID, err := spec.NewRoomID(event.RoomID) + if err != nil { + return nil, err + } + verImpl, err := gomatrixserverlib.GetRoomVersion(version) + if err != nil { + return nil, err + } + + inviter, err := r.rsAPI.QueryUserIDForSender(ctx, *validRoomID, spec.SenderID(event.SenderID)) + if err != nil { + return nil, 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(invitee.Domain()) { + return nil, fmt.Errorf("relay servers have no meaningful response for invite.") + } + + logrus.WithFields(logrus.Fields{ + "user_id": invitee.String(), + "room_id": event.RoomID, + "room_version": version, + "destination": invitee.Domain(), + }).Info("Sending invite") + + inviteReq, err := fclient.NewInviteV3Request(event, version, strippedState) + if err != nil { + return nil, fmt.Errorf("gomatrixserverlib.NewInviteV3Request: %w", err) + } + + inviteRes, err := r.federation.SendInviteV3(ctx, inviter.Domain(), invitee.Domain(), inviteReq, invitee) + if err != nil { + return nil, fmt.Errorf("r.federation.SendInviteV3: failed to send invite: %w", err) + } + + inviteEvent, err := verImpl.NewEventFromUntrustedJSON(inviteRes.Event) + if err != nil { + return nil, fmt.Errorf("r.federation.SendInviteV3 failed to decode event response: %w", err) + } + return inviteEvent, nil +} + // PerformServersAlive implements api.FederationInternalAPI func (r *FederationInternalAPI) PerformBroadcastEDU( ctx context.Context, diff --git a/federationapi/internal/perform_test.go b/federationapi/internal/perform_test.go index 2f61235a..656755f9 100644 --- a/federationapi/internal/perform_test.go +++ b/federationapi/internal/perform_test.go @@ -16,6 +16,7 @@ package internal import ( "context" + "crypto/ed25519" "testing" "github.com/matrix-org/dendrite/federationapi/api" @@ -53,10 +54,14 @@ func TestPerformWakeupServers(t *testing.T) { assert.NoError(t, err) assert.True(t, offline) + _, key, err := ed25519.GenerateKey(nil) + assert.NoError(t, err) cfg := config.FederationAPI{ Matrix: &config.Global{ SigningIdentity: fclient.SigningIdentity{ ServerName: "relay", + KeyID: "ed25519:1", + PrivateKey: key, }, }, } @@ -95,10 +100,14 @@ func TestQueryRelayServers(t *testing.T) { err := testDB.P2PAddRelayServersForServer(context.Background(), server, relayServers) assert.NoError(t, err) + _, key, err := ed25519.GenerateKey(nil) + assert.NoError(t, err) cfg := config.FederationAPI{ Matrix: &config.Global{ SigningIdentity: fclient.SigningIdentity{ ServerName: "relay", + KeyID: "ed25519:1", + PrivateKey: key, }, }, } @@ -132,10 +141,14 @@ func TestRemoveRelayServers(t *testing.T) { err := testDB.P2PAddRelayServersForServer(context.Background(), server, relayServers) assert.NoError(t, err) + _, key, err := ed25519.GenerateKey(nil) + assert.NoError(t, err) cfg := config.FederationAPI{ Matrix: &config.Global{ SigningIdentity: fclient.SigningIdentity{ ServerName: "relay", + KeyID: "ed25519:1", + PrivateKey: key, }, }, } @@ -168,10 +181,14 @@ func TestRemoveRelayServers(t *testing.T) { func TestPerformDirectoryLookup(t *testing.T) { testDB := test.NewInMemoryFederationDatabase() + _, key, err := ed25519.GenerateKey(nil) + assert.NoError(t, err) cfg := config.FederationAPI{ Matrix: &config.Global{ SigningIdentity: fclient.SigningIdentity{ ServerName: "relay", + KeyID: "ed25519:1", + PrivateKey: key, }, }, } @@ -192,7 +209,7 @@ func TestPerformDirectoryLookup(t *testing.T) { ServerName: "server", } res := api.PerformDirectoryLookupResponse{} - err := fedAPI.PerformDirectoryLookup(context.Background(), &req, &res) + err = fedAPI.PerformDirectoryLookup(context.Background(), &req, &res) assert.NoError(t, err) } @@ -203,10 +220,14 @@ func TestPerformDirectoryLookupRelaying(t *testing.T) { testDB.SetServerAssumedOffline(context.Background(), server) testDB.P2PAddRelayServersForServer(context.Background(), server, []spec.ServerName{"relay"}) + _, key, err := ed25519.GenerateKey(nil) + assert.NoError(t, err) cfg := config.FederationAPI{ Matrix: &config.Global{ SigningIdentity: fclient.SigningIdentity{ - ServerName: server, + ServerName: "relay", + KeyID: "ed25519:1", + PrivateKey: key, }, }, } @@ -227,6 +248,6 @@ func TestPerformDirectoryLookupRelaying(t *testing.T) { ServerName: server, } res := api.PerformDirectoryLookupResponse{} - err := fedAPI.PerformDirectoryLookup(context.Background(), &req, &res) + err = fedAPI.PerformDirectoryLookup(context.Background(), &req, &res) assert.Error(t, err) } diff --git a/federationapi/routing/invite.go b/federationapi/routing/invite.go index e45209a2..76fadaa9 100644 --- a/federationapi/routing/invite.go +++ b/federationapi/routing/invite.go @@ -16,6 +16,7 @@ package routing import ( "context" + "crypto/ed25519" "encoding/json" "fmt" "net/http" @@ -29,6 +30,73 @@ import ( "github.com/matrix-org/util" ) +// InviteV3 implements /_matrix/federation/v2/invite/{roomID}/{userID} +func InviteV3( + httpReq *http.Request, + request *fclient.FederationRequest, + roomID spec.RoomID, + invitedUser spec.UserID, + cfg *config.FederationAPI, + rsAPI api.FederationRoomserverAPI, + keys gomatrixserverlib.JSONVerifier, +) util.JSONResponse { + inviteReq := fclient.InviteV3Request{} + err := json.Unmarshal(request.Content(), &inviteReq) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.BadJSON(err.Error()), + } + } + 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"), + } + } + + input := gomatrixserverlib.HandleInviteV3Input{ + HandleInviteInput: 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: nil, + StrippedState: inviteReq.InviteRoomState(), + UserIDQuerier: func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) { + return rsAPI.QueryUserIDForSender(httpReq.Context(), roomID, senderID) + }, + }, + InviteProtoEvent: inviteReq.Event(), + GetOrCreateSenderID: func(ctx context.Context, userID spec.UserID, roomID spec.RoomID, roomVersion string) (spec.SenderID, ed25519.PrivateKey, error) { + // assign a roomNID, otherwise we can't create a private key for the user + _, nidErr := rsAPI.AssignRoomNID(ctx, roomID, gomatrixserverlib.RoomVersion(roomVersion)) + if nidErr != nil { + return "", nil, nidErr + } + key, keyErr := rsAPI.GetOrCreateUserRoomPrivateKey(ctx, userID, roomID) + if keyErr != nil { + return "", nil, keyErr + } + + return spec.SenderIDFromPseudoIDKey(key), key, nil + }, + } + event, jsonErr := handleInviteV3(httpReq.Context(), input, rsAPI) + if jsonErr != nil { + return *jsonErr + } + return util.JSONResponse{ + Code: http.StatusOK, + JSON: fclient.RespInviteV2{Event: event.JSON()}, + } +} + // InviteV2 implements /_matrix/federation/v2/invite/{roomID}/{eventID} func InviteV2( httpReq *http.Request, @@ -204,6 +272,15 @@ func InviteV1( func handleInvite(ctx context.Context, input gomatrixserverlib.HandleInviteInput, rsAPI api.FederationRoomserverAPI) (gomatrixserverlib.PDU, *util.JSONResponse) { inviteEvent, err := gomatrixserverlib.HandleInvite(ctx, input) + return handleInviteResult(ctx, inviteEvent, err, rsAPI) +} + +func handleInviteV3(ctx context.Context, input gomatrixserverlib.HandleInviteV3Input, rsAPI api.FederationRoomserverAPI) (gomatrixserverlib.PDU, *util.JSONResponse) { + inviteEvent, err := gomatrixserverlib.HandleInviteV3(ctx, input) + return handleInviteResult(ctx, inviteEvent, err, rsAPI) +} + +func handleInviteResult(ctx context.Context, inviteEvent gomatrixserverlib.PDU, err error, rsAPI api.FederationRoomserverAPI) (gomatrixserverlib.PDU, *util.JSONResponse) { switch e := err.(type) { case nil: case spec.InternalServerError: @@ -245,4 +322,5 @@ func handleInvite(ctx context.Context, input gomatrixserverlib.HandleInviteInput } } return inviteEvent, nil + } diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index bfa1ba8b..a090dbc8 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -187,9 +187,6 @@ func MakeJoin( } // 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: -// nolint:gocyclo func SendJoin( httpReq *http.Request, request *fclient.FederationRequest, diff --git a/federationapi/routing/routing.go b/federationapi/routing/routing.go index 8865022f..4f998821 100644 --- a/federationapi/routing/routing.go +++ b/federationapi/routing/routing.go @@ -78,6 +78,7 @@ func Setup( v2keysmux := keyMux.PathPrefix("/v2").Subrouter() v1fedmux := fedMux.PathPrefix("/v1").Subrouter() v2fedmux := fedMux.PathPrefix("/v2").Subrouter() + v3fedmux := fedMux.PathPrefix("/v3").Subrouter() wakeup := &FederationWakeups{ FsAPI: fsAPI, @@ -191,6 +192,37 @@ func Setup( }, )).Methods(http.MethodPut, http.MethodOptions) + v3fedmux.Handle("/invite/{roomID}/{userID}", MakeFedAPI( + "federation_invite", cfg.Matrix.ServerName, cfg.Matrix.IsLocalServerName, keys, wakeup, + func(httpReq *http.Request, request *fclient.FederationRequest, vars map[string]string) util.JSONResponse { + if roomserverAPI.IsServerBannedFromRoom(httpReq.Context(), rsAPI, vars["roomID"], request.Origin()) { + return util.JSONResponse{ + Code: http.StatusForbidden, + JSON: spec.Forbidden("Forbidden by server ACLs"), + } + } + + userID, err := spec.NewUserID(vars["userID"], true) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.InvalidParam("Invalid UserID"), + } + } + roomID, err := spec.NewRoomID(vars["roomID"]) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: spec.InvalidParam("Invalid RoomID"), + } + } + return InviteV3( + httpReq, request, *roomID, *userID, + cfg, rsAPI, keys, + ) + }, + )).Methods(http.MethodPut, http.MethodOptions) + v1fedmux.Handle("/3pid/onbind", httputil.MakeExternalAPI("3pid_onbind", func(req *http.Request) util.JSONResponse { return CreateInvitesFrom3PIDInvites(req, rsAPI, cfg, federation, userAPI) |