Skip to content

feat: enforce demo account isolation and organization access restrictions#204

Merged
JoachimLK merged 4 commits into
mainfrom
fix/demo-account-org-isolation
Jun 17, 2026
Merged

feat: enforce demo account isolation and organization access restrictions#204
JoachimLK merged 4 commits into
mainfrom
fix/demo-account-org-isolation

Conversation

@JoachimLK

@JoachimLK JoachimLK commented Jun 17, 2026

Copy link
Copy Markdown
Contributor
  • Added checks to prevent demo accounts from modifying organization memberships or settings in the auth API.
  • Implemented restrictions for demo accounts to join or request access to other organizations.
  • Enhanced organization search API to return an empty response for demo accounts.
  • Introduced utility functions to manage demo account email configuration and validation.
  • Updated relevant API handlers to enforce demo account rules.
  • Added unit tests to ensure demo account isolation and access restrictions are enforced correctly.

Summary

  • What does this PR change?
  • Why is this needed?

PR title must follow Conventional Commits — e.g. feat(jobs): add bulk import or fix: handle null salary. The squash-merged title is what release-please uses to generate the changelog and pick the next version. PRs with non-conventional titles are blocked by CI.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Chore

Validation

  • I tested locally
  • I added/updated relevant documentation
  • I verified multi-tenant scoping and auth behavior for affected API paths

DCO

  • All commits in this PR are signed off (Signed-off-by) via git commit -s

Summary by CodeRabbit

Release Notes

  • New Features

    • Added demo account organization isolation across auth, organization selection, and permission checks.
    • Demo accounts are now blocked from joining or modifying memberships in other organizations, and org discovery is restricted to demo-allowed results.
  • Bug Fixes

    • Improved dashboard settings save/delete handling to correctly surface failures instead of treating errors as success.
  • Tests

    • Added/expanded unit coverage for demo account isolation and settings auth error handling.
  • Chores

    • Updated CI validation and added an npm typecheck script; upgraded key dependencies (including Vue Router and OpenTelemetry).

…ions

- Added checks to prevent demo accounts from modifying organization memberships or settings in the auth API.
- Implemented restrictions for demo accounts to join or request access to other organizations.
- Enhanced organization search API to return an empty response for demo accounts.
- Introduced utility functions to manage demo account email configuration and validation.
- Updated relevant API handlers to enforce demo account rules.
- Added unit tests to ensure demo account isolation and access restrictions are enforced correctly.
@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-204 June 17, 2026 06:53 Destroyed
@railway-app

railway-app Bot commented Jun 17, 2026

Copy link
Copy Markdown

🚅 Deployed to the reqcore-pr-204 environment in applirank

Service Status Web Updated (UTC)
applirank ✅ Success (View Logs) Web Jun 17, 2026 at 8:15 am

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@JoachimLK, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 34 minutes and 36 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 33083b94-a2c4-4ab8-8c69-504635180bb6

📥 Commits

Reviewing files that changed from the base of the PR and between 989cca3 and d533052.

📒 Files selected for processing (1)
  • tests/unit/org-settings-auth-errors.test.ts
📝 Walkthrough

Walkthrough

Adds demo account organization isolation: new helpers in server/utils/demoOrg.ts identify the demo account and resolve its allowed org IDs, enforced across requireAuth, requirePermission, the Better Auth catch-all handler, and organization-discovery routes. Also improves error handling in dashboard organization settings operations, adds a typecheck npm script, updates the PR validation workflow, and bumps several dependency versions.

Changes

Demo Account Organization Isolation

Layer / File(s) Summary
Demo org utility functions
server/utils/demoOrg.ts
Adds DEFAULT_DEMO_ACCOUNT_EMAIL, getConfiguredDemoEmail, isDemoAccountEmail, getDemoAccountSlugs, getDemoAccountOrgIds, and assertDemoAccountCanUseOrg as the foundational helpers for demo account identity and org access enforcement.
requireAuth and requirePermission gates
server/utils/requireAuth.ts, server/utils/requirePermission.ts
Both auth helpers now call assertDemoAccountCanUseOrg before returning the session or evaluating permissions, blocking demo accounts from using non-demo org IDs at the middleware level.
Better Auth handler demo controls
server/api/auth/[...all].ts
The catch-all auth handler fetches and normalizes activeOrganizationId for demo accounts, blocks forbidden org-mutation endpoints with 403, filters org-list responses to allowed demo orgs, and rethrows errors with statusCode.
Route-level guards and isolation tests
server/api/org-search/index.get.ts, server/api/invite-links/accept.post.ts, server/api/join-requests/index.post.ts, tests/unit/demo-account-isolation.test.ts
org-search returns empty for demo accounts; invite-links/accept and join-requests throw 403; the Vitest suite asserts all guard patterns are present in source files.

