Skip to content

Commit

Permalink
Room versions 8 and 9: Restricted rooms
Browse files Browse the repository at this point in the history
MSCs:
* #3083
* #3289
* #3375
  • Loading branch information
turt2live committed Sep 8, 2021
1 parent fa479af commit e650fca
Show file tree
Hide file tree
Showing 12 changed files with 388 additions and 27 deletions.
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).

`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 join
rule is only available to rooms based upon [room version 8](/rooms/v8).

The allowable state transitions of membership are:

![membership-flow-diagram](/diagrams/membership.png)
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
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).

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. This
validation is additionally done over federation when using a remote server
to join the room.

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.

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

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:

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`.
5. **[New in this room version]** If the `join_rule` is `restricted`:
1. If membership state is `join`, allow.
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
`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).

### Redactions

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).
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.

## 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
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,
the `m.room.member` event with `state_key` matching
`content.join_authorised_via_users_server`. Due to the
auth rules for the event, the target membership event should
always be eligible for inclusion.

#### 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`
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. 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 based on [v8](/rooms/v8).

A resident server attempting to join a server to 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 satisfies at least
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
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:
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.
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:
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.
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:
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"
}
}
responses:
Expand Down
Loading

0 comments on commit e650fca

Please sign in to comment.