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
“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.
“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_artifactcallsArtifactView/WorkspaceArtifactView, which needcan_displayon the workspacecan_displaychecks when serving APT repositories from private workspacesusing 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 templateSTARTER: people who can use (display and run) the workflow template, but not modify itVIEWER: 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:
OWNERimplied byWorkspace.Roles.OWNERSTARTERimplied byWorkspace.Roles.CONTRIBUTORVIEWERimplied byWorkspace.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:
OWNERimplied byWorkspace.Roles.OWNERSTARTERimplied byWorkspace.Roles.OWNERVIEWERimplied byWorkspace.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.