Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move /send_leave to GMSL #387

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
27 changes: 27 additions & 0 deletions eventversion.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gomatrixserverlib

import (
"context"
"fmt"

"github.com/matrix-org/gomatrixserverlib/spec"
Expand Down Expand Up @@ -30,6 +31,8 @@ type IRoomVersion interface {
NewEventFromUntrustedJSON(eventJSON []byte) (result PDU, err error)
NewEventBuilder() *EventBuilder
NewEventBuilderFromProtoEvent(pe *ProtoEvent) *EventBuilder

HandleSendLeave(ctx context.Context, event PDU, origin spec.ServerName, eventID, roomID string, querier CurrentStateQuerier, verifier JSONVerifier) (PDU, error)
}

// StateResAlgorithm refers to a version of the state resolution algorithm.
Expand Down Expand Up @@ -112,6 +115,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
RoomVersionV2: RoomVersionImpl{
ver: RoomVersionV2,
Expand All @@ -126,6 +130,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
RoomVersionV3: RoomVersionImpl{
ver: RoomVersionV3,
Expand All @@ -140,6 +145,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
RoomVersionV4: RoomVersionImpl{
ver: RoomVersionV4,
Expand All @@ -154,6 +160,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
RoomVersionV5: RoomVersionImpl{
ver: RoomVersionV5,
Expand All @@ -168,6 +175,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
RoomVersionV6: RoomVersionImpl{
ver: RoomVersionV6,
Expand All @@ -182,6 +190,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
RoomVersionV7: RoomVersionImpl{
ver: RoomVersionV7,
Expand All @@ -196,6 +205,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnockOnly,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
RoomVersionV8: RoomVersionImpl{
ver: RoomVersionV8,
Expand All @@ -210,6 +220,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnockOnly,
allowRestrictedJoinsInEventAuth: RestrictedOnly,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
RoomVersionV9: RoomVersionImpl{
ver: RoomVersionV9,
Expand All @@ -224,6 +235,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnockOnly,
allowRestrictedJoinsInEventAuth: RestrictedOnly,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
RoomVersionV10: RoomVersionImpl{
ver: RoomVersionV10,
Expand All @@ -238,6 +250,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnockOrKnockRestricted,
allowRestrictedJoinsInEventAuth: RestrictedOrKnockRestricted,
requireIntegerPowerLevels: true,
handleSendLeaveFunc: handleSendLeave,
},
"org.matrix.msc3667": RoomVersionImpl{ // based on room version 7
ver: RoomVersion("org.matrix.msc3667"),
Expand All @@ -252,6 +265,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnockOnly,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: true,
handleSendLeaveFunc: handleSendLeave,
},
"org.matrix.msc3787": RoomVersionImpl{ // roughly, the union of v7 and v9
ver: RoomVersion("org.matrix.msc3787"),
Expand All @@ -266,6 +280,7 @@ var roomVersionMeta = map[RoomVersion]IRoomVersion{
allowKnockingInEventAuth: KnockOrKnockRestricted,
allowRestrictedJoinsInEventAuth: RestrictedOrKnockRestricted,
requireIntegerPowerLevels: false,
handleSendLeaveFunc: handleSendLeave,
},
}

Expand Down Expand Up @@ -334,6 +349,7 @@ type RoomVersionImpl struct {
powerLevelsIncludeNotifications bool
requireIntegerPowerLevels bool
stable bool
handleSendLeaveFunc func(ctx context.Context, event PDU, origin spec.ServerName, eventID, roomID string, querier CurrentStateQuerier, verifier JSONVerifier) (PDU, error)
}

func (v RoomVersionImpl) Version() RoomVersion {
Expand Down Expand Up @@ -431,6 +447,17 @@ func (v RoomVersionImpl) RedactEventJSON(eventJSON []byte) ([]byte, error) {
return v.redactionAlgorithm(eventJSON)
}

// HandleSendLeave handles requests to `/send_leave`
func (v RoomVersionImpl) HandleSendLeave(ctx context.Context,
event PDU,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is problematic if v != event.Version().

origin spec.ServerName,
eventID, roomID string,
querier CurrentStateQuerier,
verifier JSONVerifier,
) (PDU, error) {
return v.handleSendLeaveFunc(ctx, event, origin, eventID, roomID, querier, verifier)
}

func (v RoomVersionImpl) NewEventFromTrustedJSON(eventJSON []byte, redacted bool) (result PDU, err error) {
return newEventFromTrustedJSON(eventJSON, redacted, v)
}
Expand Down
109 changes: 109 additions & 0 deletions handleleave.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
package gomatrixserverlib

import (
"context"
"fmt"

"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/util"
)

type HandleMakeLeaveResponse struct {
Expand All @@ -37,6 +39,7 @@ type HandleMakeLeaveInput struct {
BuildEventTemplate func(*ProtoEvent) (PDU, []PDU, error)
}

// HandleMakeLeave handles requests to `/make_leave`
func HandleMakeLeave(input HandleMakeLeaveInput) (*HandleMakeLeaveResponse, error) {

if input.UserID.Domain() != input.RequestOrigin {
Expand Down Expand Up @@ -98,3 +101,109 @@ func HandleMakeLeave(input HandleMakeLeaveInput) (*HandleMakeLeaveResponse, erro
}
return &makeLeaveResponse, nil
}

type CurrentStateQuerier interface {
CurrentStateEvent(ctx context.Context, roomID spec.RoomID, eventType string, stateKey string) (PDU, error)
}

// handleSendLeave handles requests to `/send_leave
// Returns the parsed event or an error.
func handleSendLeave(ctx context.Context,
event PDU,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proposal I mentioned in #387 (comment) was to keep this function public, and to serve as the entry point for callers, because:

Whilst the caller could call roomVer.HandleSendLeave this then creates bad symmetry, as there are cases where you don't know the room version at this point (e.g invites).

origin spec.ServerName,
eventID, roomID string,
querier CurrentStateQuerier,
verifier JSONVerifier,
) (PDU, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sidenote: this function and others like it will likely need to be modified to support pseudo IDs, as in many cases we pull out the state key and check that it is a user ID, and then check that the domain is correct on it. I would probably structure this as:

  • static function HandleSendLeave as it is in this PR
  • Pull out the room version immediately.
  • Call roomVer.HandleSendLeave(args...) to let room versions decide how to implement this.
  • Most room versions will call a private function which does the below code, checking user IDs etc.
  • But pseudo IDs will call a custom function which omits these checks.

Whilst the caller could call roomVer.HandleSendLeave this then creates bad symmetry, as there are cases where you don't know the room version at this point (e.g invites).


rID, err := spec.NewRoomID(roomID)
if err != nil {
return nil, err
}

// Check that the room ID is correct.
if (event.RoomID()) != roomID {
return nil, spec.BadJSON("The room ID in the request path must match the room ID in the leave event JSON")
}

// Check that the event ID is correct.
if event.EventID() != eventID {
return nil, spec.BadJSON("The event ID in the request path must match the event ID in the leave event JSON")

}

// Sanity check that we really received a state event
if event.StateKey() == nil || event.StateKeyEquals("") {
return nil, spec.BadJSON("No state key was provided in the leave event.")
}
if !event.StateKeyEquals(event.Sender()) {
return nil, spec.BadJSON("Event state key must match the event sender.")
}

leavingUser, err := spec.NewUserID(*event.StateKey(), true)
if err != nil {
return nil, spec.Forbidden("The leaving user ID is invalid")
}

// Check that the sender belongs to the server that is sending us
// the request. By this point we've already asserted that the sender
// and the state key are equal so we don't need to check both.
sender, err := spec.NewUserID(event.Sender(), true)
if err != nil {
return nil, spec.Forbidden("The sender of the join is invalid")
}
if sender.Domain() != origin {
return nil, spec.Forbidden("The sender does not match the server that originated the request")
}

stateEvent, err := querier.CurrentStateEvent(ctx, *rID, spec.MRoomMember, leavingUser.String())
if err != nil {
return nil, err
}
// we weren't joined at all
if stateEvent == nil {
return nil, nil
}
// We are/were joined/invited/banned or something
if mem, merr := stateEvent.Membership(); merr == nil && mem == spec.Leave {
return nil, nil
}
// we already processed this event
if event.EventID() == stateEvent.EventID() {
return nil, nil
}

// Check that the event is signed by the server sending the request.
resultEvent := event
event.Redact()
if err != nil {
util.GetLogger(ctx).WithError(err).Errorf("unable to redact event")
return nil, spec.BadJSON("The event JSON could not be redacted")
}
verifyRequests := []VerifyJSONRequest{{
ServerName: sender.Domain(),
Message: event.JSON(),
AtTS: event.OriginServerTS(),
StrictValidityChecking: true,
}}
verifyResults, err := verifier.VerifyJSONs(ctx, verifyRequests)
if err != nil {
util.GetLogger(ctx).WithError(err).Error("keys.VerifyJSONs failed")
return nil, spec.InternalServerError{}
}
if verifyResults[0].Error != nil {
return nil, spec.Forbidden("The leave must be signed by the server it originated on")
}

// check membership is set to leave
mem, err := event.Membership()
if err != nil {
util.GetLogger(ctx).WithError(err).Error("event.Membership failed")
return nil, spec.BadJSON("missing content.membership key")
}
if mem != spec.Leave {
return nil, spec.BadJSON("The membership in the event content must be set to leave")
}

return resultEvent, nil
}
Loading