You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
matrix-spec-proposals/proposals/4056-role-based-access-cont...

7.9 KiB

MSC4056: Role-Based Access Control (mk II)

A common request in Matrix is the ability to assign a set of permissions to a set of users without having to give inherited permissions. Power levels currently provide an ability to define roles, though each higher power level inherits the permissions from under it. A more formal Role-Based Access Control (RBAC) mechanism would allow a user to inherit permissions from another role while having specifics be negated, unlike power levels.

This proposal aims to improve upon the learnings of MSC2812, particularly around the decentralization and state resolution components. The specific permissions supported by roles are left out of scope in this version of the proposal.

MSC2812 fails to operate when state resolution is considered due to how it is structured. In isolation, the MSC offers a sturdy framework for RBAC, but when two changes utilizing a role are in conflict, state resolution will fail to pick a "winning" change. This proposal addresses the problem by applying a power level structure over top of roles, providing a deliberate hierarchy of roles.

TODO: Insert text about why RBAC is cool/wanted, and why we've avoided it in the past (NTFS hell, flight console UX, etc)

Potential issues

Author's note: This section is up here to be clear about which problems are considered TODO items.

This MSC does not have:

  • Backwards compatibility with existing clients/tooling.
  • Explicit permissions a role could have. It is assumed for now that the permissions implied by power levels are maintained at a minimum. For example, ability to send an event of a given type.
  • A complete solution. It outlines a framework at the moment.

Proposal

In a future room version, due to dramatic changes relating to how rooms work, we do the following.

m.room.power_levels is stripped of its usefulness. Clients MAY send it, but it will be treated as any other random event. Servers MAY optionally decide to prevent clients from sending it by accident in applicable room versions.

A new m.role state event is introduced, with state_key being an arbitrary opaque string. The state_key becomes the "Role ID". The content for the role is as follows:

{
  "profile": {
    // display name, avatar, colour, etc? Use extensible events content blocks where reasonable.
  },
  "permissions": {
    // TODO: Replace example with actual permission (m.invite or whatever)
    "org.example.my_permission": true,
    "org.example.another": {
      "is_complicated_type": true
    }
  }
}

m.profile is inspired by MSC3949, where allowing a user to define such characteristics is clearly desirable. m.permissions are the permissions themselves (which may be non-boolean values).

The users "affected" by this role are described in a second new state event: m.role_map with an empty string state_key. The content being:

{
  "role_id": {
    "users": ["@alice:example.org"],
    "order": 1
  },
  "another_role_id": {
    "users": ["@bob:example.org"],
    "order": 5
  }
}

Each role MUST have a distinct order number. Roles with higher order override conflicting permissions from lower order roles. For example, given the following:

  • "Role A" with order: 1 and permission first: true, second: true.
  • "Role B" with order: 2 and permissions first: false, third: true.

... a user with both roles would have permissions first: false, second: true, third: true. first is overridden by the order: 2 role, but second and third are not conflicting.

When an event is sent by a user, it MUST reference the m.role_map and m.role events applicable to the sender from that map as auth_events.

MSC2812 has a fundamental flaw in how it authorizes events, preventing it from working reliably in a decentralized environment. When a user with Role A is trying to ban a user, and the target user is removing that permission from Role A at the same time, the conflict becomes impossible to predict because state resolution will ultimately tiebreak on event ID. This unexpected behaviour can be (at best) confusing for users.

This MSC fixes that flaw by retaining a concept of "power level", but only applying it to the roles themselves through order. When state resolution is attempting to resolve a power struggle, it would first determine which role has higher order, then let that event "win" the conflict. In the cases where the two involved users have the same role, timestamps and event IDs are used as tiebreaks, like with any other event. In other words, a user's power level is effectively the highest order role they possess with the required permission(s).

When the users involved in a conflict are at the same effective power level, it is less surprising to those users that a conflict would be resolved by timestamp or "flip of a coin" (event ID ordering). For other cases however, users will typically have an implied hierarchy in mind even if order didn't exist, leading to an expectation that users with "Admin" have "more power" than those with "Moderator". The order field formalizes the user preference (and therefore expectation).

For the above reasons relating to state resolution, we intentionally mix user assignment and ordering into the same event type. Ideally, the order (and possibly even assignment) would be done on the m.role events themselves, however in a conflicted set of state it becomes difficult or impossible to enforce "one role per order number" during event authorization.

Detailed authorization rule changes

TODO

Detailed state resolution changes

TODO

Detailed redaction algorithm changes

TODO

Test vectors

TODO

Potential issues

TODO: Complete more of this section.

  • A limited number of roles can be used and assigned, as m.role_map will be subject to size restrictions.
  • This MSC is intentionally does not support per-user role ordering. This is a complicated feature to represent (when considering state resolution), and is empirically not used by Discord. A future room version may introduce such a feature.

Alternatives

MSC2812 is the primary alternative to this MSC. MSC3073 inherits from MSC2812 and is subject to the same issues described by this proposal. MSC3073 does introduce per-user role ordering, though not in a way compatible with state resolution/conflicts.

MSC3949 aims to introduce the "profile information" for a role without introducing RBAC itself. This proposal inherits the ideas of MSC3949.

No other significant alternatives are considered. Some minor representation details are possible, however clients like Discord are generally seen as the "gold standard" for user-facing RBAC implementation. Those clients don't make use of many theorized alternatives.

Security considerations

TODO

If m.role_map uses a role ID which doesn't exist and assigns users to it, those users will never be able to send events because the auth_events will never be complete. This needs to be fixed before this MSC enters FCP, at least for m.room.member and similar power events.

Unstable prefix

While this proposal is not incorporated into a stable room version, implementations should use org.matrix.msc4056 as an unstable room version, using room version 11 as a base. The event types are also prefixed in this room version.

Stable Unstable
m.role org.matrix.msc4056.role
m.role_map org.matrix.msc4056.role_map

Dependencies

As of writing, this MSC is being evaluated as a potential feature for use in the MIMI working group at the IETF through the Spec Core Team's efforts.