From d507c5fc9534f2d9e994ce8706f5d51ff192dfdf Mon Sep 17 00:00:00 2001 From: devonh Date: Thu, 6 Jul 2023 15:15:24 +0000 Subject: Add pseudoID compatibility to Invites (#3126) --- roomserver/api/perform.go | 16 ++- roomserver/internal/api.go | 2 +- roomserver/internal/perform/perform_create_room.go | 62 +++------- roomserver/internal/perform/perform_invite.go | 126 ++++++++++++--------- roomserver/internal/perform/perform_join.go | 2 +- 5 files changed, 105 insertions(+), 103 deletions(-) (limited to 'roomserver') diff --git a/roomserver/api/perform.go b/roomserver/api/perform.go index b466b7ba..2818efaa 100644 --- a/roomserver/api/perform.go +++ b/roomserver/api/perform.go @@ -50,9 +50,21 @@ type PerformLeaveResponse struct { Message interface{} `json:"message,omitempty"` } +type InviteInput struct { + RoomID spec.RoomID + Inviter spec.UserID + Invitee spec.UserID + DisplayName string + AvatarURL string + Reason string + IsDirect bool + KeyID gomatrixserverlib.KeyID + PrivateKey ed25519.PrivateKey + EventTime time.Time +} + type PerformInviteRequest struct { - RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"` - Event *types.HeaderedEvent `json:"event"` + InviteInput InviteInput InviteRoomState []gomatrixserverlib.InviteStrippedState `json:"invite_room_state"` SendAsServer string `json:"send_as_server"` TransactionID *TransactionID `json:"transaction_id"` diff --git a/roomserver/internal/api.go b/roomserver/internal/api.go index 984dc7d9..712c365a 100644 --- a/roomserver/internal/api.go +++ b/roomserver/internal/api.go @@ -318,7 +318,7 @@ func (r *RoomserverInternalAPI) SigningIdentityFor(ctx context.Context, roomID s return fclient.SigningIdentity{ PrivateKey: privKey, KeyID: "ed25519:1", - ServerName: "self", + ServerName: spec.ServerName(spec.SenderIDFromPseudoIDKey(privKey)), }, nil } identity, err := r.Cfg.Global.SigningIdentityFor(senderID.Domain()) diff --git a/roomserver/internal/perform/perform_create_room.go b/roomserver/internal/perform/perform_create_room.go index 8c965645..12e756c2 100644 --- a/roomserver/internal/perform/perform_create_room.go +++ b/roomserver/internal/perform/perform_create_room.go @@ -195,7 +195,7 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo // sign all events with the pseudo ID key identity = &fclient.SigningIdentity{ - ServerName: "self", + ServerName: spec.ServerName(spec.SenderIDFromPseudoIDKey(pseudoIDKey)), KeyID: "ed25519:1", PrivateKey: pseudoIDKey, } @@ -489,7 +489,6 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo } // Process the invites. - var inviteEvent *types.HeaderedEvent for _, invitee := range createRequest.InvitedUsers { inviteeUserID, userIDErr := spec.NewUserID(invitee, true) if userIDErr != nil { @@ -499,54 +498,21 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo JSON: spec.InternalServerError{}, } } - inviteeSenderID, queryErr := c.RSAPI.QuerySenderIDForUser(ctx, roomID, *inviteeUserID) - if queryErr != nil { - util.GetLogger(ctx).WithError(queryErr).Error("rsapi.QuerySenderIDForUser failed") - return "", &util.JSONResponse{ - Code: http.StatusInternalServerError, - JSON: spec.InternalServerError{}, - } - } - inviteeString := string(inviteeSenderID) - proto := gomatrixserverlib.ProtoEvent{ - SenderID: string(senderID), - RoomID: roomID.String(), - Type: "m.room.member", - StateKey: &inviteeString, - } - - content := gomatrixserverlib.MemberContent{ - Membership: spec.Invite, - DisplayName: createRequest.UserDisplayName, - AvatarURL: createRequest.UserAvatarURL, - Reason: "", - IsDirect: createRequest.IsDirect, - } - if err = proto.SetContent(content); err != nil { - return "", &util.JSONResponse{ - Code: http.StatusInternalServerError, - JSON: spec.InternalServerError{}, - } - } - - // Build the invite event. - inviteEvent, err = eventutil.QueryAndBuildEvent(ctx, &proto, identity, createRequest.EventTime, c.RSAPI, nil) - - if err != nil { - util.GetLogger(ctx).WithError(err).Error("buildMembershipEvent failed") - continue - } - inviteStrippedState := append( - globalStrippedState, - gomatrixserverlib.NewInviteStrippedState(inviteEvent.PDU), - ) - // Send the invite event to the roomserver. - event := inviteEvent err = c.RSAPI.PerformInvite(ctx, &api.PerformInviteRequest{ - Event: event, - InviteRoomState: inviteStrippedState, - RoomVersion: event.Version(), + InviteInput: api.InviteInput{ + RoomID: roomID, + Inviter: userID, + Invitee: *inviteeUserID, + DisplayName: createRequest.UserDisplayName, + AvatarURL: createRequest.UserAvatarURL, + Reason: "", + IsDirect: createRequest.IsDirect, + KeyID: createRequest.KeyID, + PrivateKey: createRequest.PrivateKey, + EventTime: createRequest.EventTime, + }, + InviteRoomState: globalStrippedState, SendAsServer: string(userID.Domain()), }) switch e := err.(type) { diff --git a/roomserver/internal/perform/perform_invite.go b/roomserver/internal/perform/perform_invite.go index f19a508a..278ddd7d 100644 --- a/roomserver/internal/perform/perform_invite.go +++ b/roomserver/internal/perform/perform_invite.go @@ -16,6 +16,7 @@ package perform import ( "context" + "crypto/ed25519" "fmt" federationAPI "github.com/matrix-org/dendrite/federationapi/api" @@ -129,65 +130,102 @@ func (r *Inviter) PerformInvite( ctx context.Context, req *api.PerformInviteRequest, ) error { - event := req.Event - - validRoomID, err := spec.NewRoomID(event.RoomID()) + senderID, err := r.RSAPI.QuerySenderIDForUser(ctx, req.InviteInput.RoomID, req.InviteInput.Inviter) if err != nil { return err } - - sender, err := r.RSAPI.QueryUserIDForSender(ctx, *validRoomID, event.SenderID()) + info, err := r.DB.RoomInfo(ctx, req.InviteInput.RoomID.String()) if err != nil { - return spec.InvalidParam("The sender user ID is invalid") + return err } - if !r.Cfg.Matrix.IsLocalServerName(sender.Domain()) { - return api.ErrInvalidID{Err: fmt.Errorf("the invite must be from a local user")} + + proto := gomatrixserverlib.ProtoEvent{ + SenderID: string(senderID), + RoomID: req.InviteInput.RoomID.String(), + Type: "m.room.member", } - if event.StateKey() == nil || *event.StateKey() == "" { - return fmt.Errorf("invite must be a state event") + content := gomatrixserverlib.MemberContent{ + Membership: spec.Invite, + DisplayName: req.InviteInput.DisplayName, + AvatarURL: req.InviteInput.AvatarURL, + Reason: req.InviteInput.Reason, + IsDirect: req.InviteInput.IsDirect, } - invitedUser, err := r.RSAPI.QueryUserIDForSender(ctx, *validRoomID, spec.SenderID(*event.StateKey())) - if err != nil || invitedUser == nil { - return spec.InvalidParam("Could not find the matching senderID for this user") + + if err = proto.SetContent(content); err != nil { + return err + } + + if !r.Cfg.Matrix.IsLocalServerName(req.InviteInput.Inviter.Domain()) { + return api.ErrInvalidID{Err: fmt.Errorf("the invite must be from a local user")} } - isTargetLocal := r.Cfg.Matrix.IsLocalServerName(invitedUser.Domain()) - // If we're inviting a local user, we can generate the needed pseudoID key here. (if needed) - if isTargetLocal { - var roomVersion gomatrixserverlib.RoomVersion - roomVersion, err = r.DB.GetRoomVersion(ctx, event.RoomID()) + isTargetLocal := r.Cfg.Matrix.IsLocalServerName(req.InviteInput.Invitee.Domain()) + + signingKey := req.InviteInput.PrivateKey + if info.RoomVersion == gomatrixserverlib.RoomVersionPseudoIDs { + signingKey, err = r.RSAPI.GetOrCreateUserRoomPrivateKey(ctx, req.InviteInput.Inviter, req.InviteInput.RoomID) if err != nil { return err } - - switch roomVersion { - case gomatrixserverlib.RoomVersionPseudoIDs: - _, err = r.RSAPI.GetOrCreateUserRoomPrivateKey(ctx, *invitedUser, *validRoomID) - if err != nil { - return err - } - } - } - - invitedSenderID, err := r.RSAPI.QuerySenderIDForUser(ctx, *validRoomID, *invitedUser) - if err != nil { - return fmt.Errorf("failed looking up senderID for invited user") } input := gomatrixserverlib.PerformInviteInput{ - RoomID: *validRoomID, - InviteEvent: event.PDU, - InvitedUser: *invitedUser, - InvitedSenderID: invitedSenderID, + RoomID: req.InviteInput.RoomID, + RoomVersion: info.RoomVersion, + Inviter: req.InviteInput.Inviter, + Invitee: req.InviteInput.Invitee, IsTargetLocal: isTargetLocal, + EventTemplate: proto, StrippedState: req.InviteRoomState, + KeyID: req.InviteInput.KeyID, + SigningKey: signingKey, + EventTime: req.InviteInput.EventTime, MembershipQuerier: &api.MembershipQuerier{Roomserver: r.RSAPI}, StateQuerier: &QueryState{r.DB, r.RSAPI}, UserIDQuerier: func(roomID spec.RoomID, senderID spec.SenderID) (*spec.UserID, error) { return r.RSAPI.QueryUserIDForSender(ctx, roomID, senderID) }, + SenderIDQuerier: func(roomID spec.RoomID, userID spec.UserID) (spec.SenderID, error) { + return r.RSAPI.QuerySenderIDForUser(ctx, roomID, userID) + }, + SenderIDCreator: func(ctx context.Context, userID spec.UserID, roomID spec.RoomID, roomVersion string) (spec.SenderID, ed25519.PrivateKey, error) { + key, keyErr := r.RSAPI.GetOrCreateUserRoomPrivateKey(ctx, userID, roomID) + if keyErr != nil { + return "", nil, keyErr + } + + return spec.SenderIDFromPseudoIDKey(key), key, nil + }, + EventQuerier: func(ctx context.Context, roomID spec.RoomID, eventsNeeded []gomatrixserverlib.StateKeyTuple) (gomatrixserverlib.LatestEvents, error) { + req := api.QueryLatestEventsAndStateRequest{RoomID: roomID.String(), StateToFetch: eventsNeeded} + res := api.QueryLatestEventsAndStateResponse{} + err = r.RSAPI.QueryLatestEventsAndState(ctx, &req, &res) + if err != nil { + return gomatrixserverlib.LatestEvents{}, nil + } + + stateEvents := []gomatrixserverlib.PDU{} + for _, event := range res.StateEvents { + stateEvents = append(stateEvents, event.PDU) + } + return gomatrixserverlib.LatestEvents{ + RoomExists: res.RoomExists, + StateEvents: stateEvents, + PrevEventIDs: res.LatestEvents, + Depth: res.Depth, + }, nil + }, + StoreSenderIDFromPublicID: func(ctx context.Context, senderID spec.SenderID, userIDRaw string, roomID spec.RoomID) error { + storeUserID, userErr := spec.NewUserID(userIDRaw, true) + if userErr != nil { + return userErr + } + return r.RSAPI.StoreUserRoomPublicKey(ctx, senderID, *storeUserID, roomID) + }, } + inviteEvent, err := gomatrixserverlib.PerformInvite(ctx, input, r.FSAPI) if err != nil { switch e := err.(type) { @@ -199,20 +237,6 @@ func (r *Inviter) PerformInvite( return err } - // Use the returned event if there was one (due to federation), otherwise - // send the original invite event to the roomserver. - if inviteEvent == nil { - inviteEvent = event - } - - // if we invited a local user, we can also create a user room key, if it doesn't exist yet. - if isTargetLocal && event.Version() == gomatrixserverlib.RoomVersionPseudoIDs { - _, err = r.RSAPI.GetOrCreateUserRoomPrivateKey(ctx, *invitedUser, *validRoomID) - if err != nil { - return fmt.Errorf("failed to get user room private key: %w", err) - } - } - // Send the invite event to the roomserver input stream. This will // notify existing users in the room about the invite, update the // membership table and ensure that the event is ready and available @@ -223,7 +247,7 @@ func (r *Inviter) PerformInvite( { Kind: api.KindNew, Event: &types.HeaderedEvent{PDU: inviteEvent}, - Origin: sender.Domain(), + Origin: req.InviteInput.Inviter.Domain(), SendAsServer: req.SendAsServer, }, }, @@ -231,7 +255,7 @@ func (r *Inviter) PerformInvite( inputRes := &api.InputRoomEventsResponse{} r.Inputer.InputRoomEvents(context.Background(), inputReq, inputRes) if err := inputRes.Err(); err != nil { - util.GetLogger(ctx).WithField("event_id", event.EventID()).Error("r.InputRoomEvents failed") + util.GetLogger(ctx).WithField("event_id", inviteEvent.EventID()).Error("r.InputRoomEvents failed") return api.ErrNotAllowed{Err: err} } diff --git a/roomserver/internal/perform/perform_join.go b/roomserver/internal/perform/perform_join.go index c1455464..937993de 100644 --- a/roomserver/internal/perform/perform_join.go +++ b/roomserver/internal/perform/perform_join.go @@ -313,7 +313,7 @@ func (r *Joiner) performJoinRoomByID( // sign the event with the pseudo ID key identity = fclient.SigningIdentity{ - ServerName: "self", + ServerName: spec.ServerName(spec.SenderIDFromPseudoIDKey(pseudoIDKey)), KeyID: "ed25519:1", PrivateKey: pseudoIDKey, } -- cgit v1.2.3