Organizations, Teams, and Roles System
Overview
The system supports a hierarchical structure:
- Organizations (top level)
- Teams (within organizations)
- Users (within teams and/or organizations)
Roles
Global Roles (stored in profiles.role)
super_admin- Full system access, can manage everythinguser- Regular user (default)
Organization Roles (stored in user_organizations.role)
owner- Owns the organization (also stored inorganizations.owner_id)admin- Organization administratormember- Regular organization member
Team Roles (stored in user_teams.role)
admin- Team administrator (also stored inteams.admin_id)member- Regular team member
Hierarchy and Permissions
Permission Inheritance
- Super Admins have access to everything
- Org Owners have full access to their organization and all teams within it
- Team Admins have full access to their team
- Org Members can view and participate in org activities
- Team Members can view and participate in team activities
Access Rules
- Super admins can access all orgs and teams
- Org owners can access all teams in their org
- Team admins can manage their team
- Users can only access orgs/teams they're members of
Database Schema
Tables
organizations
id(UUID, primary key)name(TEXT, required)description(TEXT)owner_id(UUID, references auth.users)settings(JSONB)created_at,updated_at
teams
id(UUID, primary key)org_id(UUID, references organizations, required)name(TEXT, required)description(TEXT)admin_id(UUID, references auth.users)settings(JSONB)created_at,updated_at
user_organizations
id(UUID, primary key)user_id(UUID, references auth.users)org_id(UUID, references organizations)role(TEXT: 'owner', 'admin', 'member')created_at,updated_at- Unique constraint on (user_id, org_id)
user_teams
id(UUID, primary key)user_id(UUID, references auth.users)team_id(UUID, references teams)role(TEXT: 'admin', 'member')created_at,updated_at- Unique constraint on (user_id, team_id)
profiles (updated)
- Added
rolecolumn (TEXT: 'super_admin', 'user')
Helper Functions
Database Functions (PostgreSQL)
is_super_admin(user_id)- Check if user is super adminis_org_owner(user_id, org_id)- Check if user owns an orgis_org_member(user_id, org_id)- Check if user is member of orgis_team_admin(user_id, team_id)- Check if user is team adminis_team_member(user_id, team_id)- Check if user is team member
TypeScript Helpers (apps/backend/src/database/roles.ts)
isSuperAdmin(supabase, userId)isOrgOwner(supabase, userId, orgId)isOrgMember(supabase, userId, orgId)isTeamAdmin(supabase, userId, teamId)isTeamMember(supabase, userId, teamId)getUserOrgRole(supabase, userId, orgId)getUserTeamRole(supabase, userId, teamId)getUserOrganizations(supabase, userId)getUserTeams(supabase, userId)
Row Level Security (RLS)
All tables have RLS enabled with policies that:
- Allow super admins full access
- Allow org owners to manage their orgs
- Allow team admins to manage their teams
- Allow members to view their orgs/teams
- Prevent unauthorized access
Setting Up Super Admin
The super admin role is automatically set for sandonjurowski@gmail.com in:
- Migration
003_add_orgs_teams_roles.sql(if user exists) - Firebase importer script (when creating/updating user)
Usage Examples
Create an Organization
INSERT INTO organizations (name, description, owner_id)
VALUES ('Acme Corp', 'A great company', 'user-uuid-here');
Add User to Organization
INSERT INTO user_organizations (user_id, org_id, role)
VALUES ('user-uuid', 'org-uuid', 'member');
Create a Team
INSERT INTO teams (org_id, name, admin_id)
VALUES ('org-uuid', 'Engineering', 'user-uuid');
Add User to Team
INSERT INTO user_teams (user_id, team_id, role)
VALUES ('user-uuid', 'team-uuid', 'member');
Check Permissions (TypeScript)
import { isSuperAdmin, isOrgOwner, isTeamAdmin } from '@/database/roles'
// Check if user is super admin
const isAdmin = await isSuperAdmin(supabase, userId)
// Check if user owns an org
const ownsOrg = await isOrgOwner(supabase, userId, orgId)
// Check if user is team admin
const isAdmin = await isTeamAdmin(supabase, userId, teamId)
Notes
- Users can belong to multiple organizations
- Users can belong to multiple teams (even across different orgs)
- The
owner_idinorganizationsandadmin_idinteamsare the primary owners/admins - Additional roles are stored in
user_organizationsanduser_teamsfor flexibility - Journal entries remain user-specific (not tied to orgs/teams) - this can be extended later if needed