-
Notifications
You must be signed in to change notification settings - Fork 244
Description
π§ Epic
WARNING: MAJOR FEATURE - NEEDS DISCUSSION / ARCHITECTURAL DECISION / WORKSHOPS BEFORE IMPLEMENTING
Title: Role-Based Access Control (RBAC) - User/Team/Global Scopes
Goal: Implement full multi-tenant architecture with private, team, and global catalogs for all MCP Gateway resources
Why now: Current implementation shares all resources globally. Multi-tenancy enables secure isolation for enterprises, teams, and individual users while maintaining collaboration through controlled sharing.
Depends on:
- #87 JWT Token Catalog with Per-User Expiry and Revocation
- #282 Per-Virtual-Server API Keys with Scoped Access
π§ Type of Feature
- Enhancement to existing functionality
- New feature or capability
- Security Related (requires review)
- Major Architecture Change (multi-tenancy)
πββοΈ User Story 1 β Private Catalog Management
As a: registered user
I want: my own private catalog of tools, resources, prompts, and virtual servers
So that: I can develop and test configurations without affecting others
β Acceptance Criteria
Scenario: Create private virtual server
Given I am logged in as user "alice"
When I create a virtual server in my private catalog
Then only I can see and access this server
And it does not appear in team or global catalogs
And my JWT tokens can access my private resources
πββοΈ User Story 2 β Team Collaboration
As a: team owner
I want: to create shared team catalogs and manage member access
So that: my team can collaborate on MCP configurations while maintaining isolation from other teams
β Acceptance Criteria
Scenario: Team owner shares private resource to team
Given I am team owner of "data-science-team"
And I have a private tool "custom-analyzer"
When I share this tool to my team catalog
Then all team members can see and use "custom-analyzer"
But users outside my team cannot access it
Scenario: Team member management
Given I am team owner of "data-science-team"
When I invite "[email protected]" as a member
Then Bob receives access to team catalog resources
And when I promote Bob to owner
Then Bob can invite new members and share resources
πββοΈ User Story 3 β Global Resource Publishing
As a: platform admin or team owner
I want: to publish resources from team catalogs to the global catalog
So that: proven tools and configurations can be shared across the entire organization
β Acceptance Criteria
Scenario: Publish team resource globally
Given I am owner of team "platform-team"
And we have a team tool "company-auth-server"
When I publish this tool to the global catalog
Then all users across all teams can discover and use it
And the original team retains ownership for updates
πββοΈ User Story 4 β Scope-Aware API Access
As a: API client
I want: to access resources based on my user/team context and permissions
So that: I only see resources I'm authorized to use
β Acceptance Criteria
Scenario: Scoped resource listing
Given I authenticate as user "alice" in team "data-science"
When I call GET /tools
Then I see tools from:
- My private catalog
- My team "data-science" catalog
- The global catalog
But I do not see resources from other teams
Scenario: Cross-scope access denial
Given I have a token scoped to team "marketing"
When I try to access a private tool owned by user "alice"
Then I receive 403 Forbidden with scope violation error
π Design Sketch
Catalog Hierarchy:
Global Catalog (visible to all users)
βββ Team: data-science
β βββ Member: alice (can read team resources)
β βββ Owner: bob (can manage team, share resources)
β βββ Private: bob's personal catalog
βββ Team: platform
βββ Owner: charlie
βββ Private: charlie's personal catalog
Database Schema Extensions:
-- User management
CREATE TABLE users (
id UUID PRIMARY KEY,
username VARCHAR(255) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- Team management
CREATE TABLE teams (
id UUID PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
created_by UUID REFERENCES users(id),
created_at TIMESTAMP DEFAULT NOW()
);
-- Team membership with roles
CREATE TABLE team_members (
id UUID PRIMARY KEY,
team_id UUID REFERENCES teams(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
role ENUM('owner', 'member') DEFAULT 'member',
joined_at TIMESTAMP DEFAULT NOW(),
UNIQUE(team_id, user_id)
);
-- Extend ALL existing tables with scope fields
ALTER TABLE tools ADD COLUMN scope_type ENUM('private', 'team', 'global') DEFAULT 'global';
ALTER TABLE tools ADD COLUMN scope_owner UUID REFERENCES users(id);
ALTER TABLE tools ADD COLUMN scope_team UUID REFERENCES teams(id);
-- Apply same pattern to: servers, resources, prompts, gateways, roots
JWT Scope Claims Structure:
{
"sub": "[email protected]",
"jti": "uuid-here",
"exp": 1234567890,
"user_id": "user-uuid",
"teams": [
{"id": "team-uuid", "name": "data-science", "role": "member"},
{"id": "team-uuid-2", "name": "platform", "role": "owner"}
],
"scope": {
"type": "user", // or "team", "global"
"target": "user-uuid" // or team-uuid
}
}
Resource Sharing Workflow:
def share_to_team(resource_id: str, from_scope: str, to_team_id: str, user: User):
# Verify user owns resource in private scope
# Or user is team owner if sharing from team to global
# Create new resource entry with team scope
# Maintain ownership chain for updates
π MCP Standards Check
- Does not affect MCP spec
- No protocol impact
- If deviations exist, please describe them below:
Multi-tenancy operates at the Gateway authorization layer, transparent to MCP protocol. Clients see filtered resource catalogs based on their scope permissions.
πΌοΈ Architecture Diagrams
1 β Scope & Catalog Hierarchy
graph TD
%% Global level
G["π Global Catalog"]:::global
%% Team level
subgraph "Team Catalogs"
direction TB
T1["π§βπ€βπ§ Team Β«data-scienceΒ» Catalog"]:::team
T2["π§βπ€βπ§ Team Β«platformΒ» Catalog"]:::team
%% Private beneath each team
subgraph "Private Catalogs β data-science"
direction TB
A1["πββοΈ Alice Private"]:::private
B1["πββοΈ Bob Private"]:::private
end
subgraph "Private Catalogs β platform"
direction TB
C1["πββοΈ Charlie Private"]:::private
end
end
%% Sharing flows
A1 -->|share| T1
B1 -->|share| T1
C1 -->|share| T2
T1 -->|publish| G
T2 -->|publish| G
classDef global fill:#0057b7,color:#fff,font-weight:bold;
classDef team fill:#39c0ed,color:#000;
classDef private fill:#d3f4ff,color:#000;
2 β Request Flow (sequence)
sequenceDiagram
participant Client as API Client
participant Gateway as MCP Gateway
participant Auth as Auth Middleware
participant Scope as ScopeResolver
participant Catalog as Catalog Service
participant DB as DB (RLS/filters)
Client->>Gateway: HTTP request + JWT
Gateway->>Auth: extract / verify token
Auth-->>Gateway: user_id, teams, scope
Gateway->>Scope: resolve(request, ctx)
Scope-->>Catalog: list_tools(scope_ctx)
Catalog->>DB: SELECT β¦ WHERE scope_type IN (β¦)
DB-->>Catalog: scoped resultset
Catalog-->>Gateway: filtered resources
Gateway-->>Client: response (200/403)
3 β Database (Core RBAC Entities)
erDiagram
USERS ||--o{ TEAM_MEMBERS : has
TEAMS ||--o{ TEAM_MEMBERS : includes
USERS ||--o{ TOOLS : owns_private
TEAMS ||--o{ TOOLS : owns_team
TOOLS }o--|| GATEWAYS : originates
TOOLS }o--|| SERVERS : composed_by
USERS {
UUID id PK
varchar username
varchar email
}
TEAMS {
UUID id PK
varchar name
text description
UUID created_by FK
}
TEAM_MEMBERS {
UUID id PK
UUID user_id FK
UUID team_id FK
enum role
}
TOOLS {
int id PK
varchar name
enum scope_type
UUID scope_owner FK
UUID scope_team FK
}
4 β JWT Scope Claims Mapping
flowchart LR
subgraph JWT Claims
direction TB
sub(("sub"))
uid(("user_id"))
teams(("teams[]"))
scopeClaim(("scope"))
end
sub -->|identifies| ClientUser[Client User]
uid --> AuthService[(Auth Service)]
teams --> ScopeResolver
scopeClaim --> ScopeResolver
ScopeResolver --> Catalog[Catalog Filters]
These diagrams collectively cover scope hierarchy, runtime request flow, schema relationships, and tokenβscope resolution, providing a complete architectural view for the RBAC multi-tenancy feature.
π Alternatives Considered
Approach | Pros | Cons | Decision |
---|---|---|---|
Database-per-tenant | Perfect isolation | Complex deployment, backup | β Too complex |
Row-level security | Database-enforced | PostgreSQL-specific | β Limits DB options |
Application-level scoping | Database agnostic, flexible | More complex code | β Selected |
External IAM integration | Enterprise-ready | Dependency overhead | π Future phase |
π Additional Context
Scope Resolution Order:
- Private: User's personal resources (highest priority)
- Team: Resources shared within user's teams
- Global: Organization-wide shared resources
Permission Matrix:
Role | Private Read | Private Write | Team Read | Team Write | Team Manage | Global Read | Global Publish |
---|---|---|---|---|---|---|---|
User | Own only | Own only | If member | β | β | β | β |
Team Member | Own only | Own only | Team resources | β | β | β | β |
Team Owner | Own only | Own only | Team resources | β | β | β | Team resources |
Platform Admin | All | All | All | All | All | All | All |
Migration Strategy:
- Phase 1: Add scope columns with default 'global' (backward compatible)
- Phase 2: Implement user/team management
- Phase 3: Add private catalog functionality
- Phase 4: Enable team collaboration and sharing
π§ Tasks
-
Phase 1: Database & Schema
- Create
users
,teams
,team_members
tables with proper relationships - Add scope columns (
scope_type
,scope_owner
,scope_team
) to all resource tables:tools
,servers
,resources
,prompts
,gateways
,roots
- Create migration scripts with default
scope_type='global'
for existing data - Add database indexes for efficient scope-based queries
- Create
-
Phase 2: User & Team Management
- Implement user registration/authentication system
- Create team management service:
create_team()
,invite_member()
,remove_member()
,promote_to_owner()
- Add team management API endpoints:
POST /teams
β create teamGET /teams
β list user's teamsPOST /teams/{id}/members
β invite memberPUT /teams/{id}/members/{user_id}
β change roleDELETE /teams/{id}/members/{user_id}
β remove member
-
Phase 3: Scope-Aware Resource Management
- Extend all service classes with scope filtering:
ToolService.list_tools(user_context)
β filter by scope permissionsServerService.create_server(data, scope_context)
β create in appropriate scope
- Implement resource sharing service:
share_to_team(resource, from_scope, to_team)
publish_to_global(resource, from_scope)
- Update all API endpoints with scope context:
- Add
?scope=private|team:{id}|global
query parameter - Filter results based on user's scope permissions
- Add
- Extend all service classes with scope filtering:
-
Phase 4: Authentication & Authorization
- Extend JWT token creation with user/team context
- Update
require_auth()
to extract and validate scope permissions - Create scope-checking decorators:
@require_scope_access(scope_type, scope_id)
@require_team_role(team_id, min_role)
- Implement comprehensive access control for all endpoints
-
Phase 5: Admin UI Overhaul
- Add scope selector to top navigation (Private | Team: X | Global)
- Create team management interface:
- Team list, create team, member management
- Role assignment and team settings
- Add resource sharing workflows:
- "Share to Team" buttons on private resources
- "Publish to Global" for team owners
- Ownership indicators and sharing status
- Update all resource listing pages with scope filtering
-
Phase 6: Advanced Features
- Implement resource templates and cloning across scopes
- Add audit logging for all scope changes and access
- Create dashboard showing resource usage by scope
- Add team-scoped API tokens (building on [SECURITY FEATURE]: Per-Virtual-Server API Keys with Scoped AccessΒ #282)
- Implement resource approval workflows for global publishing
-
Phase 7: Testing & Documentation
- Comprehensive unit tests for scope resolution logic
- Integration tests for multi-tenant scenarios
- Security tests for cross-scope access prevention
- Performance tests for scope-filtered queries
- Complete documentation overhaul with multi-tenancy examples
- Migration guide for existing single-tenant deployments
π References
- Multi-Tenant SaaS Architecture β AWS Multi-Tenant Guide
- RBAC Design Patterns β NIST RBAC Standard
- Database Multi-Tenancy β Multi-Tenant Data Architecture
- JWT Claims for Authorization β RFC 7519 JWT Claims
π§© Additional Notes
- π¨ Breaking Change: This is a major architectural shift requiring careful migration planning
- Performance Impact: Scope filtering on all queries requires optimized database indexes
- Security Critical: Improper scope isolation could lead to data leakage between tenants
- Phased Rollout: Implement incrementally to maintain stability and allow testing
- Backward Compatibility: Existing single-tenant deployments should continue working with global scope
- Future Extensions: Foundation for enterprise features like SSO, advanced RBAC, compliance auditing