From c0d4315583fcd1d2af5bc7d11cd483dc3904955d Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 23 May 2022 14:08:25 +0100 Subject: [PATCH] Populate `AuthorisedVia` properly --- federationapi/routing/join.go | 30 ++++++++++++++++++------------ roomserver/api/query.go | 7 ++++--- roomserver/internal/query/query.go | 28 ++++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/federationapi/routing/join.go b/federationapi/routing/join.go index 2f9d4ca0f..e94702590 100644 --- a/federationapi/routing/join.go +++ b/federationapi/routing/join.go @@ -105,7 +105,8 @@ func MakeJoin( // Check if the restricted join is allowed. If the room doesn't // support restricted joins then this is effectively a no-op. - if res, rerr := checkRestrictedJoin(httpReq, rsAPI, verRes.RoomVersion, roomID, userID); rerr != nil { + res, authorisedVia, err := checkRestrictedJoin(httpReq, rsAPI, verRes.RoomVersion, roomID, userID) + if err != nil { util.GetLogger(httpReq.Context()).WithError(err).Error("checkRestrictedJoin failed") return jsonerror.InternalServerError() } else if res != nil { @@ -119,8 +120,11 @@ func MakeJoin( Type: "m.room.member", StateKey: &userID, } - err = builder.SetContent(map[string]interface{}{"membership": gomatrixserverlib.Join}) - if err != nil { + content := gomatrixserverlib.MemberContent{ + Membership: gomatrixserverlib.Join, + AuthorisedVia: authorisedVia, + } + if err = builder.SetContent(content); err != nil { util.GetLogger(httpReq.Context()).WithError(err).Error("builder.SetContent failed") return jsonerror.InternalServerError() } @@ -372,11 +376,11 @@ func checkRestrictedJoin( rsAPI api.FederationRoomserverAPI, roomVersion gomatrixserverlib.RoomVersion, roomID, userID string, -) (*util.JSONResponse, error) { +) (*util.JSONResponse, string, error) { if allowRestricted, err := roomVersion.AllowRestrictedJoinsInEventAuth(); err != nil { - return nil, err + return nil, "", err } else if !allowRestricted { - return nil, nil + return nil, "", nil } req := &api.QueryRestrictedJoinAllowedRequest{ RoomID: roomID, @@ -384,13 +388,13 @@ func checkRestrictedJoin( } res := &api.QueryRestrictedJoinAllowedResponse{} if err := rsAPI.QueryRestrictedJoinAllowed(httpReq.Context(), req, res); err != nil { - return nil, err + return nil, "", err } switch { case !res.Restricted: // The join rules for the room don't restrict membership. - return nil, nil + return nil, "", nil case !res.Resident: // The join rules restrict membership but our server isn't currently @@ -401,7 +405,7 @@ func checkRestrictedJoin( return &util.JSONResponse{ Code: http.StatusBadRequest, JSON: jsonerror.UnableToAuthoriseJoin("This server cannot authorise the join."), - }, nil + }, "", nil case !res.Allowed: // The join rules restrict membership, our server is in the relevant @@ -410,13 +414,15 @@ func checkRestrictedJoin( return &util.JSONResponse{ Code: http.StatusForbidden, JSON: jsonerror.Forbidden("You are not joined to any matching rooms."), - }, nil + }, "", nil default: // The join rules restrict membership, our server is in the relevant // rooms and the user was allowed to join because they belong to one - // of the allowed rooms. - return nil, nil + // of the allowed rooms. We now need to pick one of our own local users + // from within the room to use as the authorising user ID, so that it + // can be referred to from within the membership content. + return nil, res.AuthorisedVia, nil } } diff --git a/roomserver/api/query.go b/roomserver/api/query.go index 6107b3f82..a706f06c4 100644 --- a/roomserver/api/query.go +++ b/roomserver/api/query.go @@ -354,9 +354,10 @@ 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? + 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 } // 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 f298e9ae5..27a63e35d 100644 --- a/roomserver/internal/query/query.go +++ b/roomserver/internal/query/query.go @@ -824,10 +824,34 @@ func (r *Queryer) QueryRestrictedJoinAllowed(ctx context.Context, req *api.Query if err != nil { continue } - // If the user is in the room then we will allow the membership. - if isIn { + // If the user is not in the room then we will skip them. + if !isIn { + continue + } + // The user is in the room, so now we will need to authorise the + // join using the user ID of one of our own users in the room. Pick + // one. + joinNIDs, err := r.DB.GetMembershipEventNIDsForRoom(ctx, targetRoomInfo.RoomNID, true, true) + if err != nil || len(joinNIDs) == 0 { + // There should always be more than one join NID at this point + // because we are gated behind GetLocalServerInRoom, but y'know, + // sometimes strange things happen. + continue + } + // For each of the joined users, let's see if we can get a valid + // membership event. + for _, joinNID := range joinNIDs { + events, err := r.DB.Events(ctx, []types.EventNID{joinNID}) + if err != nil || len(events) != 1 { + continue + } + event := events[0] + if event.Type() != gomatrixserverlib.MRoomMember || event.StateKey() == nil { + continue // shouldn't happen + } res.Resident = true res.Allowed = true + res.AuthorisedVia = *event.StateKey() return nil } }