Skip to content

Commit

Permalink
Try to support MSC3787 (#310)
Browse files Browse the repository at this point in the history
* `knock_restricted` join rules permit knocking
* `knock_restricted` join rules permit restricted joins

Co-authored-by: Neil Alexander <[email protected]>
  • Loading branch information
DMRobertson and neilalexander authored May 26, 2022
1 parent b1c5619 commit dcfbb70
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 38 deletions.
24 changes: 17 additions & 7 deletions eventauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -983,7 +986,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
}
Expand Down Expand Up @@ -1081,7 +1084,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,
)
Expand All @@ -1090,11 +1093,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 {
Expand Down Expand Up @@ -1216,16 +1224,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
Expand Down
2 changes: 1 addition & 1 deletion eventcrypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
126 changes: 96 additions & 30 deletions eventversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ 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

// 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
Expand Down Expand Up @@ -59,6 +67,20 @@ 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
)

// 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,
Expand All @@ -70,8 +92,8 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: false,
enforceCanonicalJSON: false,
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
},
RoomVersionV2: {
Expand All @@ -84,8 +106,8 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: false,
enforceCanonicalJSON: false,
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
},
RoomVersionV3: {
Expand All @@ -98,8 +120,8 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: false,
enforceCanonicalJSON: false,
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
},
RoomVersionV4: {
Expand All @@ -112,8 +134,8 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: false,
enforceCanonicalJSON: false,
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
},
RoomVersionV5: {
Expand All @@ -126,8 +148,8 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: true,
enforceCanonicalJSON: false,
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
},
RoomVersionV6: {
Expand All @@ -140,8 +162,8 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: true,
enforceCanonicalJSON: true,
powerLevelsIncludeNotifications: true,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
},
RoomVersionV7: {
Expand All @@ -154,8 +176,8 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: true,
enforceCanonicalJSON: true,
powerLevelsIncludeNotifications: true,
allowKnockingInEventAuth: true,
allowRestrictedJoinsInEventAuth: false,
allowKnockingInEventAuth: KnockOnly,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
requireIntegerPowerLevels: false,
},
RoomVersionV8: {
Expand All @@ -168,8 +190,8 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: true,
enforceCanonicalJSON: true,
powerLevelsIncludeNotifications: true,
allowKnockingInEventAuth: true,
allowRestrictedJoinsInEventAuth: true,
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: RestrictedOnly,
requireIntegerPowerLevels: false,
},
RoomVersionV9: {
Expand All @@ -182,8 +204,8 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: true,
enforceCanonicalJSON: true,
powerLevelsIncludeNotifications: true,
allowKnockingInEventAuth: true,
allowRestrictedJoinsInEventAuth: true,
allowKnockingInEventAuth: KnocksForbidden,
allowRestrictedJoinsInEventAuth: RestrictedOnly,
requireIntegerPowerLevels: false,
},
"org.matrix.msc3667": { // based on room version 7
Expand All @@ -196,10 +218,24 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
enforceSignatureChecks: true,
enforceCanonicalJSON: true,
powerLevelsIncludeNotifications: true,
allowKnockingInEventAuth: true,
allowRestrictedJoinsInEventAuth: false,
allowKnockingInEventAuth: KnockOnly,
allowRestrictedJoinsInEventAuth: NoRestrictedJoins,
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: RestrictedOrKnockRestricted,
requireIntegerPowerLevels: false,
},
}

// RoomVersions returns information about room versions currently
Expand Down Expand Up @@ -249,11 +285,11 @@ type RoomVersionDescription struct {
eventFormat EventFormat
eventIDFormat EventIDFormat
redactionAlgorithm RedactionAlgorithm
allowKnockingInEventAuth JoinRulesPermittingKnockInEventAuth
allowRestrictedJoinsInEventAuth JoinRulesPermittingRestrictedJoinInEventAuth
enforceSignatureChecks bool
enforceCanonicalJSON bool
powerLevelsIncludeNotifications bool
allowKnockingInEventAuth bool
allowRestrictedJoinsInEventAuth bool
requireIntegerPowerLevels bool
Supported bool
Stable bool
Expand Down Expand Up @@ -309,20 +345,50 @@ 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}
}

// 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, RestrictedOrKnockRestricted:
return true, nil
}
}
return false, UnsupportedRoomVersionError{v}
}
Expand Down

0 comments on commit dcfbb70

Please sign in to comment.