Dashboard Organization Settings Error Handling

Layer / File(s) Summary
Organization update/delete error handling
app/pages/dashboard/settings/index.vue
handleSaveOrg and handleDeleteOrg now capture operation results and throw when result.error is present; org_deleted tracking is moved to occur only after successful deletion.
Error handling validation tests
tests/unit/org-settings-auth-errors.test.ts
New unit test suite that loads the dashboard settings page source and asserts that Better Auth organization operation errors prevent success tracking and navigation.

CI Workflow and Dependency Updates

Layer / File(s) Summary
typecheck script and CI workflow wiring
package.json, .github/workflows/pr-validation.yml
package.json adds the typecheck script; the workflow assigns id: install to the install step, changes typecheck to npm run typecheck, and reports the install step's actual outcome in the PR summary.
Dependency and override version bumps
package.json
Bumps @opentelemetry/* to 0.219.x/2.8.0, vue-router from ^4.6.4 to ^5.1.0, and overrides for tar, esbuild, protobufjs; adds form-data override.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AuthHandler as server/api/auth/[...all].ts
  participant demoOrg as server/utils/demoOrg.ts
  participant BetterAuth as auth.handler

  Client->>AuthHandler: Request to /api/auth/*
  AuthHandler->>demoOrg: getSession(), getDemoAccountOrgIds()
  demoOrg-->>AuthHandler: demo org IDs + normalized activeOrganizationId
  AuthHandler->>AuthHandler: persist activeOrganizationId to session

  alt forbidden org mutation + demo account
    AuthHandler-->>Client: 403 Forbidden
  else normal request
    AuthHandler->>BetterAuth: auth.handler(event)
    BetterAuth-->>AuthHandler: response
    alt org list response + demo account
      AuthHandler->>demoOrg: getDemoAccountOrgIds()
      AuthHandler->>AuthHandler: filter organizations to allowed set
    end
    AuthHandler-->>Client: (filtered) response
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly Related PRs

  • reqcore-inc/reqcore#63: Both PRs modify .github/workflows/pr-validation.yml to add step IDs and reference step outcomes (install, typecheck) in validation summary reporting.

Poem

🐇 Hoppity hop, the demo must stay,
In its own burrow, it cannot stray.
Org isolation guards block the way,
403 errors save the day!
The dashboard now checks its errors with care,
And the workflow types with confidence to spare. 🌿

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description adequately lists the changes made but does not complete required template sections: the summary questions 'What does this PR change?' and 'Why is this needed?' remain as placeholder text, and all validation/DCO checkboxes are unchecked. Fill in the summary section with clear answers to 'What does this PR change?' and 'Why is this needed?' and verify/check the validation and DCO items as applicable.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main changes: adding enforcement of demo account isolation and organization access restrictions, which aligns with all modified files and objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/demo-account-org-isolation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
tests/unit/demo-account-isolation.test.ts (1)

8-29: 💤 Low value

Pattern-based tests are fragile but serve as safety nets.

These tests verify code structure via regex rather than behavior. While this approach is brittle (formatting changes or refactoring could break tests without affecting functionality), it serves as a useful safety net to ensure demo isolation guards aren't accidentally removed during future refactoring.

Consider supplementing with integration tests that actually verify demo accounts receive 403 responses when attempting blocked operations, which would test behavior rather than implementation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/demo-account-isolation.test.ts` around lines 8 - 29, The
pattern-based tests in the describe block use regex matching to verify that demo
isolation guards exist in the codebase, which is brittle to formatting changes.
Supplement these structural verification tests by adding integration tests that
verify the actual behavioral guarantees: test that demo accounts receive 403
responses when attempting to modify non-demo organizations, when accessing
organization routes like set-active and list, and when trying to join or
discover real organizations. This ensures that the security guarantees are
enforced through actual API behavior testing rather than relying on code pattern
matching.
server/utils/demoOrg.ts (1)

65-69: 💤 Low value

Potential duplicate slug addition.

getDemoAccountSlugs() unconditionally adds env.DEMO_ORG_SLUG || DEFAULT_PREVIEW_DEMO_ORG_SLUG, but getConfiguredDemoSlugs() already adds env.DEMO_ORG_SLUG when it's set (line 33). When DEMO_ORG_SLUG is configured, it will appear twice in the Set (deduplicated) but the fallback logic is confusing: if DEMO_ORG_SLUG is unset, both the preview slug from line 38 (conditionally) and line 67 (unconditionally) could add DEFAULT_PREVIEW_DEMO_ORG_SLUG.

Consider clarifying the intent—if the demo account should always have access to the preview slug regardless of environment:

♻️ Suggested clarification
 export function getDemoAccountSlugs(): string[] {
   const slugs = new Set(getConfiguredDemoSlugs().slugs)
-  slugs.add(env.DEMO_ORG_SLUG || DEFAULT_PREVIEW_DEMO_ORG_SLUG)
+  // Always include the preview demo slug for demo account isolation
+  slugs.add(DEFAULT_PREVIEW_DEMO_ORG_SLUG)
+  if (env.DEMO_ORG_SLUG) {
+    slugs.add(env.DEMO_ORG_SLUG)
+  }
   return [...slugs]
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/utils/demoOrg.ts` around lines 65 - 69, The getDemoAccountSlugs()
function is redundantly handling env.DEMO_ORG_SLUG, which is already included by
getConfiguredDemoSlugs(). The unconditional addition of env.DEMO_ORG_SLUG or
DEFAULT_PREVIEW_DEMO_ORG_SLUG creates confusing fallback logic where the preview
slug could be added multiple times. Remove the redundant slug.add() call in
getDemoAccountSlugs() and rely solely on getConfiguredDemoSlugs() to provide all
the necessary slugs, ensuring the fallback logic is centralized and clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@server/utils/demoOrg.ts`:
- Around line 65-69: The getDemoAccountSlugs() function is redundantly handling
env.DEMO_ORG_SLUG, which is already included by getConfiguredDemoSlugs(). The
unconditional addition of env.DEMO_ORG_SLUG or DEFAULT_PREVIEW_DEMO_ORG_SLUG
creates confusing fallback logic where the preview slug could be added multiple
times. Remove the redundant slug.add() call in getDemoAccountSlugs() and rely
solely on getConfiguredDemoSlugs() to provide all the necessary slugs, ensuring
the fallback logic is centralized and clear.

In `@tests/unit/demo-account-isolation.test.ts`:
- Around line 8-29: The pattern-based tests in the describe block use regex
matching to verify that demo isolation guards exist in the codebase, which is
brittle to formatting changes. Supplement these structural verification tests by
adding integration tests that verify the actual behavioral guarantees: test that
demo accounts receive 403 responses when attempting to modify non-demo
organizations, when accessing organization routes like set-active and list, and
when trying to join or discover real organizations. This ensures that the
security guarantees are enforced through actual API behavior testing rather than
relying on code pattern matching.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b90978bc-df31-48a4-bcd9-f7cd0bc1b4d6

📥 Commits

Reviewing files that changed from the base of the PR and between 0276ae9 and 72433b9.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • .github/workflows/pr-validation.yml
  • package.json
  • server/api/auth/[...all].ts
  • server/api/invite-links/accept.post.ts
  • server/api/join-requests/index.post.ts
  • server/api/org-search/index.get.ts
  • server/utils/demoOrg.ts
  • server/utils/requireAuth.ts
  • server/utils/requirePermission.ts
  • tests/unit/demo-account-isolation.test.ts

@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-204 June 17, 2026 07:32 Destroyed
@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-204 June 17, 2026 08:01 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit/org-settings-auth-errors.test.ts`:
- Around line 20-27: The assertions at lines 25-27 checking the ordering of
patterns in settingsPage can produce false positives because indexOf returns -1
when a pattern is not found, and -1 is still less than any valid index. To fix
this, explicitly assert that each critical pattern exists in the settingsPage
string before comparing their positions using toBeLessThan. First verify that
the string 'if (result.error)' exists within the settingsPage and that 'track'
appears in the settingsPage, then verify their relative ordering. This ensures
that both patterns are actually present in the code before the ordering
assertion runs, preventing the test from passing when the error handling guard
is missing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 368a9fc9-691b-4ab2-a613-3455fdd59097

📥 Commits

Reviewing files that changed from the base of the PR and between 72433b9 and 989cca3.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • app/pages/dashboard/settings/index.vue
  • tests/unit/org-settings-auth-errors.test.ts

Comment thread tests/unit/org-settings-auth-errors.test.ts Outdated
@railway-app railway-app Bot temporarily deployed to applirank / reqcore-pr-204 June 17, 2026 08:12 Destroyed
@JoachimLK JoachimLK merged commit f47296e into main Jun 17, 2026
10 checks passed
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