diff --git a/eventauth.go b/eventauth.go index e4720d6e..0b53c8df 100644 --- a/eventauth.go +++ b/eventauth.go @@ -34,6 +34,8 @@ const ( Leave = "leave" // Invite is the string constant "invite" Invite = "invite" + // Knock is the string constant "knock" + Knock = "knock" // NOTSPEC: Peek is the string constant "peek" (MSC2753, used as the label in the sync block) Peek = "peek" // Public is the string constant "public" @@ -1013,7 +1015,29 @@ func (m *membershipAllower) membershipAllowedSelf() error { // nolint: gocyclo if m.oldMember.Membership == Leave && m.newMember.Membership == Leave { return nil } - + if m.newMember.Membership == Knock { + // A user that is not in the room is allowed to knock if the join + // rules are "knock" and they are not already joined to, invited to + // or banned from the room. + // Spec: https://spec.matrix.org/unstable/rooms/v7/ + if supported, err := m.create.RoomVersion.AllowKnockingInEventAuth(); err != nil { + return err + } else if !supported { + return m.membershipFailed() + } + if m.joinRule.JoinRule != Knock { + return m.membershipFailed() + } + switch m.oldMember.Membership { + case Join, Invite, Ban: + // The user is already joined, invited or banned, therefore they + // can't knock. + return m.membershipFailed() + default: + // A non-joined, non-invited, non-banned user is allowed to knock. + return nil + } + } if m.newMember.Membership == Join { // A user that is not in the room is allowed to join if the room // join rules are "public". diff --git a/eventversion.go b/eventversion.go index 70a43d39..32560935 100644 --- a/eventversion.go +++ b/eventversion.go @@ -66,6 +66,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: false, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, + allowKnockingInEventAuth: false, }, RoomVersionV2: { Supported: true, @@ -77,6 +78,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: false, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, + allowKnockingInEventAuth: false, }, RoomVersionV3: { Supported: true, @@ -88,6 +90,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: false, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, + allowKnockingInEventAuth: false, }, RoomVersionV4: { Supported: true, @@ -99,6 +102,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: false, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, + allowKnockingInEventAuth: false, }, RoomVersionV5: { Supported: true, @@ -110,6 +114,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: true, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, + allowKnockingInEventAuth: false, }, RoomVersionV6: { Supported: true, @@ -121,6 +126,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: true, enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, + allowKnockingInEventAuth: false, }, RoomVersionV7: { Supported: true, @@ -132,6 +138,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: true, enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, + allowKnockingInEventAuth: true, }, } @@ -185,6 +192,7 @@ type RoomVersionDescription struct { enforceSignatureChecks bool enforceCanonicalJSON bool powerLevelsIncludeNotifications bool + allowKnockingInEventAuth bool Supported bool Stable bool } @@ -239,6 +247,15 @@ func (v RoomVersion) PowerLevelsIncludeNotifications() (bool, error) { return false, UnsupportedRoomVersionError{v} } +// AllowKnockingInEventAuth returns true if the given room version allows for +// the `knock` membership state or false otherwise. +func (v RoomVersion) AllowKnockingInEventAuth() (bool, error) { + if r, ok := roomVersionMeta[v]; ok { + return r.allowKnockingInEventAuth, nil + } + return false, UnsupportedRoomVersionError{v} +} + // PowerLevelsIncludeNotifications returns true if the given room version calls // for the power level checks to cover the `notifications` key or false otherwise. func (v RoomVersion) EnforceCanonicalJSON() (bool, error) {