Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Room versions 8 and 9: Restricted rooms #3387

Merged
merged 24 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e650fca
Room versions 8 and 9: Restricted rooms
turt2live Sep 8, 2021
7a5e820
Changelogs
turt2live Sep 8, 2021
3769724
Capitalization
turt2live Sep 8, 2021
f7f2ea8
Remove verbiage for spaces because they don't exist
turt2live Sep 8, 2021
2ccae80
Iterations on text
turt2live Sep 8, 2021
e3b7735
Merge remote-tracking branch 'origin/travis/spec/v8-v9' into travis/s…
turt2live Sep 8, 2021
db2a738
Another clarification
turt2live Sep 8, 2021
c613d2e
Make error code descriptions consistent
turt2live Sep 24, 2021
d521f25
Merge remote-tracking branch 'origin/main' into travis/spec/v8-v9
turt2live Sep 24, 2021
b31298d
Apply suggestions from code review
turt2live Sep 28, 2021
9c18649
Merge branch 'main' into travis/spec/v8-v9
turt2live Dec 28, 2021
b04da31
Incorporate from merge
turt2live Dec 28, 2021
17954df
Misc language update per review
turt2live Dec 28, 2021
75fc992
Update accuracy before splitting auth rules
turt2live Dec 28, 2021
44fc526
fix wtf moment
turt2live Dec 28, 2021
3447b12
Fix up v8 and v9 to match "fully specify room versions"
turt2live Dec 28, 2021
a8fa47f
Scope auth events selection to room version
turt2live Dec 28, 2021
157f750
Apply consistency
turt2live Dec 29, 2021
42195ca
Add changelogs
turt2live Dec 29, 2021
56bf4a4
Review part 1
turt2live Jan 10, 2022
245cc17
Apply suggestions from code review
turt2live Jan 10, 2022
fd82238
Split out redaction sections
turt2live Jan 10, 2022
cc65d8a
Clarify general case of join conditions
turt2live Jan 18, 2022
e58bc1b
Update diagram
turt2live Jan 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelogs/client_server/newsfragments/3387.feature
Original file line number Diff line number Diff line change
@@ -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).
1 change: 1 addition & 0 deletions changelogs/server_server/newsfragments/3387.feature
Original file line number Diff line number Diff line change
@@ -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).
4 changes: 4 additions & 0 deletions content/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,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.

## Specification Versions

Expand Down
30 changes: 30 additions & 0 deletions content/client-server-api/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1734,6 +1734,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
to rooms based upon [room version 7](/rooms/v7).
turt2live marked this conversation as resolved.
Show resolved Hide resolved

`restricted`
This room can be joined if you were invited or if you are a member of another
turt2live marked this conversation as resolved.
Show resolved Hide resolved
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 join
rule is only available to rooms based upon [room version 8](/rooms/v8).
turt2live marked this conversation as resolved.
Show resolved Hide resolved

The allowable state transitions of membership are:

![membership-flow-diagram](/diagrams/membership.png)
turt2live marked this conversation as resolved.
Show resolved Hide resolved
turt2live marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -1781,6 +1787,30 @@ server chose to auto-accept.

{{% http-api spec="client-server" api="knocking" %}}

##### Restricted rooms

Restricted rooms are rooms with a `join_rule` of `restricted`. These rooms
turt2live marked this conversation as resolved.
Show resolved Hide resolved
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.

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).
turt2live marked this conversation as resolved.
Show resolved Hide resolved

When joining without an invite, the server MUST verify that the requesting
turt2live marked this conversation as resolved.
Show resolved Hide resolved
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. This
validation is additionally done over federation when using a remote server
to join the room.
turt2live marked this conversation as resolved.
Show resolved Hide resolved

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.
turt2live marked this conversation as resolved.
Show resolved Hide resolved

#### Leaving rooms

