This page contains information related to upcoming products, features, and functionality. It is important to note that the information presented is for informational purposes only. Please do not rely on this information for purchasing or planning purposes. As with all projects, the items mentioned on this page are subject to change or delay. The development, release, and timing of any products, features, or functionality remain at the sole discretion of GitLab Inc.
StatusAuthorsCoachDRIsOwning StageCreated
proposed @jessieay @jarka @grzesiek @hsutor @adil.farrukh devops manage 2023-03-10

Permissions Changes required to enable Custom Roles


Today, the GitLab permissions system is a backend implementation detail of our static role-based access control system.

In %15.9, we announced a customer MVC of the custom roles feature. The MVC introduced the ability to add one single permission (read_code) to a custom role based on a standard GitLab Guest role. The MVC was implemented by taking an existing permission from the GitLab authorization framework and enabling it if a custom role has it set to true.

Post-MVC, the Auth group has started work on making more permissions customizable, with the ultimate goal of making all permissions customizable.

As we’ve started planning this work, there are two large challenges:

  1. The GitLab permissions system is not a stable, backwards-compatible API. But the custom roles feature is built on top of the current permissions system. Which means that custom roles relies on permissions being a stable, backwards-compatible API. So we must change how we approach our permissions system if we plan to continue on with the current architecture.
  2. Refactoring our permissions system is difficult due to the sheer number of permissions (over 700), duplication of permissions checks throughout the codebase, and the importance of permissions for security (cost of errors is very high).

This blueprint goes into further detail on each of these challenges and suggests a path for addressing them.

What are custom roles?

Our permissions system supports 6 pre-defined roles (Guest, Reporter, Developer, Maintainer, Owner) and users are assigned to per project or group, they can’t be modified. Custom roles should solve the problem that our current system is static.

With custom roles, customers can define their own roles and give them permissions they want to. For every role they create they can assign set of permissions. For example, a newly created role “Engineer” could have read code and admin merge requests enabled but abilities such as admin issues disabled.


This plan is important to define because the custom roles project’s current architecture is built off of our current permissions system, Declarative Policy. Declarative Policy makes it inexpensive to add new permissions, which has resulted in our current state of having over 700 permissions in the gitlab-org/gitlab codebase. Even our permissions documentation contains a table with over 200 rows, each row representing a unique “permission.” Up until now, the proliferation of permissions in the code has been manageable because these checks are not part of a public API. With custom roles, however, that is changing.

Our current authorization checks are often duplicated and sprinkled throughout application code. For a single web request, there might be several different permissions checked in the UI to determine if a user can see those page elements, another few permissions checks in the Rails controller to determine if the user can access the route at all, and maybe a few more permissions checks sprinkled into other Ruby service classes that run as part of the page load. This approach is recommended in the GitLab developer documentation as a “defense-in-depth” measure.

In the context of custom roles, however, this approach will not work. When a group admin wants to enable a user to take a single action via a custom role, that group admin should be able to toggle a single, well-named permission to enable the user with the custom role to view or update a resource. This means that, for a single web request, we must ensure that only one well-named permission is checked. And, the access granted for that permission must be relatively stable so that the admin is not giving users more access than they think they are. Otherwise, creating and managing custom roles will be overly complex and a security nightmare.

While the Auth group owns permissions as a feature, each team owns a set of permissions related to their domain area. corner of the gitlab-org/gitlab codebase. As a result, all engineering teams that are contributing to the gitlab-org/gitlab codebase touch permissions. This means that it is even more important to provide clear guidelines on the future of permissions and automate the enforcement of these guidelines.


  • Make it possible to customize all permissions via custom roles.
  • Make the GitLab permissions system worthy of being a public API.
  • Improve the naming and consistency of permissions.
  • Reduce the overall number of permissions from 700+ to < 100.
  • Reduce risk of refactors related to permissions.
  • Make refactoring permissions easier by having a way to evaluate behavior other than unit tests and documentation.
  • Track ownership of individual permissions so that DRIs can be consulted on any changes related to a permission that they own
  • Create a SSoT for permissions behavior.
  • Automate generation of permissions documentation.


  • Pause custom roles project indefinitely while we refactor our existing permissions system (there is high demand for this as an Ultimate feature).
  • Perform a total re-write or re-build of our permissions system (too much upfront investment without providing customer value).
  • Iteratively work on custom roles without ever getting to feature complete (“iterate to nowhere”).


  1. Introduce a linter that ensures all new permissions adhere to naming conventions.
  2. Reduce the overall number of permissions from 700+ to < 100 by consolidating our existing permissions.
  3. Introduce ownership tags for each permission that requires owning group to review any MRs that update that permission.
  4. Create a Rake task for generating permissions documentation from code so that we have a Single Source of Truth for permissions.

Alternative Solutions

Do nothing


  • No need to lengthy architecture conversation or plan
  • May discover methods for improving permissions system organically as we move forward.


  • Slow progress in building custom role feature without blueprint for how to think about permissions system as a whole
  • Permissions system can spiral into an unmaintainable code if we iterate on it without a strategically important vision.

Leave the current permissions system as-is and build a parallel Declarative Policy-based system alongside it to be used for custom roles


  • Faster to design and build a new system than to do a large-scale refactor of the existing system.
  • Auth team can own this new system entirely.


  • Maintaining 2 systems
  • Each new “regular” permission added needs a parallel addition to the custom roles system. This makes it difficult to have feature parity between custom roles and static roles.
  • Replacing our existing RBAC system with custom roles (an eventual goal of the custom roles feature) is more difficult with this approach because it requires retiring the legacy permissions system.

Bundle existing permissions into custom permissions; use “custom permissions” for the custom roles API


  • Faster to design and build a new system than to do a large-scale refactor of the existing system.
  • Auth team can own these new bundled permissions


  • Bundling permissions is less granular; the goal of custom permissions is to enable granular access.
  • Each new “regular” permission added needs a parallel addition to the bundled permissions for custom roles. This makes it difficult to have feature parity between custom roles and static roles.


  • RBAC: Role-based access control; “a method of restricting network access based on the roles of individual users.” RBAC is the method of access control that GitLab uses.
  • Static roles: the 5 categories that GitLab users can be grouped into: Guest, Reporter, Developer, Maintainer, Owner (documentation). A static role can be thought of as a group of permissions.
  • Declarative Policy: code library used by GitLab to define our authorization logic.
  • Permissions: a specific ability that a user with a Role has. For example, a Developer can create merge requests but a Guest cannot. Each row listed in the permissions documentation represents a “permission” but these may not have a 1:1 mapping with a Declarative Policy ability. An ability is how permissions are represented in the GitLab codebase.
  • Access level: integer value representing a static role, used for determining access and calculating inherited user access in group hierarchies (documentation).