Merge branch 'main' of github.com:matrix-org/dendrite into s7evink/performerr

This commit is contained in:
Till Faelligen 2023-04-27 09:39:57 +02:00
commit 88edd01316
No known key found for this signature in database
GPG key ID: ACCDC9606D472758
26 changed files with 776 additions and 328 deletions

View file

@ -49,7 +49,6 @@ type createRoomRequest struct {
CreationContent json.RawMessage `json:"creation_content"`
InitialState []fledglingEvent `json:"initial_state"`
RoomAliasName string `json:"room_alias_name"`
GuestCanJoin bool `json:"guest_can_join"`
RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"`
PowerLevelContentOverride json.RawMessage `json:"power_level_content_override"`
IsDirect bool `json:"is_direct"`
@ -254,16 +253,19 @@ func createRoom(
}
}
var guestsCanJoin bool
switch r.Preset {
case presetPrivateChat:
joinRuleContent.JoinRule = spec.Invite
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
guestsCanJoin = true
case presetTrustedPrivateChat:
joinRuleContent.JoinRule = spec.Invite
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
for _, invitee := range r.Invite {
powerLevelContent.Users[invitee] = 100
}
guestsCanJoin = true
case presetPublicChat:
joinRuleContent.JoinRule = spec.Public
historyVisibilityContent.HistoryVisibility = historyVisibilityShared
@ -318,7 +320,7 @@ func createRoom(
}
}
if r.GuestCanJoin {
if guestsCanJoin {
guestAccessEvent = &fledglingEvent{
Type: spec.MRoomGuestAccess,
Content: eventutil.GuestAccessContent{

View file

@ -66,7 +66,6 @@ func TestJoinRoomByIDOrAlias(t *testing.T) {
Preset: presetPublicChat,
RoomAliasName: "alias",
Invite: []string{bob.ID},
GuestCanJoin: false,
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
crResp, ok := resp.JSON.(createRoomResponse)
if !ok {
@ -75,13 +74,12 @@ func TestJoinRoomByIDOrAlias(t *testing.T) {
// create a room with guest access enabled and invite Charlie
resp = createRoom(ctx, createRoomRequest{
Name: "testing",
IsDirect: true,
Topic: "testing",
Visibility: "public",
Preset: presetPublicChat,
Invite: []string{charlie.ID},
GuestCanJoin: true,
Name: "testing",
IsDirect: true,
Topic: "testing",
Visibility: "public",
Preset: presetPublicChat,
Invite: []string{charlie.ID},
}, aliceDev, &cfg.ClientAPI, userAPI, rsAPI, asAPI, time.Now())
crRespWithGuestAccess, ok := resp.JSON.(createRoomResponse)
if !ok {

View file

@ -338,9 +338,8 @@ func buildMembershipEvents(
evs := []*gomatrixserverlib.HeaderedEvent{}
for _, roomID := range roomIDs {
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(ctx, &verReq, &verRes); err != nil {
roomVersion, err := rsAPI.QueryRoomVersionForRoom(ctx, roomID)
if err != nil {
return nil, err
}
@ -358,7 +357,7 @@ func buildMembershipEvents(
content.DisplayName = newProfile.DisplayName
content.AvatarURL = newProfile.AvatarURL
if err := builder.SetContent(content); err != nil {
if err = builder.SetContent(content); err != nil {
return nil, err
}
@ -372,7 +371,7 @@ func buildMembershipEvents(
return nil, err
}
evs = append(evs, event.Headered(verRes.RoomVersion))
evs = append(evs, event.Headered(roomVersion))
}
return evs, nil

View file

@ -76,9 +76,8 @@ func SendEvent(
rsAPI api.ClientRoomserverAPI,
txnCache *transactions.Cache,
) util.JSONResponse {
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(req.Context(), &verReq, &verRes); err != nil {
roomVersion, err := rsAPI.QueryRoomVersionForRoom(req.Context(), roomID)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.UnsupportedRoomVersion(err.Error()),
@ -185,7 +184,7 @@ func SendEvent(
req.Context(), rsAPI,
api.KindNew,
[]*gomatrixserverlib.HeaderedEvent{
e.Headered(verRes.RoomVersion),
e.Headered(roomVersion),
},
device.UserDomain(),
domain,
@ -200,7 +199,7 @@ func SendEvent(
util.GetLogger(req.Context()).WithFields(logrus.Fields{
"event_id": e.EventID(),
"room_id": roomID,
"room_version": verRes.RoomVersion,
"room_version": roomVersion,
}).Info("Sent event to roomserver")
res := util.JSONResponse{

View file

@ -157,7 +157,6 @@ func SendServerNotice(
Visibility: "private",
Preset: presetPrivateChat,
CreationContent: cc,
GuestCanJoin: false,
RoomVersion: roomVersion,
PowerLevelContentOverride: pl,
}

View file

@ -16,6 +16,7 @@ import (
// FederationInternalAPI is used to query information from the federation sender.
type FederationInternalAPI interface {
gomatrixserverlib.FederatedStateClient
gomatrixserverlib.FederatedJoinClient
KeyserverFederationAPI
gomatrixserverlib.KeyDatabase
ClientFederationAPI

View file

@ -106,7 +106,9 @@ func (f *fedClient) GetServerKeys(ctx context.Context, matrixServer spec.ServerN
return keys, nil
}
func (f *fedClient) MakeJoin(ctx context.Context, origin, s spec.ServerName, roomID, userID string, roomVersions []gomatrixserverlib.RoomVersion) (res fclient.RespMakeJoin, err error) {
func (f *fedClient) MakeJoin(ctx context.Context, origin, s spec.ServerName, roomID, userID string) (res fclient.RespMakeJoin, err error) {
f.fedClientMutex.Lock()
defer f.fedClientMutex.Unlock()
for _, r := range f.allowJoins {
if r.ID == roomID {
res.RoomVersion = r.Version

View file

@ -9,14 +9,40 @@ import (
"github.com/matrix-org/gomatrixserverlib/spec"
)
const defaultTimeout = time.Second * 30
// Functions here are "proxying" calls to the gomatrixserverlib federation
// client.
func (a *FederationInternalAPI) MakeJoin(
ctx context.Context, origin, s spec.ServerName, roomID, userID string,
) (res gomatrixserverlib.MakeJoinResponse, err error) {
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.federation.MakeJoin(ctx, origin, s, roomID, userID)
if err != nil {
return &fclient.RespMakeJoin{}, err
}
return &ires, nil
}
func (a *FederationInternalAPI) SendJoin(
ctx context.Context, origin, s spec.ServerName, event *gomatrixserverlib.Event,
) (res gomatrixserverlib.SendJoinResponse, err error) {
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.federation.SendJoin(ctx, origin, s, event)
if err != nil {
return &fclient.RespSendJoin{}, err
}
return &ires, nil
}
func (a *FederationInternalAPI) GetEventAuth(
ctx context.Context, origin, s spec.ServerName,
roomVersion gomatrixserverlib.RoomVersion, roomID, eventID string,
) (res fclient.RespEventAuth, err error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) {
return a.federation.GetEventAuth(ctx, origin, s, roomVersion, roomID, eventID)
@ -30,7 +56,7 @@ func (a *FederationInternalAPI) GetEventAuth(
func (a *FederationInternalAPI) GetUserDevices(
ctx context.Context, origin, s spec.ServerName, userID string,
) (fclient.RespUserDevices, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) {
return a.federation.GetUserDevices(ctx, origin, s, userID)
@ -44,7 +70,7 @@ func (a *FederationInternalAPI) GetUserDevices(
func (a *FederationInternalAPI) ClaimKeys(
ctx context.Context, origin, s spec.ServerName, oneTimeKeys map[string]map[string]string,
) (fclient.RespClaimKeys, error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) {
return a.federation.ClaimKeys(ctx, origin, s, oneTimeKeys)
@ -70,7 +96,7 @@ func (a *FederationInternalAPI) QueryKeys(
func (a *FederationInternalAPI) Backfill(
ctx context.Context, origin, s spec.ServerName, roomID string, limit int, eventIDs []string,
) (res gomatrixserverlib.Transaction, err error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) {
return a.federation.Backfill(ctx, origin, s, roomID, limit, eventIDs)
@ -84,7 +110,7 @@ func (a *FederationInternalAPI) Backfill(
func (a *FederationInternalAPI) LookupState(
ctx context.Context, origin, s spec.ServerName, roomID, eventID string, roomVersion gomatrixserverlib.RoomVersion,
) (res gomatrixserverlib.StateResponse, err error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) {
return a.federation.LookupState(ctx, origin, s, roomID, eventID, roomVersion)
@ -99,7 +125,7 @@ func (a *FederationInternalAPI) LookupState(
func (a *FederationInternalAPI) LookupStateIDs(
ctx context.Context, origin, s spec.ServerName, roomID, eventID string,
) (res gomatrixserverlib.StateIDResponse, err error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) {
return a.federation.LookupStateIDs(ctx, origin, s, roomID, eventID)
@ -114,7 +140,7 @@ func (a *FederationInternalAPI) LookupMissingEvents(
ctx context.Context, origin, s spec.ServerName, roomID string,
missing fclient.MissingEvents, roomVersion gomatrixserverlib.RoomVersion,
) (res fclient.RespMissingEvents, err error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) {
return a.federation.LookupMissingEvents(ctx, origin, s, roomID, missing, roomVersion)
@ -128,7 +154,7 @@ func (a *FederationInternalAPI) LookupMissingEvents(
func (a *FederationInternalAPI) GetEvent(
ctx context.Context, origin, s spec.ServerName, eventID string,
) (res gomatrixserverlib.Transaction, err error) {
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
ires, err := a.doRequestIfNotBlacklisted(s, func() (interface{}, error) {
return a.federation.GetEvent(ctx, origin, s, eventID)

View file

@ -73,12 +73,6 @@ func (r *FederationInternalAPI) PerformJoin(
r.joins.Store(j, nil)
defer r.joins.Delete(j)
// Look up the supported room versions.
var supportedVersions []gomatrixserverlib.RoomVersion
for version := range version.SupportedRoomVersions() {
supportedVersions = append(supportedVersions, version)
}
// Deduplicate the server names we were provided but keep the ordering
// as this encodes useful information about which servers are most likely
// to respond.
@ -103,7 +97,6 @@ func (r *FederationInternalAPI) PerformJoin(
request.UserID,
request.Content,
serverName,
supportedVersions,
request.Unsigned,
); err != nil {
logrus.WithError(err).WithFields(logrus.Fields{
@ -146,128 +139,41 @@ func (r *FederationInternalAPI) performJoinUsingServer(
roomID, userID string,
content map[string]interface{},
serverName spec.ServerName,
supportedVersions []gomatrixserverlib.RoomVersion,
unsigned map[string]interface{},
) error {
if !r.shouldAttemptDirectFederation(serverName) {
return fmt.Errorf("relay servers have no meaningful response for join.")
}
_, origin, err := r.cfg.Matrix.SplitLocalID('@', userID)
user, err := spec.NewUserID(userID, true)
if err != nil {
return err
}
// Try to perform a make_join using the information supplied in the
// request.
respMakeJoin, err := r.federation.MakeJoin(
ctx,
origin,
serverName,
roomID,
userID,
supportedVersions,
)
if err != nil {
// TODO: Check if the user was not allowed to join the room.
r.statistics.ForServer(serverName).Failure()
return fmt.Errorf("r.federation.MakeJoin: %w", err)
joinInput := gomatrixserverlib.PerformJoinInput{
UserID: user,
RoomID: roomID,
ServerName: serverName,
Content: content,
Unsigned: unsigned,
PrivateKey: r.cfg.Matrix.PrivateKey,
KeyID: r.cfg.Matrix.KeyID,
KeyRing: r.keyRing,
EventProvider: federatedEventProvider(ctx, r.federation, r.keyRing, user.Domain(), serverName),
}
r.statistics.ForServer(serverName).Success(statistics.SendDirect)
response, joinErr := gomatrixserverlib.PerformJoin(ctx, r, joinInput)
// Set all the fields to be what they should be, this should be a no-op
// but it's possible that the remote server returned us something "odd"
respMakeJoin.JoinEvent.Type = spec.MRoomMember
respMakeJoin.JoinEvent.Sender = userID
respMakeJoin.JoinEvent.StateKey = &userID
respMakeJoin.JoinEvent.RoomID = roomID
respMakeJoin.JoinEvent.Redacts = ""
if content == nil {
content = map[string]interface{}{}
}
_ = json.Unmarshal(respMakeJoin.JoinEvent.Content, &content)
content["membership"] = spec.Join
if err = respMakeJoin.JoinEvent.SetContent(content); err != nil {
return fmt.Errorf("respMakeJoin.JoinEvent.SetContent: %w", err)
}
if err = respMakeJoin.JoinEvent.SetUnsigned(struct{}{}); err != nil {
return fmt.Errorf("respMakeJoin.JoinEvent.SetUnsigned: %w", err)
}
// Work out if we support the room version that has been supplied in
// the make_join response.
// "If not provided, the room version is assumed to be either "1" or "2"."
// https://matrix.org/docs/spec/server_server/unstable#get-matrix-federation-v1-make-join-roomid-userid
if respMakeJoin.RoomVersion == "" {
respMakeJoin.RoomVersion = setDefaultRoomVersionFromJoinEvent(respMakeJoin.JoinEvent)
}
verImpl, err := gomatrixserverlib.GetRoomVersion(respMakeJoin.RoomVersion)
if err != nil {
return err
}
// Build the join event.
event, err := respMakeJoin.JoinEvent.Build(
time.Now(),
origin,
r.cfg.Matrix.KeyID,
r.cfg.Matrix.PrivateKey,
respMakeJoin.RoomVersion,
)
if err != nil {
return fmt.Errorf("respMakeJoin.JoinEvent.Build: %w", err)
}
// Try to perform a send_join using the newly built event.
respSendJoin, err := r.federation.SendJoin(
context.Background(),
origin,
serverName,
event,
)
if err != nil {
r.statistics.ForServer(serverName).Failure()
return fmt.Errorf("r.federation.SendJoin: %w", err)
}
r.statistics.ForServer(serverName).Success(statistics.SendDirect)
// If the remote server returned an event in the "event" key of
// the send_join request then we should use that instead. It may
// contain signatures that we don't know about.
if len(respSendJoin.Event) > 0 {
var remoteEvent *gomatrixserverlib.Event
remoteEvent, err = verImpl.NewEventFromUntrustedJSON(respSendJoin.Event)
if err == nil && isWellFormedMembershipEvent(
remoteEvent, roomID, userID,
) {
event = remoteEvent
if joinErr != nil {
if !joinErr.Reachable {
r.statistics.ForServer(joinErr.ServerName).Failure()
} else {
r.statistics.ForServer(joinErr.ServerName).Success(statistics.SendDirect)
}
return joinErr.Err
}
// Sanity-check the join response to ensure that it has a create
// event, that the room version is known, etc.
authEvents := respSendJoin.AuthEvents.UntrustedEvents(respMakeJoin.RoomVersion)
if err = sanityCheckAuthChain(authEvents); err != nil {
return fmt.Errorf("sanityCheckAuthChain: %w", err)
}
// Process the join response in a goroutine. The idea here is
// that we'll try and wait for as long as possible for the work
// to complete, but if the client does give up waiting, we'll
// still continue to process the join anyway so that we don't
// waste the effort.
// TODO: Can we expand Check here to return a list of missing auth
// events rather than failing one at a time?
var respState gomatrixserverlib.StateResponse
respState, err = gomatrixserverlib.CheckSendJoinResponse(
context.Background(),
respMakeJoin.RoomVersion, &respSendJoin,
r.keyRing,
event,
federatedAuthProvider(ctx, r.federation, r.keyRing, origin, serverName),
)
if err != nil {
return fmt.Errorf("respSendJoin.Check: %w", err)
r.statistics.ForServer(serverName).Success(statistics.SendDirect)
if response == nil {
return fmt.Errorf("Received nil response from gomatrixserverlib.PerformJoin")
}
// We need to immediately update our list of joined hosts for this room now as we are technically
@ -276,60 +182,33 @@ func (r *FederationInternalAPI) performJoinUsingServer(
// joining a room, waiting for 200 OK then changing device keys and have those keys not be sent
// to other servers (this was a cause of a flakey sytest "Local device key changes get to remote servers")
// The events are trusted now as we performed auth checks above.
joinedHosts, err := consumers.JoinedHostsFromEvents(respState.GetStateEvents().TrustedEvents(respMakeJoin.RoomVersion, false))
joinedHosts, err := consumers.JoinedHostsFromEvents(response.StateSnapshot.GetStateEvents().TrustedEvents(response.JoinEvent.Version(), false))
if err != nil {
return fmt.Errorf("JoinedHostsFromEvents: failed to get joined hosts: %s", err)
}
logrus.WithField("room", roomID).Infof("Joined federated room with %d hosts", len(joinedHosts))
if _, err = r.db.UpdateRoom(context.Background(), roomID, joinedHosts, nil, true); err != nil {
return fmt.Errorf("UpdatedRoom: failed to update room with joined hosts: %s", err)
}
// If we successfully performed a send_join above then the other
// server now thinks we're a part of the room. Send the newly
// returned state to the roomserver to update our local view.
if unsigned != nil {
event, err = event.SetUnsigned(unsigned)
if err != nil {
// non-fatal, log and continue
logrus.WithError(err).Errorf("Failed to set unsigned content")
}
}
// TODO: Can I change this to not take respState but instead just take an opaque list of events?
if err = roomserverAPI.SendEventWithState(
context.Background(),
r.rsAPI,
origin,
user.Domain(),
roomserverAPI.KindNew,
respState,
event.Headered(respMakeJoin.RoomVersion),
response.StateSnapshot,
response.JoinEvent.Headered(response.JoinEvent.Version()),
serverName,
nil,
false,
); err != nil {
return fmt.Errorf("roomserverAPI.SendEventWithState: %w", err)
}
return nil
}
// isWellFormedMembershipEvent returns true if the event looks like a legitimate
// membership event.
func isWellFormedMembershipEvent(event *gomatrixserverlib.Event, roomID, userID string) bool {
if membership, err := event.Membership(); err != nil {
return false
} else if membership != spec.Join {
return false
}
if event.RoomID() != roomID {
return false
}
if !event.StateKeyEquals(userID) {
return false
}
return true
}
// PerformOutboundPeekRequest implements api.FederationInternalAPI
func (r *FederationInternalAPI) PerformOutboundPeek(
ctx context.Context,
@ -475,12 +354,12 @@ func (r *FederationInternalAPI) performOutboundPeekUsingServer(
// authenticate the state returned (check its auth events etc)
// the equivalent of CheckSendJoinResponse()
authEvents, stateEvents, err := gomatrixserverlib.CheckStateResponse(
ctx, &respPeek, respPeek.RoomVersion, r.keyRing, federatedAuthProvider(ctx, r.federation, r.keyRing, r.cfg.Matrix.ServerName, serverName),
ctx, &respPeek, respPeek.RoomVersion, r.keyRing, federatedEventProvider(ctx, r.federation, r.keyRing, r.cfg.Matrix.ServerName, serverName),
)
if err != nil {
return fmt.Errorf("error checking state returned from peeking: %w", err)
}
if err = sanityCheckAuthChain(authEvents); err != nil {
if err = checkEventsContainCreateEvent(authEvents); err != nil {
return fmt.Errorf("sanityCheckAuthChain: %w", err)
}
@ -719,9 +598,9 @@ func (r *FederationInternalAPI) MarkServersAlive(destinations []spec.ServerName)
}
}
func sanityCheckAuthChain(authChain []*gomatrixserverlib.Event) error {
func checkEventsContainCreateEvent(events []*gomatrixserverlib.Event) error {
// sanity check we have a create event and it has a known room version
for _, ev := range authChain {
for _, ev := range events {
if ev.Type() == spec.MRoomCreate && ev.StateKeyEquals("") {
// make sure the room version is known
content := ev.Content()
@ -739,43 +618,19 @@ func sanityCheckAuthChain(authChain []*gomatrixserverlib.Event) error {
}
knownVersions := gomatrixserverlib.RoomVersions()
if _, ok := knownVersions[gomatrixserverlib.RoomVersion(verBody.Version)]; !ok {
return fmt.Errorf("auth chain m.room.create event has an unknown room version: %s", verBody.Version)
return fmt.Errorf("m.room.create event has an unknown room version: %s", verBody.Version)
}
return nil
}
}
return fmt.Errorf("auth chain response is missing m.room.create event")
return fmt.Errorf("response is missing m.room.create event")
}
func setDefaultRoomVersionFromJoinEvent(
joinEvent gomatrixserverlib.EventBuilder,
) gomatrixserverlib.RoomVersion {
// if auth events are not event references we know it must be v3+
// we have to do these shenanigans to satisfy sytest, specifically for:
// "Outbound federation rejects m.room.create events with an unknown room version"
hasEventRefs := true
authEvents, ok := joinEvent.AuthEvents.([]interface{})
if ok {
if len(authEvents) > 0 {
_, ok = authEvents[0].(string)
if ok {
// event refs are objects, not strings, so we know we must be dealing with a v3+ room.
hasEventRefs = false
}
}
}
if hasEventRefs {
return gomatrixserverlib.RoomVersionV1
}
return gomatrixserverlib.RoomVersionV4
}
// FederatedAuthProvider is an auth chain provider which fetches events from the server provided
func federatedAuthProvider(
// federatedEventProvider is an event provider which fetches events from the server provided
func federatedEventProvider(
ctx context.Context, federation fclient.FederationClient,
keyRing gomatrixserverlib.JSONVerifier, origin, server spec.ServerName,
) gomatrixserverlib.AuthChainProvider {
) gomatrixserverlib.EventProvider {
// A list of events that we have retried, if they were not included in
// the auth events supplied in the send_join.
retries := map[string][]*gomatrixserverlib.Event{}

View file

@ -42,9 +42,8 @@ func MakeJoin(
roomID, userID string,
remoteVersions []gomatrixserverlib.RoomVersion,
) util.JSONResponse {
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), &verReq, &verRes); err != nil {
roomVersion, err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), roomID)
if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: jsonerror.InternalServerError(),
@ -57,7 +56,7 @@ func MakeJoin(
// https://matrix.org/docs/spec/server_server/r0.1.3#get-matrix-federation-v1-make-join-roomid-userid
remoteSupportsVersion := false
for _, v := range remoteVersions {
if v == verRes.RoomVersion {
if v == roomVersion {
remoteSupportsVersion = true
break
}
@ -66,7 +65,7 @@ func MakeJoin(
if !remoteSupportsVersion {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.IncompatibleRoomVersion(verRes.RoomVersion),
JSON: jsonerror.IncompatibleRoomVersion(roomVersion),
}
}
@ -109,7 +108,7 @@ func MakeJoin(
// Check if the restricted join is allowed. If the room doesn't
// support restricted joins then this is effectively a no-op.
res, authorisedVia, err := checkRestrictedJoin(httpReq, rsAPI, verRes.RoomVersion, roomID, userID)
res, authorisedVia, err := checkRestrictedJoin(httpReq, rsAPI, roomVersion, roomID, userID)
if err != nil {
util.GetLogger(httpReq.Context()).WithError(err).Error("checkRestrictedJoin failed")
return jsonerror.InternalServerError()
@ -144,7 +143,7 @@ func MakeJoin(
}
queryRes := api.QueryLatestEventsAndStateResponse{
RoomVersion: verRes.RoomVersion,
RoomVersion: roomVersion,
}
event, err := eventutil.QueryAndBuildEvent(httpReq.Context(), &builder, cfg.Matrix, identity, time.Now(), rsAPI, &queryRes)
if err == eventutil.ErrRoomNoExists {
@ -180,7 +179,7 @@ func MakeJoin(
Code: http.StatusOK,
JSON: map[string]interface{}{
"event": builder,
"room_version": verRes.RoomVersion,
"room_version": roomVersion,
},
}
}
@ -197,21 +196,20 @@ func SendJoin(
keys gomatrixserverlib.JSONVerifier,
roomID, eventID string,
) util.JSONResponse {
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), &verReq, &verRes); err != nil {
roomVersion, err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), roomID)
if err != nil {
util.GetLogger(httpReq.Context()).WithError(err).Error("rsAPI.QueryRoomVersionForRoom failed")
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: jsonerror.InternalServerError(),
}
}
verImpl, err := gomatrixserverlib.GetRoomVersion(verRes.RoomVersion)
verImpl, err := gomatrixserverlib.GetRoomVersion(roomVersion)
if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: jsonerror.UnsupportedRoomVersion(
fmt.Sprintf("QueryRoomVersionForRoom returned unknown room version: %s", verRes.RoomVersion),
fmt.Sprintf("QueryRoomVersionForRoom returned unknown room version: %s", roomVersion),
),
}
}

View file

@ -140,21 +140,20 @@ func SendLeave(
keys gomatrixserverlib.JSONVerifier,
roomID, eventID string,
) util.JSONResponse {
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), &verReq, &verRes); err != nil {
roomVersion, err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), roomID)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.UnsupportedRoomVersion(err.Error()),
}
}
verImpl, err := gomatrixserverlib.GetRoomVersion(verRes.RoomVersion)
verImpl, err := gomatrixserverlib.GetRoomVersion(roomVersion)
if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: jsonerror.UnsupportedRoomVersion(
fmt.Sprintf("QueryRoomVersionForRoom returned unknown version: %s", verRes.RoomVersion),
fmt.Sprintf("QueryRoomVersionForRoom returned unknown version: %s", roomVersion),
),
}
}
@ -313,7 +312,7 @@ func SendLeave(
InputRoomEvents: []api.InputRoomEvent{
{
Kind: api.KindNew,
Event: event.Headered(verRes.RoomVersion),
Event: event.Headered(roomVersion),
SendAsServer: string(cfg.Matrix.ServerName),
TransactionID: nil,
},

View file

@ -35,10 +35,8 @@ func Peek(
remoteVersions []gomatrixserverlib.RoomVersion,
) util.JSONResponse {
// TODO: check if we're just refreshing an existing peek by querying the federationapi
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), &verReq, &verRes); err != nil {
roomVersion, err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), roomID)
if err != nil {
return util.JSONResponse{
Code: http.StatusInternalServerError,
JSON: jsonerror.InternalServerError(),
@ -50,7 +48,7 @@ func Peek(
// the peek URL.
remoteSupportsVersion := false
for _, v := range remoteVersions {
if v == verRes.RoomVersion {
if v == roomVersion {
remoteSupportsVersion = true
break
}
@ -59,7 +57,7 @@ func Peek(
if !remoteSupportsVersion {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.IncompatibleRoomVersion(verRes.RoomVersion),
JSON: jsonerror.IncompatibleRoomVersion(roomVersion),
}
}
@ -69,7 +67,7 @@ func Peek(
renewalInterval := int64(60 * 60 * 1000 * 1000)
var response api.PerformInboundPeekResponse
err := rsAPI.PerformInboundPeek(
err = rsAPI.PerformInboundPeek(
httpReq.Context(),
&api.PerformInboundPeekRequest{
RoomID: roomID,

View file

@ -69,9 +69,8 @@ func CreateInvitesFrom3PIDInvites(
evs := []*gomatrixserverlib.HeaderedEvent{}
for _, inv := range body.Invites {
verReq := api.QueryRoomVersionForRoomRequest{RoomID: inv.RoomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(req.Context(), &verReq, &verRes); err != nil {
roomVersion, err := rsAPI.QueryRoomVersionForRoom(req.Context(), inv.RoomID)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.UnsupportedRoomVersion(err.Error()),
@ -86,7 +85,7 @@ func CreateInvitesFrom3PIDInvites(
return jsonerror.InternalServerError()
}
if event != nil {
evs = append(evs, event.Headered(verRes.RoomVersion))
evs = append(evs, event.Headered(roomVersion))
}
}
@ -162,9 +161,8 @@ func ExchangeThirdPartyInvite(
}
}
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err = rsAPI.QueryRoomVersionForRoom(httpReq.Context(), &verReq, &verRes); err != nil {
roomVersion, err := rsAPI.QueryRoomVersionForRoom(httpReq.Context(), roomID)
if err != nil {
return util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.UnsupportedRoomVersion(err.Error()),
@ -185,7 +183,7 @@ func ExchangeThirdPartyInvite(
// Ask the requesting server to sign the newly created event so we know it
// acknowledged it
inviteReq, err := fclient.NewInviteV2Request(event.Headered(verRes.RoomVersion), nil)
inviteReq, err := fclient.NewInviteV2Request(event.Headered(roomVersion), nil)
if err != nil {
util.GetLogger(httpReq.Context()).WithError(err).Error("failed to make invite v2 request")
return jsonerror.InternalServerError()
@ -195,9 +193,9 @@ func ExchangeThirdPartyInvite(
util.GetLogger(httpReq.Context()).WithError(err).Error("federation.SendInvite failed")
return jsonerror.InternalServerError()
}
verImpl, err := gomatrixserverlib.GetRoomVersion(verRes.RoomVersion)
verImpl, err := gomatrixserverlib.GetRoomVersion(roomVersion)
if err != nil {
util.GetLogger(httpReq.Context()).WithError(err).Errorf("unknown room version: %s", verRes.RoomVersion)
util.GetLogger(httpReq.Context()).WithError(err).Errorf("unknown room version: %s", roomVersion)
return jsonerror.InternalServerError()
}
inviteEvent, err := verImpl.NewEventFromUntrustedJSON(signedEvent.Event)
@ -211,7 +209,7 @@ func ExchangeThirdPartyInvite(
httpReq.Context(), rsAPI,
api.KindNew,
[]*gomatrixserverlib.HeaderedEvent{
inviteEvent.Headered(verRes.RoomVersion),
inviteEvent.Headered(roomVersion),
},
request.Destination(),
request.Origin(),
@ -239,12 +237,6 @@ func createInviteFrom3PIDInvite(
inv invite, federation fclient.FederationClient,
userAPI userapi.FederationUserAPI,
) (*gomatrixserverlib.Event, error) {
verReq := api.QueryRoomVersionForRoomRequest{RoomID: inv.RoomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := rsAPI.QueryRoomVersionForRoom(ctx, &verReq, &verRes); err != nil {
return nil, err
}
_, server, err := gomatrixserverlib.SplitID('@', inv.MXID)
if err != nil {
return nil, err

2
go.mod
View file

@ -22,7 +22,7 @@ require (
github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530
github.com/matrix-org/gomatrixserverlib v0.0.0-20230424155704-8daeaebaa0bc
github.com/matrix-org/gomatrixserverlib v0.0.0-20230427002343-809b162d0e4f
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66
github.com/mattn/go-sqlite3 v1.14.16

4
go.sum
View file

@ -321,8 +321,8 @@ github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91 h1:s7fexw
github.com/matrix-org/go-sqlite3-js v0.0.0-20220419092513-28aa791a1c91/go.mod h1:e+cg2q7C7yE5QnAXgzo512tgFh1RbQLC0+jozuegKgo=
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U=
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
github.com/matrix-org/gomatrixserverlib v0.0.0-20230424155704-8daeaebaa0bc h1:F73iHhpTZxWVO6qbyGZxd7Ch44v1gK6xNQZ7QVos/Es=
github.com/matrix-org/gomatrixserverlib v0.0.0-20230424155704-8daeaebaa0bc/go.mod h1:7HTbSZe+CIdmeqVyFMekwD5dFU8khWQyngKATvd12FU=
github.com/matrix-org/gomatrixserverlib v0.0.0-20230427002343-809b162d0e4f h1:nck6OTEVtxXoF9mDsvZRXaXjNkz03DuhNgrl462xOso=
github.com/matrix-org/gomatrixserverlib v0.0.0-20230427002343-809b162d0e4f/go.mod h1:7HTbSZe+CIdmeqVyFMekwD5dFU8khWQyngKATvd12FU=
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a h1:awrPDf9LEFySxTLKYBMCiObelNx/cBuv/wzllvCCH3A=
github.com/matrix-org/pinecone v0.11.1-0.20230210171230-8c3b24f2649a/go.mod h1:HchJX9oKMXaT2xYFs0Ha/6Zs06mxLU8k6F1ODnrGkeQ=
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y=

View file

@ -115,14 +115,13 @@ func (t *TxnReq) ProcessTransaction(ctx context.Context) (*fclient.RespSend, *ut
if v, ok := roomVersions[roomID]; ok {
return v
}
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := t.rsAPI.QueryRoomVersionForRoom(ctx, &verReq, &verRes); err != nil {
util.GetLogger(ctx).WithError(err).Debug("Transaction: Failed to query room version for room", verReq.RoomID)
roomVersion, err := t.rsAPI.QueryRoomVersionForRoom(ctx, roomID)
if err != nil {
util.GetLogger(ctx).WithError(err).Debug("Transaction: Failed to query room version for room", roomID)
return ""
}
roomVersions[roomID] = verRes.RoomVersion
return verRes.RoomVersion
roomVersions[roomID] = roomVersion
return roomVersion
}
for _, pdu := range t.PDUs {

View file

@ -72,14 +72,12 @@ type FakeRsAPI struct {
func (r *FakeRsAPI) QueryRoomVersionForRoom(
ctx context.Context,
req *rsAPI.QueryRoomVersionForRoomRequest,
res *rsAPI.QueryRoomVersionForRoomResponse,
) error {
roomID string,
) (gomatrixserverlib.RoomVersion, error) {
if r.shouldFailQuery {
return fmt.Errorf("Failure")
return "", fmt.Errorf("Failure")
}
res.RoomVersion = gomatrixserverlib.RoomVersionV10
return nil
return gomatrixserverlib.RoomVersionV10, nil
}
func (r *FakeRsAPI) QueryServerBannedFromRoom(
@ -722,11 +720,9 @@ func (t *testRoomserverAPI) QueryServerJoinedToRoom(
// Asks for the room version for a given room.
func (t *testRoomserverAPI) QueryRoomVersionForRoom(
ctx context.Context,
request *rsAPI.QueryRoomVersionForRoomRequest,
response *rsAPI.QueryRoomVersionForRoomResponse,
) error {
response.RoomVersion = testRoomVersion
return nil
roomID string,
) (gomatrixserverlib.RoomVersion, error) {
return testRoomVersion, nil
}
func (t *testRoomserverAPI) QueryServerBannedFromRoom(

View file

@ -162,7 +162,7 @@ type ClientRoomserverAPI interface {
QueryStateAfterEvents(ctx context.Context, req *QueryStateAfterEventsRequest, res *QueryStateAfterEventsResponse) error
// QueryKnownUsers returns a list of users that we know about from our joined rooms.
QueryKnownUsers(ctx context.Context, req *QueryKnownUsersRequest, res *QueryKnownUsersResponse) error
QueryRoomVersionForRoom(ctx context.Context, req *QueryRoomVersionForRoomRequest, res *QueryRoomVersionForRoomResponse) error
QueryRoomVersionForRoom(ctx context.Context, roomID string) (gomatrixserverlib.RoomVersion, error)
QueryPublishedRooms(ctx context.Context, req *QueryPublishedRoomsRequest, res *QueryPublishedRoomsResponse) error
GetRoomIDForAlias(ctx context.Context, req *GetRoomIDForAliasRequest, res *GetRoomIDForAliasResponse) error
@ -202,7 +202,7 @@ type FederationRoomserverAPI interface {
// QueryServerBannedFromRoom returns whether a server is banned from a room by server ACLs.
QueryServerBannedFromRoom(ctx context.Context, req *QueryServerBannedFromRoomRequest, res *QueryServerBannedFromRoomResponse) error
QueryMembershipsForRoom(ctx context.Context, req *QueryMembershipsForRoomRequest, res *QueryMembershipsForRoomResponse) error
QueryRoomVersionForRoom(ctx context.Context, req *QueryRoomVersionForRoomRequest, res *QueryRoomVersionForRoomResponse) error
QueryRoomVersionForRoom(ctx context.Context, roomID string) (gomatrixserverlib.RoomVersion, error)
GetRoomIDForAlias(ctx context.Context, req *GetRoomIDForAliasRequest, res *GetRoomIDForAliasResponse) error
// QueryEventsByID queries a list of events by event ID for one room. If no room is specified, it will try to determine
// which room to use by querying the first events roomID.

View file

@ -26,6 +26,10 @@ func IsServerAllowed(
serverCurrentlyInRoom bool,
authEvents []*gomatrixserverlib.Event,
) bool {
// In practice should not happen, but avoids unneeded CPU cycles
if serverName == "" || len(authEvents) == 0 {
return false
}
historyVisibility := HistoryVisibilityForRoom(authEvents)
// 1. If the history_visibility was set to world_readable, allow.

View file

@ -0,0 +1,85 @@
package auth
import (
"testing"
"github.com/matrix-org/dendrite/test"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/gomatrixserverlib/spec"
)
func TestIsServerAllowed(t *testing.T) {
alice := test.NewUser(t)
tests := []struct {
name string
want bool
roomFunc func() *test.Room
serverName spec.ServerName
serverCurrentlyInRoom bool
}{
{
name: "no servername specified",
roomFunc: func() *test.Room { return test.NewRoom(t, alice) },
},
{
name: "no authEvents specified",
serverName: "test",
roomFunc: func() *test.Room { return &test.Room{} },
},
{
name: "default denied",
serverName: "test2",
roomFunc: func() *test.Room { return test.NewRoom(t, alice) },
},
{
name: "world readable room",
serverName: "test",
roomFunc: func() *test.Room {
return test.NewRoom(t, alice, test.RoomHistoryVisibility(gomatrixserverlib.HistoryVisibilityWorldReadable))
},
want: true,
},
{
name: "allowed due to alice being joined",
serverName: "test",
roomFunc: func() *test.Room { return test.NewRoom(t, alice) },
want: true,
},
{
name: "allowed due to 'serverCurrentlyInRoom'",
serverName: "test2",
roomFunc: func() *test.Room { return test.NewRoom(t, alice) },
want: true,
serverCurrentlyInRoom: true,
},
{
name: "allowed due to pending invite",
serverName: "test2",
roomFunc: func() *test.Room {
bob := test.User{ID: "@bob:test2"}
r := test.NewRoom(t, alice, test.RoomHistoryVisibility(gomatrixserverlib.HistoryVisibilityInvited))
r.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
"membership": spec.Invite,
}, test.WithStateKey(bob.ID))
return r
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.roomFunc == nil {
t.Fatalf("missing roomFunc")
}
var authEvents []*gomatrixserverlib.Event
for _, ev := range tt.roomFunc().Events() {
authEvents = append(authEvents, ev.Event)
}
if got := IsServerAllowed(tt.serverName, tt.serverCurrentlyInRoom, authEvents); got != tt.want {
t.Errorf("IsServerAllowed() = %v, want %v", got, tt.want)
}
})
}
}

View file

@ -478,7 +478,7 @@ func (r *Inputer) processRoomEvent(
// If guest_access changed and is not can_join, kick all guest users.
if event.Type() == spec.MRoomGuestAccess && gjson.GetBytes(event.Content(), "guest_access").Str != "can_join" {
if err = r.kickGuests(ctx, event, roomInfo); err != nil {
if err = r.kickGuests(ctx, event, roomInfo); err != nil && err != sql.ErrNoRows {
logrus.WithError(err).Error("failed to kick guest users on m.room.guest_access revocation")
}
}

View file

@ -272,9 +272,7 @@ func publishNewRoomAndUnpublishOldRoom(
}
func (r *Upgrader) validateRoomExists(ctx context.Context, roomID string) error {
verReq := api.QueryRoomVersionForRoomRequest{RoomID: roomID}
verRes := api.QueryRoomVersionForRoomResponse{}
if err := r.URSAPI.QueryRoomVersionForRoom(ctx, &verReq, &verRes); err != nil {
if _, err := r.URSAPI.QueryRoomVersionForRoom(ctx, roomID); err != nil {
return eventutil.ErrRoomNoExists
}
return nil
@ -307,7 +305,7 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
continue
}
if event.Type() == spec.MRoomMember && !event.StateKeyEquals(userID) {
// With the exception of bans and invites which we do want to copy, we
// With the exception of bans which we do want to copy, we
// should ignore membership events that aren't our own, as event auth will
// prevent us from being able to create membership events on behalf of other
// users anyway unless they are invites or bans.
@ -317,11 +315,15 @@ func (r *Upgrader) generateInitialEvents(ctx context.Context, oldRoom *api.Query
}
switch membership {
case spec.Ban:
case spec.Invite:
default:
continue
}
}
// skip events that rely on a specific user being present
sKey := *event.StateKey()
if event.Type() != spec.MRoomMember && len(sKey) > 0 && sKey[:1] == "@" {
continue
}
state[gomatrixserverlib.StateKeyTuple{EventType: event.Type(), StateKey: *event.StateKey()}] = event
}

View file

@ -521,14 +521,10 @@ func (r *Queryer) QueryMissingEvents(
response.Events = make([]*gomatrixserverlib.HeaderedEvent, 0, len(loadedEvents)-len(eventsToFilter))
for _, event := range loadedEvents {
if !eventsToFilter[event.EventID()] {
roomVersion, verr := r.roomVersion(event.RoomID())
if verr != nil {
return verr
}
if _, ok := redactEventIDs[event.EventID()]; ok {
event.Redact()
}
response.Events = append(response.Events, event.Headered(roomVersion))
response.Events = append(response.Events, event.Headered(info.RoomVersion))
}
}
@ -696,34 +692,20 @@ func GetAuthChain(
}
// QueryRoomVersionForRoom implements api.RoomserverInternalAPI
func (r *Queryer) QueryRoomVersionForRoom(
ctx context.Context,
request *api.QueryRoomVersionForRoomRequest,
response *api.QueryRoomVersionForRoomResponse,
) error {
if roomVersion, ok := r.Cache.GetRoomVersion(request.RoomID); ok {
response.RoomVersion = roomVersion
return nil
func (r *Queryer) QueryRoomVersionForRoom(ctx context.Context, roomID string) (gomatrixserverlib.RoomVersion, error) {
if roomVersion, ok := r.Cache.GetRoomVersion(roomID); ok {
return roomVersion, nil
}
info, err := r.DB.RoomInfo(ctx, request.RoomID)
info, err := r.DB.RoomInfo(ctx, roomID)
if err != nil {
return err
return "", err
}
if info == nil {
return fmt.Errorf("QueryRoomVersionForRoom: missing room info for room %s", request.RoomID)
return "", fmt.Errorf("QueryRoomVersionForRoom: missing room info for room %s", roomID)
}
response.RoomVersion = info.RoomVersion
r.Cache.StoreRoomVersion(request.RoomID, response.RoomVersion)
return nil
}
func (r *Queryer) roomVersion(roomID string) (gomatrixserverlib.RoomVersion, error) {
var res api.QueryRoomVersionForRoomResponse
err := r.QueryRoomVersionForRoom(context.Background(), &api.QueryRoomVersionForRoomRequest{
RoomID: roomID,
}, &res)
return res.RoomVersion, err
r.Cache.StoreRoomVersion(roomID, info.RoomVersion)
return info.RoomVersion, nil
}
func (r *Queryer) QueryPublishedRooms(
@ -910,8 +892,8 @@ func (r *Queryer) QueryRestrictedJoinAllowed(ctx context.Context, req *api.Query
if err = json.Unmarshal(joinRulesEvent.Content(), &joinRules); err != nil {
return fmt.Errorf("json.Unmarshal: %w", err)
}
// If the join rule isn't "restricted" then there's nothing more to do.
res.Restricted = joinRules.JoinRule == spec.Restricted
// If the join rule isn't "restricted" or "knock_restricted" then there's nothing more to do.
res.Restricted = joinRules.JoinRule == spec.Restricted || joinRules.JoinRule == spec.KnockRestricted
if !res.Restricted {
return nil
}
@ -932,9 +914,9 @@ func (r *Queryer) QueryRestrictedJoinAllowed(ctx context.Context, req *api.Query
if err != nil {
return fmt.Errorf("r.DB.GetStateEvent: %w", err)
}
var powerLevels gomatrixserverlib.PowerLevelContent
if err = json.Unmarshal(powerLevelsEvent.Content(), &powerLevels); err != nil {
return fmt.Errorf("json.Unmarshal: %w", err)
powerLevels, err := powerLevelsEvent.PowerLevels()
if err != nil {
return fmt.Errorf("unable to get powerlevels: %w", err)
}
// Step through the join rules and see if the user matches any of them.
for _, rule := range joinRules.Allow {

View file

@ -8,10 +8,13 @@ import (
"time"
"github.com/matrix-org/dendrite/internal/caching"
"github.com/matrix-org/dendrite/internal/eventutil"
"github.com/matrix-org/dendrite/internal/httputil"
"github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/roomserver/version"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/stretchr/testify/assert"
"github.com/tidwall/gjson"
"github.com/matrix-org/dendrite/roomserver/state"
"github.com/matrix-org/dendrite/roomserver/types"
@ -577,3 +580,506 @@ func TestRedaction(t *testing.T) {
}
})
}
func TestQueryRestrictedJoinAllowed(t *testing.T) {
alice := test.NewUser(t)
bob := test.NewUser(t)
// a room we don't create in the database
allowedByRoomNotExists := test.NewRoom(t, alice)
// a room we create in the database, used for authorisation
allowedByRoomExists := test.NewRoom(t, alice)
allowedByRoomExists.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
"membership": spec.Join,
}, test.WithStateKey(bob.ID))
testCases := []struct {
name string
prepareRoomFunc func(t *testing.T) *test.Room
wantResponse api.QueryRestrictedJoinAllowedResponse
}{
{
name: "public room unrestricted",
prepareRoomFunc: func(t *testing.T) *test.Room {
return test.NewRoom(t, alice)
},
wantResponse: api.QueryRestrictedJoinAllowedResponse{
Resident: true,
},
},
{
name: "room version without restrictions",
prepareRoomFunc: func(t *testing.T) *test.Room {
return test.NewRoom(t, alice, test.RoomVersion(gomatrixserverlib.RoomVersionV7))
},
},
{
name: "restricted only", // bob is not allowed to join
prepareRoomFunc: func(t *testing.T) *test.Room {
r := test.NewRoom(t, alice, test.RoomVersion(gomatrixserverlib.RoomVersionV8))
r.CreateAndInsert(t, alice, spec.MRoomJoinRules, map[string]interface{}{
"join_rule": spec.Restricted,
}, test.WithStateKey(""))
return r
},
wantResponse: api.QueryRestrictedJoinAllowedResponse{
Resident: true,
Restricted: true,
},
},
{
name: "knock_restricted",
prepareRoomFunc: func(t *testing.T) *test.Room {
r := test.NewRoom(t, alice, test.RoomVersion(gomatrixserverlib.RoomVersionV8))
r.CreateAndInsert(t, alice, spec.MRoomJoinRules, map[string]interface{}{
"join_rule": spec.KnockRestricted,
}, test.WithStateKey(""))
return r
},
wantResponse: api.QueryRestrictedJoinAllowedResponse{
Resident: true,
Restricted: true,
},
},
{
name: "restricted with pending invite", // bob should be allowed to join
prepareRoomFunc: func(t *testing.T) *test.Room {
r := test.NewRoom(t, alice, test.RoomVersion(gomatrixserverlib.RoomVersionV8))
r.CreateAndInsert(t, alice, spec.MRoomJoinRules, map[string]interface{}{
"join_rule": spec.Restricted,
}, test.WithStateKey(""))
r.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
"membership": spec.Invite,
}, test.WithStateKey(bob.ID))
return r
},
wantResponse: api.QueryRestrictedJoinAllowedResponse{
Resident: true,
Restricted: true,
Allowed: true,
},
},
{
name: "restricted with allowed room_id, but missing room", // bob should not be allowed to join, as we don't know about the room
prepareRoomFunc: func(t *testing.T) *test.Room {
r := test.NewRoom(t, alice, test.RoomVersion(gomatrixserverlib.RoomVersionV10))
r.CreateAndInsert(t, alice, spec.MRoomJoinRules, map[string]interface{}{
"join_rule": spec.KnockRestricted,
"allow": []map[string]interface{}{
{
"room_id": allowedByRoomNotExists.ID,
"type": spec.MRoomMembership,
},
},
}, test.WithStateKey(""))
r.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
"membership": spec.Join,
"join_authorised_via_users_server": alice.ID,
}, test.WithStateKey(bob.ID))
return r
},
wantResponse: api.QueryRestrictedJoinAllowedResponse{
Restricted: true,
},
},
{
name: "restricted with allowed room_id", // bob should be allowed to join, as we know about the room
prepareRoomFunc: func(t *testing.T) *test.Room {
r := test.NewRoom(t, alice, test.RoomVersion(gomatrixserverlib.RoomVersionV10))
r.CreateAndInsert(t, alice, spec.MRoomJoinRules, map[string]interface{}{
"join_rule": spec.KnockRestricted,
"allow": []map[string]interface{}{
{
"room_id": allowedByRoomExists.ID,
"type": spec.MRoomMembership,
},
},
}, test.WithStateKey(""))
r.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{
"membership": spec.Join,
"join_authorised_via_users_server": alice.ID,
}, test.WithStateKey(bob.ID))
return r
},
wantResponse: api.QueryRestrictedJoinAllowedResponse{
Resident: true,
Restricted: true,
Allowed: true,
AuthorisedVia: alice.ID,
},
},
}
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
natsInstance := jetstream.NATSInstance{}
defer close()
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
rsAPI.SetFederationAPI(nil, nil)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if tc.prepareRoomFunc == nil {
t.Fatal("missing prepareRoomFunc")
}
testRoom := tc.prepareRoomFunc(t)
// Create the room
if err := api.SendEvents(processCtx.Context(), rsAPI, api.KindNew, testRoom.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
if err := api.SendEvents(processCtx.Context(), rsAPI, api.KindNew, allowedByRoomExists.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
req := api.QueryRestrictedJoinAllowedRequest{
UserID: bob.ID,
RoomID: testRoom.ID,
}
res := api.QueryRestrictedJoinAllowedResponse{}
if err := rsAPI.QueryRestrictedJoinAllowed(processCtx.Context(), &req, &res); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(tc.wantResponse, res) {
t.Fatalf("unexpected response, want %#v - got %#v", tc.wantResponse, res)
}
})
}
})
}
func TestUpgrade(t *testing.T) {
alice := test.NewUser(t)
bob := test.NewUser(t)
charlie := test.NewUser(t)
ctx := context.Background()
spaceChild := test.NewRoom(t, alice)
validateTuples := []gomatrixserverlib.StateKeyTuple{
{EventType: spec.MRoomCreate},
{EventType: spec.MRoomPowerLevels},
{EventType: spec.MRoomJoinRules},
{EventType: spec.MRoomName},
{EventType: spec.MRoomCanonicalAlias},
{EventType: "m.room.tombstone"},
{EventType: "m.custom.event"},
{EventType: "m.space.child", StateKey: spaceChild.ID},
{EventType: "m.custom.event", StateKey: alice.ID},
{EventType: spec.MRoomMember, StateKey: charlie.ID}, // ban should be transferred
}
validate := func(t *testing.T, oldRoomID, newRoomID string, rsAPI api.RoomserverInternalAPI) {
oldRoomState := &api.QueryCurrentStateResponse{}
if err := rsAPI.QueryCurrentState(ctx, &api.QueryCurrentStateRequest{
RoomID: oldRoomID,
StateTuples: validateTuples,
}, oldRoomState); err != nil {
t.Fatal(err)
}
newRoomState := &api.QueryCurrentStateResponse{}
if err := rsAPI.QueryCurrentState(ctx, &api.QueryCurrentStateRequest{
RoomID: newRoomID,
StateTuples: validateTuples,
}, newRoomState); err != nil {
t.Fatal(err)
}
// the old room should have a tombstone event
ev := oldRoomState.StateEvents[gomatrixserverlib.StateKeyTuple{EventType: "m.room.tombstone"}]
replacementRoom := gjson.GetBytes(ev.Content(), "replacement_room").Str
if replacementRoom != newRoomID {
t.Fatalf("tombstone event has replacement_room '%s', expected '%s'", replacementRoom, newRoomID)
}
// the new room should have a predecessor equal to the old room
ev = newRoomState.StateEvents[gomatrixserverlib.StateKeyTuple{EventType: spec.MRoomCreate}]
predecessor := gjson.GetBytes(ev.Content(), "predecessor.room_id").Str
if predecessor != oldRoomID {
t.Fatalf("got predecessor room '%s', expected '%s'", predecessor, oldRoomID)
}
for _, tuple := range validateTuples {
// Skip create and powerlevel event (new room has e.g. predecessor event, old room has restricted powerlevels)
switch tuple.EventType {
case spec.MRoomCreate, spec.MRoomPowerLevels, spec.MRoomCanonicalAlias:
continue
}
oldEv, ok := oldRoomState.StateEvents[tuple]
if !ok {
t.Logf("skipping tuple %#v as it doesn't exist in the old room", tuple)
continue
}
newEv, ok := newRoomState.StateEvents[tuple]
if !ok {
t.Logf("skipping tuple %#v as it doesn't exist in the new room", tuple)
continue
}
if !reflect.DeepEqual(oldEv.Content(), newEv.Content()) {
t.Logf("OldEvent QueryCurrentState: %s", string(oldEv.Content()))
t.Logf("NewEvent QueryCurrentState: %s", string(newEv.Content()))
t.Errorf("event content mismatch")
}
}
}
testCases := []struct {
name string
upgradeUser string
roomFunc func(rsAPI api.RoomserverInternalAPI) string
validateFunc func(t *testing.T, oldRoomID, newRoomID string, rsAPI api.RoomserverInternalAPI)
wantNewRoom bool
}{
{
name: "invalid userID",
upgradeUser: "!notvalid:test",
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
room := test.NewRoom(t, alice)
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
return room.ID
},
},
{
name: "invalid roomID",
upgradeUser: alice.ID,
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
return "!doesnotexist:test"
},
},
{
name: "powerlevel too low",
upgradeUser: bob.ID,
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
room := test.NewRoom(t, alice)
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
return room.ID
},
},
{
name: "successful upgrade on new room",
upgradeUser: alice.ID,
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
room := test.NewRoom(t, alice)
if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
return room.ID
},
wantNewRoom: true,
validateFunc: validate,
},
{
name: "successful upgrade on new room with other state events",
upgradeUser: alice.ID,
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
r := test.NewRoom(t, alice)
r.CreateAndInsert(t, alice, spec.MRoomName, map[string]interface{}{
"name": "my new name",
}, test.WithStateKey(""))
r.CreateAndInsert(t, alice, spec.MRoomCanonicalAlias, eventutil.CanonicalAliasContent{
Alias: "#myalias:test",
}, test.WithStateKey(""))
// this will be transferred
r.CreateAndInsert(t, alice, "m.custom.event", map[string]interface{}{
"random": "i should exist",
}, test.WithStateKey(""))
// the following will be ignored
r.CreateAndInsert(t, alice, "m.custom.event", map[string]interface{}{
"random": "i will be ignored",
}, test.WithStateKey(alice.ID))
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
return r.ID
},
wantNewRoom: true,
validateFunc: validate,
},
{
name: "with published room",
upgradeUser: alice.ID,
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
r := test.NewRoom(t, alice)
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
if err := rsAPI.PerformPublish(ctx, &api.PerformPublishRequest{
RoomID: r.ID,
Visibility: spec.Public,
}); err != nil {
t.Fatal(err)
}
return r.ID
},
wantNewRoom: true,
validateFunc: func(t *testing.T, oldRoomID, newRoomID string, rsAPI api.RoomserverInternalAPI) {
validate(t, oldRoomID, newRoomID, rsAPI)
// check that the new room is published
res := &api.QueryPublishedRoomsResponse{}
if err := rsAPI.QueryPublishedRooms(ctx, &api.QueryPublishedRoomsRequest{RoomID: newRoomID}, res); err != nil {
t.Fatal(err)
}
if len(res.RoomIDs) == 0 {
t.Fatalf("expected room to be published, but wasn't: %#v", res.RoomIDs)
}
},
},
{
name: "with alias",
upgradeUser: alice.ID,
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
r := test.NewRoom(t, alice)
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
if err := rsAPI.SetRoomAlias(ctx, &api.SetRoomAliasRequest{
RoomID: r.ID,
Alias: "#myroomalias:test",
}, &api.SetRoomAliasResponse{}); err != nil {
t.Fatal(err)
}
return r.ID
},
wantNewRoom: true,
validateFunc: func(t *testing.T, oldRoomID, newRoomID string, rsAPI api.RoomserverInternalAPI) {
validate(t, oldRoomID, newRoomID, rsAPI)
// check that the old room has no aliases
res := &api.GetAliasesForRoomIDResponse{}
if err := rsAPI.GetAliasesForRoomID(ctx, &api.GetAliasesForRoomIDRequest{RoomID: oldRoomID}, res); err != nil {
t.Fatal(err)
}
if len(res.Aliases) != 0 {
t.Fatalf("expected old room aliases to be empty, but wasn't: %#v", res.Aliases)
}
// check that the new room has aliases
if err := rsAPI.GetAliasesForRoomID(ctx, &api.GetAliasesForRoomIDRequest{RoomID: newRoomID}, res); err != nil {
t.Fatal(err)
}
if len(res.Aliases) == 0 {
t.Fatalf("expected room aliases to be transferred, but wasn't: %#v", res.Aliases)
}
},
},
{
name: "bans are transferred",
upgradeUser: alice.ID,
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
r := test.NewRoom(t, alice)
r.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{
"membership": spec.Ban,
}, test.WithStateKey(charlie.ID))
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
return r.ID
},
wantNewRoom: true,
validateFunc: validate,
},
{
name: "space childs are transferred",
upgradeUser: alice.ID,
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
r := test.NewRoom(t, alice)
r.CreateAndInsert(t, alice, "m.space.child", map[string]interface{}{}, test.WithStateKey(spaceChild.ID))
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
return r.ID
},
wantNewRoom: true,
validateFunc: validate,
},
{
name: "custom state is not taken to the new room", // https://github.com/matrix-org/dendrite/issues/2912
upgradeUser: charlie.ID,
roomFunc: func(rsAPI api.RoomserverInternalAPI) string {
r := test.NewRoom(t, alice, test.RoomVersion(gomatrixserverlib.RoomVersionV6))
// Bob and Charlie join
r.CreateAndInsert(t, bob, spec.MRoomMember, map[string]interface{}{"membership": spec.Join}, test.WithStateKey(bob.ID))
r.CreateAndInsert(t, charlie, spec.MRoomMember, map[string]interface{}{"membership": spec.Join}, test.WithStateKey(charlie.ID))
// make Charlie an admin so the room can be upgraded
r.CreateAndInsert(t, alice, spec.MRoomPowerLevels, gomatrixserverlib.PowerLevelContent{
Users: map[string]int64{
charlie.ID: 100,
},
}, test.WithStateKey(""))
// Alice creates a custom event
r.CreateAndInsert(t, alice, "m.custom.event", map[string]interface{}{
"random": "data",
}, test.WithStateKey(alice.ID))
r.CreateAndInsert(t, alice, spec.MRoomMember, map[string]interface{}{"membership": spec.Leave}, test.WithStateKey(alice.ID))
if err := api.SendEvents(ctx, rsAPI, api.KindNew, r.Events(), "test", "test", "test", nil, false); err != nil {
t.Errorf("failed to send events: %v", err)
}
return r.ID
},
wantNewRoom: true,
validateFunc: validate,
},
}
test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) {
cfg, processCtx, close := testrig.CreateConfig(t, dbType)
natsInstance := jetstream.NATSInstance{}
defer close()
cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions)
caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics)
rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics)
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, nil)
rsAPI.SetFederationAPI(nil, nil)
rsAPI.SetUserAPI(userAPI)
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if tc.roomFunc == nil {
t.Fatalf("missing roomFunc")
}
if tc.upgradeUser == "" {
tc.upgradeUser = alice.ID
}
roomID := tc.roomFunc(rsAPI)
newRoomID, err := rsAPI.PerformRoomUpgrade(processCtx.Context(), roomID, tc.upgradeUser, version.DefaultRoomVersion())
if err != nil && tc.wantNewRoom {
t.Fatal(err)
}
if tc.wantNewRoom && newRoomID == "" {
t.Fatalf("expected a new room, but the upgrade failed")
}
if !tc.wantNewRoom && newRoomID != "" {
t.Fatalf("expected no new room, but the upgrade succeeded")
}
if tc.validateFunc != nil {
tc.validateFunc(t, roomID, newRoomID, rsAPI)
}
})
}
})
}

View file

@ -669,13 +669,17 @@ func (d *Database) GetOrCreateRoomInfo(ctx context.Context, event *gomatrixserve
if roomVersion, err = extractRoomVersionFromCreateEvent(event); err != nil {
return nil, fmt.Errorf("extractRoomVersionFromCreateEvent: %w", err)
}
if roomVersion == "" {
rv, ok := d.Cache.GetRoomVersion(event.RoomID())
if ok {
roomVersion = rv
}
roomNID, nidOK := d.Cache.GetRoomServerRoomNID(event.RoomID())
cachedRoomVersion, versionOK := d.Cache.GetRoomVersion(event.RoomID())
// if we found both, the roomNID and version in our cache, no need to query the database
if nidOK && versionOK {
return &types.RoomInfo{
RoomNID: roomNID,
RoomVersion: cachedRoomVersion,
}, nil
}
var roomNID types.RoomNID
err = d.Writer.Do(d.DB, nil, func(txn *sql.Tx) error {
roomNID, err = d.assignRoomNID(ctx, txn, event.RoomID(), roomVersion)
if err != nil {
@ -1164,7 +1168,7 @@ func (d *Database) GetStateEvent(ctx context.Context, roomID, evType, stateKey s
if roomInfo.IsStub() {
return nil, nil
}
eventTypeNID, err := d.EventTypesTable.SelectEventTypeNID(ctx, nil, evType)
eventTypeNID, err := d.GetOrCreateEventTypeNID(ctx, evType)
if err == sql.ErrNoRows {
// No rooms have an event of this type, otherwise we'd have an event type NID
return nil, nil
@ -1172,7 +1176,7 @@ func (d *Database) GetStateEvent(ctx context.Context, roomID, evType, stateKey s
if err != nil {
return nil, err
}
stateKeyNID, err := d.EventStateKeysTable.SelectEventStateKeyNID(ctx, nil, stateKey)
stateKeyNID, err := d.GetOrCreateEventStateKeyNID(ctx, &stateKey)
if err == sql.ErrNoRows {
// No rooms have a state event with this state key, otherwise we'd have an state key NID
return nil, nil
@ -1201,6 +1205,10 @@ func (d *Database) GetStateEvent(ctx context.Context, roomID, evType, stateKey s
// return the event requested
for _, e := range entries {
if e.EventTypeNID == eventTypeNID && e.EventStateKeyNID == stateKeyNID {
cachedEvent, ok := d.Cache.GetRoomServerEvent(e.EventNID)
if ok {
return cachedEvent.Headered(roomInfo.RoomVersion), nil
}
data, err := d.EventJSONTable.BulkSelectEventJSON(ctx, nil, []types.EventNID{e.EventNID})
if err != nil {
return nil, err
@ -1324,7 +1332,7 @@ func (d *Database) GetBulkStateContent(ctx context.Context, roomIDs []string, tu
}
// we don't bother failing the request if we get asked for event types we don't know about, as all that would result in is no matches which
// isn't a failure.
eventTypeNIDMap, err := d.EventTypesTable.BulkSelectEventTypeNID(ctx, nil, eventTypes)
eventTypeNIDMap, err := d.eventTypeNIDs(ctx, nil, eventTypes)
if err != nil {
return nil, fmt.Errorf("GetBulkStateContent: failed to map event type nids: %w", err)
}

View file

@ -325,10 +325,8 @@ func (rc *reqCtx) fetchUnknownEvent(eventID, roomID string) *gomatrixserverlib.H
}
logger := util.GetLogger(rc.ctx).WithField("room_id", roomID)
// if they supplied a room_id, check the room exists.
var queryVerRes roomserver.QueryRoomVersionForRoomResponse
err := rc.rsAPI.QueryRoomVersionForRoom(rc.ctx, &roomserver.QueryRoomVersionForRoomRequest{
RoomID: roomID,
}, &queryVerRes)
roomVersion, err := rc.rsAPI.QueryRoomVersionForRoom(rc.ctx, roomID)
if err != nil {
logger.WithError(err).Warn("failed to query room version for room, does this room exist?")
return nil
@ -367,7 +365,7 @@ func (rc *reqCtx) fetchUnknownEvent(eventID, roomID string) *gomatrixserverlib.H
// Inject the response into the roomserver to remember the event across multiple calls and to set
// unexplored flags correctly.
for _, srv := range serversToQuery {
res, err := rc.MSC2836EventRelationships(eventID, srv, queryVerRes.RoomVersion)
res, err := rc.MSC2836EventRelationships(eventID, srv, roomVersion)
if err != nil {
continue
}