From 6c4aabd05348a7fb7ed3135e79ea7013c3ac9297 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 18 Jan 2022 09:55:34 -0700 Subject: [PATCH] Room versions 8 and 9: Restricted rooms (#3387) * Room versions 8 and 9: Restricted rooms MSCs: * https://github.com/matrix-org/matrix-doc/pull/3083 * https://github.com/matrix-org/matrix-doc/pull/3289 * https://github.com/matrix-org/matrix-doc/pull/3375 * Changelogs * Capitalization Co-authored-by: Patrick Cloke * Remove verbiage for spaces because they don't exist * Iterations on text * Another clarification * Make error code descriptions consistent * Apply suggestions from code review Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Incorporate from merge * Misc language update per review * Update accuracy before splitting auth rules * fix wtf moment * Fix up v8 and v9 to match "fully specify room versions" * Scope auth events selection to room version * Apply consistency * Add changelogs * Review part 1 * Apply suggestions from code review Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> * Split out redaction sections * Clarify general case of join conditions Co-authored-by: Patrick Cloke Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- .../client_server/newsfragments/3387.feature | 1 + .../newsfragments/3387.feature.1 | 1 + .../newsfragments/3387.feature.2 | 1 + .../server_server/newsfragments/3387.feature | 1 + content/client-server-api/_index.md | 51 ++++++ content/rooms/_index.md | 11 +- content/rooms/fragments/v8-auth-rules.md | 160 ++++++++++++++++++ content/rooms/v7.md | 6 +- content/rooms/v8.md | 123 ++++++++++++++ content/rooms/v9.md | 119 +++++++++++++ content/server-server-api.md | 66 ++++++-- data/api/client-server/joining.yaml | 2 + .../definitions/send_join_response.yaml | 10 ++ .../examples/pdu_v4_join_membership.json | 28 +++ data/api/server-server/joins-v1.yaml | 73 +++++++- data/api/server-server/joins-v2.yaml | 64 ++++++- .../m.room.join_rules$restricted.yaml | 18 ++ ...mber$join_authorised_via_users_server.yaml | 12 ++ .../schema/m.room.join_rules.yaml | 50 +++++- data/event-schemas/schema/m.room.member.yaml | 12 ++ static/diagrams/membership.drawio | 2 +- static/diagrams/membership.png | Bin 28770 -> 31739 bytes 22 files changed, 772 insertions(+), 39 deletions(-) create mode 100644 changelogs/client_server/newsfragments/3387.feature create mode 100644 changelogs/room_versions/newsfragments/3387.feature.1 create mode 100644 changelogs/room_versions/newsfragments/3387.feature.2 create mode 100644 changelogs/server_server/newsfragments/3387.feature create mode 100644 content/rooms/fragments/v8-auth-rules.md create mode 100644 content/rooms/v8.md create mode 100644 content/rooms/v9.md create mode 100644 data/api/server-server/examples/pdu_v4_join_membership.json create mode 100644 data/event-schemas/examples/m.room.join_rules$restricted.yaml create mode 100644 data/event-schemas/examples/m.room.member$join_authorised_via_users_server.yaml diff --git a/changelogs/client_server/newsfragments/3387.feature b/changelogs/client_server/newsfragments/3387.feature new file mode 100644 index 000000000..34cc1ee43 --- /dev/null +++ b/changelogs/client_server/newsfragments/3387.feature @@ -0,0 +1 @@ +Add support for `restricted` rooms as per [MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083), [MSC3289](https://github.com/matrix-org/matrix-doc/pull/3289), and [MSC3375](https://github.com/matrix-org/matrix-doc/pull/3375). diff --git a/changelogs/room_versions/newsfragments/3387.feature.1 b/changelogs/room_versions/newsfragments/3387.feature.1 new file mode 100644 index 000000000..324d935ab --- /dev/null +++ b/changelogs/room_versions/newsfragments/3387.feature.1 @@ -0,0 +1 @@ +Add Room Version 8 as per [MSC3289](https://github.com/matrix-org/matrix-doc/pull/3289). diff --git a/changelogs/room_versions/newsfragments/3387.feature.2 b/changelogs/room_versions/newsfragments/3387.feature.2 new file mode 100644 index 000000000..7b1239ea6 --- /dev/null +++ b/changelogs/room_versions/newsfragments/3387.feature.2 @@ -0,0 +1 @@ +Add Room Version 9 as per [MSC3375](https://github.com/matrix-org/matrix-doc/pull/3375). diff --git a/changelogs/server_server/newsfragments/3387.feature b/changelogs/server_server/newsfragments/3387.feature new file mode 100644 index 000000000..34cc1ee43 --- /dev/null +++ b/changelogs/server_server/newsfragments/3387.feature @@ -0,0 +1 @@ +Add support for `restricted` rooms as per [MSC3083](https://github.com/matrix-org/matrix-doc/pull/3083), [MSC3289](https://github.com/matrix-org/matrix-doc/pull/3289), and [MSC3375](https://github.com/matrix-org/matrix-doc/pull/3375). diff --git a/content/client-server-api/_index.md b/content/client-server-api/_index.md index b4f783df5..92ba78311 100644 --- a/content/client-server-api/_index.md +++ b/content/client-server-api/_index.md @@ -1984,6 +1984,12 @@ This room can only be joined if you were invited, and allows anyone to request an invite to the room. Note that this join rule is only available in room versions [which support knocking](/rooms/#feature-matrix). +{{% added-in v="1.2" %}} `restricted` +This room can be joined if you were invited or if you are a member of another +room listed in the join rules. If the server cannot verify membership for any +of the listed rooms then you can only join with an invite. Note that this rule +is only expected to work in room versions [which support it](/rooms/#feature-matrix). + The allowable state transitions of membership are: ![membership-flow-diagram](/diagrams/membership.png) @@ -2033,6 +2039,51 @@ server chose to auto-accept. {{% http-api spec="client-server" api="knocking" %}} +##### Restricted rooms + +{{% added-in v="1.2" %}} + +Restricted rooms are rooms with a `join_rule` of `restricted`. These rooms +are accompanied by "allow conditions" as described in the +[`m.room.join_rules`](#mroomjoin_rules) state event. + +If the user has an invite to the room then the restrictions will not affect +them. They should be able to join by simply accepting the invite. + +When joining without an invite, the server MUST verify that the requesting +user meets at least one of the conditions. If no conditions can be verified +or no conditions are satisfied, the user will not be able to join. When the +join is happening over federation, the remote server will check the conditions +before accepting the join. See the [Server-Server Spec](/server-server-api/#restricted-rooms) +for more information. + +If the room is `restricted` but no valid conditions are presented then the +room is effectively invite only. + +The user does not need to maintain the conditions in order to stay a member +of the room: the conditions are only checked/evaluated during the join process. + +###### Conditions + +Currently there is only one condition available: `m.room_membership`. This +condition requires the user trying to join the room to be a *joined* member +of another room (specifically, the `room_id` accompanying the condition). For +example, if `!restricted:example.org` wanted to allow joined members of +`!other:example.org` to join, `!restricted:example.org` would have the following +`content` for [`m.room.join_rules`](#mroomjoin_rules): + +```json +{ + "join_rule": "restricted", + "allow": [ + { + "room_id": "!other:example.org", + "type": "m.room_membership" + } + ] +} +``` + #### Leaving rooms A user can leave a room to stop receiving events for that room. A user diff --git a/content/rooms/_index.md b/content/rooms/_index.md index 9efbde4c0..4c6faae14 100644 --- a/content/rooms/_index.md +++ b/content/rooms/_index.md @@ -36,9 +36,10 @@ Alternatively, consider flipping the column/row organization to be features up top and versions on the left. --> -| Feature \ Version | 1 | 2 | 3 | 4 | 5 | 6 | 7 | -|-------------------|---|---|---|---|---|---|---| -| **Knocking** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | +| Feature \ Version | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +|-------------------|---|---|---|---|---|---|---|---|---| +| **Knocking** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | ✔ | ✔ | +| **Restricted join rules** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | ✔ | ## Complete list of room versions @@ -68,6 +69,10 @@ The available room versions are: - [Version 6](/rooms/v6) - **Stable**. Alters several authorization rules for events. - [Version 7](/rooms/v7) - **Stable**. Introduces knocking. +- [Version 8](/rooms/v8) - **Stable**. Adds a join rule to allow members + of another room to join without invite. +- [Version 9](/rooms/v9) - **Stable**. Builds on v8 to fix issues when + redacting some membership events. ## Room version grammar diff --git a/content/rooms/fragments/v8-auth-rules.md b/content/rooms/fragments/v8-auth-rules.md new file mode 100644 index 000000000..2e9f39e4e --- /dev/null +++ b/content/rooms/fragments/v8-auth-rules.md @@ -0,0 +1,160 @@ +--- +toc_hide: true +--- + +Events must be signed by the server denoted by the `sender` key. + +`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: + +- `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.member`: + 1. If no `state_key` key or `membership` key in `content`, reject. + 2. If `content` has a `join_authorised_via_users_server` + key: + 1. If the event is not validly signed by the user ID denoted + by the key, reject. + 3. 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 `restricted`: + 1. If membership state is `join` or `invite`, allow. + 2. If the `join_authorised_via_users_server` key in `content` + is not a user with sufficient permission to invite other + users, reject. + 3. Otherwise, allow. + 6. If the `join_rule` is `public`, allow. + 7. Otherwise, reject. + 4. 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. + 5. 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. + 6. 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. + 7. 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. + 8. 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. + +{{% 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 %}} \ No newline at end of file diff --git a/content/rooms/v7.md b/content/rooms/v7.md index 17ec223bc..77775656d 100644 --- a/content/rooms/v7.md +++ b/content/rooms/v7.md @@ -199,6 +199,10 @@ completeness. {{% rver-fragment name="v4-event-format" %}} +### Handling redactions + +{{% rver-fragment name="v3-handling-redactions" %}} + ### Canonical JSON {{% rver-fragment name="v6-canonical-json" %}} @@ -209,6 +213,4 @@ completeness. ### Redactions -{{% rver-fragment name="v3-handling-redactions" %}} - {{% rver-fragment name="v6-redactions" %}} diff --git a/content/rooms/v8.md b/content/rooms/v8.md new file mode 100644 index 000000000..63c18717c --- /dev/null +++ b/content/rooms/v8.md @@ -0,0 +1,123 @@ +--- +title: Room Version 8 +type: docs +weight: 60 +--- + +This room version builds on [version 7](/rooms/v7) to introduce a new +join rule that allows members to join the room based on membership in +another room. + +{{% boxes/warning %}} +This room version is known to have issues relating to redactions of member +join events. [Room version 9](/rooms/v9) should be preferred over v8 when +creating rooms. +{{% /boxes/warning %}} + +## Client considerations + +Clients are encouraged to expose the option for the join rule in their +user interface for supported room versions. + +The new join rule, `restricted`, is described in the +[Client-Server API](/client-server-api/#restricted-rooms). + +Clients which implement the redaction algorithm locally should refer to the +[redactions](#redactions) section below for a full overview. + +### Redactions + +{{% added-in this=true %}} `m.room.join_rules` events now keep `allow` in addition to other +keys in `content` when being redacted. + +{{% boxes/warning %}} +[Room version 9](/rooms/v9) adds additional cases of protected properties for behaviour +related to restricted rooms (the functionality introduced in v8). v9 is preferred over +v8 when creating new rooms. +{{% /boxes/warning %}} + +The full redaction algorithm follows. + +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 keys `join_rule`, `allow`. +- `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`. + +## Server implementation components + +{{% boxes/warning %}} +The information contained in this section is strictly for server +implementors. Applications which use the Client-Server API are generally +unaffected by the intricacies contained here. The section above +regarding client considerations is the resource that Client-Server API +use cases should reference. +{{% /boxes/warning %}} + +Room version 8 adds a new join rule to allow members of a room to join another +room without invite. Otherwise, the room version inherits all properties of +[Room version 7](/rooms/v7). + +### Authorization rules + +{{% added-in this=true %}} For checks performed upon `m.room.member` events, new +points for handling `content.join_authorised_via_users_server` are added (Rule 4.2 +and 4.3.5). + +{{% rver-fragment name="v8-auth-rules" %}} + +### Redactions + +[See above](#redactions). + +## Unchanged from v7 + +The following sections have not been modified since v7, but are included for +completeness. + +### State resolution + +{{% rver-fragment name="v2-state-res" %}} + +### Event IDs + +{{% rver-fragment name="v4-event-ids" %}} + +### Event format + +{{% rver-fragment name="v4-event-format" %}} + +### Handling redactions + +{{% rver-fragment name="v3-handling-redactions" %}} + +### Canonical JSON + +{{% rver-fragment name="v6-canonical-json" %}} + +### Signing key validity period + +{{% rver-fragment name="v5-signing-requirements" %}} diff --git a/content/rooms/v9.md b/content/rooms/v9.md new file mode 100644 index 000000000..f61c9af99 --- /dev/null +++ b/content/rooms/v9.md @@ -0,0 +1,119 @@ +--- +title: Room Version 9 +type: docs +weight: 60 +--- + +This room version builds on [version 8](/rooms/v8) to add additional redaction +rules that were unintentionally missed when incorporating v8. + +## Client considerations + +See [room version 8](/rooms/v8) for specific details regarding the addition of +restricted rooms. + +Clients which implement the redaction algorithm locally should refer to the +[redactions](#redactions) section below for a full overview. + +### Redactions + +{{% added-in this=true %}} `m.room.member` now keep `join_authorised_via_users_server` +in addition to other keys in `content` when being redacted. + +{{% boxes/rationale %}} +Without the `join_authorised_via_users_server` property, redacted join events +can become invalid when verifying the auth chain of a given event, thus creating +a split-brain scenario where the user is able to speak from one server's +perspective but most others will continually reject their events. + +This can theoretically be worked around with a rejoin to the room, being careful +not to use the faulty events as `prev_events`, though instead it is encouraged +to use v9 rooms over v8 rooms to outright avoid the situation. + +[Issue #3373](https://github.com/matrix-org/matrix-doc/issues/3373) has further +information. +{{% /boxes/rationale %}} + +The full redaction algorithm follows. + +{{% rver-fragment name="v3-handling-redactions" %}} + +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 keys `membership`, `join_authorised_via_users_server`. +- `m.room.create` allows key `creator`. +- `m.room.join_rules` allows keys `join_rule`, `allow`. +- `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`. + +## Server implementation components + +{{% boxes/warning %}} +The information contained in this section is strictly for server +implementors. Applications which use the Client-Server API are generally +unaffected by the intricacies contained here. The section above +regarding client considerations is the resource that Client-Server API +use cases should reference. +{{% /boxes/warning %}} + +Room version 8 added a new `restricted` join rule to allow members of a room +to join another room without invite. Room version 9 is based upon v8 with the +following considerations. + +### Redactions + +[See above](#redactions). + +## Unchanged from v8 + +The following sections have not been modified since v8, but are included for +completeness. + +### State resolution + +{{% rver-fragment name="v2-state-res" %}} + +### Authorization rules + +{{% rver-fragment name="v8-auth-rules" %}} + +### Event IDs + +{{% rver-fragment name="v4-event-ids" %}} + +### Event format + +{{% rver-fragment name="v4-event-format" %}} + +### Handling redactions + +{{% rver-fragment name="v3-handling-redactions" %}} + +### Canonical JSON + +{{% rver-fragment name="v6-canonical-json" %}} + +### Signing key validity period + +{{% rver-fragment name="v5-signing-requirements" %}} diff --git a/content/server-server-api.md b/content/server-server-api.md index 1ba1f284c..df8e5603e 100644 --- a/content/server-server-api.md +++ b/content/server-server-api.md @@ -407,21 +407,25 @@ the sender permission to send the event. The `auth_events` for the `m.room.create` event in a room is empty; for other events, it should be the following subset of the room state: -- The `m.room.create` event. +- The `m.room.create` event. -- The current `m.room.power_levels` event, if any. +- The current `m.room.power_levels` event, if any. -- The sender's current `m.room.member` event, if any. +- The sender's current `m.room.member` event, if any. -- If type is `m.room.member`: +- If type is `m.room.member`: - - The target's current `m.room.member` event, if any. - - If `membership` is `join` or `invite`, the current - `m.room.join_rules` event, if any. - - If membership is `invite` and `content` contains a - `third_party_invite` property, the current - `m.room.third_party_invite` event with `state_key` matching - `content.third_party_invite.signed.token`, if any. + - The target's current `m.room.member` event, if any. + - If `membership` is `join` or `invite`, the current + `m.room.join_rules` event, if any. + - If membership is `invite` and `content` contains a + `third_party_invite` property, the current + `m.room.third_party_invite` event with `state_key` matching + `content.third_party_invite.signed.token`, if any. + - If `content.join_authorised_via_users_server` is present, + and the [room version supports restricted rooms](/rooms/#feature-matrix), + then the `m.room.member` event with `state_key` matching + `content.join_authorised_via_users_server`. #### Rejection @@ -744,19 +748,47 @@ The joining server is expected to add or replace the `origin`, `origin_server_ts`, and `event_id` on the templated event received by the resident server. This event is then signed by the joining server. -To complete the join handshake, the joining server must now submit this -new event to a resident homeserver, by using the `PUT /send_join` +To complete the join handshake, the joining server submits this new event +to the resident server it used for `GET /make_join`, using the `PUT /send_join` endpoint. -The resident homeserver then accepts this event into the room's event -graph, and responds to the joining server with the full set of state for -the newly-joined room. The resident server must also send the event to -other servers participating in the room. +The resident homeserver then adds its signature to this event and +accepts it into the room's event graph. The joining server receives +the full set of state for the newly-joined room as well as the freshly +signed membership event. The resident server must also send the event +to other servers participating in the room. {{% http-api spec="server-server" api="joins-v1" %}} {{% http-api spec="server-server" api="joins-v2" %}} +### Restricted rooms + +Restricted rooms are described in detail in the +[client-server API](/client-server-api/#restricted-rooms) and are available +in room versions [which support restricted join rules](/rooms/#feature-matrix). + +A resident server processing a request to join a restricted room must +ensure that the joining server satisfies at least one of the conditions +specified by `m.room.join_rules`. If no conditions are available, or none +match the required schema, then the joining server is considered to have +failed all conditions. + +The resident server uses a 400 `M_UNABLE_TO_AUTHORISE_JOIN` error on +`/make_join` and `/send_join` to denote that the resident server is unable +to validate any of the conditions, usually because the resident server +does not have state information about rooms required by the conditions. + +The resident server uses a 400 `M_UNABLE_TO_GRANT_JOIN` error on `/make_join` +and `/send_join` to denote that the joining server should try a different +server. This is typically because the resident server can see that the +joining user satisfies one of the conditions, though the resident server +would be unable to meet the auth rules governing `join_authorised_via_users_server` +on the resulting `m.room.member` event. + +If the joining server fails all conditions then a 403 `M_FORBIDDEN` error +is used by the resident server. + ## Knocking upon a room Rooms can permit knocking through the join rules, and if permitted this diff --git a/data/api/client-server/joining.yaml b/data/api/client-server/joining.yaml index 3a4b7fb2f..70e7444eb 100644 --- a/data/api/client-server/joining.yaml +++ b/data/api/client-server/joining.yaml @@ -95,6 +95,7 @@ paths: - The room is invite-only and the user was not invited. - The user has been banned from the room. + - The room is restricted and the user failed to satisfy any of the conditions. examples: application/json: { "errcode": "M_FORBIDDEN", "error": "You are not invited to this room."} @@ -182,6 +183,7 @@ paths: - The room is invite-only and the user was not invited. - The user has been banned from the room. + - The room is restricted and the user failed to satisfy any of the conditions. examples: application/json: { "errcode": "M_FORBIDDEN", "error": "You are not invited to this room."} diff --git a/data/api/server-server/definitions/send_join_response.yaml b/data/api/server-server/definitions/send_join_response.yaml index cfa379103..744324ba5 100644 --- a/data/api/server-server/definitions/send_join_response.yaml +++ b/data/api/server-server/definitions/send_join_response.yaml @@ -55,4 +55,14 @@ properties: properties: [] example: $ref: "../examples/minimal_pdu.json" + event: + type: object + title: SignedMembershipEvent + x-addedInMatrixVersion: "1.2" + description: |- + Required if the room version [supports restricted join rules](/rooms/#feature-matrix). The signed + copy of the membership event sent to other servers by the resident server, including the resident + server's signature. + example: + $ref: "../examples/minimal_pdu.json" required: ["auth_chain", "state", "origin"] diff --git a/data/api/server-server/examples/pdu_v4_join_membership.json b/data/api/server-server/examples/pdu_v4_join_membership.json new file mode 100644 index 000000000..1057ea940 --- /dev/null +++ b/data/api/server-server/examples/pdu_v4_join_membership.json @@ -0,0 +1,28 @@ +{ + "$ref": "unsigned_pdu_base.json", + "hashes": { + "sha256": "thishashcoversallfieldsincasethisisredacted" + }, + "signatures": { + "example.com": { + "ed25519:key_version": "these86bytesofbase64signaturecoveressentialfieldsincludinghashessocancheckredactedpdus" + }, + "resident.example.com": { + "ed25519:other_key_version": "a different signature" + } + }, + "auth_events": [ + "$urlsafe_base64_encoded_eventid", + "$a-different-event-id" + ], + "prev_events": [ + "$urlsafe_base64_encoded_eventid", + "$a-different-event-id" + ], + "type": "m.room.member", + "state_key": "@alice:example.com", + "content": { + "membership": "join", + "join_authorised_via_users_server": "@arbitrary:resident.example.com" + } +} diff --git a/data/api/server-server/joins-v1.yaml b/data/api/server-server/joins-v1.yaml index adf7298b6..3da50f450 100644 --- a/data/api/server-server/joins-v1.yaml +++ b/data/api/server-server/joins-v1.yaml @@ -115,6 +115,18 @@ paths: type: string description: The value `join`. example: "join" + join_authorised_via_users_server: + type: string + x-addedInMatrixVersion: "1.2" + description: |- + Required if the room is [restricted](/client-server-api/#restricted-rooms) + and is joining through one of the conditions available. If the + user is responding to an invite, this is not required. + + An arbitrary user ID belonging to the resident server in + the room being joined that is able to issue invites to other + users. This is used in later validation of the auth rules for + the `m.room.member` event. required: ['membership'] required: - state_key @@ -134,18 +146,37 @@ paths: "origin_server_ts": 1549041175876, "sender": "@someone:example.org", "content": { - "membership": "join" + "membership": "join", + "join_authorised_via_users_server": "@anyone:resident.example.org" } } } 400: description: |- - The request is invalid or the room the server is attempting + The request is invalid, the room the server is attempting to join has a version that is not listed in the `ver` - parameters. + parameters, or the server was unable to validate + [restricted room conditions](#restricted-rooms). The error should be passed through to clients so that they may give better feedback to users. + + New in `v1.2`, the following error conditions might happen: + + If the room is [restricted](/client-server-api/#restricted-rooms) + and none of the conditions can be validated by the server then + the `errcode` `M_UNABLE_TO_AUTHORISE_JOIN` must be used. This can + happen if the server does not know about any of the rooms listed + as conditions, for example. + + `M_UNABLE_TO_GRANT_JOIN` is returned to denote that a different + server should be attempted for the join. This is typically because + the resident server can see that the joining user satisfies one or + more conditions, such as in the case of + [restricted rooms](/client-server-api/#restricted-rooms), but the + resident server would be unable to meet the auth rules governing + `join_authorised_via_users_server` on the resulting `m.room.member` + event. schema: allOf: - $ref: "../client-server/definitions/errors/error.yaml" @@ -162,7 +193,20 @@ paths: "error": "Your homeserver does not support the features required to join this room", "room_version": "3" } + 403: + schema: + $ref: "../client-server/definitions/errors/error.yaml" + description: |- + The room that the joining server is attempting to join does not permit the user + to join. + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "You are not invited to this room", + } 404: + schema: + $ref: "../client-server/definitions/errors/error.yaml" description: |- The room that the joining server is attempting to join is unknown to the receiving server. @@ -240,6 +284,23 @@ paths: type: string description: The value `join`. example: "join" + join_authorised_via_users_server: + type: string + x-addedInMatrixVersion: "1.2" + description: |- + Required if the room is [restricted](/client-server-api/#restricted-rooms) + and is joining through one of the conditions available. If the + user is responding to an invite, this is not required. + + An arbitrary user ID belonging to the resident server in + the room being joined that is able to issue invites to other + users. This is used in later validation of the auth rules for + the `m.room.member` event. + + The resident server which owns the provided user ID must have a + valid signature on the event. If the resident server is receiving + the `/send_join` request, the signature must be added before sending + or persisting the event to other servers. required: ['membership'] required: - state_key @@ -256,7 +317,8 @@ paths: "origin_server_ts": 1549041175876, "sender": "@someone:example.org", "content": { - "membership": "join" + "membership": "join", + "join_authorised_via_users_server": "@anyone:resident.example.org" } } responses: @@ -278,6 +340,7 @@ paths: { "origin": "matrix.org", "auth_chain": [{"$ref": "examples/minimal_pdu.json"}], - "state": [{"$ref": "examples/minimal_pdu.json"}] + "state": [{"$ref": "examples/minimal_pdu.json"}], + "event": {"$ref": "examples/pdu_v4_join_membership.json"} } ] diff --git a/data/api/server-server/joins-v2.yaml b/data/api/server-server/joins-v2.yaml index b3ade8547..d4b909944 100644 --- a/data/api/server-server/joins-v2.yaml +++ b/data/api/server-server/joins-v2.yaml @@ -103,6 +103,23 @@ paths: type: string description: The value `join`. example: "join" + join_authorised_via_users_server: + type: string + x-addedInMatrixVersion: "1.2" + description: |- + Required if the room is [restricted](/client-server-api/#restricted-rooms) + and is joining through one of the conditions available. If the + user is responding to an invite, this is not required. + + An arbitrary user ID belonging to the resident server in + the room being joined that is able to issue invites to other + users. This is used in later validation of the auth rules for + the `m.room.member` event. + + The resident server which owns the provided user ID must have a + valid signature on the event. If the resident server is receiving + the `/send_join` request, the signature must be added before sending + or persisting the event to other servers. required: ['membership'] required: - state_key @@ -119,10 +136,52 @@ paths: "origin_server_ts": 1549041175876, "sender": "@someone:example.org", "content": { - "membership": "join" + "membership": "join", + "join_authorised_via_users_server": "@anyone:resident.example.org" } } responses: + 400: + description: |- + The request is invalid in some way. + + The error should be passed through to clients so that they + may give better feedback to users. + + New in `v1.2`, the following error conditions might happen: + + If the room is [restricted](/client-server-api/#restricted-rooms) + and none of the conditions can be validated by the server then + the `errcode` `M_UNABLE_TO_AUTHORISE_JOIN` must be used. This can + happen if the server does not know about any of the rooms listed + as conditions, for example. + + `M_UNABLE_TO_GRANT_JOIN` is returned to denote that a different + server should be attempted for the join. This is typically because + the resident server can see that the joining user satisfies one or + more conditions, such as in the case of + [restricted rooms](/client-server-api/#restricted-rooms), but the + resident server would be unable to meet the auth rules governing + `join_authorised_via_users_server` on the resulting `m.room.member` + event. + schema: + $ref: "../client-server/definitions/errors/error.yaml" + examples: + application/json: { + "errcode": "M_UNABLE_TO_GRANT_JOIN", + "error": "This server cannot send invites to you." + } + 403: + schema: + $ref: "../client-server/definitions/errors/error.yaml" + description: |- + The room that the joining server is attempting to join does not permit the user + to join. + examples: + application/json: { + "errcode": "M_FORBIDDEN", + "error": "You are not invited to this room", + } 200: description: |- The full state for the room, having accepted the join event. @@ -132,5 +191,6 @@ paths: application/json: { "origin": "matrix.org", "auth_chain": [{"$ref": "examples/minimal_pdu.json"}], - "state": [{"$ref": "examples/minimal_pdu.json"}] + "state": [{"$ref": "examples/minimal_pdu.json"}], + "event": {"$ref": "examples/pdu_v4_join_membership.json"} } diff --git a/data/event-schemas/examples/m.room.join_rules$restricted.yaml b/data/event-schemas/examples/m.room.join_rules$restricted.yaml new file mode 100644 index 000000000..bf807e54f --- /dev/null +++ b/data/event-schemas/examples/m.room.join_rules$restricted.yaml @@ -0,0 +1,18 @@ +{ + "$ref": "core/state_event.json", + "type": "m.room.join_rules", + "state_key": "", + "content": { + "join_rule": "restricted", + "allow": [ + { + "type": "m.room_membership", + "room_id": "!other:example.org" + }, + { + "type": "m.room_membership", + "room_id": "!elsewhere:example.org" + } + ] + } +} diff --git a/data/event-schemas/examples/m.room.member$join_authorised_via_users_server.yaml b/data/event-schemas/examples/m.room.member$join_authorised_via_users_server.yaml new file mode 100644 index 000000000..eb8c84bc8 --- /dev/null +++ b/data/event-schemas/examples/m.room.member$join_authorised_via_users_server.yaml @@ -0,0 +1,12 @@ +{ + "$ref": "m.room.member.yaml", + "content": { + "membership": "join", + "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF", + "displayname": "Alice Margatroid", + "join_authorised_via_users_server": "@bob:other.example.org" + }, + "unsigned": { + "age": 1234 + } +} diff --git a/data/event-schemas/schema/m.room.join_rules.yaml b/data/event-schemas/schema/m.room.join_rules.yaml index 5f0e11afe..dbac60d56 100644 --- a/data/event-schemas/schema/m.room.join_rules.yaml +++ b/data/event-schemas/schema/m.room.join_rules.yaml @@ -2,15 +2,17 @@ allOf: - $ref: core-event-schema/state_event.yaml description: | - A room may be `public` meaning anyone can join the room without any prior action. - Alternatively, it can be `invite` meaning that a user who wishes to join the room - must first receive an invite to the room from someone already inside of the room. - `knock` means that users are able to ask for permission to join the room, where - they are either allowed (invited) or denied (kicked/banned) access. Join rules - of `knock` are otherwise the same as `invite`: the user needs an explicit invite - to join the room. - - Currently, `private` is a reserved keyword which is not implemented. + A room may have one of the following designations: + * `public` - anyone can join the room without any prior action. + * `invite` - a user must first receive an invite from someone already in the room + in order to join. + * `knock` - a user can request an invite to the room. They can be allowed (invited) + or denied (kicked/banned) access. Otherwise, users need to be invited in. Only + available in rooms [which support knocking](/rooms/#feature-matrix). + * `restricted` - anyone able to satisfy at least one of the allow conditions is + able to join the room without prior action. Otherwise, an invite is required. + Only available in rooms [which support the join rule](/rooms/#feature-matrix). + * `private` - reserved without implementation. No significant meaning. properties: content: properties: @@ -21,7 +23,37 @@ properties: - knock - invite - private + - restricted type: string + allow: + x-addedInMatrixVersion: "1.2" + description: |- + For `restricted` rooms, the conditions the user will be tested against. The + user needs only to satisfy one of the conditions to join the `restricted` + room. If the user fails to meet any condition, or the condition is unable + to be confirmed as satisfied, then the user requires an invite to join the + room. Improper or no `allow` conditions on a `restricted` join rule imply + the room is effectively invite-only (no conditions can be satisfied). + type: array + items: + type: object + title: AllowCondition + properties: + type: + type: string + description: |- + The type of condition: + * `m.room_membership` - the user satisfies the condition if they are + joined to the referenced room. + enum: ['m.room_membership'] + room_id: + type: string + description: |- + Required if `type` is `m.room_membership`. The room ID to check the + user's membership against. If the user is joined to this room, they + satisfy the condition and thus are permitted to join the `restricted` + room. + required: ['type'] required: - join_rule type: object diff --git a/data/event-schemas/schema/m.room.member.yaml b/data/event-schemas/schema/m.room.member.yaml index 9427d25f0..7d196f4d2 100644 --- a/data/event-schemas/schema/m.room.member.yaml +++ b/data/event-schemas/schema/m.room.member.yaml @@ -63,6 +63,18 @@ properties: is_direct: description: Flag indicating if the room containing this event was created with the intention of being a direct chat. See [Direct Messaging](/client-server-api/#direct-messaging). type: boolean + join_authorised_via_users_server: + x-addedInMatrixVersion: "1.2" + type: string + description: |- + Usually found on `join` events, this field is used to denote which homeserver (through representation of a user with sufficient power level) + authorised the user's join. More information about this field can be found in the [Restricted Rooms Specification](#restricted-rooms). + + Client and server implementations should be aware of the [signing implications](/rooms/v8/#authorization-rules) of including this + field in further events: in particular, the event must be signed by the server which + owns the user ID in the field. When copying the membership event's `content` + (for profile updates and similar) it is therefore encouraged to exclude this + field in the copy, as otherwise the event might fail event authorization. reason: x-addedInMatrixVersion: "1.1" type: string diff --git a/static/diagrams/membership.drawio b/static/diagrams/membership.drawio index 1c6708deb..9eaa707b2 100644 --- a/static/diagrams/membership.drawio +++ b/static/diagrams/membership.drawio @@ -1 +1 @@ -3Vvdb6M4EP9rIt09NMIGA3ncZtvbh73TSn243X1ZOYmT0BIcOaZJ9q8/E0z4cggEgulVqoqHsbHHv/nwjDsyp5vDXwxv13/TBfFH0FgcRubnEYTAAa74E1GOMWViopiwYt5CMqWEF+83kURDUkNvQXY5Rk6pz71tnjinQUDmPEfDjNF9nm1J/fxXt3hFSoSXOfbL1H+9BV/HVBc6Kf0L8Vbr5MvAnsRvNjhhlivZrfGC7jMk82lkThmlPH7aHKbEj4SXyCXu93zh7XlijAS8TodvBvr96euj/xL83G+Dn2gaTvgDkMO8Yz+UKxYjvFIvkJPmx0QSZCEEI5uU8TVd0QD7Tyn1kdEwWJDoc4ZopTxfKd0KIhDEV8L5Ue4yDjkVpDXf+PJt/M3oQxfXKEk7GrI5qVqYxApmK8Ir+OB5JwSECd0Qzo6iHyM+5t57fh5YYml15pNdPzGGjxmGrZAf32VG/hYRBINUCziRmJBKYdqFrWvGLx7iGSStzFJS0gkOTaBhqaDhEyykogEb5ODx75nnH9FQY4hk8/NBDn1qHJNGIGTwPcMZtX9kX6b9Tq2kY4dIhKgmFC01FCUGjDEwHDMeqhk6S3Cy3DycoDUZ25kfx8qPGM9cDpLCrinqLVD4rG1Wot4CqIo/j/q0dzIdulzuCC/06EYzSorhBe8eL6tFHvT7teB52eITWvbCXaqM3zthnByqQXcRI9ApSBjJ9j51XcCQtHXGbSV8KgzlpNdYVHaFETEoG0VO6/nNm799cH8DO1JysRduOyW/P/4hVG3qDGuJGRK/YIxRxjOA29wC6NEt1ASMWQmYB2NsI6ulW+gBMSXAKEPMRvZySQP+jDeeH23OVIjbI5E5+Yfs72NMLUu7MXVVehcGujUvF5HVDMhAVu1kr34Uz2xnqWsrWautNks7rdrkQekLQoVDiqldX5R+KrY8hreMVhfOfG8+grYv5vI4E/KwV9FTxPOLhT7Z6XVp8Caf5ug66lg1VavtobsdKsq6Jca5EMH3sduXNq7HYKT2xqFr0QgaeigCnEEGr05W0426mq4tqVEXMBei1340vZzAUmevBuVGzWKuT7sbVUadGu1lO53RFnfCSWdG1rVdq52dPddnCgkjB+WHiFdVSv31arCNjx3F5RPW1ccjLXBraaLrIcuy6yGrs4yDsrCly82nHrtRhHdLbksHhK7mqFxogrsYLGTVg1XTWgUo1CqQWV2hs41Kfn21CqhMwA/Ae9+SNGp6tM3Fal+I/064N8cdK0lHZycRzN+iIU2BbVr5ohqYoIEAVXkuG0qlqGWV4eOitrpeJUy7A4ZffjAHFQzcEBf+L6B0NUpwEGoZJfRgpspJhbeAKozSoJMK5zBKW1IhuRXT8SWKvouC+m9YJEF6RpBPB06YMEknl1k8FP8R0JOmiN8N2cwI+7NC4qChxMsmpl1JqXCGVKXCoELY9r2EbSpzEdIAxMmIU+MBz4QwB5qQKJSVaiebu/E8r+Fmmyw0oAFJSPGsHV1JkKtxjuvCm3JuTQP0wt08YFUfPM+3EtT8Gi/Jle2S/pjrVug3O3ZmIY7ZvGNI171OejXcmvRy4ISTwoETXcmkwEr+ewBaNNNr+DF7+s8M5tN/ \ No newline at end of file +1Vvbbts4EP0aA7sPMUTq6sfGTdIusrvFGtht+lLQNm0rlUWDpnzp15eyKOvGeEnJkpwAQcTRkBKHZ2YOh8rAHK8PTxRtVn+SOQ4G0JgfBubHAYTABR7/E0uOQmJAkEiW1J8LWSaY+D9xqiikkT/H24IiIyRg/qYonJEwxDNWkCFKyb6otiBB8akbtMQVwWSGgqr0P3/OVonUg24m/4T95Sp9MnBGyZ01SpXFTLYrNCf7nMh8GJhjSghLrtaHMQ5i66V2Sfo9vnH3/GIUh0ylwxfD/vnh+T6YhN/2m/CbPY5G7A6IYXYoiMSM+QivxA/FS7Njagk854YRTULZiixJiIKHTHpPSRTOcfw4g7cynWdCNlwIuPAVM3YUq4wiRrhoxdaBuFudkni9LYnoDF+ah4AGokvMLujBRC+eS+4BwmBPmKwxo0euQHGAmL8rggAJLC3PeqLrB0rRMaew4fZj29zIX2IBVxB+AUcCE8IrTKe0dHr6/CJ5g7SVm0omOsFBBxqWDBoBRtwqPWADH3z2NXf9Eg81hLZofjyIoU+NY9oIuQ2+5jTj9kv+Ztbv1Eo71kcif44aFC1FKAoMGEMLABFb9NBZgZPlFeEErdHQyf24VnHEZCpikAx2uqi3QOmxjnkR9RawL+kXUZ/1Tl+HLBZbzEo9ruMZFcfww53Pqm5RBP1+xXUmG3SCz57nS8Xgt8OU4YMKRqBbsrAt2vssdQFDyFa5tJXqyTBUsJ62qZwLQcQgdBAnrccf/uzH+8o3sC0nN0aO18zJ28c/hLJFnaJeOEOaF4yhncsMoF5aAO2lBUXAmHqAuTOGjm2ZN4+YCmCkFFMrXi5IyB7R2g/ixRlz+/s4Did/4X07wdSyeg+mnszvorBvzyswMleRkYG836XdWnE988qxWtnNGi22WVlr2TLflMfYdmmbAnv3GGmmSmKP4S8G0An4K9xPuRmcZXyFgoDsuT2hMT1W78Ydv9MowNvqvd820TTwZwM4jjHEZr/3mwxhrWzodrRJshRd8urb9WZoqvokH+cN7t/Far+1cO3RGOWFs7V5jH3rJAa4N0h7a5ZDiskXtpd8VQGjynuv7ul/YMcJJn9/frr/5/Hfw/dnRl8+S5JvvEsNSbxNleaN0707NOWLq5U1+k0Sbj5JGIrQqRVrSuvacvCx/i/WwAJPubv50FMtxcrrsDdFB81y1bp3OijdP/WYv5s5YjGGtxfC4ai9pO85ntXM+c5HjaXSp2sXh0imWSlid0ogDBn+crsR47z9KOeJeDdivJvtR5GUXOYkXeDy2txCDYKWowbBqxXZpGe5ffHTjC9o0YU65dwOIKRflvWgCVqJbLalBivd4zlQOp6zzcuH0o5xUb+/4zkoPXO6gTSvERVr12QKpO4TDnaY+TPUzEna2vSfeXi7X1uYVvEcGYzsGwGqtKAgmH05wfd9VtrwnO3dgFjzxDY+gGsW5tvHmXlTxKAGR6z9GU+fQNKnDK5tN6QMHcSsailC1MXeUynizKl6K0WkufLKHxF1fSje/xdGKYXPGfLhwDDlMemUMCsb5ZCcPIX/rvF6iml1p5xZHGhaXCnEaByoljaUpsTYUGJspy1jm9IKRr4wbqhWwvtkMLBW3fs6mec1Wm/SiYYkxKkoeWu3o4qIPsvxPFirUqfL1kvfpgLr8i70/FWOXL/Hj0Srcal/0tUJ9PMIR3TWDNGqX1Prs61RJ5tPOCptPm29qkpJvw0882b2XyiJevbPPObDLw== \ No newline at end of file diff --git a/static/diagrams/membership.png b/static/diagrams/membership.png index 891b338b92a2307a0049aeb04e30781a8ded4386..586bf28ced248da753e87eb9c482c6b46d92e350 100644 GIT binary patch literal 31739 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 arL~JkS+X^yPJrHlByfeh@&&Sne*XdiKIv-!