A user can leave a room to stop receiving events for that room. A user
Expand Down
2 changes: 2 additions & 0 deletions content/rooms/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ weight: 60
* [Room Version 5](v5)
* [Room Version 6](v6)
* [Room Version 7](v7)
* [Room Version 8](v8)
* [Room Version 9](v9)
79 changes: 79 additions & 0 deletions content/rooms/v8.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
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. Specifically, this feature
is intended to be used primarily in conjunction with
[MSC1772-style Spaces](https://github.com/matrix-org/matrix-doc/pull/1772)
(allowing members of a space to join a given room), though any v8 capable
room will be able to support this newly introduced join rule.
turt2live marked this conversation as resolved.
Show resolved Hide resolved

The new join rule, `restricted`, is described in the Client-Server API
under the [`m.room.join_rules`](/client-server-api/#mroomjoin_rules) section.

## 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 for events

`m.room.member` events for `membership` of `join` are now validated as such:
turt2live marked this conversation as resolved.
Show resolved Hide resolved

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
`knock`.
turt2live marked this conversation as resolved.
Show resolved Hide resolved
5. **[New in this room version]** If the `join_rule` is `restricted`:
1. If membership state is `join`, allow.
turt2live marked this conversation as resolved.
Show resolved Hide resolved
2. If `content.join_authorised_via_users_server` is not a user with
sufficient permission to invite other users, reject.
3. If the event is not validly signed by the server denoted by the user ID in
turt2live marked this conversation as resolved.
Show resolved Hide resolved
`content.join_authorised_via_users_server`, reject.
4. Otherwise, allow.
6. If the `join_rule` is `public`, allow.
7. Otherwise, reject.

The remaining rules are the same as in [room version 7](/rooms/v7#server-implementation-components).
turt2live marked this conversation as resolved.
Show resolved Hide resolved

### Redactions
turt2live marked this conversation as resolved.
Show resolved Hide resolved

Events of type `m.room.join_rules` now keep the following `content` properties
when the event is redacted:
* `join_rule`
* **[New in this room version]** `allow`

{{% 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 remaining rules are the same as in [room version 6](/rooms/v6#redactions) (the
last room version to modify the redaction rules).
turt2live marked this conversation as resolved.
Show resolved Hide resolved
52 changes: 52 additions & 0 deletions content/rooms/v9.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
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 a local redaction algorithm are encouraged to read on.
clokep marked this conversation as resolved.
Show resolved Hide resolved

## 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

Events of type `m.room.member` now keep the following `content` properties
when the event is redacted:
* `membership`
* **[New in this room version]** `join_authorised_via_users_server`

The remaining rules are the same as in [room version 8](/rooms/v8#redactions).

{{% boxes/rationale %}}
Without the `join_authorised_via_users_server` property redacted join events
turt2live marked this conversation as resolved.
Show resolved Hide resolved
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 %}}
61 changes: 46 additions & 15 deletions content/server-server-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,21 +407,26 @@ 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,
turt2live marked this conversation as resolved.
Show resolved Hide resolved
the `m.room.member` event with `state_key` matching
`content.join_authorised_via_users_server`. Due to the
turt2live marked this conversation as resolved.
Show resolved Hide resolved
auth rules for the event, the target membership event should
always be eligible for inclusion.
turt2live marked this conversation as resolved.
Show resolved Hide resolved

#### Rejection

Expand Down Expand Up @@ -721,15 +726,41 @@ To complete the join handshake, the joining server must now submit this
new event to a resident homeserver, by using the `PUT /send_join`
turt2live marked this conversation as resolved.
Show resolved Hide resolved
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
turt2live marked this conversation as resolved.
Show resolved Hide resolved
accepts it into the room's event graph. The joining server receives
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.
turt2live marked this conversation as resolved.
Show resolved Hide resolved

{{% 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 based on [v8](/rooms/v8).
turt2live marked this conversation as resolved.
Show resolved Hide resolved

A resident server attempting to join a server to a restricted room must
turt2live marked this conversation as resolved.
Show resolved Hide resolved
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 satisfies at least
one of the conditions, though the resident server would be unable to
turt2live marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
2 changes: 2 additions & 0 deletions data/api/client-server/joining.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,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."}
Expand Down Expand Up @@ -180,6 +181,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."}
Expand Down
53 changes: 51 additions & 2 deletions data/api/server-server/joins-v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ paths:
type: string
description: The value `join`.
example: "join"
join_authorised_via_users_server:
turt2live marked this conversation as resolved.
Show resolved Hide resolved
type: string
description: |-
Required if the room is [restricted](/client-server-api/#restricted-rooms).
turt2live marked this conversation as resolved.
Show resolved Hide resolved
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
Expand All @@ -134,7 +142,8 @@ paths:
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "join"
"membership": "join",
"join_authorised_via_users_server": "@anyone:resident.example.org"
}
}
}
Expand All @@ -146,6 +155,19 @@ paths:

turt2live marked this conversation as resolved.
Show resolved Hide resolved
The error should be passed through to clients so that they
may give better feedback to users.

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.

If the room is [restricted](/client-server-api/#restricted-rooms)
and the user meets at least one of the conditions, but the server
does not have permission to send invites (a prerequisite for later
authorisation of the `m.room.member` event) then an `errcode` of
`M_UNABLE_TO_GRANT_JOIN` is returned. The joining server should
attempt another resident server.
turt2live marked this conversation as resolved.
Show resolved Hide resolved
schema:
allOf:
- $ref: "../client-server/definitions/errors/error.yaml"
Expand All @@ -162,7 +184,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.
Expand Down Expand Up @@ -240,6 +275,19 @@ paths:
type: string
description: The value `join`.
example: "join"
join_authorised_via_users_server:
turt2live marked this conversation as resolved.
Show resolved Hide resolved
type: string
description: |-
Required if the room is [restricted](/client-server-api/#restricted-rooms).
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
Expand All @@ -256,7 +304,8 @@ paths:
"origin_server_ts": 1549041175876,
"sender": "@someone:example.org",
"content": {
"membership": "join"
"membership": "join",
"join_authorised_via_users_server": "@anyone:resident.example.org"
turt2live marked this conversation as resolved.
Show resolved Hide resolved
}
}
responses:
Expand Down