-
Notifications
You must be signed in to change notification settings - Fork 411
[WIP] MSC2812: Role-based power/permissions #2812
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: old_master
Are you sure you want to change the base?
Changes from all commits
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,375 @@ | ||||
# MSC2812: Role-based power structures | ||||
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. So, soru has been asked to comment on the concept of permission roles and why she personally thinks that Now, to start off, permission roles are more flexible. While power levels allow you to finely grain In sorus opinion permission roles only really start to shine when gouverning multiple rooms at once, So, why is the added flexibility so good? It is sadly hard to pinpoint one big thing that just There are often such kind of things where the extra flexibility is needed outside of safespaces, too.
#2962 actually already saw the need of something like permission roles: Access to a room is automatically Furthermore, most people want to give people a specific permission to do something. They don't want Of course one has to think about how to make permission roles work well across an entire space, what For the simple room where there is only really admin, moderator and everyone else needed, a set of 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'd actually like to bring up another argument for permission-based access control as a concept, that's potentially a much stronger argument for Matrix: permission-based access control is generic over nearly every other form of access control. That is, you can express every common access control mechanism using a sufficiently complete set of permissions. This is especially important for Matrix due to bridging; it is currently often difficult to 1:1 map between Matrix's access control system and that of the bridged platform, since bridged platforms do not always have a strictly linear access control system. Subtly tweaking the powerlevel system could only solve this issue for specific other access control systems. Permissions would resolve this by making arbitrary other access control systems representable, at the potential cost of some state event size overhead, as the permission-based representation can get pretty expansive for complex access systems - you'd essentially be 'unpacking' or 'evaluating' the other platform's access control algorithm into a full set of individual permissions.
The alternative proposal I'm working on (incomplete draft, MSC in progress), should be able to deal with a permission-based system just fine as far as I can tell. 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 find the idea of "permission-roles-as-rooms" interesting and came to post that here: In light of #3083, it would be interesting to be able to specify for each permission, one (or more) rooms whose members would inherit the permission. 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. @uranuspucksaxophone says:
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. Hi @turt2live thanks for the quick answer. Why convert that into a separate comment? 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. (there's no reply - just moved it to a thread) Comments on MSCs which aren't in threads are not considered part of the discussion, so when we can we gently move such comments into threads for consideration. 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. @turt2live Do you think my idea could work? I just came up with it today 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 honestly haven't read it, but it's a threaded comment now so it'll be considered when this MSC ends up back on my list of things to look at :) 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. Hybrid system seems unclear/complex. 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. ftr, the hybrid model actually fixes a security issue, and isn't that complex. It's what Discord does. Edit: see #4056 |
||||
|
||||
**Caution to the reader**: this is presently an information dump from my thoughts on how this | ||||
could work. It needs a lot of validation and work before it's ready as a proper proposal. | ||||
|
||||
Currently Matrix operates off a power level structure where higher numbers have more power in a | ||||
room and lower numbers (with zero being a typical default) have the least power. This structure | ||||
can be used to represent a number of systems and can allow for a form of roles (moderator, admin, | ||||
etc) to be represented, though can be challenging to bridge to other platforms. | ||||
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 have yet to ever come up against the need to use roles in native Matrix. To try to gauge how to prioritise the MSC, it'd be really useful to have a real life bridging example - i.e. of a permissions bridge that can't be represented in Matrix today. 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. Discord is the largest example, though this needs to be codified into the MSC properly. Other examples (which imo are weaker arguments) are familiarity and ease of use - trying to do the mental math for whether or not you're about to give someone the power to destroy the room is often times difficult or requires a notepad (when trying to do non-standard, complex, things). 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. Telegram is a less obvious example, but their permissions are also not linear. There are 6 admin permissions (change group info, delete messages, ban users, add users, pin messages, add new admins) and they can be set completely arbitrarily for each user. Right now it's impossible to bridge permissions. With this proposal, the permissions could be bridged, although the bridge would need to invent a role name for every combination. 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. Discord's permission system is a patchwork, though. Its role hierarchy is bolted-on, the override ordering is only partially intuitive (some orderings are additive, some are hierarchical). 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 could also attract business users - being able to tightly couple roles in Matrix to groups in Active Directory. Not applicable to everyone, but if businesses are looking to replace Teams (because it's a dreadful mess) this may be the feature that convinces them to switch. 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. Soru thinks most real-life examples come when you combine role-based permissions with spaces, so that the same roles gouvern all rooms in that space. 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.
While this is likely the end goal, it's outside of the scope for this specific change. A separate proposal for allowing this was being drafted but I'm not sure the status of that. 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. in random exploration there's a potential use case in virtual worlds where objects need dynamic ownership - power levels could be used for this, but scale might become a concern if too many things are trying to update power levels at once. Equally, granting power levels would mean granting permission to the whole world, which might not be desirable (unless we use objects as rooms? That feels a bit wasteful at first glance though) |
||||
|
||||
A true role-based power structure, in the eyes of this proposal, would be one which is more of a | ||||
permissions model rather than power model - a user could be granted a ban permission to ban other | ||||
members of the room, but could be denied all other typical moderator functions. | ||||
|
||||
## Proposal | ||||
|
||||
In a future room version... | ||||
|
||||
**Note**: All identifiers are to follow [MSC2758](https://github.com/matrix-org/matrix-doc/pull/2758) | ||||
in this proposal. | ||||
|
||||
Roles are declared using `m.role` state events where the state keys are arbitrary identifiers used | ||||
to differentiate between roles. An example `m.role` event's `content` would be: | ||||
|
||||
```json | ||||
{ | ||||
"m.name": { | ||||
"en": "Administrator" | ||||
}, | ||||
"m.permissions": { | ||||
"m.ban": {"m.allowed": true}, | ||||
"m.roles": { | ||||
"m.change": ["org.example.sponsors"], | ||||
"m.assign": ["*"], | ||||
"m.revoke": [] | ||||
} | ||||
}, | ||||
"org.example.colour": "#f00" | ||||
} | ||||
``` | ||||
|
||||
The content is highly extensible/namespaced to permit additional fields being added by implementations | ||||
which may be interested, such as (in the example) a colour to represent the role. Role names have | ||||
translation support, and must at least have an English definition for consistency reasons. Language codes | ||||
are per [BCP47](https://tools.ietf.org/html/bcp47), with `en` being representative of English. | ||||
|
||||
Roles are only required to have an English name. By default, a role has no permissions associated with | ||||
it. This can be used to simply categorize members of a role for easy identification rather than granting | ||||
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.
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. Names reserved in the spec could be admin, default and moderator, just like it is currently with PLs (not sure if the PL thing is in the spec, but clients practically do that) |
||||
them any specific power - such examples may be wanting to identify supporters of a project within a room. | ||||
Comment on lines
+43
to
+49
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
Note: groups (or communities) might also be used to categorize members within a room through flair. The | ||||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
difference with roles is that they'd typically affect organization/grouping within the room list rather | ||||
than at a per-message level as flair currently does. | ||||
|
||||
Permissions are identifiers with an associated object which varies depending on the permission itself. | ||||
Most permissions will have a single `m.allowed` boolean property (which defaults to `false`). The | ||||
proposed `m.*` namespace of permissions are defined later in this proposal with their relevant | ||||
specifications. | ||||
|
||||
When using this roles system, `m.room.power_levels` serves zero meaning including for the purposes of | ||||
authorization rules. The changes to the authorization rules are defined later in this proposal. | ||||
|
||||
### Identifying members in a role | ||||
|
||||
On the applicable user's `m.room.member` state event, a new field of `m.roles` is added to be an array | ||||
of role IDs (state keys for `m.role` state events). For example: | ||||
|
||||
```json | ||||
{ | ||||
"type": "m.room.member", | ||||
"sender": "@alice:example.org", | ||||
"content": { | ||||
"membership": "join", | ||||
"displayname": "Alice", | ||||
"m.roles": [ | ||||
"m.admin", | ||||
"org.example.supporter" | ||||
] | ||||
}, | ||||
"state_key": "@alice:example.org", | ||||
"origin_server_ts": 1579809459351, | ||||
"event_id": "$tKStv-i0ympmbHEhnxZxwSkXJP5r-0Svf19HACNYKG4", | ||||
"room_id": "!example:example.org" | ||||
} | ||||
``` | ||||
|
||||
Adding/removing (changing) roles associated with a user is protected by a permission - see the proposed | ||||
permissions later in this proposal for more information. | ||||
|
||||
### Default permissions structure | ||||
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. How about also adding a default role, maybe if the state key is |
||||
|
||||
Upon creation of a room, the server creates a default `m.role` state event with state key `m.admin`. | ||||
This role consists of all permissions being granted as per each permission's specification. This | ||||
role is automatically assigned to the room creator when they join for the first time, and the | ||||
authorization rules will be modified to allow this. | ||||
Comment on lines
+92
to
+95
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
Permissions are otherwise granted as per their defaults to all users without any roles defined on | ||||
their membership event. By default, members do not get any roles associated with them upon joining | ||||
the room (with the exception of the room creator, as outlined above). | ||||
|
||||
### Execution order / inheritence | ||||
|
||||
Users can perform an action if any of their roles permit it. Roles do not have inheritence under this | ||||
proposal, though in future it may be possible to do so. Instead, it is recommended that applications | ||||
needing inheritence will create smaller, more specific, roles and assign those as needed. | ||||
|
||||
### Proposed initial permissions | ||||
|
||||
The following permissions are proposed to be included in the spec. They are all direct correlations | ||||
to the existing `m.room.power_levels` fields. | ||||
|
||||
#### Common permission format | ||||
|
||||
For simple permissions (ones that can be represented as an allowed/disallowed flag), the following | ||||
permission body is used: | ||||
|
||||
```json | ||||
{ | ||||
"m.allowed": true | ||||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
} | ||||
``` | ||||
|
||||
By default, unless indicated otherwise, `m.allowed` is `false`. When `true`, users with the applicable | ||||
role are able to perform the specified action. | ||||
|
||||
#### `m.invite` | ||||
|
||||
Whether or not a user can be invited to the room by someone with the applicable role. | ||||
|
||||
This uses the common permission format and is **disallowed** by default. When the server creates the `m.admin` | ||||
role, this would be explicitly set to allowed. | ||||
Comment on lines
+130
to
+131
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
#### `m.ban` | ||||
|
||||
Whether or not a user can be banned from the room by someone with the applicable role. | ||||
|
||||
This uses the common permission format and is **disallowed** by default. When the server creates the `m.admin` | ||||
role, this would be explicitly set to allowed. | ||||
Comment on lines
+137
to
+138
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
#### `m.kick` | ||||
|
||||
Whether or not a user can be kicked from the room by someone with the applicable role. | ||||
|
||||
This uses the common permission format and is **disallowed** by default. When the server creates the `m.admin` | ||||
role, this would be explicitly set to allowed. | ||||
Comment on lines
+144
to
+145
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
#### `m.redact` | ||||
|
||||
Which senders can have their events redacted by someone with the applicable role. Like `redact` in the | ||||
power level structure, this only affects other people than the sender - the event sending permissions | ||||
cover restricting self-redaction. | ||||
|
||||
The permission body for this would be: | ||||
|
||||
```json | ||||
{ | ||||
"m.senders": [ | ||||
"@*:example.org" | ||||
] | ||||
} | ||||
``` | ||||
|
||||
The `m.senders` is an array of [globs under MSC2810](https://github.com/matrix-org/matrix-doc/pull/2810) | ||||
for which senders can have their events redacted by users with the applicable role. By default, this | ||||
array will be empty to denote that users do not have permission to redact other people's messages. When | ||||
the server creates the `m.admin` role, this would be explicitly set to `["*"]` to denote that anyone | ||||
may have their messages redacted by users in the applicable role. | ||||
Comment on lines
+166
to
+167
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
#### `m.events` | ||||
|
||||
Which room events (state and otherwise) can be sent by users with the applicable role. | ||||
|
||||
The permission body for this would be: | ||||
|
||||
```json | ||||
{ | ||||
"m.state": [ | ||||
{"type": "*", "m.allowed": true} | ||||
], | ||||
"m.room": [ | ||||
{"type": "m.room.message", "m.allowed": true}, | ||||
{"type": "*", "m.allowed": false} | ||||
] | ||||
Comment on lines
+180
to
+183
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. How does this play together we e2ee? The server that is validating these room events would only see the 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'm not sure I understand your question. I don't believe the role system would interact with e2ee at all. That is, they would be entirely unaware of each other. You should only be sent events that are relevant to you. So if you have access to a room but not encrypted contents, you should simply not be sent events for when an encrypted message is sent, modified, or deleted. Likewise, you would not receive events for roles that you are not a member of. This isn't really a function of e2ee, but rather the eventing/messaging bus being efficient with network traffic and limiting unnecessary dispersal of information. 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 my question was a bit confusing, sorry, I try to explain it better. I'm also not sure if I misunderstood something. So for the whole proposed system, my expectation is that a homeserver is validating the permissions when sending events. While e2ee is just a transport protocol detail, in this case I think it is relevant. As far as I understand e2ee, the original event (e.g. 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 proposal isn't the one to fix power levels with respect to event types in encrypted rooms - that's more likely to be done by #3842 The example here is for demonstrative (unencrypted) purposes, largely. 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. Thanks for the pointer 👍 |
||||
} | ||||
``` | ||||
|
||||
Room events are split into two kinds: `m.state` for state events, and `m.room` for all other room events. | ||||
Note that EDUs like presence and typing notifications are not (currently) handled by this proposal. Each | ||||
kind of event is an array of rules which are executed in order - the first rule that matches as allowed | ||||
will permit the user to send the applicable event. | ||||
|
||||
The `type` within the rule is a glob ([MSC2810](https://github.com/matrix-org/matrix-doc/pull/2810)). | ||||
|
||||
`m.allowed` is simply an indicator for whether or not event types matching the given rule are allowed. | ||||
|
||||
Both the `type` and `m.allowed` properties are required on rules, however `m.state` and `m.room` are | ||||
not required and have the following defaults: | ||||
|
||||
* `m.state` defaults to an implicit `{"type": "*", "m.allowed": false}` rule. When the array is explicitly | ||||
empty, this deny rule persists. | ||||
* `m.room` defaults to an implicit `{"type": "*", "m.allowed": true}` rule. When the array is explicitly | ||||
empty, an implicit deny rule of `{"type": "*", "m.allowed": false}` is present. This is to ensure that | ||||
announcement-only rooms can be created by simply specifying `"m.room": []`. | ||||
|
||||
When the server creates the default `m.admin` role, the following permission body is to be used: | ||||
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
```json | ||||
{ | ||||
"m.state": [ | ||||
{"type": "*", "m.allowed": true} | ||||
], | ||||
"m.room": [ | ||||
{"type": "*", "m.allowed": true} | ||||
] | ||||
} | ||||
``` | ||||
|
||||
#### `m.notifications` | ||||
|
||||
Which kinds of notifications users in the applicable role are able to trigger. | ||||
|
||||
The permission body for this would be: | ||||
|
||||
```json | ||||
{ | ||||
"m.room": true | ||||
} | ||||
``` | ||||
|
||||
The key of the object is the notification kind (with `m.room` being the `@room` permission level), and the | ||||
value is whether or not the role allows it to be triggered. By default, all notifications are disallowed. | ||||
Comment on lines
+224
to
+231
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
When the server creates the default `m.admin` role, the `m.room` permission must be set as `true`. | ||||
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
#### `m.roles` | ||||
turt2live marked this conversation as resolved.
Show resolved
Hide resolved
|
||||
|
||||
Whether or not users in the applicable role are able to add/change roles or add/remove them to users. | ||||
|
||||
The permission body for this would be: | ||||
|
||||
```json | ||||
{ | ||||
"m.change": [ | ||||
"org.example.*", | ||||
], | ||||
"m.assign": [ | ||||
"m.admin" | ||||
], | ||||
"m.revoke": [ | ||||
"*" | ||||
] | ||||
} | ||||
``` | ||||
|
||||
All three properties are arrays of globs ([MSC2810](https://github.com/matrix-org/matrix-doc/pull/2810)) | ||||
which are matched against role IDs (state keys of `m.role` events). All 3 arrays default to empty, implying | ||||
that all related actions are denied. Arrays are ordered and are matched as first-allowed wins. | ||||
|
||||
`m.change` denotes which roles a user in the applicable role will be able to modify the properties of. | ||||
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.
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.
|
||||
For example, if the array lists `m.admin` then users in the role will be able to modify the name, permissions, | ||||
and other properties of the `m.admin` role. This can mean that the user might be able to modify a role they | ||||
are currently assigned to. | ||||
|
||||
`m.assign` denotes which roles a user in the applicable role will be able to assign (add) to users in | ||||
the room. This would be done through the `m.roles` property of the target user's membership event. The user | ||||
is able to target themselves. | ||||
|
||||
`m.revoke` is the opposite of `m.assign`: it is which roles users in the applicable role will be able | ||||
to *remove* from a user's `m.roles` array on their membership event. Users are still able to target themselves | ||||
here. | ||||
|
||||
When the server is creating the default `m.admin` role, the following permission body is to be used: | ||||
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
```json | ||||
{ | ||||
"m.change": ["*"], | ||||
"m.assign": ["*"], | ||||
"m.revoke": ["*"] | ||||
} | ||||
``` | ||||
|
||||
### Use-case adoption | ||||
|
||||
Not all rooms will require the changes proposed here, and thus it may be important to support the existing | ||||
power levels structure in parallel. Some potential solutions for this include sending state events into | ||||
a room to indicate the switch of systems, however this could potentially cause problems with authorization | ||||
if a server were to miss an event. This proposal offers an awkward, but hopefully viable, solution that | ||||
may be extended to other similar features in the future. | ||||
|
||||
Room versions reserved by the Matrix protocol ending with `.1` are indicative of the server supporting | ||||
the principles of that room version with the role system proposed here used in place of `m.room.power_levels`. | ||||
For the purposes of authorization rules, this proposal does not support room versions 1 through 5 as | ||||
currently reserved by the specification - the minimum viable set of authorization rules are a modified | ||||
v6 set as described later in this proposal. | ||||
|
||||
The specification will remain responsible for defining what the `.1` version of a room version looks like, | ||||
when new versions are being introduced. | ||||
|
||||
**Rationale**: The specification reserves room versions consisting of `[0-9.]` for use by the protocol, | ||||
but does not reserve anything using `[a-z\-]` as otherwise allowed by room versions. Ideally, the protocol | ||||
would have reserved a dash and some letters to assist with denoting various features that may be included | ||||
in a given room version, however `.1` works just as well. | ||||
|
||||
For clarity: room version `6.1` would mean the room uses a role-based permission system while room version | ||||
`6` uses the existing power levels structure. When room version `7` is introduced through an MSC, it would | ||||
also define a `7.1` with any modifications required to continue supporting a role-based approach. | ||||
|
||||
This proposal does not include a solution for custom room versions intentionally. Implementations using | ||||
custom room versions are welcome to invent their own scheme for identifying role-based approach usage. | ||||
|
||||
### Expected server behaviour for profile/membership changes | ||||
|
||||
***TODO - This needs defining*** | ||||
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
### Precise changes to v6's authorization rules | ||||
|
||||
Using room version 6 as a reference for authorization rules, the authorization rules for this MSC | ||||
would be as follows. | ||||
|
||||
For determining whether a given user in a given room has a given permission: | ||||
|
||||
* If the user's membership is not `join`, the user does not have any permissions. | ||||
* For each role ID defined by the `m.roles` array (default empty, ordered) on the user's membership event: | ||||
* If there is no associated `m.role` state event in the room, skip. | ||||
* If the `m.role` state event does not have an English name, skip. | ||||
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. Remove this requirement and derive an algorithmic fallback name in the form of "can do this, cannot do that" using permission metadata instead.
Suggested change
|
||||
* Interpret the permission on the `m.role` state event to a single boolean flag to denote whether | ||||
the user is allowed (true) or disallowed (false) to continue. | ||||
* For unknown permission types (eg: custom namespaces), the default is to imply disallowed. | ||||
* If the user is granted (allowed) the permission, return true to let the user continue the action. | ||||
* If no roles have granted (allowed) the permission, return false to deny the user's action. | ||||
|
||||
For authorizing events themselves: | ||||
|
||||
***TODO - This needs defining*** | ||||
This comment was marked as resolved.
Sorry, something went wrong. |
||||
|
||||
## Potential issues | ||||
|
||||
Roles are controversial as a power scheme and moderation structure - this is why the proposal actively | ||||
tries to keep the `m.room.power_levels` around. A roles approach is often better bridged to some | ||||
platforms (like Discord), whereas a power levels approach has a much stronger use case for others. | ||||
Similarly, it can be argued by several communities that roles are more natural feeling while other | ||||
communities will argue that power levels are more natural - it's largely a matter of preference and | ||||
community-specific interactions which define which is "better". | ||||
|
||||
This roles approach is quite confusing as well and may lead to several implementation issues. This | ||||
MSC, and the relevant specification if this MSC makes it that far, should include examples ranging | ||||
from simple to complex for implementations to test against. As the ecosystem makes more general use | ||||
of a roles-based approach, those examples should be updated to better represent what is available | ||||
in the wild. | ||||
|
||||
As already discussed, the room version identification approach is suboptimal but appears to be a | ||||
good enough compromise pending larger discussions with members of the ecosystem. Refer to that | ||||
section for more information. | ||||
|
||||
## Alternatives | ||||
|
||||
Roles are already an alternative to existing permissions model. By extension, there are several other | ||||
systems which may be valuable and have their own merits. The intention of this proposal is to | ||||
demonstrate an opt-in style permissions systems for the rooms/communities which have a requirement | ||||
to use such a system. It is not proposed that this system become the default under any circumstance | ||||
for all of Matrix. | ||||
|
||||
## Security considerations | ||||
|
||||
Changing the entire permissions system is dangerous and could lead to multiple security vulnerabilities. | ||||
Many have been already solved or considered by the existing power level system, and where possible | ||||
those semantics have been brought into this proposal. | ||||
|
||||
TODO: There's certainly more words that can be put here, such as why roles are the way they are. | ||||
|
||||
## Unstable prefix | ||||
|
||||
Implementations should use a room version of `org.matrix.msc2812` while this MSC is not in a published | ||||
version of the specification. Because all the events would be isolated to this highly customized | ||||
room version, there is no requirement to avoid the usage of the `m.*` namespace. |
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.
there's interest in a tristate. #2812 (comment) needs unpacking.
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.
What is tri-state:
Each permission gets three states: allow, deny, nothing
By default every permission has nothing. As it is not allowed, you don't have that permission. If a permission is allowed in one role, you have it, except if it is denied in another role.
Arguments against tri-state:
Additional complexity
Arguments in favour of tri-state:
It allows greater flexibility, for example giving someone a temporary "timeout" role. Example given:
I user is bad and the moderators give them a timeout role for 24h, which denies them to talk. Without tri-state, they would have to manually remove all existing roles and then add the timeout role, only to revert that after 24h.
Depending on how the default role should work (needs clarification, too) that might even be impossible as perhaps the default role would be impossible to un-assign.
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.
IMO it should be implemented by specifying that permissions from roles are applied in some specific order (e.g. in the
m.roles
array order), and that new values override previous ones. That way it's tri-state of omit/allow/denyUh 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.
There are two systems to consider here:
m.deny
values take precedence.Pros of ordered overrides:
Cons of ordered overrides:
Considerations of ordered overrides:
m.none
orm.omit
meaning they cannot send messages, does that overridem.allow
or does it act as though it's transparent even if it comes after?Pros of explicit deny wins:
Cons of explicit deny wins:
Considerations of explicit deny wins:
m.none
should be the default value.m.none
should act as a soft deny when no explicit allow or deny is found. That is, if the send messages permission ism.none
, then the user does not have permission to send messages.m.none
should behave transparently (or effectively ignored) when explicit allow or deny permissions are found. That is,m.allow
+m.none
=m.allow
, regardless of order.Calculating permissions in explicit deny wins:
m.none
.m.deny
.m.allow
. Result is allow.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.
Correction: on Discord, explicit allow wins, not explicit deny.
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.
How about having both? Like,
*
m.allow
immediately followedm.deny
=m.none
*
m.none
immediately followed bym.deny
=m.deny
*
m.deny
orm.none
immediately followed bym.allow
=m.allow
,*anything followed by
m.none
returns the preceding power state for the permission in questioncollapsed in a left-to-right, left-associative manner.
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.
Both is not a good option for this from two perspectives:
Also, to keep discussion focused, there are two pieces of a permissions system that we should look at here:
a. Additive: A user has two roles. One gives permissions to read a channel. One gives permissions to write in a channel. Effective permissions are Read/Write. Examples: File shares, Discord, Active Directory roles.
b. Most restrictive: A user has two roles. One gives permissions to read a channel. One gives permissions to write in a channel. Effective permissions are Read. Examples: NTFS.
Personally, I would propose we use explicit deny wins with additive permissions. In this system, the effective permissions would be calculated like this:
a. If any role denies the permission, effective permission is deny.
b. If no explicit denies are found, any explicit allow means effective permission is allow.
c. If no explicit denies or allows are found, effective permission is no permission (having no permission is treated the same as being denied permission, but is not enforced as a deny and can be overridden).
Effective permissions would be calculated several times, once for global permissions and once for each channel, per user. Permissions calculations with this system are sufficiently fast, especially when adhering to standard RBAC practices. For instance, only granting the user the minimum permissions required to allow them use the channel.
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.
This is your truth table (non-commutative, non-alternative):
allow·allow = allow
allow·neutral = allow
allow·deny = neutral
neutral·allow = allow
neutral·neutral = neutral
neutral·deny = deny
deny·allow = allow
deny·neutral = deny
deny·deny = deny
It is very easy to combine those, left-to-right. First, you combine 1st and 2nd term, then the result of this with the 3rd term, then with the 4th term and so on. Specificity appendments (not overwrites) would be handled the same way: first, combine the two most generic terms, then the result of this with the 3rd most generic term, and so on.
My consideration was clients and servers with low memory and primitive processors. The currently proposed system requires an n-operand operation. My proposal, on the other hand, only requires chained two-operand calculations. Moreover, my proposal simplifies power updates, as it allows append-only updates (any permission changes will not overwrite, but append to previous permission declarations of the same level).
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.
Your suggestion assumes that roles would exist in a hierarchy or somehow be ordered. If that's the case, then that works, but now I have to be careful about how I order my roles and would need to be cognizant of their relationship to each other in this hierarchy.
Also, there is an issue with your truth table that makes things complicated for administrators. Let's say I am a member, so I have a few roles but ultimately my permission to speak is allow. As a punishment for breaking the rules, I get muted. So I get the mute role applied to me, which has speak set to deny. My effective permission is now neutral which depending on context could mean anything. If neutral allows me to speak, then this mute role becomes useless. I'd have to have two mute roles so my permission calculation becomes this:
allow + deny = neutral. Result: can (still) speak.
neutral + deny = deny. Result: cannot speak.
This becomes rather cumbersome to maintain. If I outright deny a permission, then that permission should be outright denied, regardless of whatever other permissions they have. There should be sane defaults here (such as a neutral result/no permissions meaning cannot speak), however if this sane default means that no permissions is equivalent to an allow state, then we run into the same issue above when denying that permission.
Explicit deny win calculations work extremely well for slower/older machines too, since there are only three steps:
These operations can short circuit as well. If there are any denies, then it doesn't need to check if there are any allows. At worst, the speed of this is O(n) where n is the number of roles the user has. It can potentially faster than this though. Here is some sample code I've written in C#:
The slowest piece of this code is iterating through the user's role collection. However, any proposal would have us do the same so I don't feel this is a big concession. This might be improved some, performance-wise, but it should be pretty fast as is. Also, this code is hierarchy-agnostic, meaning it'll function regardless of whether the roles exist in a hierarchical structure or are all equivalent (think Discord vs Active Directory).
As an added benefit, this code is very simple, meaning it can be easily understood at a glance and can therefore be easily maintained and diagnosed should something go wrong.
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.
@Foxtrek64 Thanks for your input. I am now preparing a new MSC with those suggestions applied, as Travis said it diverged from this proposal so much that it should be discussed separately.