From 813fb0b94b38716c5bed2153d352790ca673072b Mon Sep 17 00:00:00 2001 From: David Robertson Date: Thu, 19 May 2022 18:17:09 +0100 Subject: [PATCH 1/5] `knock_restricted` join rules permit knocking --- eventauth.go | 22 +++++++++++++----- eventversion.go | 62 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 21 deletions(-) diff --git a/eventauth.go b/eventauth.go index f2fdbc4e..eee704b9 100644 --- a/eventauth.go +++ b/eventauth.go @@ -38,6 +38,9 @@ const ( Knock = "knock" // Restricted is the string constant "restricted" Restricted = "restricted" + // NOTSPEC: Restricted is the string constant "knock_restricted" (MSC3787) + // REVIEW: the MSC is merged though... so is this specced? Idk. + KnockRestricted = "knock_restricted" // NOTSPEC: Peek is the string constant "peek" (MSC2753, used as the label in the sync block) Peek = "peek" // Public is the string constant "public" @@ -1089,7 +1092,7 @@ func (m *membershipAllower) membershipAllowedSelf() error { // nolint: gocyclo switch m.newMember.Membership { case Knock: - if m.joinRule.JoinRule != Knock { + if m.joinRule.JoinRule != Knock && m.joinRule.JoinRule != KnockRestricted { return m.membershipFailed( "join rule %q does not allow knocking", m.joinRule.JoinRule, ) @@ -1098,11 +1101,16 @@ func (m *membershipAllower) membershipAllowedSelf() error { // nolint: gocyclo // 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.roomVersion.AllowKnockingInEventAuth(); err != nil { + // 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 + if supported, err := m.roomVersion.AllowKnockingInEventAuth(m.joinRule.JoinRule); err != nil { return fmt.Errorf("m.roomVersion.AllowKnockingInEventAuth: %w", err) } else if !supported { return m.membershipFailed( - "room version %q does not support knocking", m.roomVersion, + "room version %q does not support knocking on rooms with join rule %q", + m.roomVersion, + m.joinRule.JoinRule, ) } switch m.oldMember.Membership { @@ -1230,16 +1238,18 @@ func (m *membershipAllower) membershipAllowedOther() error { // nolint: gocyclo } // A user can invite in response to a knock. if m.oldMember.Membership == Knock && senderLevel >= m.powerLevels.Invite { - if m.joinRule.JoinRule != Knock { + if m.joinRule.JoinRule != Knock && m.joinRule.JoinRule != KnockRestricted { return m.membershipFailed( "join rule %q does not allow knocking", m.joinRule.JoinRule, ) } - if supported, err := m.roomVersion.AllowKnockingInEventAuth(); err != nil { + if supported, err := m.roomVersion.AllowKnockingInEventAuth(m.joinRule.JoinRule); err != nil { return fmt.Errorf("m.roomVersion.AllowKnockingInEventAuth: %w", err) } else if !supported { return m.membershipFailed( - "room version %q does not allow knocking", m.roomVersion, + "room version %q does not support knocking on rooms with join rule %q", + m.roomVersion, + m.joinRule.JoinRule, ) } return nil diff --git a/eventversion.go b/eventversion.go index 65931910..118b5a9e 100644 --- a/eventversion.go +++ b/eventversion.go @@ -17,6 +17,10 @@ type EventIDFormat int // RedactionAlgorithm refers to the redaction algorithm used in a room version. type RedactionAlgorithm int +// JoinRulesPermittingKnockInEventAuth specifies which kinds of join_rule allow +// a room to be knocked upon. +type JoinRulesPermittingKnockInEventAuth int + // Room version constants. These are strings because the version grammar // allows for future expansion. // https://matrix.org/docs/spec/#room-version-grammar @@ -59,6 +63,13 @@ const ( RedactionAlgorithmV4 // protects membership 'join_authorised_via_users_server' key ) +// Which join_rules permit knocking? +const ( + KnocksForbidden JoinRulesPermittingKnockInEventAuth = iota + 1 // no rooms can be knocked upon + KnockOnly // rooms with join_rule "knock" can be knocked upon + KnockOrKnockRestricted // rooms with join_rule "knock" or "knock_restricted" can be knocked upon +) + var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ RoomVersionV1: { Supported: true, @@ -70,7 +81,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: false, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, - allowKnockingInEventAuth: false, + allowKnockingInEventAuth: KnocksForbidden, allowRestrictedJoinsInEventAuth: false, requireIntegerPowerLevels: false, }, @@ -84,7 +95,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: false, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, - allowKnockingInEventAuth: false, + allowKnockingInEventAuth: KnocksForbidden, allowRestrictedJoinsInEventAuth: false, requireIntegerPowerLevels: false, }, @@ -98,7 +109,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: false, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, - allowKnockingInEventAuth: false, + allowKnockingInEventAuth: KnocksForbidden, allowRestrictedJoinsInEventAuth: false, requireIntegerPowerLevels: false, }, @@ -112,7 +123,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: false, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, - allowKnockingInEventAuth: false, + allowKnockingInEventAuth: KnocksForbidden, allowRestrictedJoinsInEventAuth: false, requireIntegerPowerLevels: false, }, @@ -126,7 +137,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: true, enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, - allowKnockingInEventAuth: false, + allowKnockingInEventAuth: KnocksForbidden, allowRestrictedJoinsInEventAuth: false, requireIntegerPowerLevels: false, }, @@ -140,7 +151,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: true, enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, - allowKnockingInEventAuth: false, + allowKnockingInEventAuth: KnocksForbidden, allowRestrictedJoinsInEventAuth: false, requireIntegerPowerLevels: false, }, @@ -154,7 +165,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: true, enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, - allowKnockingInEventAuth: true, + allowKnockingInEventAuth: KnockOnly, allowRestrictedJoinsInEventAuth: false, requireIntegerPowerLevels: false, }, @@ -168,7 +179,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: true, enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, - allowKnockingInEventAuth: true, + allowKnockingInEventAuth: KnocksForbidden, allowRestrictedJoinsInEventAuth: true, requireIntegerPowerLevels: false, }, @@ -182,7 +193,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: true, enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, - allowKnockingInEventAuth: true, + allowKnockingInEventAuth: KnocksForbidden, allowRestrictedJoinsInEventAuth: true, requireIntegerPowerLevels: false, }, @@ -196,10 +207,24 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceSignatureChecks: true, enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, - allowKnockingInEventAuth: true, + allowKnockingInEventAuth: KnockOnly, allowRestrictedJoinsInEventAuth: false, requireIntegerPowerLevels: true, }, + "org.matrix.msc3787": { // roughly, the union of v7 and v9 + Supported: true, + Stable: false, + stateResAlgorithm: StateResV2, + eventFormat: EventFormatV2, + eventIDFormat: EventIDFormatV3, + redactionAlgorithm: RedactionAlgorithmV4, + enforceSignatureChecks: true, + enforceCanonicalJSON: true, + powerLevelsIncludeNotifications: true, + allowKnockingInEventAuth: KnockOrKnockRestricted, + allowRestrictedJoinsInEventAuth: true, + requireIntegerPowerLevels: false, + }, } // RoomVersions returns information about room versions currently @@ -252,7 +277,7 @@ type RoomVersionDescription struct { enforceSignatureChecks bool enforceCanonicalJSON bool powerLevelsIncludeNotifications bool - allowKnockingInEventAuth bool + allowKnockingInEventAuth JoinRulesPermittingKnockInEventAuth allowRestrictedJoinsInEventAuth bool requireIntegerPowerLevels bool Supported bool @@ -309,11 +334,18 @@ 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) { +// AllowKnockingInEventAuth returns true if the given room version and given +// join rule allows for the `knock` membership state or false otherwise. +func (v RoomVersion) AllowKnockingInEventAuth(joinRule string) (bool, error) { if r, ok := roomVersionMeta[v]; ok { - return r.allowKnockingInEventAuth, nil + switch r.allowKnockingInEventAuth { + case KnockOnly: + return joinRule == Knock, nil + case KnockOrKnockRestricted: + return (joinRule == Knock || joinRule == KnockRestricted), nil + case KnocksForbidden: + return false, nil + } } return false, UnsupportedRoomVersionError{v} } From 4c788e7d0e83d536acaeb0a437991fc895183498 Mon Sep 17 00:00:00 2001 From: David Robertson Date: Thu, 19 May 2022 18:35:15 +0100 Subject: [PATCH 2/5] `knock_restricted` join rules permit restricted joins --- eventauth.go | 2 +- eventcrypto.go | 2 +- eventversion.go | 67 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/eventauth.go b/eventauth.go index eee704b9..73eda157 100644 --- a/eventauth.go +++ b/eventauth.go @@ -994,7 +994,7 @@ func (m *membershipAllower) membershipAllowed(event *Event) error { // nolint: g func (m *membershipAllower) membershipAllowedSelfForRestrictedJoin() error { // Special case for restricted room joins, where we will check if the membership // event is signed by one of the allowed servers in the join rule content. - allowsRestricted, err := m.roomVersion.AllowRestrictedJoinsInEventAuth() + allowsRestricted, err := m.roomVersion.AllowRestrictedJoinsInEventAuth(m.joinRule.JoinRule) if err != nil { return err } diff --git a/eventcrypto.go b/eventcrypto.go index b3ebb28d..5873f731 100644 --- a/eventcrypto.go +++ b/eventcrypto.go @@ -83,7 +83,7 @@ func (e *Event) VerifyEventSignatures(ctx context.Context, verifier JSONVerifier } // For restricted join rules, the authorising server should have signed. - if restricted, err := e.roomVersion.AllowRestrictedJoinsInEventAuth(); err != nil { + if restricted, err := e.roomVersion.MayAllowRestrictedJoinsInEventAuth(); err != nil { return fmt.Errorf("failed to check if restricted joins allowed: %w", err) } else if restricted && membership == Join { if v := gjson.GetBytes(e.Content(), "join_authorised_via_users_server"); v.Exists() { diff --git a/eventversion.go b/eventversion.go index 118b5a9e..ef591fe8 100644 --- a/eventversion.go +++ b/eventversion.go @@ -21,6 +21,10 @@ type RedactionAlgorithm int // a room to be knocked upon. type JoinRulesPermittingKnockInEventAuth int +// JoinRulesPermittingRestrictedJoinInEventAuth specifies which kinds of join_rule allow +// a room to be joined via a space. +type JoinRulesPermittingRestrictedJoinInEventAuth int + // Room version constants. These are strings because the version grammar // allows for future expansion. // https://matrix.org/docs/spec/#room-version-grammar @@ -70,6 +74,13 @@ const ( KnockOrKnockRestricted // rooms with join_rule "knock" or "knock_restricted" can be knocked upon ) +// Which join_rules permit restricted joins? +const ( + NoRestrictedJoins JoinRulesPermittingRestrictedJoinInEventAuth = iota + 1 // no rooms can be joined via a space + RestrictedOnly // rooms with join_rule "restricted" can be joined via a space + RestrictedOrKnockRestricted // rooms with join_rule "restricted" or "knock_restricted" can be joined via a space +) + var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ RoomVersionV1: { Supported: true, @@ -82,7 +93,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: KnocksForbidden, - allowRestrictedJoinsInEventAuth: false, + allowRestrictedJoinsInEventAuth: NoRestrictedJoins, requireIntegerPowerLevels: false, }, RoomVersionV2: { @@ -96,7 +107,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: KnocksForbidden, - allowRestrictedJoinsInEventAuth: false, + allowRestrictedJoinsInEventAuth: NoRestrictedJoins, requireIntegerPowerLevels: false, }, RoomVersionV3: { @@ -110,7 +121,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: KnocksForbidden, - allowRestrictedJoinsInEventAuth: false, + allowRestrictedJoinsInEventAuth: NoRestrictedJoins, requireIntegerPowerLevels: false, }, RoomVersionV4: { @@ -124,7 +135,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: KnocksForbidden, - allowRestrictedJoinsInEventAuth: false, + allowRestrictedJoinsInEventAuth: NoRestrictedJoins, requireIntegerPowerLevels: false, }, RoomVersionV5: { @@ -138,7 +149,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: false, powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: KnocksForbidden, - allowRestrictedJoinsInEventAuth: false, + allowRestrictedJoinsInEventAuth: NoRestrictedJoins, requireIntegerPowerLevels: false, }, RoomVersionV6: { @@ -152,7 +163,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: KnocksForbidden, - allowRestrictedJoinsInEventAuth: false, + allowRestrictedJoinsInEventAuth: NoRestrictedJoins, requireIntegerPowerLevels: false, }, RoomVersionV7: { @@ -166,7 +177,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: KnockOnly, - allowRestrictedJoinsInEventAuth: false, + allowRestrictedJoinsInEventAuth: NoRestrictedJoins, requireIntegerPowerLevels: false, }, RoomVersionV8: { @@ -180,7 +191,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: KnocksForbidden, - allowRestrictedJoinsInEventAuth: true, + allowRestrictedJoinsInEventAuth: RestrictedOnly, requireIntegerPowerLevels: false, }, RoomVersionV9: { @@ -194,7 +205,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: KnocksForbidden, - allowRestrictedJoinsInEventAuth: true, + allowRestrictedJoinsInEventAuth: RestrictedOnly, requireIntegerPowerLevels: false, }, "org.matrix.msc3667": { // based on room version 7 @@ -208,7 +219,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: KnockOnly, - allowRestrictedJoinsInEventAuth: false, + allowRestrictedJoinsInEventAuth: NoRestrictedJoins, requireIntegerPowerLevels: true, }, "org.matrix.msc3787": { // roughly, the union of v7 and v9 @@ -222,7 +233,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ enforceCanonicalJSON: true, powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: KnockOrKnockRestricted, - allowRestrictedJoinsInEventAuth: true, + allowRestrictedJoinsInEventAuth: RestrictedOrKnockRestricted, requireIntegerPowerLevels: false, }, } @@ -278,7 +289,7 @@ type RoomVersionDescription struct { enforceCanonicalJSON bool powerLevelsIncludeNotifications bool allowKnockingInEventAuth JoinRulesPermittingKnockInEventAuth - allowRestrictedJoinsInEventAuth bool + allowRestrictedJoinsInEventAuth JoinRulesPermittingRestrictedJoinInEventAuth requireIntegerPowerLevels bool Supported bool Stable bool @@ -350,11 +361,35 @@ func (v RoomVersion) AllowKnockingInEventAuth(joinRule string) (bool, error) { return false, UnsupportedRoomVersionError{v} } -// AllowRestrictedJoinsInEventAuth returns true if the given room version allows -// for memberships signed by servers in the restricted join rules. -func (v RoomVersion) AllowRestrictedJoinsInEventAuth() (bool, error) { +// AllowRestrictedJoinsInEventAuth returns true if the given room version and +// join rule allows for memberships signed by servers in the restricted join rules. +func (v RoomVersion) AllowRestrictedJoinsInEventAuth(joinRule string) (bool, error) { if r, ok := roomVersionMeta[v]; ok { - return r.allowRestrictedJoinsInEventAuth, nil + switch r.allowRestrictedJoinsInEventAuth { + case NoRestrictedJoins: + return false, nil + case RestrictedOnly: + return joinRule == Restricted, nil + case RestrictedOrKnockRestricted: + return (joinRule == Restricted || joinRule == KnockRestricted), nil + } + } + return false, UnsupportedRoomVersionError{v} +} + +// MayAllowRestrictedJoinsInEventAuth returns true if the given room version +// might allow for memberships signed by servers in the restricted join rules. +// (For an authoritative answer, the room's join rules must be known. If they +// are, use AllowRestrictedJoinsInEventAuth.) +func (v RoomVersion) MayAllowRestrictedJoinsInEventAuth() (bool, error) { + if r, ok := roomVersionMeta[v]; ok { + switch r.allowRestrictedJoinsInEventAuth { + case NoRestrictedJoins: + return false, nil + case RestrictedOnly: + case RestrictedOrKnockRestricted: + return true, nil + } } return false, UnsupportedRoomVersionError{v} } From 8904a93b5a9993a33daeb158f5ecf278c33308ef Mon Sep 17 00:00:00 2001 From: David Robertson Date: Thu, 19 May 2022 18:48:12 +0100 Subject: [PATCH 3/5] Big struct members go first? --- eventversion.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eventversion.go b/eventversion.go index ef591fe8..e36be7f5 100644 --- a/eventversion.go +++ b/eventversion.go @@ -285,11 +285,11 @@ type RoomVersionDescription struct { eventFormat EventFormat eventIDFormat EventIDFormat redactionAlgorithm RedactionAlgorithm + allowKnockingInEventAuth JoinRulesPermittingKnockInEventAuth + allowRestrictedJoinsInEventAuth JoinRulesPermittingRestrictedJoinInEventAuth enforceSignatureChecks bool enforceCanonicalJSON bool powerLevelsIncludeNotifications bool - allowKnockingInEventAuth JoinRulesPermittingKnockInEventAuth - allowRestrictedJoinsInEventAuth JoinRulesPermittingRestrictedJoinInEventAuth requireIntegerPowerLevels bool Supported bool Stable bool From b6f33bc40ed88e233af16984d97e210d6fdfe05f Mon Sep 17 00:00:00 2001 From: David Robertson Date: Thu, 26 May 2022 13:51:51 +0100 Subject: [PATCH 4/5] Update eventversion.go Co-authored-by: Neil Alexander --- eventversion.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/eventversion.go b/eventversion.go index e36be7f5..6459a18e 100644 --- a/eventversion.go +++ b/eventversion.go @@ -385,10 +385,9 @@ func (v RoomVersion) MayAllowRestrictedJoinsInEventAuth() (bool, error) { if r, ok := roomVersionMeta[v]; ok { switch r.allowRestrictedJoinsInEventAuth { case NoRestrictedJoins: - return false, nil - case RestrictedOnly: - case RestrictedOrKnockRestricted: - return true, nil + return false, nil + case RestrictedOnly, RestrictedOrKnockRestricted: + return true, nil } } return false, UnsupportedRoomVersionError{v} From 4ecc8ae2749fc16e92d20637f26aa5279186851b Mon Sep 17 00:00:00 2001 From: David Robertson Date: Thu, 26 May 2022 14:24:27 +0100 Subject: [PATCH 5/5] `goimports` (which is also apparently a formatter?) --- eventversion.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eventversion.go b/eventversion.go index 6459a18e..bf99d433 100644 --- a/eventversion.go +++ b/eventversion.go @@ -385,9 +385,9 @@ func (v RoomVersion) MayAllowRestrictedJoinsInEventAuth() (bool, error) { if r, ok := roomVersionMeta[v]; ok { switch r.allowRestrictedJoinsInEventAuth { case NoRestrictedJoins: - return false, nil + return false, nil case RestrictedOnly, RestrictedOrKnockRestricted: - return true, nil + return true, nil } } return false, UnsupportedRoomVersionError{v}