diff --git a/federationapi/api/api.go b/federationapi/api/api.go index 5b49e509e..40b1abcb2 100644 --- a/federationapi/api/api.go +++ b/federationapi/api/api.go @@ -157,8 +157,9 @@ type PerformDirectoryLookupResponse struct { } type PerformJoinRequest struct { - RoomID string `json:"room_id"` - UserID string `json:"user_id"` + RoomID string `json:"room_id"` + UserID string `json:"user_id"` + SenderID spec.SenderID // The sorted list of servers to try. Servers will be tried sequentially, after de-duplication. ServerNames types.ServerNames `json:"server_names"` Content map[string]interface{} `json:"content"` diff --git a/federationapi/federationapi_test.go b/federationapi/federationapi_test.go index 173908437..dcddc6947 100644 --- a/federationapi/federationapi_test.go +++ b/federationapi/federationapi_test.go @@ -235,6 +235,7 @@ func testFederationAPIJoinThenKeyUpdate(t *testing.T, dbType test.DBType) { fsapi.PerformJoin(context.Background(), &api.PerformJoinRequest{ RoomID: room.ID, UserID: joiningUser.ID, + SenderID: spec.SenderID(joiningUser.ID), ServerNames: []spec.ServerName{serverA}, }, &resp) if resp.JoinedVia != serverA { diff --git a/federationapi/internal/perform.go b/federationapi/internal/perform.go index 485b79a03..f230299d9 100644 --- a/federationapi/internal/perform.go +++ b/federationapi/internal/perform.go @@ -96,6 +96,7 @@ func (r *FederationInternalAPI) PerformJoin( ctx, request.RoomID, request.UserID, + request.SenderID, request.Content, serverName, request.Unsigned, @@ -137,7 +138,7 @@ func (r *FederationInternalAPI) PerformJoin( func (r *FederationInternalAPI) performJoinUsingServer( ctx context.Context, - roomID, userID string, + roomID, userID string, senderID spec.SenderID, content map[string]interface{}, serverName spec.ServerName, unsigned map[string]interface{}, @@ -154,10 +155,6 @@ func (r *FederationInternalAPI) performJoinUsingServer( if err != nil { return err } - senderID, err := r.rsAPI.QuerySenderIDForUser(ctx, roomID, *user) - if err != nil { - return err - } joinInput := gomatrixserverlib.PerformJoinInput{ UserID: user, diff --git a/roomserver/internal/perform/perform_join.go b/roomserver/internal/perform/perform_join.go index 83c3b7c3e..2dcf8e0cc 100644 --- a/roomserver/internal/perform/perform_join.go +++ b/roomserver/internal/perform/perform_join.go @@ -174,44 +174,6 @@ func (r *Joiner) performJoinRoomByID( req.ServerNames = append(req.ServerNames, roomID.Domain()) } - // Prepare the template for the join event. - userID, err := spec.NewUserID(req.UserID, true) - if err != nil { - return "", "", rsAPI.ErrInvalidID{Err: fmt.Errorf("user ID %q is invalid: %w", req.UserID, err)} - } - senderID, err := r.RSAPI.QuerySenderIDForUser(ctx, req.RoomIDOrAlias, *userID) - if err != nil { - return "", "", rsAPI.ErrInvalidID{Err: fmt.Errorf("user ID %q is invalid: %w", req.UserID, err)} - } - senderIDString := string(senderID) - userDomain := userID.Domain() - proto := gomatrixserverlib.ProtoEvent{ - Type: spec.MRoomMember, - SenderID: senderIDString, - StateKey: &senderIDString, - RoomID: req.RoomIDOrAlias, - Redacts: "", - } - if err = proto.SetUnsigned(struct{}{}); err != nil { - return "", "", fmt.Errorf("eb.SetUnsigned: %w", err) - } - - // It is possible for the request to include some "content" for the - // event. We'll always overwrite the "membership" key, but the rest, - // like "display_name" or "avatar_url", will be kept if supplied. - if req.Content == nil { - req.Content = map[string]interface{}{} - } - req.Content["membership"] = spec.Join - if authorisedVia, aerr := r.populateAuthorisedViaUserForRestrictedJoin(ctx, req, senderID); aerr != nil { - return "", "", aerr - } else if authorisedVia != "" { - req.Content["join_authorised_via_users_server"] = authorisedVia - } - if err = proto.SetContent(req.Content); err != nil { - return "", "", fmt.Errorf("eb.SetContent: %w", err) - } - // Force a federated join if we aren't in the room and we've been // given some server names to try joining by. inRoomReq := &rsAPI.QueryServerJoinedToRoomRequest{ @@ -224,6 +186,31 @@ func (r *Joiner) performJoinRoomByID( serverInRoom := inRoomRes.IsInRoom forceFederatedJoin := len(req.ServerNames) > 0 && !serverInRoom + userID, err := spec.NewUserID(req.UserID, true) + if err != nil { + return "", "", rsAPI.ErrInvalidID{Err: fmt.Errorf("user ID %q is invalid: %w", req.UserID, err)} + } + + var senderID spec.SenderID + var roomVersion gomatrixserverlib.RoomVersion + if forceFederatedJoin { + // TODO: pseudoIDs - lookup room version kere! + } else { + roomVersion, err = r.RSAPI.QueryRoomVersionForRoom(ctx, roomID.String()) + if err != nil { + return "", "", err + } + } + + if roomVersion == gomatrixserverlib.RoomVersionPseudoIDs { + // TODO: pseudoIDs - generate senderID kere! + senderID = "pseudo_id.sender.key" + } else { + senderID = spec.SenderID(userID.String()) + } + senderIDString := string(senderID) + userDomain := userID.Domain() + // Force a federated join if we're dealing with a pending invite // and we aren't in the room. isInvitePending, inviteSender, _, inviteEvent, err := helpers.IsInvitePending(ctx, r.DB, req.RoomIDOrAlias, senderID) @@ -274,7 +261,7 @@ func (r *Joiner) performJoinRoomByID( // If we should do a forced federated join then do that. var joinedVia spec.ServerName if forceFederatedJoin { - joinedVia, err = r.performFederatedJoinRoomByID(ctx, req) + joinedVia, err = r.performFederatedJoinRoomByID(ctx, req, senderID) return req.RoomIDOrAlias, joinedVia, err } @@ -289,6 +276,34 @@ func (r *Joiner) performJoinRoomByID( if err != nil { return "", "", fmt.Errorf("error joining local room: %q", err) } + + // Prepare the template for the join event. + proto := gomatrixserverlib.ProtoEvent{ + Type: spec.MRoomMember, + SenderID: senderIDString, + StateKey: &senderIDString, + RoomID: req.RoomIDOrAlias, + Redacts: "", + } + if err = proto.SetUnsigned(struct{}{}); err != nil { + return "", "", fmt.Errorf("eb.SetUnsigned: %w", err) + } + + // It is possible for the request to include some "content" for the + // event. We'll always overwrite the "membership" key, but the rest, + // like "display_name" or "avatar_url", will be kept if supplied. + if req.Content == nil { + req.Content = map[string]interface{}{} + } + req.Content["membership"] = spec.Join + if authorisedVia, aerr := r.populateAuthorisedViaUserForRestrictedJoin(ctx, req, senderID); aerr != nil { + return "", "", aerr + } else if authorisedVia != "" { + req.Content["join_authorised_via_users_server"] = authorisedVia + } + if err = proto.SetContent(req.Content); err != nil { + return "", "", fmt.Errorf("eb.SetContent: %w", err) + } event, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, time.Now(), r.RSAPI, &buildRes) switch err.(type) { @@ -334,7 +349,7 @@ func (r *Joiner) performJoinRoomByID( } // Perform a federated room join. - joinedVia, err = r.performFederatedJoinRoomByID(ctx, req) + joinedVia, err = r.performFederatedJoinRoomByID(ctx, req, senderID) return req.RoomIDOrAlias, joinedVia, err default: @@ -352,14 +367,16 @@ func (r *Joiner) performJoinRoomByID( func (r *Joiner) performFederatedJoinRoomByID( ctx context.Context, req *rsAPI.PerformJoinRequest, + senderID spec.SenderID, ) (spec.ServerName, error) { // Try joining by all of the supplied server names. fedReq := fsAPI.PerformJoinRequest{ RoomID: req.RoomIDOrAlias, // the room ID to try and join UserID: req.UserID, // the user ID joining the room - ServerNames: req.ServerNames, // the server to try joining with - Content: req.Content, // the membership event content - Unsigned: req.Unsigned, // the unsigned event content, if any + SenderID: spec.SenderID(senderID), + ServerNames: req.ServerNames, // the server to try joining with + Content: req.Content, // the membership event content + Unsigned: req.Unsigned, // the unsigned event content, if any } fedRes := fsAPI.PerformJoinResponse{} r.FSAPI.PerformJoin(ctx, &fedReq, &fedRes) diff --git a/roomserver/storage/shared/storage.go b/roomserver/storage/shared/storage.go index f9c9ceabb..265ea3c4c 100644 --- a/roomserver/storage/shared/storage.go +++ b/roomserver/storage/shared/storage.go @@ -717,12 +717,6 @@ func (d *Database) GetOrCreateRoomInfoFromID(ctx context.Context, roomID string) if err != nil { return nil, err } - if roomInfo == nil { - return nil, fmt.Errorf("Failed to find room info for %s", roomID) - } - - d.Cache.StoreRoomServerRoomID(roomInfo.RoomNID, roomID) - d.Cache.StoreRoomVersion(roomID, roomInfo.RoomVersion) return roomInfo, nil }