Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MSC3667 implementation #284

Merged
merged 3 commits into from
Jan 24, 2022
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
6 changes: 3 additions & 3 deletions event.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
92 changes: 52 additions & 40 deletions eventcontent.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions eventcontent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,24 @@ 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 {
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")
}
}
33 changes: 33 additions & 0 deletions eventversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
requireIntegerPowerLevels: false,
},
RoomVersionV2: {
Supported: true,
Expand All @@ -85,6 +86,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
requireIntegerPowerLevels: false,
},
RoomVersionV3: {
Supported: true,
Expand All @@ -98,6 +100,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
requireIntegerPowerLevels: false,
},
RoomVersionV4: {
Supported: true,
Expand All @@ -111,6 +114,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
requireIntegerPowerLevels: false,
},
RoomVersionV5: {
Supported: true,
Expand All @@ -124,6 +128,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
powerLevelsIncludeNotifications: false,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
requireIntegerPowerLevels: false,
},
RoomVersionV6: {
Supported: true,
Expand All @@ -137,6 +142,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
powerLevelsIncludeNotifications: true,
allowKnockingInEventAuth: false,
allowRestrictedJoinsInEventAuth: false,
requireIntegerPowerLevels: false,
},
RoomVersionV7: {
Supported: true,
Expand All @@ -150,6 +156,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
powerLevelsIncludeNotifications: true,
allowKnockingInEventAuth: true,
allowRestrictedJoinsInEventAuth: false,
requireIntegerPowerLevels: false,
},
RoomVersionV8: {
Supported: true,
Expand All @@ -163,6 +170,7 @@ var roomVersionMeta = map[RoomVersion]RoomVersionDescription{
powerLevelsIncludeNotifications: true,
allowKnockingInEventAuth: true,
allowRestrictedJoinsInEventAuth: true,
requireIntegerPowerLevels: false,
},
RoomVersionV9: {
Supported: true,
Expand All @@ -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,
},
}

Expand Down Expand Up @@ -231,6 +254,7 @@ type RoomVersionDescription struct {
powerLevelsIncludeNotifications bool
allowKnockingInEventAuth bool
allowRestrictedJoinsInEventAuth bool
requireIntegerPowerLevels bool
Supported bool
Stable bool
}
Expand Down Expand Up @@ -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 {
Expand Down