Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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