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 <clokep@users.noreply.github.com>

* 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 <clokep@users.noreply.github.com>
Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com>
pull/977/head
Travis Ralston 3 years ago committed by GitHub
parent 3475ef62ab
commit 6c4aabd053
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

@ -0,0 +1 @@
Add Room Version 8 as per [MSC3289](https://github.com/matrix-org/matrix-doc/pull/3289).

@ -0,0 +1 @@
Add Room Version 9 as per [MSC3375](https://github.com/matrix-org/matrix-doc/pull/3375).

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

@ -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 request an invite to the room. Note that this join rule is only available
in room versions [which support knocking](/rooms/#feature-matrix). 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: The allowable state transitions of membership are:
![membership-flow-diagram](/diagrams/membership.png) ![membership-flow-diagram](/diagrams/membership.png)
@ -2033,6 +2039,51 @@ server chose to auto-accept.
{{% http-api spec="client-server" api="knocking" %}} {{% 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 #### Leaving rooms
A user can leave a room to stop receiving events for that room. A user A user can leave a room to stop receiving events for that room. A user

@ -36,9 +36,10 @@ Alternatively, consider flipping the column/row organization to be features
up top and versions on the left. up top and versions on the left.
--> -->
| Feature \ Version | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | Feature \ Version | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|-------------------|---|---|---|---|---|---|---| |-------------------|---|---|---|---|---|---|---|---|---|
| **Knocking** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | | **Knocking** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | ✔ | ✔ |
| **Restricted join rules** | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔ | ✔ |
## Complete list of room versions ## Complete list of room versions
@ -68,6 +69,10 @@ The available room versions are:
- [Version 6](/rooms/v6) - **Stable**. Alters several - [Version 6](/rooms/v6) - **Stable**. Alters several
authorization rules for events. authorization rules for events.
- [Version 7](/rooms/v7) - **Stable**. Introduces knocking. - [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 ## Room version grammar

@ -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 %}}

@ -199,6 +199,10 @@ completeness.
{{% rver-fragment name="v4-event-format" %}} {{% rver-fragment name="v4-event-format" %}}
### Handling redactions
{{% rver-fragment name="v3-handling-redactions" %}}
### Canonical JSON ### Canonical JSON
{{% rver-fragment name="v6-canonical-json" %}} {{% rver-fragment name="v6-canonical-json" %}}
@ -209,6 +213,4 @@ completeness.
### Redactions ### Redactions
{{% rver-fragment name="v3-handling-redactions" %}}
{{% rver-fragment name="v6-redactions" %}} {{% rver-fragment name="v6-redactions" %}}

@ -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" %}}

@ -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" %}}

@ -422,6 +422,10 @@ the following subset of the room state:
`third_party_invite` property, the current `third_party_invite` property, the current
`m.room.third_party_invite` event with `state_key` matching `m.room.third_party_invite` event with `state_key` matching
`content.third_party_invite.signed.token`, if any. `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 #### 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 `origin_server_ts`, and `event_id` on the templated event received by
the resident server. This event is then signed by the joining server. the resident server. This event is then signed by the joining server.
To complete the join handshake, the joining server must now submit this To complete the join handshake, the joining server submits this new event
new event to a resident homeserver, by using the `PUT /send_join` to the resident server it used for `GET /make_join`, using the `PUT /send_join`
endpoint. endpoint.
The resident homeserver then accepts this event into the room's event The resident homeserver then adds its signature to this event and
graph, and responds to the joining server with the full set of state for accepts it into the room's event graph. The joining server receives
the newly-joined room. The resident server must also send the event to the full set of state for the newly-joined room as well as the freshly
other servers participating in the room. 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-v1" %}}
{{% http-api spec="server-server" api="joins-v2" %}} {{% 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 ## Knocking upon a room
Rooms can permit knocking through the join rules, and if permitted this Rooms can permit knocking through the join rules, and if permitted this

@ -95,6 +95,7 @@ paths:
- The room is invite-only and the user was not invited. - The room is invite-only and the user was not invited.
- The user has been banned from the room. - The user has been banned from the room.
- The room is restricted and the user failed to satisfy any of the conditions.
examples: examples:
application/json: { application/json: {
"errcode": "M_FORBIDDEN", "error": "You are not invited to this room."} "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 room is invite-only and the user was not invited.
- The user has been banned from the room. - The user has been banned from the room.
- The room is restricted and the user failed to satisfy any of the conditions.
examples: examples:
application/json: { application/json: {
"errcode": "M_FORBIDDEN", "error": "You are not invited to this room."} "errcode": "M_FORBIDDEN", "error": "You are not invited to this room."}

@ -55,4 +55,14 @@ properties:
properties: [] properties: []
example: example:
$ref: "../examples/minimal_pdu.json" $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"] required: ["auth_chain", "state", "origin"]

@ -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"
}
}

@ -115,6 +115,18 @@ paths:
type: string type: string
description: The value `join`. description: The value `join`.
example: "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: ['membership']
required: required:
- state_key - state_key
@ -134,18 +146,37 @@ paths:
"origin_server_ts": 1549041175876, "origin_server_ts": 1549041175876,
"sender": "@someone:example.org", "sender": "@someone:example.org",
"content": { "content": {
"membership": "join" "membership": "join",
"join_authorised_via_users_server": "@anyone:resident.example.org"
} }
} }
} }
400: 400:
description: |- 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` 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 The error should be passed through to clients so that they
may give better feedback to users. 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: schema:
allOf: allOf:
- $ref: "../client-server/definitions/errors/error.yaml" - $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", "error": "Your homeserver does not support the features required to join this room",
"room_version": "3" "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: 404:
schema:
$ref: "../client-server/definitions/errors/error.yaml"
description: |- description: |-
The room that the joining server is attempting to join is unknown The room that the joining server is attempting to join is unknown
to the receiving server. to the receiving server.
@ -240,6 +284,23 @@ paths:
type: string type: string
description: The value `join`. description: The value `join`.
example: "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: ['membership']
required: required:
- state_key - state_key
@ -256,7 +317,8 @@ paths:
"origin_server_ts": 1549041175876, "origin_server_ts": 1549041175876,
"sender": "@someone:example.org", "sender": "@someone:example.org",
"content": { "content": {
"membership": "join" "membership": "join",
"join_authorised_via_users_server": "@anyone:resident.example.org"
} }
} }
responses: responses:
@ -278,6 +340,7 @@ paths:
{ {
"origin": "matrix.org", "origin": "matrix.org",
"auth_chain": [{"$ref": "examples/minimal_pdu.json"}], "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"}
} }
] ]

