Skip to content

feat: Organizations & team-based access control #261

@kayvanaarssen

Description

@kayvanaarssen

Background

PR #259 added per-user server access — you can give a single user access to a specific server and optionally restrict them to one or more databases. That covers the simple case really well.

But during the review a good point came up: for teams working on multiple client environments it makes more sense to group servers and volumes under an Organization and manage access at that level, rather than wiring up every user individually. At the same time, the single-user-to-single-database path should still work without having to spin up a full org for it.

This issue is to plan that out and get everyone's input before writing code.


Proposed model

Two access paths, both supported

Org path — for teams
Create an org → assign servers/volumes to it → add users as members. Everyone in the org gets access to those resources automatically.

Direct path — for individuals
Assign a user directly to a server (with optional DB restriction) using the existing UserServerAccess mechanism. No org needed.

Data model sketch

organizations
  id, name, slug, created_by

organization_members  (pivot)
  organization_id, user_id, role: owner | admin | member | viewer

database_servers  →  + organization_id (nullable)
volumes           →  + organization_id (nullable)

user_server_accesses  (already exists from PR #259)
  user_id, database_server_id, allowed_databases[], can_restore

organization_id being nullable keeps the direct-access path intact — a server with no org is only reachable by app admins and users with an explicit grant.

Permission resolution

  1. App Admin → full access, no restrictions
  2. Org member/viewer → sees servers and volumes belonging to their active org, scoped to their allowed_databases if set
  3. User with a direct UserServerAccess grant (no org) → sees only those specific servers/databases

Org switcher

Session-based selector in the nav (same approach as the theme switcher) — keeps routing simple and doesn't require touching every URL.


Suggested build order

Since this touches a lot of models and policies, splitting it into two PRs makes sense:

PR 1 — Organizations

  • Migrations: organizations, organization_members, organization_id on servers + volumes
  • Auto-migration: existing servers/volumes stay accessible (nullable FK, no breaking change)
  • CRUD: manage orgs and members (admin only)
  • Nav org-switcher
  • Policies updated to respect org membership

PR 2 — Fine-grained access (builds on PR 1)


Open questions

  1. Org member roles — does viewer = read-only (no backup/restore triggers) and member = full actions make sense? Or should that be configurable per org?

  2. Org-level DB restriction vs per-member restriction — should an org be able to say "everyone in this org only sees databases X and Y on server Z"? Or is per-member restriction (via UserServerAccess) enough?

  3. Personal org or truly optional? — always giving every user a personal org (GitHub-style) simplifies the "always have an active org" assumption but adds noise for solo installs. Truly optional is cleaner for simple setups.

  4. Existing installs — for people already running the app, what's the migration story? Auto-create a default org and move everything into it, or leave organization_id null and let admins organise things manually?


Happy to start building once there's some alignment on the questions above. Curious what others think about the org roles and whether an org-level DB restriction is worth the extra complexity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions