aboutsummaryrefslogtreecommitdiff
path: root/federationapi
diff options
context:
space:
mode:
authordevonh <devon.dmytro@gmail.com>2023-07-06 15:15:24 +0000
committerGitHub <noreply@github.com>2023-07-06 15:15:24 +0000
commitd507c5fc9534f2d9e994ce8706f5d51ff192dfdf (patch)
treed357b40f29b89b0b5a4466a3915f1b26ba0cb080 /federationapi
parentfea946d9148338f75ca3b4eb7ef224a6ea4d0e5b (diff)
Add pseudoID compatibility to Invites (#3126)
Diffstat (limited to 'federationapi')
-rw-r--r--federationapi/api/api.go2
-rw-r--r--federationapi/internal/api.go5
-rw-r--r--federationapi/internal/perform.go52
-rw-r--r--federationapi/internal/perform_test.go27
-rw-r--r--federationapi/routing/invite.go78
-rw-r--r--federationapi/routing/join.go3
-rw-r--r--federationapi/routing/routing.go32
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)