diff --git a/eventauth.go b/eventauth.go index 8397fffb..eb9f5d89 100644 --- a/eventauth.go +++ b/eventauth.go @@ -1158,7 +1158,12 @@ func (m *membershipAllower) membershipAllowedSelf() error { // nolint: gocyclo switch m.newMember.Membership { case spec.Knock: // Check if the given roomVersionImpl allows knocking. - return m.roomVersionImpl.CheckKnockingAllowed(m) + return m.roomVersionImpl.CheckKnockingAllowed( + string(m.roomVersionImpl.Version()), + m.senderID, m.targetID, + m.joinRule.JoinRule, + m.oldMember.Membership, + ) case spec.Join: if m.joinRule.JoinRule == spec.Restricted || m.joinRule.JoinRule == spec.KnockRestricted { if err := m.membershipAllowedSelfForRestrictedJoin(); err != nil { @@ -1227,11 +1232,17 @@ func disallowRestrictedJoins() error { return errorf("restricted joins are not supported in this room version") } -func disallowKnocking(m *membershipAllower) error { - return m.membershipFailed( - "room version %q does not support knocking on rooms with join rule %q", - m.roomVersionImpl.Version(), - m.joinRule.JoinRule, +func disallowKnocking(roomVer, sender, target, joinRule, prevMembership string) error { + if sender == target { + return errorf( + "%q is not allowed to change their membership from %q as room version %q does not support knocking on rooms with join rule %q", + sender, prevMembership, roomVer, joinRule, + ) + } + + return errorf( + "%q is not allowed to change the membership of %q from %q as room version %q does not support knocking on rooms with join rule %q", + sender, target, prevMembership, roomVer, joinRule, ) } @@ -1242,23 +1253,25 @@ func disallowKnocking(m *membershipAllower) error { // MSC3787 extends this: the behaviour above is also permitted if the // join rules are "knock_restricted" // Spec: https://github.com/matrix-org/matrix-spec-proposals/pull/3787 -func checkKnocking(m *membershipAllower) error { +func checkKnocking(roomVer, sender, target, joinRule, prevMembership string) error { // If the join_rule is anything other than knock or knock_restricted, reject. - supported := m.joinRule.JoinRule == spec.Knock || m.joinRule.JoinRule == spec.KnockRestricted + supported := joinRule == spec.Knock || joinRule == spec.KnockRestricted if !supported { - return m.membershipFailed( - "room version %q does not support knocking on rooms with join rule %q", - m.roomVersionImpl.Version(), - m.joinRule.JoinRule, + return errorf( + "%q is not allowed to change the membership of %q from %q as room version %q does not support knocking on rooms with join rule %q", + sender, target, prevMembership, + roomVer, + joinRule, ) } - switch m.oldMember.Membership { + switch prevMembership { case spec.Join, spec.Invite, spec.Ban: // The user is already joined, invited or banned, therefore they // can't knock. - return m.membershipFailed( - "sender is already joined/invited/banned", + return errorf( + "%q is not allowed to change the membership of %q from %q as sender is already joined/invited/banned", + sender, target, prevMembership, ) } // A non-joined, non-invited, non-banned user is allowed to knock. diff --git a/eventauth_test.go b/eventauth_test.go index 428f6a69..30b53748 100644 --- a/eventauth_test.go +++ b/eventauth_test.go @@ -1999,3 +1999,21 @@ func TestJoinRuleKnockRestricted(t *testing.T) { }] }`, RoomVersionV10) } + +type CustomVer struct { + IRoomVersion +} + +func (v CustomVer) Version() RoomVersion { + return "hello" +} + +func TestSetRoomVersion(t *testing.T) { + // ensure we can set and get custom room versions. + // GMSL doesn't use this but users of GMSL do. + v := CustomVer{ + IRoomVersion: MustGetRoomVersion("10"), + } + SetRoomVersion(v) + MustGetRoomVersion("hello") +} diff --git a/eventcontent.go b/eventcontent.go index ac85acc0..ae67244d 100644 --- a/eventcontent.go +++ b/eventcontent.go @@ -581,11 +581,11 @@ type RelatesTo struct { RelationType string `json:"rel_type"` } -func noCheckCreateEvent(event PDU, knownRoomVersion knownRoomVersionFunc) error { +func noCheckCreateEvent(event PDU, knownRoomVersion KnownRoomVersionFunc) error { return nil } -func checkCreateEvent(event PDU, knownRoomVersion knownRoomVersionFunc) error { +func checkCreateEvent(event PDU, knownRoomVersion KnownRoomVersionFunc) error { c := struct { Creator *string `json:"creator"` RoomVersion *RoomVersion `json:"room_version"` diff --git a/eventversion.go b/eventversion.go index e6b906f7..21375f5a 100644 --- a/eventversion.go +++ b/eventversion.go @@ -12,6 +12,8 @@ import ( // RoomVersion refers to the room version for a specific room. type RoomVersion string +// The interface which needs to be implemented to register a room version with gomatrixserverlib. +// All types should be public to allow for extensibility. type IRoomVersion interface { Version() RoomVersion Stable() bool @@ -29,14 +31,14 @@ type IRoomVersion interface { RestrictedJoinServername(content []byte) (spec.ServerName, error) CheckRestrictedJoinsAllowed() error - CheckKnockingAllowed(m *membershipAllower) error + CheckKnockingAllowed(roomVer, sender, target, joinRule, prevMembership string) error CheckNotificationLevels(senderLevel int64, oldPowerLevels, newPowerLevels PowerLevelContent) error CheckCanonicalJSON(input []byte) error ParsePowerLevels(contentBytes []byte, c *PowerLevelContent) error - CheckCreateEvent(event PDU, knownRoomVersion knownRoomVersionFunc) error + CheckCreateEvent(event PDU, knownRoomVersion KnownRoomVersionFunc) error } -type knownRoomVersionFunc func(RoomVersion) bool +type KnownRoomVersionFunc func(RoomVersion) bool // StateResAlgorithm refers to a version of the state resolution algorithm. type StateResAlgorithm int @@ -416,6 +418,14 @@ func StableRoomVersions() map[RoomVersion]IRoomVersion { return versions } +// SetRoomVersion sets a room version implementation so it is recognised by gomatrixserverlib. +// This is useful when you are testing custom room versions which gomatrixserverlib may be unaware of, +// e.g for Complement usage. Full room version impls should be defined in gomatrixserverlib, but +// partial impls for testing can be set here. +func SetRoomVersion(ver IRoomVersion) { + roomVersionMeta[ver.Version()] = ver +} + // RoomVersionDescription contains information about a room version, // namely whether it is marked as supported or stable in this server // version, along with the state resolution algorithm, event ID etc @@ -440,8 +450,8 @@ type RoomVersionImpl struct { checkRestrictedJoin restrictedJoinCheckFunc restrictedJoinServernameFunc func(content []byte) (spec.ServerName, error) checkRestrictedJoinAllowedFunc func() error - checkKnockingAllowedFunc func(m *membershipAllower) error - checkCreateEvent func(e PDU, knownRoomVersion knownRoomVersionFunc) error + checkKnockingAllowedFunc func(roomVer, sender, target, joinRule, prevMembership string) error + checkCreateEvent func(e PDU, knownRoomVersion KnownRoomVersionFunc) error newEventFromUntrustedJSONFunc func(eventJSON []byte, roomVersion IRoomVersion) (result PDU, err error) newEventFromTrustedJSONFunc func(eventJSON []byte, redacted bool, roomVersion IRoomVersion) (result PDU, err error) newEventFromTrustedJSONWithEventIDFunc func(eventID string, eventJSON []byte, redacted bool, roomVersion IRoomVersion) (result PDU, err error) @@ -483,8 +493,8 @@ func (v RoomVersionImpl) CheckNotificationLevels(senderLevel int64, oldPowerLeve } // CheckKnockingAllowed checks if this room version supports knocking on rooms. -func (v RoomVersionImpl) CheckKnockingAllowed(m *membershipAllower) error { - return v.checkKnockingAllowedFunc(m) +func (v RoomVersionImpl) CheckKnockingAllowed(roomVer, sender, target, joinRule, prevMembership string) error { + return v.checkKnockingAllowedFunc(roomVer, sender, target, joinRule, prevMembership) } // CheckRestrictedJoinsAllowed checks if this room version allows restricted joins. @@ -508,7 +518,7 @@ func (v RoomVersionImpl) ParsePowerLevels(contentBytes []byte, c *PowerLevelCont return v.parsePowerLevelsFunc(contentBytes, c) } -func (v RoomVersionImpl) CheckCreateEvent(event PDU, knownRoomVersion knownRoomVersionFunc) error { +func (v RoomVersionImpl) CheckCreateEvent(event PDU, knownRoomVersion KnownRoomVersionFunc) error { return v.checkCreateEvent(event, knownRoomVersion) }