diff --git a/federationapi/internal/perform.go b/federationapi/internal/perform.go index 17c00b77d..970a64653 100644 --- a/federationapi/internal/perform.go +++ b/federationapi/internal/perform.go @@ -213,7 +213,9 @@ func (r *FederationInternalAPI) performJoinUsingServer( // 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 respSendJoin.Event != nil { + if respSendJoin.Event != nil && isWellFormedMembershipEvent( + respSendJoin.Event, roomID, userID, r.cfg.Matrix.ServerName, + ) { event = respSendJoin.Event } @@ -277,6 +279,26 @@ func (r *FederationInternalAPI) performJoinUsingServer( return nil } +// isWellFormedMembershipEvent returns true if the event looks like a legitimate +// membership event. +func isWellFormedMembershipEvent(event *gomatrixserverlib.Event, roomID, userID string, origin gomatrixserverlib.ServerName) bool { + if membership, err := event.Membership(); err != nil { + return false + } else if membership != gomatrixserverlib.Join { + return false + } + if event.RoomID() != roomID { + return false + } + if event.Origin() != origin { + return false + } + if !event.StateKeyEquals(userID) { + return false + } + return true +} + // PerformOutboundPeekRequest implements api.FederationInternalAPI func (r *FederationInternalAPI) PerformOutboundPeek( ctx context.Context, diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index 4b627f21f..9f8d94105 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -408,6 +408,17 @@ func SendJoin( } } +// checkRestrictedJoin finds out whether or not we can assist in processing +// a restricted room join. If the room version does not support restricted +// joins then this function returns with no side effects. This returns three +// values: +// * an optional JSON response body (i.e. M_UNABLE_TO_AUTHORISE_JOIN) which +// should always be sent back to the client if one is specified +// * a user ID of an authorising user, typically a user that has power to +// issue invites in the room, if one has been found +// * an error if there was a problem finding out if this was allowable, +// like if the room version isn't known or a problem happened talking to +// the roomserver func checkRestrictedJoin( httpReq *http.Request, rsAPI api.FederationRoomserverAPI, diff --git a/roomserver/api/api.go b/roomserver/api/api.go index 01479bec8..f87ff2962 100644 --- a/roomserver/api/api.go +++ b/roomserver/api/api.go @@ -134,7 +134,6 @@ type ClientRoomserverAPI interface { QueryRoomVersionForRoom(ctx context.Context, req *QueryRoomVersionForRoomRequest, res *QueryRoomVersionForRoomResponse) error QueryPublishedRooms(ctx context.Context, req *QueryPublishedRoomsRequest, res *QueryPublishedRoomsResponse) error QueryRoomVersionCapabilities(ctx context.Context, req *QueryRoomVersionCapabilitiesRequest, res *QueryRoomVersionCapabilitiesResponse) error - QueryRestrictedJoinAllowed(ctx context.Context, req *QueryRestrictedJoinAllowedRequest, res *QueryRestrictedJoinAllowedResponse) error GetRoomIDForAlias(ctx context.Context, req *GetRoomIDForAliasRequest, res *GetRoomIDForAliasResponse) error GetAliasesForRoomID(ctx context.Context, req *GetAliasesForRoomIDRequest, res *GetAliasesForRoomIDResponse) error diff --git a/roomserver/api/query.go b/roomserver/api/query.go index a706f06c4..f157a9025 100644 --- a/roomserver/api/query.go +++ b/roomserver/api/query.go @@ -354,10 +354,18 @@ type QueryRestrictedJoinAllowedRequest struct { } type QueryRestrictedJoinAllowedResponse struct { - Restricted bool `json:"restricted"` // Is the room membership restricted? - Resident bool `json:"resident"` // Is our homeserver in the relevant rooms? - Allowed bool `json:"allowed"` // Is the join allowed by the rules? - AuthorisedVia string `json:"authorised_via,omitempty"` // The user that authorises the join + // True if the room membership is restricted by the join rule being set to "restricted" + Restricted bool `json:"restricted"` + // True if our local server is joined to all of the allowed rooms specified in the "allow" + // key of the join rule, false if we are missing from some of them and therefore can't + // reliably decide whether or not we can satisfy the join + Resident bool `json:"resident"` + // True if the restricted join is allowed because we found the membership in one of the + // allowed rooms from the join rule, false if not + Allowed bool `json:"allowed"` + // Contains the user ID of the selected user ID that has power to issue invites, this will + // get populated into the "join_authorised_via_users_server" content in the membership + AuthorisedVia string `json:"authorised_via,omitempty"` } // MarshalJSON stringifies the room ID and StateKeyTuple keys so they can be sent over the wire in HTTP API mode. diff --git a/roomserver/internal/query/query.go b/roomserver/internal/query/query.go index f176476aa..70613baab 100644 --- a/roomserver/internal/query/query.go +++ b/roomserver/internal/query/query.go @@ -809,7 +809,6 @@ func (r *Queryer) QueryRestrictedJoinAllowed(ctx context.Context, req *api.Query // We need to get the power levels content so that we can determine which // users in the room are entitled to issue invites. We need to use one of // these users as the authorising user. - // Get the join rules to work out if the join rule is "restricted". powerLevelsEvent, err := r.DB.GetStateEvent(ctx, req.RoomID, gomatrixserverlib.MRoomPowerLevels, "") if err != nil { return fmt.Errorf("r.DB.GetStateEvent: %w", err)