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
CONTRIBUTORroleimplied 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
VIEWERrole on the workspaceThe group has been assigned the
CONTRIBUTORrole on the workspaceThe group has been assigned the
OWNERrole on the workspaceThe group has been assigned the
OWNERrole on the containing scope