==================================
Permissions for workflow templates
==================================

This blueprint covers the design for a permission system for workflow
templates.

The general idea is that workflow templates are something like method calls on
workspaces, defining their API, and some of that API can be a public interface
for the workspace, or a limited internal API.

We need a permission system to define who can access such APIs.


Use cases
=========

1. "Publish to proposed-updates": users do not have write access to a workspace
   but can invoke one of its workflow templates to publish packages to a
   hardcoded collection. The target collection has index files that
   get properly generated and signed.

2. "Maintenance": workspace owners can set up workflow templates to use as
   workspace maintenance tasks, which only workspace owners can run


Current situation
=================

Starting a workflow
-------------------

Currently, workflow templates have a ``can_display`` permission which is
granted to users who have the ``VIEWER`` role on the containing workspace.

Workflow templates also have a ``can_run`` permission which is granted to users
who have the ``CONTRIBUTOR`` role on the containing workspace.

This distinction is too coarse grained and too limited: it does not allow to
grant a group of users ``can_run`` access without making them ``CONTRIBUTOR``
on the workspace, and it doesn't allow to limit ``can_run`` to a more
restricted set of users.

Permissions while running the workflow
--------------------------------------

Currently, when permission checks are performed while running a workflow or one
of its work requests, they are done as the user who started the workflow.

This is going to change soon to be more restrictive, that is, running with a
subset of the permissions that the user who started the workflow would have, so
that only permissions relevant to workflow activities are granted. For example,
running code for a work request is not going to be granted permissions to
affect a group membership, or create workspaces.

The actual permissions checks currently being done are:

* accessing build environments and other artifacts:
  ``ExternalTask.fetch_artifact`` calls
  ``ArtifactView``/``WorkspaceArtifactView``, which need ``can_display`` on the
  workspace
* ``can_display`` checks when serving APT repositories from private workspaces
* using assets to sign releases

Adding results to collections is performed by event reactions, that currently
do not perform permission checks. They are however set up by workspace code,
which can validate inputs and perform the appropriate gatekeeping.

The rest of workflow code currently runs without permission checks.

There is currently no way to grant a user permission to sign the release files
on a collection, even if they could start a workflow template that published to
that collection.


Proposed updates to starting a workflow
=======================================

Define roles for ``WorkflowTemplate``
-------------------------------------

Define possible roles that users can have on ``WorkflowTemplate``:

* ``OWNER``: people who can edit and run the workflow template
* ``STARTER``: people who can use (display and run) the workflow template, but not
  modify it
* ``VIEWER``: people who can display, but not run, the workflow template

Add explicit role assignments to ``WorkflowTemplate``
-----------------------------------------------------

Add a ``WorkflowTemplateRole`` model to explicitly grant roles to a group for a
``WorkflowTemplate`` instance.

Add role inferences
-------------------

To handle the common cases as they are now, we can have these role inferences:

* ``OWNER`` implied by ``Workspace.Roles.OWNER``
* ``STARTER`` implied by ``Workspace.Roles.CONTRIBUTOR``
* ``VIEWER`` implied by ``Workspace.Roles.VIEWER``

This implements the status quo, and allows granting extra roles to groups of
people, addressing the starting side of the "Publish to proposed-updates" use
case for users that are not part of the ``WorkflowTemplate``'s workspace.

This is not sufficient to handle the "Maintenance" use case.

Add a ``restricted`` flag to ``WorkflowTemplate``
-------------------------------------------------

We can add a ``restricted`` flag to ``WorkflowTemplate``, which changes the
implications: when set to ``True``, the implications become:

* ``OWNER`` implied by ``Workspace.Roles.OWNER``
* ``STARTER`` implied by ``Workspace.Roles.OWNER``
* ``VIEWER`` implied by ``Workspace.Roles.VIEWER``

This would address the "Maintenance" use case: the workflow templates in
question can be configured with ``restricted=True``, and access would be
restricted to workspace owners, handling the "Maintenance" use case.

It is still possible to have a group for helpers of the workspace owners, who
can be allowed to start some such maintenance workflow templates.

Starting workflows on private repositories
------------------------------------------

These changes would allow to grant users the ability to start a workflow on a
private repository that they cannot access.

While we currently have no use cases for this, it could become a useful tool
for modeling embargoed workspaces with a limited public API.

This would however not be easily supported by the UI, which treats an
inaccessible private workspace as a workspace that does not exist.

While we could consider changing the UI to treat private workspaces with
viewable workflow templates differently, the problem could be sidestepped by
setting up "proxy" workflow templates in different, accessible workspaces that,
when started, start designated workflow templates in the private workspaces.

We may need special care to see if and how we want to allow the person who
created such a workflow to see its progress even if it is in a workspace they
cannot access.


Proposed updates to permission checking while running a workflow
================================================================

Permission checking during the workflow execution
-------------------------------------------------

We can allow a workflow template to optionally be configured with additional
groups. If that is present, all permission checks run as part of the workflow
execution consider the user as if it were also a member of those groups.

The user is never effectively added to the group, though, and their membership
is unchanged for all operations outside of that workflow execution.

This would solve the signing permission issue of the "Publish to
proposed-updates" use case: one can define a group for signers with the
appropriate roles, and configure it as an additional group for the workflow
template.

Alternatively, one can assign the workspace owners group as additional group,
which would be a simpler setup that is adequate for most organizational needs.

Refactor permission predicates
------------------------------

Recent changes around permission predicates have led to various special casing
for checks running as part of workflows or work requests, and this would add
another one.

We can refactor permission predicates to take an argument which contains the
user to check, the work request or workflow if executing for a worker, extra
groups to be granted, and possibly more as will be needed.

Such argument could be an object that is typed as a superclass of
``debusine.db.context.Context``, or something easily constructed from the same:
this would allow to pass the current application context, or to construct
alternate inputs for other kinds of permission checking.

Permission check and predicate decorators can then access the structure being
passed instead of using the application context, avoiding the risk of broken
assumptions when checking permissions of users other than the current one.
