Fully specify room versions (#3432)
* Fully specify room versions * Misc typo clarifications * Try to clarify redactions a bit * Update content/client-server-api/_index.md Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update content/rooms/v6.md Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Update content/rooms/v6.md Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Better describe client considerations * Doc template params * Move redaction "new stuff" to v3 * Remove unhelpful sentences about "here follows unchanged stuff" * Simplify event signing text * Clean up handling redactions sections * Move v4's event schema to unchanged section * Update content/rooms/v4.md Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>pull/977/head
parent
b873ba984c
commit
241e01c332
@ -0,0 +1,144 @@
|
||||
The types of state events that affect authorization are:
|
||||
|
||||
- `m.room.create`
|
||||
- `m.room.member`
|
||||
- `m.room.join_rules`
|
||||
- `m.room.power_levels`
|
||||
- `m.room.third_party_invite`
|
||||
|
||||
{{% boxes/note %}}
|
||||
Power levels are inferred from defaults when not explicitly supplied.
|
||||
For example, mentions of the `sender`'s power level can also refer to
|
||||
the default power level for users in the room.
|
||||
{{% /boxes/note %}}
|
||||
|
||||
The rules are as follows:
|
||||
|
||||
1. If type is `m.room.create`:
|
||||
1. If it has any previous events, reject.
|
||||
2. If the domain of the `room_id` does not match the domain of the
|
||||
`sender`, reject.
|
||||
3. If `content.room_version` is present and is not a recognised
|
||||
version, reject.
|
||||
4. If `content` has no `creator` field, reject.
|
||||
5. Otherwise, allow.
|
||||
2. Reject if event has `auth_events` that:
|
||||
1. have duplicate entries for a given `type` and `state_key` pair
|
||||
2. have entries whose `type` and `state_key` don't match those
|
||||
specified by the [auth events
|
||||
selection](/server-server-api#auth-events-selection)
|
||||
algorithm described in the server specification.
|
||||
3. If event does not have a `m.room.create` in its `auth_events`,
|
||||
reject.
|
||||
4. If type is `m.room.aliases`:
|
||||
1. If event has no `state_key`, reject.
|
||||
2. If sender's domain doesn't matches `state_key`, reject.
|
||||
3. Otherwise, allow.
|
||||
5. If type is `m.room.member`:
|
||||
1. If no `state_key` key or `membership` key in `content`, reject.
|
||||
2. If `membership` is `join`:
|
||||
1. If the only previous event is an `m.room.create` and the
|
||||
`state_key` is the creator, allow.
|
||||
2. If the `sender` does not match `state_key`, reject.
|
||||
3. If the `sender` is banned, reject.
|
||||
4. If the `join_rule` is `invite` then allow if membership
|
||||
state is `invite` or `join`.
|
||||
5. If the `join_rule` is `public`, allow.
|
||||
6. Otherwise, reject.
|
||||
3. If `membership` is `invite`:
|
||||
1. If `content` has `third_party_invite` key:
|
||||
1. If *target user* is banned, reject.
|
||||
2. If `content.third_party_invite` does not have a `signed`
|
||||
key, reject.
|
||||
3. If `signed` does not have `mxid` and `token` keys,
|
||||
reject.
|
||||
4. If `mxid` does not match `state_key`, reject.
|
||||
5. If there is no `m.room.third_party_invite` event in the
|
||||
current room state with `state_key` matching `token`,
|
||||
reject.
|
||||
6. If `sender` does not match `sender` of the
|
||||
`m.room.third_party_invite`, reject.
|
||||
7. If any signature in `signed` matches any public key in
|
||||
the `m.room.third_party_invite` event, allow. The public
|
||||
keys are in `content` of `m.room.third_party_invite` as:
|
||||
1. A single public key in the `public_key` field.
|
||||
2. A list of public keys in the `public_keys` field.
|
||||
8. Otherwise, reject.
|
||||
2. If the `sender`'s current membership state is not `join`,
|
||||
reject.
|
||||
3. If *target user*'s current membership state is `join` or
|
||||
`ban`, reject.
|
||||
4. If the `sender`'s power level is greater than or equal to
|
||||
the *invite level*, allow.
|
||||
5. Otherwise, reject.
|
||||
4. If `membership` is `leave`:
|
||||
1. If the `sender` matches `state_key`, allow if and only if
|
||||
that user's current membership state is `invite` or `join`.
|
||||
2. If the `sender`'s current membership state is not `join`,
|
||||
reject.
|
||||
3. If the *target user*'s current membership state is `ban`,
|
||||
and the `sender`'s power level is less than the *ban level*,
|
||||
reject.
|
||||
4. If the `sender`'s power level is greater than or equal to
|
||||
the *kick level*, and the *target user*'s power level is
|
||||
less than the `sender`'s power level, allow.
|
||||
5. Otherwise, reject.
|
||||
5. If `membership` is `ban`:
|
||||
1. If the `sender`'s current membership state is not `join`,
|
||||
reject.
|
||||
2. If the `sender`'s power level is greater than or equal to
|
||||
the *ban level*, and the *target user*'s power level is less
|
||||
than the `sender`'s power level, allow.
|
||||
3. Otherwise, reject.
|
||||
6. Otherwise, the membership is unknown. Reject.
|
||||
6. If the `sender`'s current membership state is not `join`, reject.
|
||||
7. If type is `m.room.third_party_invite`:
|
||||
1. Allow if and only if `sender`'s current power level is greater
|
||||
than or equal to the *invite level*.
|
||||
8. If the event type's *required power level* is greater than the
|
||||
`sender`'s power level, reject.
|
||||
9. If the event has a `state_key` that starts with an `@` and does not
|
||||
match the `sender`, reject.
|
||||
10. If type is `m.room.power_levels`:
|
||||
1. If `users` key in `content` is not a dictionary with keys that
|
||||
are valid user IDs with values that are integers (or a string
|
||||
that is an integer), reject.
|
||||
2. If there is no previous `m.room.power_levels` event in the room,
|
||||
allow.
|
||||
3. For the keys `users_default`, `events_default`, `state_default`,
|
||||
`ban`, `redact`, `kick`, `invite` check if they were added,
|
||||
changed or removed. For each found alteration:
|
||||
1. If the current value is higher than the `sender`'s current
|
||||
power level, reject.
|
||||
2. If the new value is higher than the `sender`'s current power
|
||||
level, reject.
|
||||
4. For each entry being added, changed or removed in both the
|
||||
`events` and `users` keys:
|
||||
1. If the current value is higher than the `sender`'s current
|
||||
power level, reject.
|
||||
2. If the new value is higher than the `sender`'s current power
|
||||
level, reject.
|
||||
5. For each entry being changed under the `users` key, other than
|
||||
the `sender`'s own entry:
|
||||
1. If the current value is equal to the `sender`'s current
|
||||
power level, reject.
|
||||
6. Otherwise, allow.
|
||||
11. If type is `m.room.redaction`:
|
||||
1. If the `sender`'s power level is greater than or equal to the
|
||||
*redact level*, allow.
|
||||
2. If the domain of the `event_id` of the event being redacted is
|
||||
the same as the domain of the `event_id` of the
|
||||
`m.room.redaction`, allow.
|
||||
3. Otherwise, reject.
|
||||
12. Otherwise, allow.
|
||||
|
||||
{{% boxes/note %}}
|
||||
Some consequences of these rules:
|
||||
|
||||
- Unless you are a member of the room, the only permitted operations
|
||||
(apart from the initial create/join) are: joining a public room;
|
||||
accepting or rejecting an invitation to a room.
|
||||
- To unban somebody, you must have power level greater than or equal
|
||||
to both the kick *and* ban levels, *and* greater than the target
|
||||
user's power level.
|
||||
{{% /boxes/note %}}
|
@ -0,0 +1,3 @@
|
||||
Servers MUST NOT strictly enforce the JSON format specified in the
|
||||
[appendices](/appendices#canonical-json) for the reasons
|
||||
described there.
|
@ -0,0 +1,29 @@
|
||||
Upon receipt of a redaction event, the server must strip off any keys
|
||||
not in the following list:
|
||||
|
||||
- `event_id`
|
||||
- `type`
|
||||
- `room_id`
|
||||
- `sender`
|
||||
- `state_key`
|
||||
- `content`
|
||||
- `hashes`
|
||||
- `signatures`
|
||||
- `depth`
|
||||
- `prev_events`
|
||||
- `prev_state`
|
||||
- `auth_events`
|
||||
- `origin`
|
||||
- `origin_server_ts`
|
||||
- `membership`
|
||||
|
||||
The content object must also be stripped of all keys, unless it is one
|
||||
of one of the following event types:
|
||||
|
||||
- `m.room.member` allows key `membership`.
|
||||
- `m.room.create` allows key `creator`.
|
||||
- `m.room.join_rules` allows key `join_rule`.
|
||||
- `m.room.power_levels` allows keys `ban`, `events`, `events_default`,
|
||||
`kick`, `redact`, `state_default`, `users`, `users_default`.
|
||||
- `m.room.aliases` allows key `aliases`.
|
||||
- `m.room.history_visibility` allows key `history_visibility`.
|
@ -0,0 +1,149 @@
|
||||
---
|
||||
# unused frontmatter - just fixing a hugo issue where it doesn't parse
|
||||
# shortcodes at the start of a file.
|
||||
---
|
||||
|
||||
{{% added-in this=true %}} In room versions 1 and 2, events need a
|
||||
signature from the domain of the `event_id` in order to be considered
|
||||
valid. This room version does not include an `event_id` over federation
|
||||
in the same respect, so does not need a signature from that server.
|
||||
The event must still be signed by the server denoted by the `sender`,
|
||||
however.
|
||||
|
||||
The types of state events that affect authorization are:
|
||||
|
||||
- `m.room.create`
|
||||
- `m.room.member`
|
||||
- `m.room.join_rules`
|
||||
- `m.room.power_levels`
|
||||
- `m.room.third_party_invite`
|
||||
|
||||
{{% boxes/note %}}
|
||||
Power levels are inferred from defaults when not explicitly supplied.
|
||||
For example, mentions of the `sender`'s power level can also refer to
|
||||
the default power level for users in the room.
|
||||
{{% /boxes/note %}}
|
||||
|
||||
The complete list of rules, as of room version 3, is as follows:
|
||||
|
||||
1. If type is `m.room.create`:
|
||||
1. If it has any previous events, reject.
|
||||
2. If the domain of the `room_id` does not match the domain of the
|
||||
`sender`, reject.
|
||||
3. If `content.room_version` is present and is not a recognised
|
||||
version, reject.
|
||||
4. If `content` has no `creator` field, reject.
|
||||
5. Otherwise, allow.
|
||||
2. Reject if event has `auth_events` that:
|
||||
1. have duplicate entries for a given `type` and `state_key` pair
|
||||
2. have entries whose `type` and `state_key` don't match those
|
||||
specified by the [auth events
|
||||
selection](/server-server-api#auth-events-selection)
|
||||
algorithm described in the server specification.
|
||||
3. If event does not have a `m.room.create` in its `auth_events`,
|
||||
reject.
|
||||
4. If type is `m.room.aliases`:
|
||||
1. If event has no `state_key`, reject.
|
||||
2. If sender's domain doesn't matches `state_key`, reject.
|
||||
3. Otherwise, allow.
|
||||
5. If type is `m.room.member`:
|
||||
1. If no `state_key` key or `membership` key in `content`, reject.
|
||||
2. If `membership` is `join`:
|
||||
1. If the only previous event is an `m.room.create` and the
|
||||
`state_key` is the creator, allow.
|
||||
2. If the `sender` does not match `state_key`, reject.
|
||||
3. If the `sender` is banned, reject.
|
||||
4. If the `join_rule` is `invite` then allow if membership
|
||||
state is `invite` or `join`.
|
||||
5. If the `join_rule` is `public`, allow.
|
||||
6. Otherwise, reject.
|
||||
3. If `membership` is `invite`:
|
||||
1. If `content` has `third_party_invite` key:
|
||||
1. If *target user* is banned, reject.
|
||||
2. If `content.third_party_invite` does not have a `signed`
|
||||
key, reject.
|
||||
3. If `signed` does not have `mxid` and `token` keys,
|
||||
reject.
|
||||
4. If `mxid` does not match `state_key`, reject.
|
||||
5. If there is no `m.room.third_party_invite` event in the
|
||||
current room state with `state_key` matching `token`,
|
||||
reject.
|
||||
6. If `sender` does not match `sender` of the
|
||||
`m.room.third_party_invite`, reject.
|
||||
7. If any signature in `signed` matches any public key in
|
||||
the `m.room.third_party_invite` event, allow. The public
|
||||
keys are in `content` of `m.room.third_party_invite` as:
|
||||
1. A single public key in the `public_key` field.
|
||||
2. A list of public keys in the `public_keys` field.
|
||||
8. Otherwise, reject.
|
||||
2. If the `sender`'s current membership state is not `join`,
|
||||
reject.
|
||||
3. If *target user*'s current membership state is `join` or
|
||||
`ban`, reject.
|
||||
4. If the `sender`'s power level is greater than or equal to
|
||||
the *invite level*, allow.
|
||||
5. Otherwise, reject.
|
||||
4. If `membership` is `leave`:
|
||||
1. If the `sender` matches `state_key`, allow if and only if
|
||||
that user's current membership state is `invite` or `join`.
|
||||
2. If the `sender`'s current membership state is not `join`,
|
||||
reject.
|
||||
3. If the *target user*'s current membership state is `ban`,
|
||||
and the `sender`'s power level is less than the *ban level*,
|
||||
reject.
|
||||
4. If the `sender`'s power level is greater than or equal to
|
||||
the *kick level*, and the *target user*'s power level is
|
||||
less than the `sender`'s power level, allow.
|
||||
5. Otherwise, reject.
|
||||
5. If `membership` is `ban`:
|
||||
1. If the `sender`'s current membership state is not `join`,
|
||||
reject.
|
||||
2. If the `sender`'s power level is greater than or equal to
|
||||
the *ban level*, and the *target user*'s power level is less
|
||||
than the `sender`'s power level, allow.
|
||||
3. Otherwise, reject.
|
||||
6. Otherwise, the membership is unknown. Reject.
|
||||
6. If the `sender`'s current membership state is not `join`, reject.
|
||||
7. If type is `m.room.third_party_invite`:
|
||||
1. Allow if and only if `sender`'s current power level is greater
|
||||
than or equal to the *invite level*.
|
||||
8. If the event type's *required power level* is greater than the
|
||||
`sender`'s power level, reject.
|
||||
9. If the event has a `state_key` that starts with an `@` and does not
|
||||
match the `sender`, reject.
|
||||
10. If type is `m.room.power_levels`:
|
||||
1. If `users` key in `content` is not a dictionary with keys that
|
||||
are valid user IDs with values that are integers (or a string
|
||||
that is an integer), reject.
|
||||
2. If there is no previous `m.room.power_levels` event in the room,
|
||||
allow.
|
||||
3. For the keys `users_default`, `events_default`, `state_default`,
|
||||
`ban`, `redact`, `kick`, `invite` check if they were added,
|
||||
changed or removed. For each found alteration:
|
||||
1. If the current value is higher than the `sender`'s current
|
||||
power level, reject.
|
||||
2. If the new value is higher than the `sender`'s current power
|
||||
level, reject.
|
||||
4. For each entry being added, changed or removed in both the
|
||||
`events` and `users` keys:
|
||||
1. If the current value is higher than the `sender`'s current
|
||||
power level, reject.
|
||||
2. If the new value is higher than the `sender`'s current power
|
||||
level, reject.
|
||||
5. For each entry being changed under the `users` key, other than
|
||||
the `sender`'s own entry:
|
||||
1. If the current value is equal to the `sender`'s current
|
||||
power level, reject.
|
||||
6. Otherwise, allow.
|
||||
11. Otherwise, allow.
|
||||
|
||||
{{% boxes/note %}}
|
||||
Some consequences of these rules:
|
||||
|
||||
- Unless you are a member of the room, the only permitted operations
|
||||
(apart from the initial create/join) are: joining a public room;
|
||||
accepting or rejecting an invitation to a room.
|
||||
- To unban somebody, you must have power level greater than or equal
|
||||
to both the kick *and* ban levels, *and* greater than the target
|
||||
user's power level.
|
||||
{{% /boxes/note %}}
|
@ -0,0 +1,25 @@
|
||||
---
|
||||
# unused frontmatter - just fixing a hugo issue where it doesn't parse
|
||||
# shortcodes at the start of a file.
|
||||
---
|
||||
|
||||
{{% added-in this=true %}} In room versions 1 and 2, redactions were
|
||||
explicitly part of the [authorization rules](/rooms/v1/#authorization-rules)
|
||||
under Rule 11. As of room version 3, these conditions no longer exist as
|
||||
represented by the above rules.
|
||||
|
||||
While redactions are always accepted by the authorization rules for
|
||||
events, they should not be sent to clients until both the redaction
|
||||
event and the event the redaction affects have been received, and can
|
||||
be validated. If both events are valid and have been seen by the server,
|
||||
then the server applies the redaction if one of the following conditions
|
||||
is met:
|
||||
|
||||
1. The power level of the redaction event's `sender` is greater than or
|
||||
equal to the *redact level*.
|
||||
2. The domain of the redaction event's `sender` matches that of the
|
||||
original event's `sender`.
|
||||
|
||||
If the server would apply a redaction, the redaction event is also sent
|
||||
to clients. Otherwise, the server simply waits for a valid partner event
|
||||
to arrive where it can then re-check the above.
|
@ -0,0 +1,15 @@
|
||||
The event ID is the [reference
|
||||
hash](/server-server-api#calculating-the-reference-hash-for-an-event) of
|
||||
the event encoded using a variation of [Unpadded
|
||||
Base64](/appendices#unpadded-base64) which replaces the 62nd and
|
||||
63rd characters with `-` and `_` instead of using `+` and `/`. This
|
||||
matches [RFC4648's definition of URL-safe
|
||||
base64](https://tools.ietf.org/html/rfc4648#section-5). Event IDs are
|
||||
still prefixed with `$` and may result in looking like
|
||||
`$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg`.
|
||||
|
||||
Just like in room version 3, event IDs should not be sent over
|
||||
federation to servers when the room uses this room version. On the
|
||||
receiving end of an event, the server should compute the relevant event
|
||||
ID for itself. Room version 3 also changes the format of `auth_events`
|
||||
and `prev_events` in a PDU.
|
@ -0,0 +1,15 @@
|
||||
When validating event signatures, servers MUST enforce the
|
||||
`valid_until_ts` property from a key request is at least as large as the
|
||||
`origin_server_ts` for the event being validated. Servers missing a copy
|
||||
of the signing key MUST try to obtain one via the [GET
|
||||
/\_matrix/key/v2/server](/server-server-api#get_matrixkeyv2serverkeyid)
|
||||
or [POST
|
||||
/\_matrix/key/v2/query](/server-server-api#post_matrixkeyv2query)
|
||||
APIs. When using the `/query` endpoint, servers MUST set the
|
||||
`minimum_valid_until_ts` property to prompt the notary server to attempt
|
||||
to refresh the key if appropriate.
|
||||
|
||||
Servers MUST use the lesser of `valid_until_ts` and 7 days into the
|
||||
future when determining if a key is valid. This is to avoid a situation
|
||||
where an attacker publishes a key which is valid for a significant
|
||||
amount of time without a way for the homeserver owner to revoke it.
|
@ -0,0 +1,6 @@
|
||||
Servers MUST strictly enforce the JSON format specified in the
|
||||
[appendices](/appendices#canonical-json). This translates to a
|
||||
400 `M_BAD_JSON` error on most endpoints, or discarding of events over
|
||||
federation. For example, the Federation API's `/send` endpoint would
|
||||
discard the event whereas the Client Server API's `/send/{eventType}`
|
||||
endpoint would return a `M_BAD_JSON` error.
|
@ -0,0 +1,28 @@
|
||||
Upon receipt of a redaction event, the server must strip off any keys
|
||||
not in the following list:
|
||||
|
||||
- `event_id`
|
||||
- `type`
|
||||
- `room_id`
|
||||
- `sender`
|
||||
- `state_key`
|
||||
- `content`
|
||||
- `hashes`
|
||||
- `signatures`
|
||||
- `depth`
|
||||
- `prev_events`
|
||||
- `prev_state`
|
||||
- `auth_events`
|
||||
- `origin`
|
||||
- `origin_server_ts`
|
||||
- `membership`
|
||||
|
||||
The content object must also be stripped of all keys, unless it is one
|
||||
of one of the following event types:
|
||||
|
||||
- `m.room.member` allows key `membership`.
|
||||
- `m.room.create` allows key `creator`.
|
||||
- `m.room.join_rules` allows key `join_rule`.
|
||||
- `m.room.power_levels` allows keys `ban`, `events`, `events_default`,
|
||||
`kick`, `redact`, `state_default`, `users`, `users_default`.
|
||||
- `m.room.history_visibility` allows key `history_visibility`.
|
@ -0,0 +1,25 @@
|
||||
{{/*
|
||||
|
||||
This template is used to render a "room version fragment". Fragments are blocks of
|
||||
text which describe a portion of the room version specification. They should be
|
||||
prefixed with the room version which introduces the fragment, and be reusable for
|
||||
two or more versions.
|
||||
|
||||
The `name` parameter is the file name without extension.
|
||||
|
||||
The `withVersioning` parameter is optional and defaults to false. When true, any
|
||||
mentions of "New in this version" from the `added-in` shortcode are removed prior
|
||||
to rendering. This is useful if needing to use a fragment where part of it describes
|
||||
new functionality in a given room version but isn't new for subsequent versions.
|
||||
|
||||
*/}}
|
||||
|
||||
{{ $name := .Params.name }}
|
||||
{{ $withVersioning := .Params.withVersioning }}
|
||||
|
||||
{{ $page := .Site.GetPage (path.Join .Page.Dir "fragments" (printf "%s%s" $name ".md"))}}
|
||||
{{ $content := $page.Content }}
|
||||
{{ if not $withVersioning }}
|
||||
{{ $content = (replace $content "[New in this version]" "") }}
|
||||
{{ end }}
|
||||
{{ $content | safeHTML }}
|
Loading…
Reference in New Issue