@ -103,6 +103,23 @@ paths:
type: string type: string
description: The value `join`. description: The value `join`.
example: "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: ['membership']
required: required:
- state_key - state_key
@ -119,10 +136,52 @@ paths:
"origin_server_ts": 1549041175876, "origin_server_ts": 1549041175876,
"sender": "@someone:example.org", "sender": "@someone:example.org",
"content": { "content": {
"membership": "join" "membership": "join",
"join_authorised_via_users_server": "@anyone:resident.example.org"
} }
} }
responses: 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: 200:
description: |- description: |-
The full state for the room, having accepted the join event. The full state for the room, having accepted the join event.
@ -132,5 +191,6 @@ paths:
application/json: { application/json: {
"origin": "matrix.org", "origin": "matrix.org",
"auth_chain": [{"$ref": "examples/minimal_pdu.json"}], "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"}
} }

@ -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"
}
]
}
}

@ -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
}
}

@ -2,15 +2,17 @@
allOf: allOf:
- $ref: core-event-schema/state_event.yaml - $ref: core-event-schema/state_event.yaml
description: | description: |
A room may be `public` meaning anyone can join the room without any prior action. A room may have one of the following designations:
Alternatively, it can be `invite` meaning that a user who wishes to join the room * `public` - anyone can join the room without any prior action.
must first receive an invite to the room from someone already inside of the room. * `invite` - a user must first receive an invite from someone already in the room
`knock` means that users are able to ask for permission to join the room, where in order to join.
they are either allowed (invited) or denied (kicked/banned) access. Join rules * `knock` - a user can request an invite to the room. They can be allowed (invited)
of `knock` are otherwise the same as `invite`: the user needs an explicit invite or denied (kicked/banned) access. Otherwise, users need to be invited in. Only
to join the room. available in rooms [which support knocking](/rooms/#feature-matrix).
* `restricted` - anyone able to satisfy at least one of the allow conditions is
Currently, `private` is a reserved keyword which is not implemented. 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: properties:
content: content:
properties: properties:
@ -21,7 +23,37 @@ properties:
- knock - knock
- invite - invite
- private - private
- restricted
type: string 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: required:
- join_rule - join_rule
type: object type: object

@ -63,6 +63,18 @@ properties:
is_direct: 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). 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 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: reason:
x-addedInMatrixVersion: "1.1" x-addedInMatrixVersion: "1.1"
type: string type: string

@ -1 +1 @@
<mxfile host="app.diagrams.net" modified="2021-04-28T19:35:50.494Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36" etag="-IOh23FjJiPnGlGWLseU" version="14.6.6" type="device"><diagram id="4a_pTli-mcEMNPq0ciXK" name="Page-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/</diagram></mxfile> <mxfile host="Electron" modified="2022-01-18T16:40:44.155Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.1.2 Chrome/96.0.4664.55 Electron/16.0.5 Safari/537.36" etag="XO6otxm1zp3-pSiWzgkD" version="16.1.2" type="device"><diagram id="4a_pTli-mcEMNPq0ciXK" name="Page-1">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==</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Loading…
Cancel
Save