┌─────────────────────────────────────────────────────────────────────────┐
│ PRECEDENCE ORDER FOR EFFECTIVE ROLE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1️⃣ EXPLICIT PROJECT ROLE (most specific) │
│ └── project_admin, editor, viewer │
│ └── ALWAYS wins for that specific project │
│ └── Can RESTRICT higher roles (e.g., super_admin → viewer) │
│ │
│ 2️⃣ EXPLICIT TEAM ROLE (less specific) │
│ └── team_admin → admin access to all team projects │
│ └── team_member → read + execute access to all team projects │
│ │
│ 3️⃣ GLOBAL ROLE (fallback when no explicit team/project role) │
│ └── super_admin → bypasses access checks, full permissions │
│ └── org_admin → bypasses access checks, org-wide permissions │
│ └── member → basic read access only │
│ │
└─────────────────────────────────────────────────────────────────────────┘
User requests access to Project X
│
▼
┌──────────────────────────────┐
│ Does user have explicit │
│ project_role in Project X? │
└──────────────────────────────┘
│
┌─────┴─────┐
│ │
YES NO
│ │
▼ ▼
Use project ┌──────────────────────────────┐
_role │ Does user have team_role │
│ in Project's team? │
└──────────────────────────────┘
│
┌─────┴─────┐
│ │
YES NO
│ │
▼ ▼
Use team_ Use global_role
role (fallback)
┌─────────────┐ ┌─────────────────────────────────────────────┐ ┌─────────────────┐
│ Client │────▶│ Auth Guard │────▶│ Backend Svc │
│ │ │ │ │ │
│ JWT Token │ │ 1. ✅ Validate JWT signature │ │ │
│ (contains │ │ 2. ✅ Check user is active │ │ Receives: │
│ team_id, │ │ 3. ✅ Verify user is team member │ │ X-User-ID │
│ project_id)│ │ 4. ✅ Verify team has service access │ │ X-Team-ID │
│ │ │ 5. ✅ Verify user is project member │ │ X-Project-ID │
│ │ │ 6. ✅ Apply rate limiting │ │ │
│ │ │ 7. ✅ Inject verified headers │ │ Must: │
│ │ │ 8. ✅ Add OIDC token │ │ Filter by │
│ │ │ │ │ X-Project-ID │
│ │ │ If any check fails → 401/403 │ │ │
└─────────────┘ └─────────────────────────────────────────────┘ └─────────────────┘
CREATE TABLE team_policies (
id UUID PRIMARY KEY,
team_id UUID NOT NULL REFERENCES teams(id),
allowed_services JSONB NOT NULL DEFAULT '[]',
enabled BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT NOW()
);