-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat(webapp): consolidate auth path + add comprehensive auth tests #3499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
matt-aitken
wants to merge
61
commits into
main
Choose a base branch
from
rbac-packages
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
61 commits
Select commit
Hold shift + click to select a range
dc56a6c
Initial commit of RBAC split setup
matt-aitken 698280f
Verdaccio publish
matt-aitken 157e9c5
Every change will republish when in dev
matt-aitken 1003e08
RBAC/plugin updates
matt-aitken c9af9a8
Update RBAC plugin interface: authenticateBearer/Session, drop Prisma…
matt-aitken 73ad39a
JWT/realtime token integration: publicJWT subject, jwt metadata, allo…
matt-aitken 38c7b25
Lazy loading of plugin
matt-aitken 29780a4
RBAC: force-fallback flag + env var + e2e fallback wiring (TRI-8715)
matt-aitken 8a3c5b6
RBAC: API auth e2e coverage — action + PAT + edge cases (TRI-8716)
matt-aitken 385fe3c
RBAC: resource-scoped JWT e2e coverage (TRI-8716 follow-up)
matt-aitken 399e4b1
RBAC: pre-migration JWT behaviour tests for TRI-8719 risks (TRI-8716)
matt-aitken e154745
RBAC plugin: array resources + action alias wrapper (TRI-8719 Phase A)
matt-aitken 82cb5d3
RBAC: migrate apiBuilder to rbac.authenticateBearer + ability.can (TR…
matt-aitken a9cb0e7
RBAC: dashboardLoader / dashboardAction + migrate admin pages (TRI-8717)
matt-aitken 5ed8cdb
RBAC plugin: authenticateAuthorize* accepts array resources
matt-aitken bb5079e
RBAC tests: shared-container test harness for the comprehensive auth …
matt-aitken b9d119a
RBAC plugin: Result types on mutation methods + OSS fallback (TRI-8747)
matt-aitken 91b6504
RBAC: split dashboardBuilder so client-bundle imports resolve
matt-aitken 943e601
Code comments/formatting
matt-aitken b9f27b5
Batch added resource
matt-aitken 4d76bd5
Batch add resource
matt-aitken 727d74c
RBAC: Teams page UI — role dropdowns, plan-aware disabling, manage ga…
matt-aitken 8bd9f81
Delete API batches
matt-aitken ac55499
RBAC: auto-assign system roles on org create + invite accept (TRI-8854)
matt-aitken 79856c7
RBAC: PAT creation flow with role selection (TRI-8749)
matt-aitken c9ecf99
Use defaultValue instead of lots of useState
matt-aitken 923a9bf
RBAC tests: PAT auth comprehensive matrix (TRI-8741)
matt-aitken 5da9ba3
RBAC tests: dashboard session auth for admin pages (TRI-8742)
matt-aitken 6744bf2
RBAC tests: cross-cutting auth edge cases (TRI-8743)
matt-aitken 5872687
RBAC tests: waitpoint completions + input streams (TRI-8740)
matt-aitken 7b98d52
RBAC tests: trigger task routes (TRI-8733)
matt-aitken 3911b7a
RBAC tests: run lists (TRI-8736)
matt-aitken 0cba469
RBAC tests: run mutations — cancel + idempotencyKeys.reset (TRI-8735)
matt-aitken 167e681
RBAC tests: run resource routes — multi-key (TRI-8734)
matt-aitken ce586da
RBAC tests: batch retrieve + realtime (TRI-8737)
matt-aitken f7f14cd
RBAC tests: prompts (TRI-8738)
matt-aitken 90762be
RBAC tests: deployments + query (TRI-8739)
matt-aitken 3d19d40
RBAC tests: unblock e2e.full harness; all 162 tests pass (TRI-8731)
matt-aitken 0b7eaa6
RBAC tests: parameterise RBAC_FORCE_FALLBACK in testcontainers (TRI-8…
matt-aitken 6bd2768
RBAC tests: extract projectCreated to break platform.v3.server cycle …
matt-aitken 46c35c4
Latest lockfile… although it'll probably get conflicted again
matt-aitken a29e8b7
RBAC: Roles page (TRI-8880)
matt-aitken d2bf617
RBAC: drop upfront UserRole inserts from org-creation and invite flows
matt-aitken 71901dd
RBAC: scrub "enterprise" / "OSS" / cloud-side references from comments
matt-aitken c68d0a9
RBAC: scrub enterprise reference from rbac-force-fallback server-chan…
matt-aitken c4abb4b
RBAC: drop Wildcards group from Roles page client-side mapping
matt-aitken b93f247
RBAC: extend Permission + RbacResource for CASL conditional rules (TR…
matt-aitken d620473
RBAC: rework Roles page as a permission × role comparison Table (TRI-…
matt-aitken f5c34c7
RBAC: flex Roles page header + cell content horizontally with gap-1
matt-aitken 885a6f9
RBAC: left-align Roles page role columns (header + cells)
matt-aitken cac9a38
RBAC: shrink-to-content sizing for non-Description columns on Roles page
matt-aitken 7d48bf4
RBAC: revert column shrink-to-content sizing on Roles page
matt-aitken f984824
RBAC: render conditional cells as plain dimmed text (no tick + badge)
matt-aitken 15b9aab
RBAC: invite flow role picker via OrgMemberInvite.rbacRoleId (TRI-8892)
matt-aitken f73fb79
Tightened up comments and log an error for failed role assignments
matt-aitken 0e430c0
RBAC: rbac.systemRoleIds() instead of duplicating role-id constants
matt-aitken 6f87bd4
RBAC: give upgrade-link rows a value so Ariakit handles the click
matt-aitken 445d9b0
RBAC: preserve render prop in SelectItem outside Combobox context
matt-aitken c5327d2
Fixed role link
matt-aitken f5dabbe
RBAC: replace systemRoleIds() with systemRoles() catalogue
matt-aitken 398ca55
RBAC: address PR review — batch trigger AND, fallback resilience, pic…
matt-aitken File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/plugins": patch | ||
| --- | ||
|
|
||
| RBAC plugin: new `getAssignableRoleIds(organizationId)` method on `RoleBaseAccessController`. Returns the subset of `allRoles(organizationId)` IDs that may be assigned right now — used by the Teams page UI to disable role-dropdown options that aren't currently assignable. The default fallback returns `[]` (permissive — `allRoles` already returns `[]` so there's nothing to gate); a plugin may apply its own gating policy and return the assignable subset. Server-side enforcement (rejecting an actual `setUserRole` to a non-assignable role) is unchanged and remains the source of truth — this method is purely a UI affordance. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/plugins": minor | ||
| --- | ||
|
|
||
| RBAC plugin: `RoleBaseAccessController.authenticateAuthorizeBearer` and `authenticateAuthorizeSession` now accept `RbacResource | RbacResource[]` for `check.resource`, matching `RbacAbility.can`. This was an inconsistency — abilities accepted arrays but the convenience methods didn't, so callers wanting the array form had to call `authenticateBearer` + `ability.can` manually. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/plugins": patch | ||
| --- | ||
|
|
||
| RBAC plugin: mutation methods on `RoleBaseAccessController` now return discriminated `Result` types instead of throwing on expected error paths. `createRole` and `updateRole` return `RoleMutationResult` (`{ ok: true; role: Role } | { ok: false; error: string }`); `deleteRole`, `setUserRole`, `removeUserRole`, `setTokenRole`, and `removeTokenRole` return `RoleAssignmentResult` (`{ ok: true } | { ok: false; error: string }`). The webapp surfaces the `error` strings directly to users (system role edits, plan-tier gating, validation conflicts) so a thrown exception now signals only an unexpected failure (DB outage, bug). Read methods (`getUserRole`, `getTokenRole`, `allRoles`, `allPermissions`) are unchanged. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/plugins": minor | ||
| --- | ||
|
|
||
| RBAC plugin: `RbacAbility.can(action, resource)` now accepts either a single `RbacResource` or `RbacResource[]`. Array form means "grant access if any resource in the array passes", matching the multi-key authorisation semantics expected by routes that touch multiple resources (e.g. a run that also carries a batch id, tags, and a task identifier — a JWT scoped to any of them grants access). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/plugins": patch | ||
| --- | ||
|
|
||
| RBAC plugin: new `systemRoleIds(): Promise<SystemRoleIds | null>` method on `RoleBaseAccessController`. Returns `{ owner, admin, developer, member }` with the seed-migration role IDs when a plugin is loaded; returns `null` when no plugin is installed (matches the `allRoles → []` semantics — there are no seeded roles to refer to). Lets consumers (invite flow, PAT defaults, Roles page comparison grid) get the canonical IDs without duplicating constants in the consuming app. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/plugins": patch | ||
| --- | ||
|
|
||
| RBAC plugin: replaced `systemRoleIds()` with `systemRoles()`. The new method returns an ordered `SystemRole[]` (highest authority first) where each entry carries `id`, `name`, `description`, and `available`. The OSS no longer needs to know individual role names — it just iterates the canonical order from the plugin. `available: false` lets a plugin advertise a role without exposing it (used by v1 to ship Owner/Admin/Developer while keeping Member's prod-restriction promise unmade until the env-tier route wiring lands). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| name: "🛡️ E2E Tests: Webapp Auth (full)" | ||
|
|
||
| # Comprehensive RBAC auth test suite — see TRI-8731. Runs separately from | ||
| # the smoke e2e-webapp.yml because it covers every route family with a | ||
| # pass/fail matrix and would otherwise dominate per-PR CI time. | ||
| # | ||
| # Triggered: | ||
| # - Manually via workflow_dispatch. | ||
| # - Nightly via schedule. | ||
| # - On pull requests touching auth-relevant files only (paths filter). | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| schedule: | ||
| - cron: "0 4 * * *" # 04:00 UTC daily | ||
| pull_request: | ||
| paths: | ||
| - "apps/webapp/app/services/routeBuilders/**" | ||
| - "apps/webapp/app/services/rbac.server.ts" | ||
| - "apps/webapp/app/services/apiAuth.server.ts" | ||
| - "apps/webapp/app/services/personalAccessToken.server.ts" | ||
| - "apps/webapp/app/services/sessionStorage.server.ts" | ||
| - "apps/webapp/app/routes/api.v*.**" | ||
| - "apps/webapp/app/routes/realtime.v*.**" | ||
| - "apps/webapp/test/**/*.e2e.full.test.ts" | ||
| - "apps/webapp/test/setup/global-e2e-full-setup.ts" | ||
| - "apps/webapp/test/helpers/sharedTestServer.ts" | ||
| - "apps/webapp/test/helpers/seedTestSession.ts" | ||
| - "apps/webapp/vitest.e2e.full.config.ts" | ||
| - "internal-packages/rbac/**" | ||
| - "packages/plugins/**" | ||
| - ".github/workflows/e2e-webapp-auth-full.yml" | ||
|
|
||
| jobs: | ||
| e2eAuthFull: | ||
| name: "🛡️ E2E Auth Tests (full)" | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 30 | ||
| env: | ||
| DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| steps: | ||
| - name: 🔧 Disable IPv6 | ||
| run: | | ||
| sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1 | ||
| sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1 | ||
| sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1 | ||
|
|
||
| - name: 🔧 Configure docker address pool | ||
| run: | | ||
| CONFIG='{ | ||
| "default-address-pools" : [ | ||
| { | ||
| "base" : "172.17.0.0/12", | ||
| "size" : 20 | ||
| }, | ||
| { | ||
| "base" : "192.168.0.0/16", | ||
| "size" : 24 | ||
| } | ||
| ] | ||
| }' | ||
| mkdir -p /etc/docker | ||
| echo "$CONFIG" | sudo tee /etc/docker/daemon.json | ||
|
|
||
| - name: 🔧 Restart docker daemon | ||
| run: sudo systemctl restart docker | ||
|
|
||
| - name: ⬇️ Checkout repo | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: ⎔ Setup pnpm | ||
| uses: pnpm/action-setup@v4 | ||
| with: | ||
| version: 10.23.0 | ||
|
|
||
| - name: ⎔ Setup node | ||
| uses: buildjet/setup-node@v4 | ||
| with: | ||
| node-version: 20.20.0 | ||
| cache: "pnpm" | ||
|
|
||
| - name: 🐳 Login to DockerHub | ||
| if: ${{ env.DOCKERHUB_USERNAME }} | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| username: ${{ secrets.DOCKERHUB_USERNAME }} | ||
| password: ${{ secrets.DOCKERHUB_TOKEN }} | ||
| - name: 🐳 Skipping DockerHub login (no secrets available) | ||
| if: ${{ !env.DOCKERHUB_USERNAME }} | ||
| run: echo "DockerHub login skipped because secrets are not available." | ||
|
|
||
| - name: 🐳 Pre-pull testcontainer images | ||
| if: ${{ env.DOCKERHUB_USERNAME }} | ||
| run: | | ||
| docker pull postgres:14 | ||
| docker pull redis:7.2 | ||
| docker pull testcontainers/ryuk:0.11.0 | ||
|
|
||
| - name: 📥 Download deps | ||
| run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: 📀 Generate Prisma Client | ||
| run: pnpm run generate | ||
|
|
||
| - name: 🏗️ Build Webapp | ||
| run: pnpm run build --filter webapp | ||
|
|
||
| - name: 🛡️ Run Webapp Full Auth E2E Tests | ||
| run: cd apps/webapp && pnpm exec vitest run --config vitest.e2e.full.config.ts --reporter=default | ||
| env: | ||
| WEBAPP_TEST_VERBOSE: "1" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| area: webapp | ||
| type: improvement | ||
| --- | ||
|
|
||
| Migrate `apiBuilder.server.ts` to `rbac.authenticateBearer` + `ability.can` (TRI-8719). All 30 API routes that used `authorization.superScopes` now rely on the RBAC plugin's scope-algebra plus an `ACTION_ALIASES` compatibility map (`trigger`/`batchTrigger`/`update` satisfied by `write:*` scopes). Two intentional improvements: empty-resource routes (`/api/v1/batches`, `/api/v1/idempotencyKeys/:key/reset`) now accept JWTs (previously denied by the legacy empty-resource short-circuit); id-scoped `write:tasks:*` scopes now grant `POST /api/v1/tasks/:taskId/trigger` (previously denied by literal superScope mismatch). Both are strict supersets of the current accept set — no JWT regresses. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| area: webapp | ||
| type: improvement | ||
| --- | ||
|
|
||
| Add `dashboardLoader` / `dashboardAction` route builders that route session auth through the RBAC plugin (`rbac.authenticateSession` + `ability.canSuper()` / `ability.can`) and migrate the platform admin pages onto them. Routes that only need a logged-in user with no authorisation continue to use `requireUser` / `requireUserId` — the builder is opt-in for routes with explicit auth checks. Migrated routes: `admin.tsx`, `admin._index.tsx`, `admin.concurrency.tsx`, `admin.feature-flags.tsx`, `admin.notifications.tsx`, `admin.orgs.tsx`, `admin.data-stores.tsx`, `admin.back-office.tsx` (+ children), `admin.llm-models.*` (5 routes). Behavioural change: actions that previously threw `403 Unauthorized` on non-admins now redirect to `/` along with the loader — uniform with the builder's behaviour. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| area: webapp | ||
| type: improvement | ||
| --- | ||
|
|
||
| RBAC plugin: add RBAC_FORCE_FALLBACK env var so tests can pin the loader to the default fallback without depending on whether a plugin happens to be installed in node_modules. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| --- | ||
| area: webapp | ||
| type: feature | ||
| --- | ||
|
|
||
| RBAC: invite flow now lets the inviter pick the new member's role. | ||
| The dropdown is filtered to roles the inviter is allowed to assign | ||
| (strictly below their own level — Owner > Admin > Developer > Member) | ||
| and gated by the org's plan tier (so Free/Hobby see Owner+Admin, Pro+ | ||
| unlocks Developer+Member). With no RBAC plugin installed the picker | ||
| is hidden entirely and the legacy invite flow is unchanged. | ||
|
|
||
| Schema: new nullable `OrgMemberInvite.rbacRoleId text` column. Legacy | ||
| `role` enum stays untouched and is set to ADMIN or MEMBER based on | ||
| the chosen RBAC role for OSS-side auth compatibility. On accept, when | ||
| `rbacRoleId` is non-null the plugin's `setUserRole` is called after | ||
| the OrgMember insert; otherwise the runtime fallback derives the role | ||
| from the legacy `role` field. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| --- | ||
| area: webapp | ||
| type: feature | ||
| --- | ||
|
|
||
| RBAC: PAT creation flow now lets users pick a system role at create | ||
| time, persisted via the RBAC plugin's `setTokenRole`. Defaults to the | ||
| caller's own role so a PAT can't be more privileged than the person | ||
| creating it. Custom (org-defined) roles are out of scope for v1 — only | ||
| the four global system roles are offered, and the binding is global to | ||
| the PAT regardless of which org the request later targets. A | ||
| compensating-delete on `setTokenRole` failure keeps the PAT row and | ||
| the role row consistent without cross-store transaction wrestling. | ||
| With no RBAC plugin installed the dropdown is hidden, no roleId is | ||
| submitted, and the PAT works exactly as before. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.