mirror of
https://github.com/hoernschen/dendrite.git
synced 2025-08-01 13:52:46 +00:00
Restricted join support on /make_join
, /send_join
(#2478)
* Add `QueryRestrictedJoinAllowed` * Add `Resident` flag to `QueryRestrictedJoinAllowedResponse` * Check restricted joins on federation API * Return `Restricted` to determine if the room was restricted or not * Populate `AuthorisedVia` properly * Sign the event on `/send_join`, return it in the `/send_join` response in the `"event"` key * Kick back joins with invalid authorising user IDs, use event from `"event"` key if returned in `RespSendJoin` * Use invite helper in `QueryRestrictedJoinAllowed` * Only use users with the power to invite, change error bubbling a bit * Placate the almighty linter One day I will nuke `gocyclo` from orbit and everything in the world will be much better for it. * Review comments
This commit is contained in:
parent
d621dd2986
commit
81843e8836
10 changed files with 369 additions and 4 deletions
|
@ -15,6 +15,7 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
@ -103,6 +104,16 @@ 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)
|
||||
if err != nil {
|
||||
util.GetLogger(httpReq.Context()).WithError(err).Error("checkRestrictedJoin failed")
|
||||
return jsonerror.InternalServerError()
|
||||
} else if res != nil {
|
||||
return *res
|
||||
}
|
||||
|
||||
// Try building an event for the server
|
||||
builder := gomatrixserverlib.EventBuilder{
|
||||
Sender: userID,
|
||||
|
@ -110,8 +121,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()
|
||||
}
|
||||
|
@ -161,6 +175,7 @@ func MakeJoin(
|
|||
// SendJoin implements the /send_join API
|
||||
// The make-join send-join dance makes much more sense as a single
|
||||
// flow so the cyclomatic complexity is high:
|
||||
// nolint:gocyclo
|
||||
func SendJoin(
|
||||
httpReq *http.Request,
|
||||
request *gomatrixserverlib.FederationRequest,
|
||||
|
@ -314,6 +329,40 @@ func SendJoin(
|
|||
}
|
||||
}
|
||||
|
||||
// If the membership content contains a user ID for a server that is not
|
||||
// ours then we should kick it back.
|
||||
var memberContent gomatrixserverlib.MemberContent
|
||||
if err := json.Unmarshal(event.Content(), &memberContent); err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.BadJSON(err.Error()),
|
||||
}
|
||||
}
|
||||
if memberContent.AuthorisedVia != "" {
|
||||
_, domain, err := gomatrixserverlib.SplitID('@', memberContent.AuthorisedVia)
|
||||
if err != nil {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.BadJSON(fmt.Sprintf("The authorising username %q is invalid.", memberContent.AuthorisedVia)),
|
||||
}
|
||||
}
|
||||
if domain != cfg.Matrix.ServerName {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.BadJSON(fmt.Sprintf("The authorising username %q does not belong to this server.", memberContent.AuthorisedVia)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sign the membership event. This is required for restricted joins to work
|
||||
// in the case that the authorised via user is one of our own users. It also
|
||||
// doesn't hurt to do it even if it isn't a restricted join.
|
||||
signed := event.Sign(
|
||||
string(cfg.Matrix.ServerName),
|
||||
cfg.Matrix.KeyID,
|
||||
cfg.Matrix.PrivateKey,
|
||||
)
|
||||
|
||||
// Send the events to the room server.
|
||||
// We are responsible for notifying other servers that the user has joined
|
||||
// the room, so set SendAsServer to cfg.Matrix.ServerName
|
||||
|
@ -323,7 +372,7 @@ func SendJoin(
|
|||
InputRoomEvents: []api.InputRoomEvent{
|
||||
{
|
||||
Kind: api.KindNew,
|
||||
Event: event.Headered(stateAndAuthChainResponse.RoomVersion),
|
||||
Event: signed.Headered(stateAndAuthChainResponse.RoomVersion),
|
||||
SendAsServer: string(cfg.Matrix.ServerName),
|
||||
TransactionID: nil,
|
||||
},
|
||||
|
@ -354,10 +403,77 @@ func SendJoin(
|
|||
StateEvents: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(stateAndAuthChainResponse.StateEvents),
|
||||
AuthEvents: gomatrixserverlib.NewEventJSONsFromHeaderedEvents(stateAndAuthChainResponse.AuthChainEvents),
|
||||
Origin: cfg.Matrix.ServerName,
|
||||
Event: &signed,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
roomVersion gomatrixserverlib.RoomVersion,
|
||||
roomID, userID string,
|
||||
) (*util.JSONResponse, string, error) {
|
||||
if allowRestricted, err := roomVersion.AllowRestrictedJoinsInEventAuth(); err != nil {
|
||||
return nil, "", err
|
||||
} else if !allowRestricted {
|
||||
return nil, "", nil
|
||||
}
|
||||
req := &api.QueryRestrictedJoinAllowedRequest{
|
||||
RoomID: roomID,
|
||||
UserID: userID,
|
||||
}
|
||||
res := &api.QueryRestrictedJoinAllowedResponse{}
|
||||
if err := rsAPI.QueryRestrictedJoinAllowed(httpReq.Context(), req, res); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
switch {
|
||||
case !res.Restricted:
|
||||
// The join rules for the room don't restrict membership.
|
||||
return nil, "", nil
|
||||
|
||||
case !res.Resident:
|
||||
// The join rules restrict membership but our server isn't currently
|
||||
// joined to all of the allowed rooms, so we can't actually decide
|
||||
// whether or not to allow the user to join. This error code should
|
||||
// tell the joining server to try joining via another resident server
|
||||
// instead.
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: jsonerror.UnableToAuthoriseJoin("This server cannot authorise the join."),
|
||||
}, "", nil
|
||||
|
||||
case !res.Allowed:
|
||||
// The join rules restrict membership, our server is in the relevant
|
||||
// rooms and the user wasn't joined to join any of the allowed rooms
|
||||
// and therefore can't join this room.
|
||||
return &util.JSONResponse{
|
||||
Code: http.StatusForbidden,
|
||||
JSON: jsonerror.Forbidden("You are not joined to any matching rooms."),
|
||||
}, "", 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. 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
|
||||
}
|
||||
}
|
||||
|
||||
type eventsByDepth []*gomatrixserverlib.HeaderedEvent
|
||||
|
||||
func (e eventsByDepth) Len() int {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue