-
Notifications
You must be signed in to change notification settings - Fork 434
MSC4155: Invite filtering #4155
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
base: main
Are you sure you want to change the base?
Changes from 37 commits
6adc165
43aef70
b8b226a
dcde420
04169f8
8c81fd5
d132a13
2345a5b
a558adb
a47dbce
e9478f0
29324e9
2f17aa7
3b248c5
230ed37
f916f98
44ecf7e
88fe9cc
be64f07
e526862
52c8bd5
0eee397
21521f0
643c5bc
5c3cab8
16dd841
e735ee8
f65b9b2
8c30b56
eb06849
f17d2ee
195199e
8d87122
64128c5
e36e71d
6fada45
8ed8f33
31bbbb6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,223 @@ | ||
| # MSC4155: Invite filtering | ||
|
|
||
| Matrix supports ignoring users via the ["Ignoring Users" module] and the `m.ignored_user_list` account data | ||
| event. This is a nuclear option though and will suppress both invites and room events from the ignored | ||
| users. Additionally, the `m.ignored_user_list` event only allows for block-list configurations that ignore | ||
| specific users but doesn't have a mechanism to ignore entire servers. These shortcomings make the module | ||
|
Johennes marked this conversation as resolved.
|
||
| insufficient for use cases such as tightly locked down applications where ignoring needs to be the default | ||
| behaviour. | ||
|
|
||
| This proposal generalises the ignoring users module to allow filtering invites specifically. The scheme | ||
| outlined below was conceptually borrowed from the [gematik specification]. | ||
|
|
||
|
|
||
| ## Proposal | ||
|
|
||
| To allow users to configure which other users are allowed to invite them into rooms, a new account data | ||
| event `m.invite_permission_config` is introduced. | ||
|
|
||
| ```json5 | ||
| { | ||
| "type": "m.invite_permission_config", | ||
| "content": { | ||
| // Global on/off toggle | ||
| "enabled": true, | ||
| // User-level settings | ||
| "allowed_users": [ "@john:goodguys.org", ... ], | ||
| "ignored_users": [ ... ], | ||
| "blocked_users": [ ... ], | ||
| // Server-level settings | ||
|
turt2live marked this conversation as resolved.
|
||
| "allowed_servers": [ ... ], | ||
| "ignored_servers": [ ... ], | ||
| "blocked_servers": [ "*" ] // A feature of all the fields at this level, globs | ||
| } | ||
|
Johennes marked this conversation as resolved.
|
||
| } | ||
| ``` | ||
|
|
||
| `enabled` is a boolean property and defaults to `true` if omitted. It provides clients with a convenience on/off | ||
| toggle that lets them deactivate the configuration without purging it. | ||
|
Comment on lines
+37
to
+38
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAICT,
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this maybe needs more clarification as to what exactly "deactivate the configuration" means. After discussion, and reading the associated thread, it seems like the intention is that
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also (h/t @clokep here), could we have a more descriptive name?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh! I had interpreted it exactly the opposite way. This is why the proposal text says
Upon re-reading the thread, I think your interpretation is correct though. 🤦♂️ Putting this into perspective with your concern about the UI in clients, I wonder if we should drop
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Half-Shot, @turt2live: as those arguing for
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Coming back to this months later, I think the block_all was intended for clients who didn't support the full filtering spec to still have a block/unblock toggle in settings. Element Web at the moment implements this by adding |
||
|
|
||
| All other properties in `content` are optional arrays. A missing property MUST be treated like an empty array. | ||
| Any value that is not an array of strings MUST lead to the entire event being considered invalid. | ||
|
|
||
| The array elements are [glob expressions]. Any `*_users` glob is to be matched against full user IDs (localpart | ||
|
richvdh marked this conversation as resolved.
|
||
| and domain). Any `*_servers` glob is to be matched against server names / domain parts of user IDs after | ||
| stripping any port suffix. This matches the way the globs from [server ACLs] are applied. | ||
|
Comment on lines
+44
to
+45
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this does not match the current implementation in Synapse, which matches the entire domain. |
||
|
|
||
| When evaluating an invite, implementations MUST first apply the existing `m.ignored_user_list` as per | ||
| the current spec. If the invite didn't match, implementations MUST then apply `m.invite_permission_config`. | ||
| The complete processing logic is as follows: | ||
|
|
||
| 1. Verify the invite against `m.ignored_user_list`: | ||
| 1. If it matches `ignored_users`, stop processing and ignore. | ||
| 2. Verify the invite against `m.invite_permission_config`: | ||
| 1. If `enabled` is `false`, stop processing and allow. | ||
| 2. If it matches `allowed_users`, stop processing and allow. | ||
| 3. If it matches `ignored_users`, stop processing and ignore. | ||
| 4. If it matches `blocked_users`, stop processing and block. | ||
| 5. If it matches `allowed_servers`, stop processing and allow. | ||
| 6. If it matches `ignored_servers`, stop processing and ignore. | ||
| 7. If it matches `blocked_servers`, stop processing and block. | ||
| 3. Otherwise, allow. | ||
|
|
||
| The semantics of "ignore" and "block" follow [MSC4283] which means ignoring hides the invite with no | ||
| feedback to the inviter whereas blocking rejects (or refuses, in the case of servers) the invite. | ||
|
Johennes marked this conversation as resolved.
|
||
|
|
||
| When blocking an inviter, the server MUST respond to the following endpoints with an error: | ||
|
|
||
| - `PUT /_matrix/federation/(v1|v2)/invite/{roomId}/{eventId}` | ||
| - `POST /_matrix/client/v3/rooms/{roomId}/invite` | ||
| - `POST /_matrix/client/v3/createRoom` (checking the `invite` list) | ||
| - `PUT /_matrix/client/v3/rooms/{roomId}/state/m.room.member/{stateKey}` (for invite membership) | ||
|
|
||
| The error SHOULD be `M_INVITE_BLOCKED` with a HTTP 403 status code. | ||
|
|
||
| When ignoring an invite, these endpoints MUST handle an invite normally as if accepted. However, the server | ||
| MUST NOT include the invite down client synchronization endpoints such as `GET /_matrix/client/v3/sync` or | ||
| MSC4186's sliding sync endpoint. In addition, these invites MUST be ignored when calculating push notifications. | ||
|
Johennes marked this conversation as resolved.
|
||
| For clarity, this means that the invited user will get a regular `invite` membership event in the target room | ||
| but will never be notified about that event, unless they modify their `m.invite_permission_config` such that the invite is no longer ignored. As with `m.ignored_user_list`, clients SHOULD perform an | ||
| initial `/sync` after relaxing their ignore configuration in order to receive potentially pending invites. | ||
|
|
||
| Otherwise, all other endpoints (such as `GET /_matrix/client/v3/rooms/{roomId}/state`) should work as before. | ||
|
|
||
| Servers are not expected to process these rules for appservice users when calculating events to send down | ||
| `PUT /_matrix/app/v1/transactions`. Appservices are not expected to be run directly by client users, and | ||
| should be able to handle their own spam prevention. | ||
|
|
||
| The semantics and order of evaluation enable a number of use cases, for instance: | ||
|
|
||
| ```json5 | ||
| // Invites from everyone | ||
| { | ||
| "type": "m.invite_permission_config", | ||
| "content": { } | ||
| } | ||
|
|
||
| // No invites at all | ||
| { | ||
| "type": "m.invite_permission_config", | ||
| "content": { | ||
| "blocked_servers": [ "*" ] | ||
| } | ||
| } | ||
|
|
||
| // Only invites from goodguys.org | ||
| { | ||
| "type": "m.invite_permission_config", | ||
| "content": { | ||
| "allowed_servers": [ "goodguys.org" ], | ||
| "blocked_servers": [ "*" ] | ||
| } | ||
| } | ||
|
|
||
| // Invites from everyone except badguys.org | ||
| { | ||
| "type": "m.invite_permission_config", | ||
| "content": { | ||
| "blocked_servers": [ "badguys.org" ] | ||
| } | ||
| } | ||
|
|
||
|
Johennes marked this conversation as resolved.
|
||
| // Only invites from goodguys.org except for @notactuallyguy:goodguys.org | ||
| { | ||
| "type": "m.invite_permission_config", | ||
| "content": { | ||
| "blocked_users": [ "@notactuallyguy:goodguys.org" ], | ||
| "allowed_servers": [ "goodguys.org" ] | ||
| "blocked_servers": [ "*" ] | ||
| } | ||
| } | ||
|
|
||
| // No invites from badguys.org unless it's @goodguy:badguys.org | ||
| { | ||
| "type": "m.invite_permission_config", | ||
| "content": { | ||
| "allowed_users": [ "@goodguy:badguys.org" ], | ||
| "blocked_servers": [ "badguys.org" ] | ||
| } | ||
| } | ||
|
|
||
| // Only invites from goodguys.org and don't provide feedback to reallybadguys.org | ||
| { | ||
| "type": "m.invite_permission_config", | ||
| "content": { | ||
| "allowed_servers": [ "goodguys.org" ], | ||
| "ignored_servers": [ "reallybadguys.org" ], | ||
| "blocked_servers": [ "*" ] | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
|
turt2live marked this conversation as resolved.
|
||
| Servers MUST enforce `m.invite_permission_config` against incoming new invites. Additionally, Servers | ||
| SHOULD apply the config against existing pending invites as well. | ||
|
|
||
|
|
||
| ## Potential issues | ||
|
Johennes marked this conversation as resolved.
|
||
|
|
||
| Enforcing the permission configuration exclusively on the server means users have no way to review | ||
| processed invites. This is desirable in most cases as a spam protection measure. It does mean, however, | ||
| that if the user has accidentally blocked a good actor and is informed about it through a different | ||
| communication channel, they'll have to update their permission configuration and request a re-invite. | ||
|
|
||
| The multitude of properties in `m.invite_permission_config` offers powerful configuration options. However, | ||
| making changes in complex configurations can lead to side effects that may be unanticipated for users. For | ||
| example, if the user already allowed `@alice:example.org` to send them invites and then adds a block for | ||
| `example.org` as a server, Alice can still send them invites which might be unexpected. To mitigate this, | ||
| clients that expose more advanced configuration interfaces SHOULD inform users about the impact of their | ||
| changes. | ||
|
|
||
|
turt2live marked this conversation as resolved.
|
||
|
|
||
| ## Alternatives | ||
|
|
||
| Instead of introducing a separate account data event, the existing `m.ignored_user_list` could have | ||
| been expanded. This would, however, not only affect invites but also events in existing rooms which | ||
| makes it a much more nuclear option. Additionally, the existing schema of `m.ignored_user_list` | ||
| complicates morphing it into something that optionally supports allow-list semantics. | ||
|
|
||
| Regarding `m.invite_permission_config`, the split between user settings and server settings is | ||
| technically not needed because glob expressions are powerful enough to allow matching either. | ||
| Splitting them is more explicit and prevents unintended globbing mistakes, however. The fact that | ||
| a user glob and a server glob can overlap does not seem problematic because this proposal includes | ||
| a deterministic processing order for all settings. | ||
|
Comment on lines
+180
to
+182
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's absolutely a problem, because presenting a meaningful UX for multiple overlapping settings is very difficult. AFAICT, nobody has yet done so, which does little to persuade me it's possible. |
||
|
|
||
| A previous version of this proposal included a base setting of block / allow all with user and | ||
| server exceptions applied on top. In this scheme, flipping the base setting also inverts the semantics | ||
| of all exceptions, however, which makes changing the configuration quite complicated. | ||
|
richvdh marked this conversation as resolved.
|
||
|
|
||
| A comprehensive comparison of existing invite filtering proposals may be found in [MSC4192]. The | ||
| present proposal is functionally inferior to some of the alternatives outline there. It does, for | ||
| instance, not cover the change history of the permission config or sharing the config among different | ||
| users. The proposal is, however, a straightforward and easy to implement extension of the existing | ||
| `m.ignored_user_list` mechanism. See also [this comment] for further details. This proposal is additionally | ||
| extensible for further types of blocking in the future. For example, a future MSC could create definitions | ||
| and behaviours to block/ignore/allow invites from contacts, of a particular type (DM, space, etc), | ||
| to a particular room, or even with given keywords. | ||
|
|
||
|
|
||
| ## Security considerations | ||
|
|
||
| None. | ||
|
|
||
|
|
||
| ## Unstable prefix | ||
|
|
||
| Until this proposal is accepted into the spec, implementations should refer to `m.invite_permission_config` | ||
| and `m.invite_permission_config_enforced` as `org.matrix.msc4155.invite_permission_config` and | ||
| `org.matrix.msc4155.invite_permission_config_enforced`, respectively. Note that the [gematik specification], | ||
| which predates this MSC, uses an event type of `de.gematik.tim.account.permissionconfig.v1` and | ||
| a different event schema. | ||
|
|
||
| The error `M_INVITE_BLOCKED` should be `ORG.MATRIX.MSC4155.M_INVITE_BLOCKED` until this proposal is accepted into the spec. | ||
|
Johennes marked this conversation as resolved.
|
||
| ## Dependencies | ||
|
|
||
| This proposal loosely depends on [MSC4283] for the semantics of "ignore" and "block". | ||
|
|
||
|
|
||
| [gematik specification]: https://github.com/gematik/api-ti-messenger/blob/9b9f21b87949e778de85dbbc19e25f53495871e2/src/schema/permissionConfig.json | ||
| [glob expressions]: https://spec.matrix.org/v1.14/appendices/#glob-style-matching | ||
| [MSC4192]: https://github.com/matrix-org/matrix-spec-proposals/pull/4192 | ||
| [MSC4283]: https://github.com/matrix-org/matrix-spec-proposals/pull/4283 | ||
| ["Ignoring Users" module]: https://spec.matrix.org/v1.10/client-server-api/#ignoring-users | ||
| [this comment]: https://github.com/matrix-org/matrix-spec-proposals/pull/4192#discussion_r2025188127 | ||
| [server ACLs]: https://spec.matrix.org/v1.15/client-server-api/#mroomserver_acl | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation requirements:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since I believe all the gematik implementations will be closed source, I'll reference #3860 (comment) as an example for how cases like this were handled in the past. Thanks @clokep for digging it up.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
element-hq/synapse#18288 is a serverside implementation, albeit with #4155 (comment) "corrected"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
element-hq/element-web#29603 exposes the serverside settings in the client, but does no filtering of itself. @Johennes does this evoke any worries from you if the invite filtering is done server-side?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I this is fine and consistent with the proposal which allows but doesn't enforce the filtering on either the client or the server. Now that I think of it, we might need a capability so that the client knows when the server does not filter in which case the client needs to filter itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The gematik implementations are based on the initial version of this proposal where we had a base setting of either "allow all" or "block all" and exceptions for users and servers applied on top of it. It also didn't include any globbing.
FWIW, this is how my own health insurance has implemented it:
Main screen with radio buttons for the base setting at the top ("Alle" = "(Allow) everyone" / "Niemand" = "(Allow) noone"). Below that are two buttons to add exceptions for users ("Benutzer") and servers.
For adding user exceptions, they pop up a bottom sheet with a few options ("Teilnehmende hinzufügen"). The first two are for obtaining the MXID by searching gematik-specific directories. The third option scans the MXID from a QR code. And the last one lets you input it manually.
For server exceptions, the only option is to enter the server name manually.
Once entered, the exceptions show in a list at the bottom of the main screen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @Johennes, that's extremely helpful. I'm concerned, however, that it only (appears to) offer controls for
blocked_usersandblocked_servers(i.e, it does not exposeallowed_users,ignored_users,allowed_serversorignored_servers), and nor does it expose globbing.It is the interplay between these settings that I think makes this proposal complicated to implement in a client, and my concern remains that we don't yet have a viable client implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the gematik implementations are based on the initial version of this MSC which used the following scheme:
I think the only apparent UX problem this has is that when you flip
default, your previous exceptions suddenly apply the other way around.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose a client can help manage that UX problem by clearing
user_exceptionsandserver_exceptionsat the same time as changingdefault? That doesn't feel too bad to me, tbh.(I'm sorry you've been sent so far around the houses on this, @Johennes. For now I'm going to focus on landing something in the spec, and then we can come back here and see if we can figure out a plausible way forward.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries at all. 👍