Add multi-tenancy with Teams feature

Implement team-based organization for projects with role-based access control:

Backend:
- Add teams and team_memberships database tables (migrations 009, 009b)
- Add Team and TeamMembership ORM models with relationships
- Implement TeamAuthorizationService for team-level access control
- Add team CRUD, membership, and projects API endpoints
- Update project creation to support team assignment

Frontend:
- Add TeamContext for managing team state with localStorage persistence
- Add TeamSelector component for switching between teams
- Add TeamsPage, TeamDashboardPage, TeamSettingsPage, TeamMembersPage
- Add team API client functions
- Update navigation with Teams link

Security:
- Team role hierarchy: owner > admin > member
- Membership checked before system admin fallback
- Self-modification prevention for role changes
- Email visibility restricted to team admins/owners
- Slug validation rejects consecutive hyphens

Tests:
- Unit tests for TeamAuthorizationService
- Integration tests for all team API endpoints
This commit is contained in:
Mondo Diaz
2026-01-27 23:28:31 +00:00
parent a5796f5437
commit a1bf38de04
24 changed files with 4399 additions and 17 deletions

View File

@@ -1,5 +1,6 @@
import { Routes, Route, Navigate, useLocation } from 'react-router-dom';
import { AuthProvider, useAuth } from './contexts/AuthContext';
import { TeamProvider } from './contexts/TeamContext';
import Layout from './components/Layout';
import Home from './pages/Home';
import ProjectPage from './pages/ProjectPage';
@@ -11,6 +12,10 @@ import APIKeysPage from './pages/APIKeysPage';
import AdminUsersPage from './pages/AdminUsersPage';
import AdminOIDCPage from './pages/AdminOIDCPage';
import ProjectSettingsPage from './pages/ProjectSettingsPage';
import TeamsPage from './pages/TeamsPage';
import TeamDashboardPage from './pages/TeamDashboardPage';
import TeamSettingsPage from './pages/TeamSettingsPage';
import TeamMembersPage from './pages/TeamMembersPage';
// Component that checks if user must change password
function RequirePasswordChange({ children }: { children: React.ReactNode }) {
@@ -45,6 +50,10 @@ function AppRoutes() {
<Route path="/settings/api-keys" element={<APIKeysPage />} />
<Route path="/admin/users" element={<AdminUsersPage />} />
<Route path="/admin/oidc" element={<AdminOIDCPage />} />
<Route path="/teams" element={<TeamsPage />} />
<Route path="/teams/:slug" element={<TeamDashboardPage />} />
<Route path="/teams/:slug/settings" element={<TeamSettingsPage />} />
<Route path="/teams/:slug/members" element={<TeamMembersPage />} />
<Route path="/project/:projectName" element={<ProjectPage />} />
<Route path="/project/:projectName/settings" element={<ProjectSettingsPage />} />
<Route path="/project/:projectName/:packageName" element={<PackagePage />} />
@@ -60,7 +69,9 @@ function AppRoutes() {
function App() {
return (
<AuthProvider>
<AppRoutes />
<TeamProvider>
<AppRoutes />
</TeamProvider>
</AuthProvider>
);
}