From 297c74222396b1b0760240920ddcdd6d4627c7c7 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 24 Jan 2022 09:32:26 +0000 Subject: [PATCH 1/3] Add `org.matrix.msc3667` room version --- eventcontent.go | 92 ++++++++++++++++++++++++++++--------------------- eventversion.go | 33 ++++++++++++++++++ 2 files changed, 85 insertions(+), 40 deletions(-) diff --git a/eventcontent.go b/eventcontent.go index b3453888..1fbb9ff3 100644 --- a/eventcontent.go +++ b/eventcontent.go @@ -348,53 +348,65 @@ func NewPowerLevelContentFromEvent(event *Event) (c PowerLevelContent, err error // Set the levels to their default values. c.Defaults() - // We can't extract the JSON directly to the powerLevelContent because we - // need to convert string values to int values. - var content struct { - InviteLevel levelJSONValue `json:"invite"` - BanLevel levelJSONValue `json:"ban"` - KickLevel levelJSONValue `json:"kick"` - RedactLevel levelJSONValue `json:"redact"` - UserLevels map[string]levelJSONValue `json:"users"` - UsersDefaultLevel levelJSONValue `json:"users_default"` - EventLevels map[string]levelJSONValue `json:"events"` - StateDefaultLevel levelJSONValue `json:"state_default"` - EventDefaultLevel levelJSONValue `json:"event_default"` - NotificationLevels map[string]levelJSONValue `json:"notifications"` - } - if err = json.Unmarshal(event.Content(), &content); err != nil { - err = errorf("unparsable power_levels event content: %s", err.Error()) + var strict bool + if strict, err = event.roomVersion.RequireIntegerPowerLevels(); err != nil { return - } + } else if strict { + // Unmarshal directly to PowerLevelContent, since that will kick up an + // error if one of the power levels isn't an int64. + if err = json.Unmarshal(event.Content(), &c); err != nil { + err = errorf("unparsable power_levels event content: %s", err.Error()) + return + } + } else { + // We can't extract the JSON directly to the powerLevelContent because we + // need to convert string values to int values. + var content struct { + InviteLevel levelJSONValue `json:"invite"` + BanLevel levelJSONValue `json:"ban"` + KickLevel levelJSONValue `json:"kick"` + RedactLevel levelJSONValue `json:"redact"` + UserLevels map[string]levelJSONValue `json:"users"` + UsersDefaultLevel levelJSONValue `json:"users_default"` + EventLevels map[string]levelJSONValue `json:"events"` + StateDefaultLevel levelJSONValue `json:"state_default"` + EventDefaultLevel levelJSONValue `json:"event_default"` + NotificationLevels map[string]levelJSONValue `json:"notifications"` + } + if err = json.Unmarshal(event.Content(), &content); err != nil { + err = errorf("unparsable power_levels event content: %s", err.Error()) + return + } - // Update the levels with the values that are present in the event content. - content.InviteLevel.assignIfExists(&c.Invite) - content.BanLevel.assignIfExists(&c.Ban) - content.KickLevel.assignIfExists(&c.Kick) - content.RedactLevel.assignIfExists(&c.Redact) - content.UsersDefaultLevel.assignIfExists(&c.UsersDefault) - content.StateDefaultLevel.assignIfExists(&c.StateDefault) - content.EventDefaultLevel.assignIfExists(&c.EventsDefault) - - for k, v := range content.UserLevels { - if c.Users == nil { - c.Users = make(map[string]int64) + // Update the levels with the values that are present in the event content. + content.InviteLevel.assignIfExists(&c.Invite) + content.BanLevel.assignIfExists(&c.Ban) + content.KickLevel.assignIfExists(&c.Kick) + content.RedactLevel.assignIfExists(&c.Redact) + content.UsersDefaultLevel.assignIfExists(&c.UsersDefault) + content.StateDefaultLevel.assignIfExists(&c.StateDefault) + content.EventDefaultLevel.assignIfExists(&c.EventsDefault) + + for k, v := range content.UserLevels { + if c.Users == nil { + c.Users = make(map[string]int64) + } + c.Users[k] = v.value } - c.Users[k] = v.value - } - for k, v := range content.EventLevels { - if c.Events == nil { - c.Events = make(map[string]int64) + for k, v := range content.EventLevels { + if c.Events == nil { + c.Events = make(map[string]int64) + } + c.Events[k] = v.value } - c.Events[k] = v.value - } - for k, v := range content.NotificationLevels { - if c.Notifications == nil { - c.Notifications = make(map[string]int64) + for k, v := range content.NotificationLevels { + if c.Notifications == nil { + c.Notifications = make(map[string]int64) + } + c.Notifications[k] = v.value } - c.Notifications[k] = v.value } return diff --git a/eventversion.go b/eventversion.go index fb685cf0..65931910 100644 --- a/eventversion.go +++ b/eventversion.go @@ -72,6 +72,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: false, allowRestrictedJoinsInEventAuth: false, + requireIntegerPowerLevels: false, }, RoomVersionV2: { Supported: true, @@ -85,6 +86,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: false, allowRestrictedJoinsInEventAuth: false, + requireIntegerPowerLevels: false, }, RoomVersionV3: { Supported: true, @@ -98,6 +100,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: false, allowRestrictedJoinsInEventAuth: false, + requireIntegerPowerLevels: false, }, RoomVersionV4: { Supported: true, @@ -111,6 +114,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: false, allowRestrictedJoinsInEventAuth: false, + requireIntegerPowerLevels: false, }, RoomVersionV5: { Supported: true, @@ -124,6 +128,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ powerLevelsIncludeNotifications: false, allowKnockingInEventAuth: false, allowRestrictedJoinsInEventAuth: false, + requireIntegerPowerLevels: false, }, RoomVersionV6: { Supported: true, @@ -137,6 +142,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: false, allowRestrictedJoinsInEventAuth: false, + requireIntegerPowerLevels: false, }, RoomVersionV7: { Supported: true, @@ -150,6 +156,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: true, allowRestrictedJoinsInEventAuth: false, + requireIntegerPowerLevels: false, }, RoomVersionV8: { Supported: true, @@ -163,6 +170,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: true, allowRestrictedJoinsInEventAuth: true, + requireIntegerPowerLevels: false, }, RoomVersionV9: { Supported: true, @@ -176,6 +184,21 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{ powerLevelsIncludeNotifications: true, allowKnockingInEventAuth: true, allowRestrictedJoinsInEventAuth: true, + requireIntegerPowerLevels: false, + }, + "org.matrix.msc3667": { // based on room version 7 + Supported: true, + Stable: false, + stateResAlgorithm: StateResV2, + eventFormat: EventFormatV2, + eventIDFormat: EventIDFormatV3, + redactionAlgorithm: RedactionAlgorithmV2, + enforceSignatureChecks: true, + enforceCanonicalJSON: true, + powerLevelsIncludeNotifications: true, + allowKnockingInEventAuth: true, + allowRestrictedJoinsInEventAuth: false, + requireIntegerPowerLevels: true, }, } @@ -231,6 +254,7 @@ type RoomVersionDescription struct { powerLevelsIncludeNotifications bool allowKnockingInEventAuth bool allowRestrictedJoinsInEventAuth bool + requireIntegerPowerLevels bool Supported bool Stable bool } @@ -312,6 +336,15 @@ func (v RoomVersion) EnforceCanonicalJSON() (bool, error) { return false, UnsupportedRoomVersionError{v} } +// RequireIntegerPowerLevels returns true if the given room version calls for +// power levels as integers only, false otherwise. +func (v RoomVersion) RequireIntegerPowerLevels() (bool, error) { + if r, ok := roomVersionMeta[v]; ok { + return r.requireIntegerPowerLevels, nil + } + return false, UnsupportedRoomVersionError{v} +} + // UnsupportedRoomVersionError occurs when a call has been made with a room // version that is not supported by this version of gomatrixserverlib. type UnsupportedRoomVersionError struct { From 558e957c9cf24ff78c034b61245293e3a7f465bf Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 24 Jan 2022 09:51:50 +0000 Subject: [PATCH 2/3] Add test --- event.go | 6 +++--- eventcontent_test.go | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/event.go b/event.go index 5a89498d..efed4d1a 100644 --- a/event.go +++ b/event.go @@ -978,11 +978,11 @@ func (e *Event) PowerLevels() (*PowerLevelContent, error) { if !e.StateKeyEquals("") { return nil, fmt.Errorf("gomatrixserverlib: PowerLevels() event is not a m.room.power_levels event, bad state key") } - var content PowerLevelContent - if err := e.extractContent(MRoomPowerLevels, &content); err != nil { + c, err := NewPowerLevelContentFromEvent(e) + if err != nil { return nil, err } - return &content, nil + return &c, nil } // AuthEvents returns references to the events needed to auth the event. diff --git a/eventcontent_test.go b/eventcontent_test.go index f87aa31a..fdecb607 100644 --- a/eventcontent_test.go +++ b/eventcontent_test.go @@ -70,3 +70,21 @@ func TestLevelJSONValueInvalid(t *testing.T) { } } } + +func TestStrictPowerLevelContent(t *testing.T) { + eventJSON := `{"content":{"ban":50,"events":{"m.room.avatar":50,"m.room.canonical_alias":50,"m.room.encryption":100,"m.room.history_visibility":100,"m.room.name":50,"m.room.power_levels":100,"m.room.server_acl":100,"m.room.tombstone":100},"events_default":0,"historical":100,"invite":0,"kick":50,"redact":50,"state_default":50,"users":{"@neilalexander:matrix.org":"100"},"users_default":0},"origin_server_ts":1643017369993,"sender":"@neilalexander:matrix.org","state_key":"","type":"m.room.power_levels","unsigned":{"age":592},"event_id":"$2CT2RSF8B4XJyysh7i6Zdw0oYSs53JkIhTMrapIVYnw","room_id":"!CeUyQRqMxuBnjcxiIr:matrix.org"}` + goodEvent, err := NewEventFromTrustedJSON([]byte(eventJSON), false, RoomVersionV7) + if err != nil { + t.Fatal(err) + } + badEvent, err := NewEventFromTrustedJSON([]byte(eventJSON), false, "org.matrix.msc3667") + if err != nil { + t.Fatal(err) + } + if _, err := goodEvent.PowerLevels(); err != nil { + t.Fatalf("good content should not have errored but did: %s", err) + } + if _, err := badEvent.PowerLevels(); err == nil { + t.Fatal("bad content should have errored but didn't") + } +} From 3d6e5b94d20a96fa38ec5225d314e1bf0ce39e34 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 24 Jan 2022 09:52:25 +0000 Subject: [PATCH 3/3] Comment --- eventcontent_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eventcontent_test.go b/eventcontent_test.go index fdecb607..19fa8c2a 100644 --- a/eventcontent_test.go +++ b/eventcontent_test.go @@ -72,6 +72,9 @@ func TestLevelJSONValueInvalid(t *testing.T) { } func TestStrictPowerLevelContent(t *testing.T) { + // The event JSON has a "100" instead of a 100 in the power level. In + // room version 7, this is permissible, but it isn't in our new experimental + // room version. eventJSON := `{"content":{"ban":50,"events":{"m.room.avatar":50,"m.room.canonical_alias":50,"m.room.encryption":100,"m.room.history_visibility":100,"m.room.name":50,"m.room.power_levels":100,"m.room.server_acl":100,"m.room.tombstone":100},"events_default":0,"historical":100,"invite":0,"kick":50,"redact":50,"state_default":50,"users":{"@neilalexander:matrix.org":"100"},"users_default":0},"origin_server_ts":1643017369993,"sender":"@neilalexander:matrix.org","state_key":"","type":"m.room.power_levels","unsigned":{"age":592},"event_id":"$2CT2RSF8B4XJyysh7i6Zdw0oYSs53JkIhTMrapIVYnw","room_id":"!CeUyQRqMxuBnjcxiIr:matrix.org"}` goodEvent, err := NewEventFromTrustedJSON([]byte(eventJSON), false, RoomVersionV7) if err != nil {