Skip to content

feat(access-control): share saved pipelines and honour inherited run scopes - #2856

Closed
morgan-wowk wants to merge 1 commit into
masterfrom
access-control-mvp
Closed

morgan-wowk wants to merge 1 commit into
masterfrom
access-control-mvp

Conversation

@morgan-wowk

@morgan-wowk morgan-wowk commented Oct 8, 2026 •

Copy link
Copy Markdown

Manage who can operate, edit or manage a saved pipeline from the run view. Cancel and Run Notes now follow the run scopes inherited from the pipeline, not only "you started this run".

Where to find it

The Manage pipeline access button sits in the v2 run view's actions bar, next to the run status. It appears only when the run came from a saved pipeline that the viewer holds some scope on. The access list is only readable by people it names, so anyone else doesn't see the button.

Where the access button lives

Dialog

Owner shares Grant holder without share
Owner dialog Read-only dialog
operate grant: Cancel run (v2 run view) No grant: no Cancel run, no access button (v2 run view)
Operator run view Stranger run view

This change is v2 only; the v1 run view is unchanged. All screenshots are from the v2 run view. They come from the oasis-frontend screenshot E2E against this branch (https://github.com/Shopify/oasis-frontend/pull/731).

Secrets stay with the owner

Edit changes everything except secrets. The backend refuses an editor's save that adds, swaps or removes a secret, changes a task that takes one, or changes an input such a task reads (https://github.com/Shopify/oasis-backend/pull/745). The UI points editors to the owner rather than letting them hit that error.

In the dialog, an editor who isn't the owner sees one line naming who to ask:

Editor dialog

There's no UI on master that edits a saved pipeline yet, so the editor tips are shown in a throwaway PR built on the remote-pipeline work: #2857

Task with a secret Adding a secret
Secret task Add secret

UX

flowchart TD
  run["Run view"] --> ann{"run has pipeline-id annotation?"}
  ann -- no --> creator["creator-only Cancel and Notes, as before"]
  ann -- yes --> pscopes["GET my_scopes for the pipeline"]
  pscopes --> granted{"any scope?"}
  granted -- yes --> btn["Manage pipeline access button"]
  granted -- no --> nobtn["no button"]
  ann -- yes --> scopes["GET my_scopes for the run"]
  scopes --> cancel["Cancel shown with run:cancel"]
  scopes --> notes["Notes editable with run:annotate"]
  btn --> dialog["Manage access dialog"]
  dialog --> share{"caller has pipeline:share?"}
  share -- yes --> edit["edit rows, Save sends users and revision"]
  share -- no --> ro["read-only rows"]
Loading

API model

sequenceDiagram
  participant UI as tangle-ui
  participant API as oasis-backend
  participant DB as extra_data
  UI->>API: GET /api/access_control/pipeline/ID
  API->>DB: read access_control
  alt caller holds any scope
    API-->>UI: owner, users, history, revision, my_scopes, warning
  else no grant
    API-->>UI: 403
  end
  UI->>API: PUT /api/access_control/pipeline/ID with users and revision
  API->>DB: lock row, compare revision
  alt revision matches and caller has pipeline:share
    API->>DB: write users, append history entry
    API-->>UI: 200 with the new state
  else revision is stale
    API-->>UI: 409
  else no pipeline:share
    API-->>UI: 403
  end
Loading

Code lives in src/components/shared/AccessControl/. The other edits swap isRunCreator for canCancel and canAnnotate.

Scopes

Scope Includes
operate run:cancel, run:annotate
edit operate, plus pipeline:update (rename included)
manage edit, plus pipeline:delete, pipeline:share

Owners and admins hold every scope. Runs inherit run:* from their saved pipeline. Starting a run isn't a scope: anyone can run a saved pipeline, as on prod today.

Related

🤖 Generated with Claude Code

@morgan-wowk
morgan-wowk requested a review from a team as a code owner October 8, 2026 00:10
@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

🎩 Preview

A preview build has been created at: access-control-mvp/e166557

…scopes

Adds src/components/shared/AccessControl, the UI half of the RBAC MVP, wired
into the v2 run view only:

- accessControlApi: hand-written calls for GET/PUT /api/access_control/{type}/{id}
  and GET .../my_scopes. Errors read `detail`, then `message`, then the status.
- useRunPermissions: the run's creator can cancel and annotate as before; anyone
  else asks the backend for the run:* scopes they inherit from the run's saved
  pipeline (tangleml.com/user-pipeline/pipeline-id). Backends without the
  endpoint answer with an error, which reads as "no scopes", so the behaviour
  there is unchanged.
- ManageAccessDialog / ManageAccessButton: list who has operate/edit/manage on
  the run's saved pipeline. Holders of pipeline:share can add, change and remove
  people. Saves send the full list plus the last-seen revision; a 409 refetches.
  The backend's run-as-owner note is shown above the list as an info box.
  Only the owner can add or change secrets, so a non-owner with edit sees a
  one-line tip naming the owner, and the role summary says edit excludes them.

In the v2 run view, Cancel (RunActionsBar, RunMenu, RunToolsContent) follows
canCancel and the notes editor (RunDetailsContent) follows canAnnotate. The
access button sits in the run actions bar and only appears when the run came
from a saved pipeline the caller holds some
scope on (the access list itself is only readable by people it names). The v1 run view is unchanged.
@morgan-wowk

Copy link
Copy Markdown
Author

Closing for now. The UI work will be revisited later in new PRs; the backend changes continue in TangleML/tangle#387 and https://github.com/Shopify/oasis-backend/pull/745.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant