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>
This commit is contained in:
parent
b873ba984c
commit
241e01c332
19 changed files with 1082 additions and 439 deletions
|
@ -1598,7 +1598,9 @@ content from the databases. Servers should include a copy of the
|
||||||
when serving the redacted event to clients.
|
when serving the redacted event to clients.
|
||||||
|
|
||||||
The exact algorithm to apply against an event is defined in the [room
|
The exact algorithm to apply against an event is defined in the [room
|
||||||
version specification](/rooms).
|
version specification](/rooms), as are the criteria homeservers should
|
||||||
|
use when deciding whether to accept a redaction event from a remote
|
||||||
|
homeserver.
|
||||||
|
|
||||||
When a client receives an `m.room.redaction` event, it should change
|
When a client receives an `m.room.redaction` event, it should change
|
||||||
the affected event in the same way a server does.
|
the affected event in the same way a server does.
|
||||||
|
|
144
content/rooms/fragments/v1-auth-rules.md
Normal file
144
content/rooms/fragments/v1-auth-rules.md
Normal file
|
@ -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 %}}
|
3
content/rooms/fragments/v1-canonical-json.md
Normal file
3
content/rooms/fragments/v1-canonical-json.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Servers MUST NOT strictly enforce the JSON format specified in the
|
||||||
|
[appendices](/appendices#canonical-json) for the reasons
|
||||||
|
described there.
|
29
content/rooms/fragments/v1-redactions.md
Normal file
29
content/rooms/fragments/v1-redactions.md
Normal file
|
@ -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`.
|
165
content/rooms/fragments/v2-state-res.md
Normal file
165
content/rooms/fragments/v2-state-res.md
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
The room state *S*′(*E*) after an event *E* is defined in terms of the
|
||||||
|
room state *S*(*E*) before *E*, and depends on whether *E* is a state
|
||||||
|
event or a message event:
|
||||||
|
|
||||||
|
- If *E* is a message event, then *S*′(*E*) = *S*(*E*).
|
||||||
|
- If *E* is a state event, then *S*′(*E*) is *S*(*E*), except that its
|
||||||
|
entry corresponding to *E*'s `event_type` and `state_key` is
|
||||||
|
replaced by *E*'s `event_id`.
|
||||||
|
|
||||||
|
The room state *S*(*E*) before *E* is the *resolution* of the set of
|
||||||
|
states {*S*′(*E*<sub>1</sub>), *S*′(*E*<sub>2</sub>), …} consisting of
|
||||||
|
the states after each of *E*'s `prev_event`s
|
||||||
|
{*E*<sub>1</sub>, *E*<sub>2</sub>, …}, where the resolution of a set of
|
||||||
|
states is given in the algorithm below.
|
||||||
|
|
||||||
|
#### Definitions
|
||||||
|
|
||||||
|
The state resolution algorithm for version 2 rooms uses the following
|
||||||
|
definitions, given the set of room states
|
||||||
|
{*S*<sub>1</sub>, *S*<sub>2</sub>, …}:
|
||||||
|
|
||||||
|
Power events
|
||||||
|
A *power event* is a state event with type `m.room.power_levels` or
|
||||||
|
`m.room.join_rules`, or a state event with type `m.room.member` where
|
||||||
|
the `membership` is `leave` or `ban` and the `sender` does not match the
|
||||||
|
`state_key`. The idea behind this is that power events are events that
|
||||||
|
might remove someone's ability to do something in the room.
|
||||||
|
|
||||||
|
Unconflicted state map and conflicted state set
|
||||||
|
The *unconflicted state map* is the state where the value of each key
|
||||||
|
exists and is the same in each state *S*<sub>*i*</sub>. The *conflicted
|
||||||
|
state set* is the set of all other state events. Note that the
|
||||||
|
unconflicted state map only has one event per `(event_type, state_key)`,
|
||||||
|
whereas the conflicted state set may have multiple events.
|
||||||
|
|
||||||
|
Auth difference
|
||||||
|
The *auth difference* is calculated by first calculating the full auth
|
||||||
|
chain for each state *S*<sub>*i*</sub>, that is the union of the auth
|
||||||
|
chains for each event in *S*<sub>*i*</sub>, and then taking every event
|
||||||
|
that doesn't appear in every auth chain. If *C*<sub>*i*</sub> is the
|
||||||
|
full auth chain of *S*<sub>*i*</sub>, then the auth difference is
|
||||||
|
∪ *C*<sub>*i*</sub> − ∩ *C*<sub>*i*</sub>.
|
||||||
|
|
||||||
|
Full conflicted set
|
||||||
|
The *full conflicted set* is the union of the conflicted state set and
|
||||||
|
the auth difference.
|
||||||
|
|
||||||
|
Reverse topological power ordering
|
||||||
|
The *reverse topological power ordering* of a set of events is the
|
||||||
|
lexicographically smallest topological ordering based on the DAG formed
|
||||||
|
by auth events. The reverse topological power ordering is ordered from
|
||||||
|
earliest event to latest. For comparing two topological orderings to
|
||||||
|
determine which is the lexicographically smallest, the following
|
||||||
|
comparison relation on events is used: for events *x* and *y*,
|
||||||
|
*x* < *y* if
|
||||||
|
|
||||||
|
1. *x*'s sender has *greater* power level than *y*'s sender, when
|
||||||
|
looking at their respective `auth_event`s; or
|
||||||
|
2. the senders have the same power level, but *x*'s `origin_server_ts`
|
||||||
|
is *less* than *y*'s `origin_server_ts`; or
|
||||||
|
3. the senders have the same power level and the events have the same
|
||||||
|
`origin_server_ts`, but *x*'s `event_id` is *less* than *y*'s
|
||||||
|
`event_id`.
|
||||||
|
|
||||||
|
The reverse topological power ordering can be found by sorting the
|
||||||
|
events using Kahn's algorithm for topological sorting, and at each step
|
||||||
|
selecting, among all the candidate vertices, the smallest vertex using
|
||||||
|
the above comparison relation.
|
||||||
|
|
||||||
|
Mainline ordering
|
||||||
|
Given an `m.room.power_levels` event *P*, the *mainline of* *P* is the
|
||||||
|
list of events generated by starting with *P* and recursively taking the
|
||||||
|
`m.room.power_levels` events from the `auth_events`, ordered such that
|
||||||
|
*P* is last. Given another event *e*, the *closest mainline event to*
|
||||||
|
*e* is the first event encountered in the mainline when iteratively
|
||||||
|
descending through the `m.room.power_levels` events in the `auth_events`
|
||||||
|
starting at *e*. If no mainline event is encountered when iteratively
|
||||||
|
descending through the `m.room.power_levels` events, then the closest
|
||||||
|
mainline event to *e* can be considered to be a dummy event that is
|
||||||
|
before any other event in the mainline of *P* for the purposes of
|
||||||
|
condition 1 below.
|
||||||
|
|
||||||
|
The *mainline ordering based on* *P* of a set of events is the ordering,
|
||||||
|
from smallest to largest, using the following comparison relation on
|
||||||
|
events: for events *x* and *y*, *x* < *y* if
|
||||||
|
|
||||||
|
1. the closest mainline event to *x* appears *before* the closest
|
||||||
|
mainline event to *y*; or
|
||||||
|
2. the closest mainline events are the same, but *x*'s
|
||||||
|
`origin_server_ts` is *less* than *y*'s `origin_server_ts`; or
|
||||||
|
3. the closest mainline events are the same and the events have the
|
||||||
|
same `origin_server_ts`, but *x*'s `event_id` is *less* than *y*'s
|
||||||
|
`event_id`.
|
||||||
|
|
||||||
|
Iterative auth checks
|
||||||
|
The *iterative auth checks algorithm* takes as input an initial room
|
||||||
|
state and a sorted list of state events, and constructs a new room state
|
||||||
|
by iterating through the event list and applying the state event to the
|
||||||
|
room state if the state event is allowed by the [authorization
|
||||||
|
rules](/server-server-api#authorization-rules).
|
||||||
|
If the state event is not allowed by the authorization rules, then the
|
||||||
|
event is ignored. If a `(event_type, state_key)` key that is required
|
||||||
|
for checking the authorization rules is not present in the state, then
|
||||||
|
the appropriate state event from the event's `auth_events` is used if
|
||||||
|
the auth event is not rejected.
|
||||||
|
|
||||||
|
#### Algorithm
|
||||||
|
|
||||||
|
The *resolution* of a set of states is obtained as follows:
|
||||||
|
|
||||||
|
1. Take all *power events* and any events in their auth chains,
|
||||||
|
recursively, that appear in the *full conflicted set* and order them
|
||||||
|
by the *reverse topological power ordering*.
|
||||||
|
2. Apply the *iterative auth checks algorithm*, starting from the
|
||||||
|
*unconflicted state map*, to the list of events from the previous
|
||||||
|
step to get a partially resolved state.
|
||||||
|
3. Take all remaining events that weren't picked in step 1 and order
|
||||||
|
them by the mainline ordering based on the power level in the
|
||||||
|
partially resolved state obtained in step 2.
|
||||||
|
4. Apply the *iterative auth checks algorithm* on the partial resolved
|
||||||
|
state and the list of events from the previous step.
|
||||||
|
5. Update the result by replacing any event with the event with the
|
||||||
|
same key from the *unconflicted state map*, if such an event exists,
|
||||||
|
to get the final resolved state.
|
||||||
|
|
||||||
|
#### Rejected events
|
||||||
|
|
||||||
|
Events that have been rejected due to failing auth based on the state at
|
||||||
|
the event (rather than based on their auth chain) are handled as usual
|
||||||
|
by the algorithm, unless otherwise specified.
|
||||||
|
|
||||||
|
Note that no events rejected due to failure to auth against their auth
|
||||||
|
chain should appear in the process, as they should not appear in state
|
||||||
|
(the algorithm only uses events that appear in either the state sets or
|
||||||
|
in the auth chain of the events in the state sets).
|
||||||
|
|
||||||
|
{{% boxes/rationale %}}
|
||||||
|
This helps ensure that different servers' view of state is more likely
|
||||||
|
to converge, since rejection state of an event may be different. This
|
||||||
|
can happen if a third server gives an incorrect version of the state
|
||||||
|
when a server joins a room via it (either due to being faulty or
|
||||||
|
malicious). Convergence of state is a desirable property as it ensures
|
||||||
|
that all users in the room have a (mostly) consistent view of the state
|
||||||
|
of the room. If the view of the state on different servers diverges it
|
||||||
|
can lead to bifurcation of the room due to e.g. servers disagreeing on
|
||||||
|
who is in the room.
|
||||||
|
|
||||||
|
Intuitively, using rejected events feels dangerous, however:
|
||||||
|
|
||||||
|
1. Servers cannot arbitrarily make up state, since they still need to
|
||||||
|
pass the auth checks based on the event's auth chain (e.g. they
|
||||||
|
can't grant themselves power levels if they didn't have them
|
||||||
|
before).
|
||||||
|
2. For a previously rejected event to pass auth there must be a set of
|
||||||
|
state that allows said event. A malicious server could therefore
|
||||||
|
produce a fork where it claims the state is that particular set of
|
||||||
|
state, duplicate the rejected event to point to that fork, and send
|
||||||
|
the event. The duplicated event would then pass the auth checks.
|
||||||
|
Ignoring rejected events would therefore not eliminate any potential
|
||||||
|
attack vectors.
|
||||||
|
{{% /boxes/rationale %}}
|
||||||
|
|
||||||
|
Rejected auth events are deliberately excluded from use in the iterative
|
||||||
|
auth checks, as auth events aren't re-authed (although non-auth events
|
||||||
|
are) during the iterative auth checks.
|
149
content/rooms/fragments/v3-auth-rules.md
Normal file
149
content/rooms/fragments/v3-auth-rules.md
Normal file
|
@ -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 %}}
|
25
content/rooms/fragments/v3-handling-redactions.md
Normal file
25
content/rooms/fragments/v3-handling-redactions.md
Normal file
|
@ -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.
|
15
content/rooms/fragments/v4-event-explainer.md
Normal file
15
content/rooms/fragments/v4-event-explainer.md
Normal file
|
@ -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.
|
15
content/rooms/fragments/v5-signing-requirements.md
Normal file
15
content/rooms/fragments/v5-signing-requirements.md
Normal file
|
@ -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.
|
6
content/rooms/fragments/v6-canonical-json.md
Normal file
6
content/rooms/fragments/v6-canonical-json.md
Normal file
|
@ -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.
|
28
content/rooms/fragments/v6-redactions.md
Normal file
28
content/rooms/fragments/v6-redactions.md
Normal file
|
@ -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`.
|
|
@ -9,40 +9,12 @@ building blocks for other room versions.
|
||||||
|
|
||||||
## Client considerations
|
## Client considerations
|
||||||
|
|
||||||
Clients may need to consider some algorithms performed by the server for
|
Clients which implement the redaction algorithm locally should refer to the
|
||||||
their own implementation.
|
[redactions](#redactions) section below.
|
||||||
|
|
||||||
### Redactions
|
### Redactions
|
||||||
|
|
||||||
Upon receipt of a redaction event, the server must strip off any keys
|
{{% rver-fragment name="v1-redactions" %}}
|
||||||
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`.
|
|
||||||
|
|
||||||
## Server implementation components
|
## Server implementation components
|
||||||
|
|
||||||
|
@ -127,150 +99,7 @@ affected are said to be *conflicting* events.
|
||||||
|
|
||||||
### Authorization rules
|
### Authorization rules
|
||||||
|
|
||||||
The types of state events that affect authorization are:
|
{{% rver-fragment name="v1-auth-rules" %}}
|
||||||
|
|
||||||
- `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 %}}
|
|
||||||
|
|
||||||
### Event format
|
### Event format
|
||||||
|
|
||||||
|
@ -280,6 +109,4 @@ Events in version 1 rooms have the following structure:
|
||||||
|
|
||||||
### Canonical JSON
|
### Canonical JSON
|
||||||
|
|
||||||
Servers MUST NOT strictly enforce the JSON format specified in the
|
{{% rver-fragment name="v1-canonical-json" %}}
|
||||||
[appendices](/appendices#canonical-json) for the reasons
|
|
||||||
described there.
|
|
||||||
|
|
|
@ -4,9 +4,15 @@ type: docs
|
||||||
weight: 20
|
weight: 20
|
||||||
---
|
---
|
||||||
|
|
||||||
This room version builds off of [version 1](/rooms/v1) with an improved
|
This room version builds on [version 1](/rooms/v1) with an improved
|
||||||
state resolution algorithm.
|
state resolution algorithm.
|
||||||
|
|
||||||
|
## Client considerations
|
||||||
|
|
||||||
|
There are no client considerations introduced in this room version. Clients
|
||||||
|
which implement the redaction algorithm locally should refer to the
|
||||||
|
[redactions](#redactions) section below for a full overview of the algorithm.
|
||||||
|
|
||||||
## Server implementation components
|
## Server implementation components
|
||||||
|
|
||||||
{{% boxes/warning %}}
|
{{% boxes/warning %}}
|
||||||
|
@ -21,168 +27,29 @@ changing only the state resolution algorithm.
|
||||||
|
|
||||||
### State resolution
|
### State resolution
|
||||||
|
|
||||||
The room state *S*′(*E*) after an event *E* is defined in terms of the
|
{{% added-in this=true %}}
|
||||||
room state *S*(*E*) before *E*, and depends on whether *E* is a state
|
|
||||||
event or a message event:
|
|
||||||
|
|
||||||
- If *E* is a message event, then *S*′(*E*) = *S*(*E*).
|
{{% rver-fragment name="v2-state-res" %}}
|
||||||
- If *E* is a state event, then *S*′(*E*) is *S*(*E*), except that its
|
|
||||||
entry corresponding to *E*'s `event_type` and `state_key` is
|
|
||||||
replaced by *E*'s `event_id`.
|
|
||||||
|
|
||||||
The room state *S*(*E*) before *E* is the *resolution* of the set of
|
## Unchanged from v1
|
||||||
states {*S*′(*E*<sub>1</sub>), *S*′(*E*<sub>2</sub>), …} consisting of
|
|
||||||
the states after each of *E*'s `prev_event`s
|
|
||||||
{*E*<sub>1</sub>, *E*<sub>2</sub>, …}, where the resolution of a set of
|
|
||||||
states is given in the algorithm below.
|
|
||||||
|
|
||||||
#### Definitions
|
The following sections have not been modified since v1, but are included for
|
||||||
|
completeness.
|
||||||
|
|
||||||
The state resolution algorithm for version 2 rooms uses the following
|
### Authorization rules
|
||||||
definitions, given the set of room states
|
|
||||||
{*S*<sub>1</sub>, *S*<sub>2</sub>, …}:
|
|
||||||
|
|
||||||
Power events
|
{{% rver-fragment name="v1-auth-rules" %}}
|
||||||
A *power event* is a state event with type `m.room.power_levels` or
|
|
||||||
`m.room.join_rules`, or a state event with type `m.room.member` where
|
|
||||||
the `membership` is `leave` or `ban` and the `sender` does not match the
|
|
||||||
`state_key`. The idea behind this is that power events are events that
|
|
||||||
might remove someone's ability to do something in the room.
|
|
||||||
|
|
||||||
Unconflicted state map and conflicted state set
|
### Event format
|
||||||
The *unconflicted state map* is the state where the value of each key
|
|
||||||
exists and is the same in each state *S*<sub>*i*</sub>. The *conflicted
|
|
||||||
state set* is the set of all other state events. Note that the
|
|
||||||
unconflicted state map only has one event per `(event_type, state_key)`,
|
|
||||||
whereas the conflicted state set may have multiple events.
|
|
||||||
|
|
||||||
Auth difference
|
Events in rooms of this version have the following structure:
|
||||||
The *auth difference* is calculated by first calculating the full auth
|
|
||||||
chain for each state *S*<sub>*i*</sub>, that is the union of the auth
|
|
||||||
chains for each event in *S*<sub>*i*</sub>, and then taking every event
|
|
||||||
that doesn't appear in every auth chain. If *C*<sub>*i*</sub> is the
|
|
||||||
full auth chain of *S*<sub>*i*</sub>, then the auth difference is
|
|
||||||
∪ *C*<sub>*i*</sub> − ∩ *C*<sub>*i*</sub>.
|
|
||||||
|
|
||||||
Full conflicted set
|
{{% definition path="api/server-server/definitions/pdu" %}}
|
||||||
The *full conflicted set* is the union of the conflicted state set and
|
|
||||||
the auth difference.
|
|
||||||
|
|
||||||
Reverse topological power ordering
|
### Canonical JSON
|
||||||
The *reverse topological power ordering* of a set of events is the
|
|
||||||
lexicographically smallest topological ordering based on the DAG formed
|
|
||||||
by auth events. The reverse topological power ordering is ordered from
|
|
||||||
earliest event to latest. For comparing two topological orderings to
|
|
||||||
determine which is the lexicographically smallest, the following
|
|
||||||
comparison relation on events is used: for events *x* and *y*,
|
|
||||||
*x* < *y* if
|
|
||||||
|
|
||||||
1. *x*'s sender has *greater* power level than *y*'s sender, when
|
{{% rver-fragment name="v1-canonical-json" %}}
|
||||||
looking at their respective `auth_event`s; or
|
|
||||||
2. the senders have the same power level, but *x*'s `origin_server_ts`
|
|
||||||
is *less* than *y*'s `origin_server_ts`; or
|
|
||||||
3. the senders have the same power level and the events have the same
|
|
||||||
`origin_server_ts`, but *x*'s `event_id` is *less* than *y*'s
|
|
||||||
`event_id`.
|
|
||||||
|
|
||||||
The reverse topological power ordering can be found by sorting the
|
### Redactions
|
||||||
events using Kahn's algorithm for topological sorting, and at each step
|
|
||||||
selecting, among all the candidate vertices, the smallest vertex using
|
|
||||||
the above comparison relation.
|
|
||||||
|
|
||||||
Mainline ordering
|
{{% rver-fragment name="v1-redactions" %}}
|
||||||
Given an `m.room.power_levels` event *P*, the *mainline of* *P* is the
|
|
||||||
list of events generated by starting with *P* and recursively taking the
|
|
||||||
`m.room.power_levels` events from the `auth_events`, ordered such that
|
|
||||||
*P* is last. Given another event *e*, the *closest mainline event to*
|
|
||||||
*e* is the first event encountered in the mainline when iteratively
|
|
||||||
descending through the `m.room.power_levels` events in the `auth_events`
|
|
||||||
starting at *e*. If no mainline event is encountered when iteratively
|
|
||||||
descending through the `m.room.power_levels` events, then the closest
|
|
||||||
mainline event to *e* can be considered to be a dummy event that is
|
|
||||||
before any other event in the mainline of *P* for the purposes of
|
|
||||||
condition 1 below.
|
|
||||||
|
|
||||||
The *mainline ordering based on* *P* of a set of events is the ordering,
|
|
||||||
from smallest to largest, using the following comparison relation on
|
|
||||||
events: for events *x* and *y*, *x* < *y* if
|
|
||||||
|
|
||||||
1. the closest mainline event to *x* appears *before* the closest
|
|
||||||
mainline event to *y*; or
|
|
||||||
2. the closest mainline events are the same, but *x*'s
|
|
||||||
`origin_server_ts` is *less* than *y*'s `origin_server_ts`; or
|
|
||||||
3. the closest mainline events are the same and the events have the
|
|
||||||
same `origin_server_ts`, but *x*'s `event_id` is *less* than *y*'s
|
|
||||||
`event_id`.
|
|
||||||
|
|
||||||
Iterative auth checks
|
|
||||||
The *iterative auth checks algorithm* takes as input an initial room
|
|
||||||
state and a sorted list of state events, and constructs a new room state
|
|
||||||
by iterating through the event list and applying the state event to the
|
|
||||||
room state if the state event is allowed by the [authorization
|
|
||||||
rules](/server-server-api#authorization-rules).
|
|
||||||
If the state event is not allowed by the authorization rules, then the
|
|
||||||
event is ignored. If a `(event_type, state_key)` key that is required
|
|
||||||
for checking the authorization rules is not present in the state, then
|
|
||||||
the appropriate state event from the event's `auth_events` is used if
|
|
||||||
the auth event is not rejected.
|
|
||||||
|
|
||||||
#### Algorithm
|
|
||||||
|
|
||||||
The *resolution* of a set of states is obtained as follows:
|
|
||||||
|
|
||||||
1. Take all *power events* and any events in their auth chains,
|
|
||||||
recursively, that appear in the *full conflicted set* and order them
|
|
||||||
by the *reverse topological power ordering*.
|
|
||||||
2. Apply the *iterative auth checks algorithm*, starting from the
|
|
||||||
*unconflicted state map*, to the list of events from the previous
|
|
||||||
step to get a partially resolved state.
|
|
||||||
3. Take all remaining events that weren't picked in step 1 and order
|
|
||||||
them by the mainline ordering based on the power level in the
|
|
||||||
partially resolved state obtained in step 2.
|
|
||||||
4. Apply the *iterative auth checks algorithm* on the partial resolved
|
|
||||||
state and the list of events from the previous step.
|
|
||||||
5. Update the result by replacing any event with the event with the
|
|
||||||
same key from the *unconflicted state map*, if such an event exists,
|
|
||||||
to get the final resolved state.
|
|
||||||
|
|
||||||
#### Rejected events
|
|
||||||
|
|
||||||
Events that have been rejected due to failing auth based on the state at
|
|
||||||
the event (rather than based on their auth chain) are handled as usual
|
|
||||||
by the algorithm, unless otherwise specified.
|
|
||||||
|
|
||||||
Note that no events rejected due to failure to auth against their auth
|
|
||||||
chain should appear in the process, as they should not appear in state
|
|
||||||
(the algorithm only uses events that appear in either the state sets or
|
|
||||||
in the auth chain of the events in the state sets).
|
|
||||||
|
|
||||||
{{% boxes/rationale %}}
|
|
||||||
This helps ensure that different servers' view of state is more likely
|
|
||||||
to converge, since rejection state of an event may be different. This
|
|
||||||
can happen if a third server gives an incorrect version of the state
|
|
||||||
when a server joins a room via it (either due to being faulty or
|
|
||||||
malicious). Convergence of state is a desirable property as it ensures
|
|
||||||
that all users in the room have a (mostly) consistent view of the state
|
|
||||||
of the room. If the view of the state on different servers diverges it
|
|
||||||
can lead to bifurcation of the room due to e.g. servers disagreeing on
|
|
||||||
who is in the room.
|
|
||||||
|
|
||||||
Intuitively, using rejected events feels dangerous, however:
|
|
||||||
|
|
||||||
1. Servers cannot arbitrarily make up state, since they still need to
|
|
||||||
pass the auth checks based on the event's auth chain (e.g. they
|
|
||||||
can't grant themselves power levels if they didn't have them
|
|
||||||
before).
|
|
||||||
2. For a previously rejected event to pass auth there must be a set of
|
|
||||||
state that allows said event. A malicious server could therefore
|
|
||||||
produce a fork where it claims the state is that particular set of
|
|
||||||
state, duplicate the rejected event to point to that fork, and send
|
|
||||||
the event. The duplicated event would then pass the auth checks.
|
|
||||||
Ignoring rejected events would therefore not eliminate any potential
|
|
||||||
attack vectors.
|
|
||||||
{{% /boxes/rationale %}}
|
|
||||||
|
|
||||||
Rejected auth events are deliberately excluded from use in the iterative
|
|
||||||
auth checks, as auth events aren't re-authed (although non-auth events
|
|
||||||
are) during the iterative auth checks.
|
|
||||||
|
|
|
@ -20,6 +20,10 @@ Clients should expect to see event IDs changed from the format of
|
||||||
`$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk` (note the lack of domain
|
`$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk` (note the lack of domain
|
||||||
and the potentially problematic slash).
|
and the potentially problematic slash).
|
||||||
|
|
||||||
|
Though unchanged in this room version, clients which implement the
|
||||||
|
redaction algorithm locally should refer to the [redactions](#redactions)
|
||||||
|
section below for a full overview.
|
||||||
|
|
||||||
## Server implementation components
|
## Server implementation components
|
||||||
|
|
||||||
{{% boxes/warning %}}
|
{{% boxes/warning %}}
|
||||||
|
@ -30,8 +34,8 @@ regarding client considerations is the resource that Client-Server API
|
||||||
use cases should reference.
|
use cases should reference.
|
||||||
{{% /boxes/warning %}}
|
{{% /boxes/warning %}}
|
||||||
|
|
||||||
Room version 3 uses the state resolution algorithm defined in [room
|
Room version 3 uses the event format described here in addition to
|
||||||
version 2](/rooms/v2), and the event format defined here.
|
all the remaining behaviour described by [room version 2](/rooms/v2).
|
||||||
|
|
||||||
### Event IDs
|
### Event IDs
|
||||||
|
|
||||||
|
@ -77,24 +81,34 @@ room version during a request.
|
||||||
|
|
||||||
### Authorization rules for events
|
### Authorization rules for events
|
||||||
|
|
||||||
The authorization rules for a given event have changed in this room
|
{{% added-in this=true %}} `m.room.redaction` events are no longer
|
||||||
version due to the change in event format:
|
explicitly part of the auth rules. They are still subject to the
|
||||||
|
minimum power level rules, but should always fall into "11. Otherwise,
|
||||||
|
allow". Instead of being authorized at the time of receipt, they are
|
||||||
|
authorized at a later stage: see the [Handling Redactions](#handling-redactions)
|
||||||
|
section below for more information.
|
||||||
|
|
||||||
- The event no longer needs to be signed by the domain of the event ID
|
<!-- set withVersioning=true so we get all the "new in this version" stuff -->
|
||||||
(as there is no domain in the event ID), but still needs to be
|
{{% rver-fragment name="v3-auth-rules" withVersioning=true %}}
|
||||||
signed by the sender's domain.
|
|
||||||
- In past room versions, redactions were only permitted to enter the
|
|
||||||
DAG if the sender's domain matched the domain in the event ID being
|
|
||||||
redacted, or the sender had appropriate permissions per the power
|
|
||||||
levels. Due to servers now not being able to determine where an
|
|
||||||
event came from during event authorization, redaction events are
|
|
||||||
always accepted (provided the event is allowed by `events` and
|
|
||||||
`events_default` in the power levels). However, servers should not
|
|
||||||
apply or send redactions to clients until both the redaction event
|
|
||||||
and original event have been seen, and are valid. Servers should
|
|
||||||
only apply redactions to events where the sender's domains match, or
|
|
||||||
the sender of the redaction has the appropriate permissions per the
|
|
||||||
power levels.
|
|
||||||
|
|
||||||
The remaining rules are the same as [room version
|
### Handling redactions
|
||||||
1](/rooms/v1#authorization-rules).
|
|
||||||
|
<!-- set withVersioning=true so we get all the "new in this version" stuff -->
|
||||||
|
{{% rver-fragment name="v3-handling-redactions" withVersioning=true %}}
|
||||||
|
|
||||||
|
## Unchanged from v2
|
||||||
|
|
||||||
|
The following sections have not been modified since v2, but are included for
|
||||||
|
completeness.
|
||||||
|
|
||||||
|
### State resolution
|
||||||
|
|
||||||
|
{{% rver-fragment name="v2-state-res" %}}
|
||||||
|
|
||||||
|
### Canonical JSON
|
||||||
|
|
||||||
|
{{% rver-fragment name="v1-canonical-json" %}}
|
||||||
|
|
||||||
|
### Redactions
|
||||||
|
|
||||||
|
{{% rver-fragment name="v1-redactions" %}}
|
||||||
|
|
|
@ -19,6 +19,10 @@ Clients should expect to see event IDs changed from the format of
|
||||||
`$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg` (note the lack of
|
`$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg` (note the lack of
|
||||||
domain).
|
domain).
|
||||||
|
|
||||||
|
Though unchanged in this room version, clients which implement the
|
||||||
|
redaction algorithm locally should refer to the [redactions](#redactions)
|
||||||
|
section below for a full overview.
|
||||||
|
|
||||||
## Server implementation components
|
## Server implementation components
|
||||||
|
|
||||||
{{% boxes/warning %}}
|
{{% boxes/warning %}}
|
||||||
|
@ -42,20 +46,37 @@ being interpretted differently by some reverse proxy software, and
|
||||||
generally made administration harder.
|
generally made administration harder.
|
||||||
{{% /boxes/rationale %}}
|
{{% /boxes/rationale %}}
|
||||||
|
|
||||||
The event ID is the [reference
|
{{% rver-fragment name="v4-event-explainer" %}}
|
||||||
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
|
## Unchanged from v3
|
||||||
federation to servers when the room uses this room version. On the
|
|
||||||
receiving end of an event, the server should compute the relevant event
|
The following sections have not been modified since v3, but are included for
|
||||||
ID for itself. Room version 3 also changes the format of `auth_events`
|
completeness.
|
||||||
and `prev_events` in a PDU.
|
|
||||||
|
### State resolution
|
||||||
|
|
||||||
|
{{% rver-fragment name="v2-state-res" %}}
|
||||||
|
|
||||||
|
### Authorization rules
|
||||||
|
|
||||||
|
{{% rver-fragment name="v3-auth-rules" %}}
|
||||||
|
|
||||||
|
### Handling redactions
|
||||||
|
|
||||||
|
{{% rver-fragment name="v3-handling-redactions" %}}
|
||||||
|
|
||||||
|
### Canonical JSON
|
||||||
|
|
||||||
|
{{% rver-fragment name="v1-canonical-json" %}}
|
||||||
|
|
||||||
|
### Redactions
|
||||||
|
|
||||||
|
{{% rver-fragment name="v1-redactions" %}}
|
||||||
|
|
||||||
|
### Event format
|
||||||
|
|
||||||
|
The event format is the same as [room version 3](/rooms/v3#event-ids),
|
||||||
|
however the event IDs in the following example are updated to reflect
|
||||||
|
the changes in this room version.
|
||||||
|
|
||||||
{{% definition path="api/server-server/definitions/pdu_v4" %}}
|
{{% definition path="api/server-server/definitions/pdu_v4" %}}
|
||||||
|
|
|
@ -9,9 +9,9 @@ key validity periods for events.
|
||||||
|
|
||||||
## Client considerations
|
## Client considerations
|
||||||
|
|
||||||
There are no specific requirements for clients in this room version.
|
There are no client considerations introduced in this room version. Clients
|
||||||
Clients should be aware of event ID changes in [room version
|
which implement the redaction algorithm locally should refer to the
|
||||||
4](/rooms/v4), however.
|
[redactions](#redactions) section below for a full overview of the algorithm.
|
||||||
|
|
||||||
## Server implementation components
|
## Server implementation components
|
||||||
|
|
||||||
|
@ -28,18 +28,35 @@ Room version 5 uses the same algorithms defined in [room version
|
||||||
|
|
||||||
### Signing key validity period
|
### Signing key validity period
|
||||||
|
|
||||||
When validating event signatures, servers MUST enforce the
|
{{% rver-fragment name="v5-signing-requirements" %}}
|
||||||
`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
|
## Unchanged from v4
|
||||||
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
|
The following sections have not been modified since v4, but are included for
|
||||||
amount of time without a way for the homeserver owner to revoke it.
|
completeness.
|
||||||
|
|
||||||
|
### State resolution
|
||||||
|
|
||||||
|
{{% rver-fragment name="v2-state-res" %}}
|
||||||
|
|
||||||
|
### Authorization rules
|
||||||
|
|
||||||
|
{{% rver-fragment name="v3-auth-rules" %}}
|
||||||
|
|
||||||
|
### Handling redactions
|
||||||
|
|
||||||
|
{{% rver-fragment name="v3-handling-redactions" %}}
|
||||||
|
|
||||||
|
### Event format
|
||||||
|
|
||||||
|
{{% rver-fragment name="v4-event-explainer" %}}
|
||||||
|
|
||||||
|
{{% definition path="api/server-server/definitions/pdu_v4" %}}
|
||||||
|
|
||||||
|
### Canonical JSON
|
||||||
|
|
||||||
|
{{% rver-fragment name="v1-canonical-json" %}}
|
||||||
|
|
||||||
|
### Redactions
|
||||||
|
|
||||||
|
{{% rver-fragment name="v1-redactions" %}}
|
||||||
|
|
|
@ -9,10 +9,17 @@ authorization rules performed on events.
|
||||||
|
|
||||||
## Client considerations
|
## Client considerations
|
||||||
|
|
||||||
The redaction algorithm has changed from [room version 1](/rooms/v1) to
|
There are no client considerations introduced in this room version. Clients
|
||||||
remove all rules against events of type `m.room.aliases`. Room versions
|
which implement the redaction algorithm locally should refer to the
|
||||||
2, 3, 4, and 5 all use v1's redaction algorithm. The algorithm is
|
[redactions](#redactions) section below for a full overview of the algorithm.
|
||||||
otherwise unchanged.
|
|
||||||
|
### Redactions
|
||||||
|
|
||||||
|
{{% added-in this=true %}} All significant meaning for `m.room.aliases`
|
||||||
|
has been removed from the redaction algorithm. The remaining rules are
|
||||||
|
the same as past room versions.
|
||||||
|
|
||||||
|
{{% rver-fragment name="v6-redactions" %}}
|
||||||
|
|
||||||
## Server implementation components
|
## Server implementation components
|
||||||
|
|
||||||
|
@ -29,56 +36,180 @@ in [room version 5](/rooms/v5).
|
||||||
|
|
||||||
### Redactions
|
### Redactions
|
||||||
|
|
||||||
As mentioned in the client considerations portion of this specification,
|
[See above](#redactions).
|
||||||
all special meaning has been removed for events of type
|
|
||||||
`m.room.aliases`. The algorithm is otherwise unchanged.
|
|
||||||
|
|
||||||
### Authorization rules for events
|
### Authorization rules for events
|
||||||
|
|
||||||
Like redactions, all rules relating specifically to events of type
|
`m.room.redaction` events are not explicitly part of the auth rules.
|
||||||
`m.room.aliases` are removed. They must still pass authorization checks
|
They are still subject to the minimum power level rules, but should always
|
||||||
relating to state events.
|
fall into "10. Otherwise, allow". Instead of being authorized at the time
|
||||||
|
of receipt, they are authorized at a later stage: see the
|
||||||
|
[Handling Redactions](#handling-redactions) section below for more information.
|
||||||
|
|
||||||
Additionally, the authorization rules for events of type
|
{{% added-in this=true %}} Rule 4, which related specifically to events
|
||||||
`m.room.power_levels` now include the content key `notifications`. This
|
of type `m.room.aliases`, is removed. `m.room.aliases` events must still pass
|
||||||
new rule takes the place of the rule which checks the `events` and
|
authorization checks relating to state events.
|
||||||
|
|
||||||
|
{{% added-in this=true %}} Additionally, the authorization rules for events
|
||||||
|
of type `m.room.power_levels` now include the content key `notifications`.
|
||||||
|
This new rule takes the place of rule 10.4, which checked the `events` and
|
||||||
`users` keys.
|
`users` keys.
|
||||||
|
|
||||||
For completeness, the changes to the auth rules can be represented as
|
Events must be signed by the server denoted by the `sender` key.
|
||||||
follows:
|
|
||||||
|
|
||||||
...
|
The types of state events that affect authorization are:
|
||||||
|
|
||||||
-If type is `m.room.aliases`:
|
- `m.room.create`
|
||||||
-
|
- `m.room.member`
|
||||||
- a. If event has no `state_key`, reject.
|
- `m.room.join_rules`
|
||||||
- b. If sender's domain doesn't matches `state_key`, reject.
|
- `m.room.power_levels`
|
||||||
- c. Otherwise, allow.
|
- `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 %}}
|
||||||
|
|
||||||
If type is `m.room.power_levels`:
|
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.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.
|
||||||
|
5. If the `sender`'s current membership state is not `join`, reject.
|
||||||
|
6. 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*.
|
||||||
|
7. If the event type's *required power level* is greater than the
|
||||||
|
`sender`'s power level, reject.
|
||||||
|
8. If the event has a `state_key` that starts with an `@` and does not
|
||||||
|
match the `sender`, reject.
|
||||||
|
9. 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`, `users`, and `notifications` 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.
|
||||||
|
10. Otherwise, allow.
|
||||||
|
|
||||||
- * For each entry being added, changed or removed in both the `events` and `users` keys:
|
{{% boxes/note %}}
|
||||||
+ * For each entry being added, changed or removed in the `events`, `users`, and `notifications` keys:
|
Some consequences of these rules:
|
||||||
|
|
||||||
i. If the current value is higher than the `sender`'s current power level, reject.
|
- Unless you are a member of the room, the only permitted operations
|
||||||
|
(apart from the initial create/join) are: joining a public room;
|
||||||
ii. If the new value is higher than the `sender`'s current power level, reject.
|
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.
|
||||||
The remaining rules are the same as in [room version
|
{{% /boxes/note %}}
|
||||||
3](/rooms/v3#authorization-rules-for-events) (the last inherited room
|
|
||||||
version to specify the authorization rules).
|
|
||||||
|
|
||||||
### Canonical JSON
|
### Canonical JSON
|
||||||
|
|
||||||
Servers MUST strictly enforce the JSON format specified in the
|
{{% rver-fragment name="v6-canonical-json" %}}
|
||||||
[appendices](/appendices#canonical-json). This translates to a
|
|
||||||
400 `M_BAD_JSON` error on most endpoints, or discarding of events over
|
## Unchanged from v5
|
||||||
federation. For example, the Federation API's `/send` endpoint would
|
|
||||||
discard the event whereas the Client Server API's `/send/{eventType}`
|
The following sections have not been modified since v5, but are included for
|
||||||
endpoint would return a `M_BAD_JSON` error.
|
completeness.
|
||||||
|
|
||||||
|
### State resolution
|
||||||
|
|
||||||
|
{{% rver-fragment name="v2-state-res" %}}
|
||||||
|
|
||||||
|
### Event format
|
||||||
|
|
||||||
|
{{% rver-fragment name="v4-event-explainer" %}}
|
||||||
|
|
||||||
|
{{% definition path="api/server-server/definitions/pdu_v4" %}}
|
||||||
|
|
||||||
|
### Handling redactions
|
||||||
|
|
||||||
|
{{% rver-fragment name="v3-handling-redactions" %}}
|
|
@ -12,6 +12,10 @@ as a possible join rule and membership state.
|
||||||
This is the first room version to support knocking completely. As such,
|
This is the first room version to support knocking completely. As such,
|
||||||
users will not be able to knock on rooms which are not based off v7.
|
users will not be able to knock on rooms which are not based off v7.
|
||||||
|
|
||||||
|
Though unchanged in this room version, clients which implement the
|
||||||
|
redaction algorithm locally should refer to the [redactions](#redactions)
|
||||||
|
section below for a full overview.
|
||||||
|
|
||||||
## Server implementation components
|
## Server implementation components
|
||||||
|
|
||||||
{{% boxes/warning %}}
|
{{% boxes/warning %}}
|
||||||
|
@ -26,27 +30,183 @@ Room version 7 adds new authorization rules for events to support knocking.
|
||||||
[Room version 6](/rooms/v6) has details of other authorization rule changes,
|
[Room version 6](/rooms/v6) has details of other authorization rule changes,
|
||||||
as do the versions v6 is based upon.
|
as do the versions v6 is based upon.
|
||||||
|
|
||||||
{{% added-in this=true %}} For checks perfomed upon `m.room.member` events, the following conditions
|
### Authorization rules
|
||||||
are added in context:
|
|
||||||
|
|
||||||
If type is `m.room.member`:
|
{{% added-in this=true %}} For checks perfomed upon `m.room.member` events, a
|
||||||
|
new point for `membership=knock` is added.
|
||||||
|
|
||||||
...
|
Events must be signed by the server denoted by the `sender` key.
|
||||||
|
|
||||||
* If `membership` is `ban`:
|
`m.room.redaction` events are not explicitly part of the auth rules.
|
||||||
|
They are still subject to the minimum power level rules, but should always
|
||||||
|
fall into "10. Otherwise, allow". Instead of being authorized at the time
|
||||||
|
of receipt, they are authorized at a later stage: see the
|
||||||
|
[Redactions](#redactions) section below for more information.
|
||||||
|
|
||||||
...
|
The types of state events that affect authorization are:
|
||||||
|
|
||||||
* If `membership` is `knock`:
|
- `m.room.create`
|
||||||
|
- `m.room.member`
|
||||||
|
- `m.room.join_rules`
|
||||||
|
- `m.room.power_levels`
|
||||||
|
- `m.room.third_party_invite`
|
||||||
|
|
||||||
i. If the `join_rule` is anything other than `knock`, reject.
|
{{% 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 %}}
|
||||||
|
|
||||||
ii. If `sender` does not match `state_key`, reject.
|
The rules are as follows:
|
||||||
|
|
||||||
iii. If the `sender`'s current membership is not `ban`, `invite`, or `join`, allow.
|
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.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. If `membership` is `knock`:
|
||||||
|
1. If the `join_rule` is anything other than `knock`, reject.
|
||||||
|
2. If `sender` does not match `state_key`, reject.
|
||||||
|
3. If the `sender`'s current membership is not `ban`, `invite`,
|
||||||
|
or `join`, allow.
|
||||||
|
7. Otherwise, the membership is unknown. Reject.
|
||||||
|
5. If the `sender`'s current membership state is not `join`, reject.
|
||||||
|
6. 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*.
|
||||||
|
7. If the event type's *required power level* is greater than the
|
||||||
|
`sender`'s power level, reject.
|
||||||
|
8. If the event has a `state_key` that starts with an `@` and does not
|
||||||
|
match the `sender`, reject.
|
||||||
|
9. 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`, `users`, and `notifications` 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.
|
||||||
|
10. Otherwise, allow.
|
||||||
|
|
||||||
iv. Otherwise, reject.
|
{{% 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 %}}
|
||||||
|
|
||||||
The remaining rules are the same as in [room version 6](/rooms/v6#authorization-rules-for-events).
|
## Unchanged from v6
|
||||||
|
|
||||||
|
The following sections have not been modified since v6, but are included for
|
||||||
|
completeness.
|
||||||
|
|
||||||
|
### State resolution
|
||||||
|
|
||||||
|
{{% rver-fragment name="v2-state-res" %}}
|
||||||
|
|
||||||
|
### Event format
|
||||||
|
|
||||||
|
{{% rver-fragment name="v4-event-explainer" %}}
|
||||||
|
|
||||||
|
{{% definition path="api/server-server/definitions/pdu_v4" %}}
|
||||||
|
|
||||||
|
### Canonical JSON
|
||||||
|
|
||||||
|
{{% rver-fragment name="v6-canonical-json" %}}
|
||||||
|
|
||||||
|
### Signing key validity period
|
||||||
|
|
||||||
|
{{% rver-fragment name="v5-signing-requirements" %}}
|
||||||
|
|
||||||
|
### Redactions
|
||||||
|
|
||||||
|
{{% rver-fragment name="v3-handling-redactions" %}}
|
||||||
|
|
||||||
|
{{% rver-fragment name="v6-redactions" %}}
|
||||||
|
|
25
layouts/shortcodes/rver-fragment.html
Normal file
25
layouts/shortcodes/rver-fragment.html
Normal file
|
@ -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…
Add table
Add a link
Reference in a new issue