.. _explanation-permissions: =========== 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 :py:attr:`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