Add MXIDMapping for pseudoID rooms (#3112)

Add `MXIDMapping` on membership events when
creating/joining rooms.
This commit is contained in:
Till 2023-06-28 20:29:49 +02:00 committed by GitHub
parent 4722f12fab
commit 23cd7877a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 593 additions and 177 deletions

View file

@ -115,6 +115,7 @@ func (r *RoomserverInternalAPI) GetAliasesForRoomID(
// nolint:gocyclo
// RemoveRoomAlias implements alias.RoomserverInternalAPI
// nolint: gocyclo
func (r *RoomserverInternalAPI) RemoveRoomAlias(
ctx context.Context,
request *api.RemoveRoomAliasRequest,
@ -188,9 +189,11 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias(
return err
}
senderDomain := sender.Domain()
identity, err := r.Cfg.Global.SigningIdentityFor(senderDomain)
validRoomID, err := spec.NewRoomID(roomID)
if err != nil {
return err
}
identity, err := r.SigningIdentityFor(ctx, *validRoomID, *sender)
if err != nil {
return err
}
@ -216,7 +219,7 @@ func (r *RoomserverInternalAPI) RemoveRoomAlias(
return err
}
newEvent, err := eventutil.BuildEvent(ctx, proto, identity, time.Now(), &eventsNeeded, stateRes)
newEvent, err := eventutil.BuildEvent(ctx, proto, &identity, time.Now(), &eventsNeeded, stateRes)
if err != nil {
return err
}

View file

@ -6,6 +6,7 @@ import (
"github.com/getsentry/sentry-go"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util"
"github.com/nats-io/nats.go"
@ -110,11 +111,6 @@ func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.RoomserverFederatio
r.fsAPI = fsAPI
r.KeyRing = keyRing
identity, err := r.Cfg.Global.SigningIdentityFor(r.ServerName)
if err != nil {
logrus.Panic(err)
}
r.Inputer = &input.Inputer{
Cfg: &r.Cfg.RoomServer,
ProcessContext: r.ProcessContext,
@ -125,7 +121,7 @@ func (r *RoomserverInternalAPI) SetFederationAPI(fsAPI fsAPI.RoomserverFederatio
NATSClient: r.NATSClient,
Durable: nats.Durable(r.Durable),
ServerName: r.ServerName,
SigningIdentity: identity,
SigningIdentity: r.SigningIdentityFor,
FSAPI: fsAPI,
KeyRing: keyRing,
ACLs: r.ServerACLs,
@ -292,3 +288,45 @@ func (r *RoomserverInternalAPI) GetOrCreateUserRoomPrivateKey(ctx context.Contex
}
return key, nil
}
func (r *RoomserverInternalAPI) StoreUserRoomPublicKey(ctx context.Context, senderID spec.SenderID, userID spec.UserID, roomID spec.RoomID) error {
pubKeyBytes, err := senderID.RawBytes()
if err != nil {
return err
}
_, err = r.DB.InsertUserRoomPublicKey(ctx, userID, roomID, ed25519.PublicKey(pubKeyBytes))
return err
}
func (r *RoomserverInternalAPI) SigningIdentityFor(ctx context.Context, roomID spec.RoomID, senderID spec.UserID) (fclient.SigningIdentity, error) {
roomVersion, ok := r.Cache.GetRoomVersion(roomID.String())
if !ok {
roomInfo, err := r.DB.RoomInfo(ctx, roomID.String())
if err != nil {
return fclient.SigningIdentity{}, err
}
if roomInfo != nil {
roomVersion = roomInfo.RoomVersion
}
}
if roomVersion == gomatrixserverlib.RoomVersionPseudoIDs {
privKey, err := r.GetOrCreateUserRoomPrivateKey(ctx, senderID, roomID)
if err != nil {
return fclient.SigningIdentity{}, err
}
return fclient.SigningIdentity{
PrivateKey: privKey,
KeyID: "ed25519:1",
ServerName: "self",
}, nil
}
identity, err := r.Cfg.Global.SigningIdentityFor(senderID.Domain())
if err != nil {
return fclient.SigningIdentity{}, err
}
return *identity, err
}
func (r *RoomserverInternalAPI) AssignRoomNID(ctx context.Context, roomID spec.RoomID, roomVersion gomatrixserverlib.RoomVersion) (roomNID types.RoomNID, err error) {
return r.DB.AssignRoomNID(ctx, roomID, roomVersion)
}

View file

@ -81,7 +81,7 @@ type Inputer struct {
JetStream nats.JetStreamContext
Durable nats.SubOpt
ServerName spec.ServerName
SigningIdentity *fclient.SigningIdentity
SigningIdentity func(ctx context.Context, roomID spec.RoomID, senderID spec.UserID) (fclient.SigningIdentity, error)
FSAPI fedapi.RoomserverFederationAPI
KeyRing gomatrixserverlib.JSONVerifier
ACLs *acls.ServerACLs

View file

@ -406,7 +406,7 @@ func (r *Inputer) processRoomEvent(
)
if !isRejected && !isCreateEvent {
resolver := state.NewStateResolution(r.DB, roomInfo, r.Queryer)
redactionEvent, redactedEvent, err = r.DB.MaybeRedactEvent(ctx, roomInfo, eventNID, event, &resolver)
redactionEvent, redactedEvent, err = r.DB.MaybeRedactEvent(ctx, roomInfo, eventNID, event, &resolver, r.Queryer)
if err != nil {
return err
}
@ -895,7 +895,22 @@ func (r *Inputer) kickGuests(ctx context.Context, event gomatrixserverlib.PDU, r
return err
}
event, err := eventutil.BuildEvent(ctx, fledglingEvent, r.SigningIdentity, time.Now(), &eventsNeeded, latestRes)
validRoomID, err := spec.NewRoomID(event.RoomID())
if err != nil {
return err
}
userID, err := spec.NewUserID(stateKey, true)
if err != nil {
return err
}
signingIdentity, err := r.SigningIdentity(ctx, *validRoomID, *userID)
if err != nil {
return err
}
event, err := eventutil.BuildEvent(ctx, fledglingEvent, &signingIdentity, time.Now(), &eventsNeeded, latestRes)
if err != nil {
return err
}

View file

@ -647,7 +647,7 @@ func persistEvents(ctx context.Context, db storage.Database, querier api.QuerySe
resolver := state.NewStateResolution(db, roomInfo, querier)
_, redactedEvent, err := db.MaybeRedactEvent(ctx, roomInfo, eventNID, ev, &resolver)
_, redactedEvent, err := db.MaybeRedactEvent(ctx, roomInfo, eventNID, ev, &resolver, querier)
if err != nil {
logrus.WithError(err).WithField("event_id", ev.EventID()).Error("Failed to redact event")
continue

View file

@ -31,6 +31,7 @@ import (
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util"
"github.com/sirupsen/logrus"
)
const (
@ -64,6 +65,16 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
}
}
}
_, err = c.DB.AssignRoomNID(ctx, roomID, createRequest.RoomVersion)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("failed to assign roomNID")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
var senderID spec.SenderID
if createRequest.RoomVersion == gomatrixserverlib.RoomVersionPseudoIDs {
// create user room key if needed
@ -75,7 +86,7 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
JSON: spec.InternalServerError{},
}
}
senderID = spec.SenderID(spec.Base64Bytes(key.Public().(ed25519.PublicKey)).Encode())
senderID = spec.SenderIDFromPseudoIDKey(key)
} else {
senderID = spec.SenderID(userID.String())
}
@ -138,13 +149,59 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
membershipEvent := gomatrixserverlib.FledglingEvent{
Type: spec.MRoomMember,
StateKey: string(senderID),
Content: gomatrixserverlib.MemberContent{
Membership: spec.Join,
DisplayName: createRequest.UserDisplayName,
AvatarURL: createRequest.UserAvatarURL,
},
}
memberContent := gomatrixserverlib.MemberContent{
Membership: spec.Join,
DisplayName: createRequest.UserDisplayName,
AvatarURL: createRequest.UserAvatarURL,
}
// get the signing identity
identity, err := c.Cfg.Matrix.SigningIdentityFor(userID.Domain()) // we MUST use the server signing mxid_mapping
if err != nil {
logrus.WithError(err).WithField("domain", userID.Domain()).Error("unable to find signing identity for domain")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
// If we are creating a room with pseudo IDs, create and sign the MXIDMapping
if createRequest.RoomVersion == gomatrixserverlib.RoomVersionPseudoIDs {
var pseudoIDKey ed25519.PrivateKey
pseudoIDKey, err = c.RSAPI.GetOrCreateUserRoomPrivateKey(ctx, userID, roomID)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("GetOrCreateUserRoomPrivateKey failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
mapping := &gomatrixserverlib.MXIDMapping{
UserRoomKey: spec.SenderIDFromPseudoIDKey(pseudoIDKey),
UserID: userID.String(),
}
// Sign the mapping with the server identity
if err = mapping.Sign(identity.ServerName, identity.KeyID, identity.PrivateKey); err != nil {
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
memberContent.MXIDMapping = mapping
// sign all events with the pseudo ID key
identity = &fclient.SigningIdentity{
ServerName: "self",
KeyID: "ed25519:1",
PrivateKey: pseudoIDKey,
}
}
membershipEvent.Content = memberContent
var nameEvent *gomatrixserverlib.FledglingEvent
var topicEvent *gomatrixserverlib.FledglingEvent
var guestAccessEvent *gomatrixserverlib.FledglingEvent
@ -322,7 +379,7 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
JSON: spec.InternalServerError{},
}
}
ev, err = builder.Build(createRequest.EventTime, userID.Domain(), createRequest.KeyID, createRequest.PrivateKey)
ev, err = builder.Build(createRequest.EventTime, identity.ServerName, identity.KeyID, identity.PrivateKey)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("buildEvent failed")
return "", &util.JSONResponse{
@ -363,17 +420,8 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
})
}
// first send the `m.room.create` event, so we have a roomNID
if err = api.SendInputRoomEvents(ctx, c.RSAPI, userID.Domain(), inputs[:1], false); err != nil {
util.GetLogger(ctx).WithError(err).Error("roomserverAPI.SendInputRoomEvents failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: spec.InternalServerError{},
}
}
// send the remaining events
if err = api.SendInputRoomEvents(ctx, c.RSAPI, userID.Domain(), inputs[1:], false); err != nil {
// send the events to the roomserver
if err = api.SendInputRoomEvents(ctx, c.RSAPI, userID.Domain(), inputs, false); err != nil {
util.GetLogger(ctx).WithError(err).Error("roomserverAPI.SendInputRoomEvents failed")
return "", &util.JSONResponse{
Code: http.StatusInternalServerError,
@ -483,11 +531,6 @@ func (c *Creator) PerformCreateRoom(ctx context.Context, userID spec.UserID, roo
}
// Build the invite event.
identity := &fclient.SigningIdentity{
ServerName: userID.Domain(),
KeyID: createRequest.KeyID,
PrivateKey: createRequest.PrivateKey,
}
inviteEvent, err = eventutil.QueryAndBuildEvent(ctx, &proto, identity, createRequest.EventTime, c.RSAPI, nil)
if err != nil {

View file

@ -153,6 +153,23 @@ func (r *Inviter) PerformInvite(
}
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())
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")

View file

@ -16,6 +16,7 @@ package perform
import (
"context"
"crypto/ed25519"
"database/sql"
"errors"
"fmt"
@ -24,6 +25,7 @@ import (
"github.com/getsentry/sentry-go"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util"
"github.com/sirupsen/logrus"
@ -202,14 +204,15 @@ func (r *Joiner) performJoinRoomByID(
senderID, err = r.Queryer.QuerySenderIDForUser(ctx, *roomID, *userID)
if err == nil {
checkInvitePending = true
} else {
}
if senderID == "" {
// create user room key if needed
key, keyErr := r.RSAPI.GetOrCreateUserRoomPrivateKey(ctx, *userID, *roomID)
if keyErr != nil {
util.GetLogger(ctx).WithError(keyErr).Error("GetOrCreateUserRoomPrivateKey failed")
return "", "", fmt.Errorf("GetOrCreateUserRoomPrivateKey failed: %w", keyErr)
}
senderID = spec.SenderID(spec.Base64Bytes(key).Encode())
senderID = spec.SenderIDFromPseudoIDKey(key)
}
default:
checkInvitePending = true
@ -283,11 +286,39 @@ func (r *Joiner) performJoinRoomByID(
// but everyone has since left. I suspect it does the wrong thing.
var buildRes rsAPI.QueryLatestEventsAndStateResponse
identity, err := r.Cfg.Matrix.SigningIdentityFor(userDomain)
identity, err := r.RSAPI.SigningIdentityFor(ctx, *roomID, *userID)
if err != nil {
return "", "", fmt.Errorf("error joining local room: %q", err)
}
// at this point we know we have an existing room
if inRoomRes.RoomVersion == gomatrixserverlib.RoomVersionPseudoIDs {
var pseudoIDKey ed25519.PrivateKey
pseudoIDKey, err = r.RSAPI.GetOrCreateUserRoomPrivateKey(ctx, *userID, *roomID)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("GetOrCreateUserRoomPrivateKey failed")
return "", "", err
}
mapping := &gomatrixserverlib.MXIDMapping{
UserRoomKey: spec.SenderIDFromPseudoIDKey(pseudoIDKey),
UserID: userID.String(),
}
// Sign the mapping with the server identity
if err = mapping.Sign(identity.ServerName, identity.KeyID, identity.PrivateKey); err != nil {
return "", "", err
}
req.Content["mxid_mapping"] = mapping
// sign the event with the pseudo ID key
identity = fclient.SigningIdentity{
ServerName: "self",
KeyID: "ed25519:1",
PrivateKey: pseudoIDKey,
}
}
senderIDString := string(senderID)
// Prepare the template for the join event.
@ -317,7 +348,7 @@ func (r *Joiner) performJoinRoomByID(
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)
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, &identity, time.Now(), r.RSAPI, &buildRes)
switch err.(type) {
case nil:

View file

@ -177,12 +177,17 @@ func (r *Leaver) performLeaveRoomByID(
// TODO: Check what happens if the room exists on the server
// but everyone has since left. I suspect it does the wrong thing.
validRoomID, err := spec.NewRoomID(req.RoomID)
if err != nil {
return nil, err
}
var buildRes rsAPI.QueryLatestEventsAndStateResponse
identity, err := r.Cfg.Matrix.SigningIdentityFor(req.Leaver.Domain())
identity, err := r.RSAPI.SigningIdentityFor(ctx, *validRoomID, req.Leaver)
if err != nil {
return nil, fmt.Errorf("SigningIdentityFor: %w", err)
}
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, identity, time.Now(), r.RSAPI, &buildRes)
event, err := eventutil.QueryAndBuildEvent(ctx, &proto, &identity, time.Now(), r.RSAPI, &buildRes)
if err != nil {
return nil, fmt.Errorf("eventutil.QueryAndBuildEvent: %w", err)
}

View file

@ -478,6 +478,9 @@ func (r *Queryer) QueryServerJoinedToRoom(
if err != nil {
return fmt.Errorf("r.DB.RoomInfo: %w", err)
}
if info != nil {
response.RoomVersion = info.RoomVersion
}
if info == nil || info.IsStub() {
return nil
}