Permissions

Concepts

Debusine uses a permission system inspired from Role-Based Access Control, where permissions are assigned on resources based on the roles that a user has on them.

Resources are concrete objects in Debusine’s data model, like scopes, workspaces, collections, artifacts, work requests, and so on.

Roles define the access level of a group of users on a resource. They are often written in all-uppercase, like OWNER, ADMIN, CONTRIBUTOR, VIEWER. Note that roles are assigned to groups, not users, as Debusine is built on the idea that all individual work could become teamwork with minimal effort.

Permissions are checked by permission predicates, which are methods of resource objects that take the current user as input and check if a permission should be granted.

Finally, when a user is added to a group they are assigned a role on the group, which can currently be MEMBER or ADMIN, to explicitly allow some users to add and remove members from the group.

Roles can be inferred

Checking for permissions on a resource does not only depend on the roles the user has on that resource, because roles can be inferred from other roles:

  • Having certain roles on a scope gives some permissions over its workspaces, collections, artifacts, workflows, and so on, without needing direct roles over them.

  • Having roles on a workspace gives permissions over collections, artifacts, workflows, and so on, without needing direct roles over them.

In other words, permission checks on a resource may take into consideration whether the user has broader roles or roles on a containing resource. For example, any workspace CONTRIBUTOR is also a workspace VIEWER, and any scope OWNER is also a workspace OWNER.

Roles may also be inferred from resource properties. For example, if debusine.db.models.Workspace.public attribute is True then any user will have the VIEWER role on the workspace.

Permission checks

Permission predicates can be used to test permissions on an individual resource: can the user display the contents of the collection being requested? Can the user write to it?

Permission filters

Permission predicates can be used to filter resources to display for UI navigation or selection lists in forms. For example:

  • list visible workspaces

  • list executable workflows

  • list writable collections as targets for a workflow

Example of a permission check

Let’s see, as an example, what happens if a user tries to display a workspace.

Workspaces have a Workspace.can_display method that is called by the web UI or the server API whenever a workspace is accessed, and the method checks if the current user has the VIEWER role.

Roles are not assigned directly to users, so Debusine will check roles on all the groups that the current user is a member of. If the user is a member of any group with a VIEWER role on the workspace, then the predicate will succeed, otherwise it will fail and Debusine will deny the request.

The VIEWER role is defined so it can be:

  • directly assigned to a group

  • implied from the CONTRIBUTOR role

  • implied by the workspace being public

The CONTRIBUTOR role is defined so it can be assigned directly or implied by the OWNER role. In turn the OWNER role can be assigned directly or implied by the OWNER role on the containing scope. The OWNER role on the containing scope can only be assigned directly.

There can be complex sets of implications, but they are set up so that Debusine is able to efficiently resolve them. In this example, the Workspace.can_display permission predicate will succeed if either the workspace is public, or if the user is a member of any group for which any of these condition is valid:

  • The group has been assigned the VIEWER role on the workspace

  • The group has been assigned the CONTRIBUTOR role on the workspace

  • The group has been assigned the OWNER role on the workspace

  • The group has been assigned the OWNER role on the containing scope