-
Notifications
You must be signed in to change notification settings - Fork 615
[EPIC][TESTING][SECURITY]: RBAC automated regression suite (visibility, teams, token scope) #2387
Description
🧪 Epic: RBAC Automated Regression Suite (Visibility, Teams, Token Scope)
Goal
Implement a comprehensive automated RBAC regression suite that validates the two-layer security model (Token Scoping + RBAC Permissions), visibility enforcement, team-based access control, ownership rules, and MCP permissive mode across all protected endpoint categories: the 6 MCP resource types (tools, servers, resources, prompts, gateways, A2A agents), user management, team lifecycle, token management, RBAC role administration, SSO provider management, and admin-only operations. This suite encodes the full security contract into deterministic, parameterized tests that run in CI and prevent regressions.
Why Now?
ContextForge's RBAC spans 4 core modules (auth.py, token_scoping.py, rbac.py, permission_service.py), 5 built-in roles, 73+ permissions, 3 visibility tiers, and 6 resource types. Recent fixes (#2883, #2891, #2900) exposed critical gaps where the interaction between these layers produced incorrect access decisions. Current test coverage is strong on individual components but lacks:
- Cross-layer integration tests: Token scoping (Layer 1) and RBAC (Layer 2) are tested in isolation, but their intersection is not systematically validated
- Role-based behavior verification: No tests verify that each built-in role (
platform_admin,team_admin,developer,viewer,platform_viewer) enforces its exact permission set across all resource types - Parameterized regression matrices: Manual testing catches bugs but doesn't prevent regressions — we need test matrices covering
visibility x token_type x role x resource_type x operation - Edge case coverage: Personal team exclusion, expired roles, deleted teams, cache invalidation on role changes, and multi-team token team derivation tiers are untested or minimally tested
- Management endpoint coverage: User management (
admin.user_management), team lifecycle (teams.*), token management (tokens.*), RBAC administration, and SSO provider management have zero or minimal automated RBAC test coverage despite being high-value attack surfaces - E2E role-based flows: Playwright tests only cover admin and developer roles for gateway/server creation — no viewer denial flows, no cross-team access tests, no multi-resource-type coverage
📖 User Stories
US-1: QA Engineer - Automated Coverage of the RBAC Security Contract
As a QA Engineer
I want automated tests that encode the full RBAC permission matrix
So that every release validates access behavior without manual effort
Acceptance Criteria:
Given the RBAC test suite runs against a fresh database
And test users exist for each built-in role (platform_admin, team_admin, developer, viewer, platform_viewer)
And test resources exist with all visibility levels (public, team, private)
When the parameterized test matrix executes
Then every combination of (role x resource_type x operation x visibility) is tested
And the expected status codes match the RBAC permission model
And all tests pass deterministically without manual intervention
And test execution completes in under 5 minutesTechnical Requirements:
- Parameterized test matrix across all 6 MCP resource types AND management endpoints (user, team, token, RBAC, SSO, admin)
- Deterministic setup and cleanup with module-scoped fixtures
- Coverage across all visibility levels (public, team, private)
- Coverage of all endpoint categories: MCP resources, user management, team lifecycle, token management, RBAC administration, SSO providers, admin-only operations
- Tests align with the documented permission model in
docs/manage/rbac.md
US-2: Security Engineer - Token Scope Intersection with RBAC
As a Security Engineer
I want automated tests ensuring token scopes can only restrict, never expand, RBAC permissions
So that a compromised token cannot escalate privileges beyond its scope
Acceptance Criteria:
Given a developer user with team-scoped RBAC permissions
And an API token scoped to only Team A
Scenario: RBAC allow + Token allow = ALLOW
When the user creates a tool in Team A
Then the request succeeds (200/201)
Scenario: RBAC allow + Token deny = DENY
When the user creates a tool in Team B (not in token scope)
Then the request is denied (403)
And the error message references token scope, not RBAC
Scenario: RBAC deny + Token allow = DENY
Given a viewer user with a token scoped to Team A
When the viewer attempts to create a tool in Team A
Then the request is denied (403)
And the error message references insufficient permissions
Scenario: Token scope restricts visibility
Given a team-scoped tool belongs to Team B
When the developer lists tools with their Team A token
Then the Team B tool is not included in resultsTechnical Requirements:
- Test all 4 quadrants of the scope/permission matrix
- Verify error messages distinguish scope denial from permission denial
- Test with session tokens (DB-resolved teams) and API tokens (JWT-embedded teams)
- Test multi-team API tokens accessing non-primary teams
US-3: Platform Engineer - Built-in Role Behavior Verification
As a Platform Engineer
I want tests that verify each built-in role enforces its exact permission boundaries
So that role changes don't silently expand or contract access
Acceptance Criteria:
Given the 5 built-in roles are bootstrapped:
| Role | Scope | Key Permissions |
| platform_admin | global | * (wildcard - all permissions) |
| team_admin | team | 34 permissions (full team management) |
| developer | team | 32 permissions (no teams.delete/manage) |
| viewer | team | 8 permissions (read-only + admin.dashboard)|
| platform_viewer | global | 8 permissions (read-only global) |
When each role attempts CRUD operations on all resource types
Then the access decisions match the role's permission set exactly:
# platform_admin: full access
And platform_admin can create, read, update, delete all resource types
And platform_admin can manage RBAC roles and user assignments
And platform_admin can access admin.system_config
# team_admin: full team access
And team_admin can create, read, update, delete tools/servers/resources/prompts/gateways/a2a in their team
And team_admin can manage team members (teams.manage_members)
And team_admin can delete teams (teams.delete)
And team_admin CANNOT manage RBAC roles globally
And team_admin CANNOT access admin.system_config
# developer: create/read/update/delete but no team management
And developer can create, read, update, delete tools/servers/resources/prompts/gateways/a2a in their team
And developer CANNOT delete teams (no teams.delete)
And developer CANNOT manage team members (no teams.manage_members)
# viewer: read-only
And viewer can read tools/servers/resources/prompts/gateways/a2a
And viewer can access admin.dashboard
And viewer CANNOT create, update, or delete any resource type
And viewer CANNOT execute tools (no tools.execute)
# platform_viewer: global read-only
And platform_viewer can read tools/servers/resources/prompts/gateways/a2a across all teams
And platform_viewer CANNOT create, update, or delete any resource typeTechnical Requirements:
- Test each role against each CRUD operation for each resource type
- Verify exact permission boundary (not just "some access works")
- Test both
allow_admin_bypass=True(API) andallow_admin_bypass=False(Admin UI) paths - Verify role changes (adding/removing permissions) take effect
US-4: Security Auditor - Visibility Enforcement Across Resource Types
As a Security Auditor
I want tests verifying that visibility levels (public/team/private) are enforced consistently across all resource types
So that no resource type has a visibility bypass that could leak data
Acceptance Criteria:
Given resources of each type exist with each visibility level:
| Resource Type | Visibility | Team | Owner |
| tool | public | Team A | admin@example.com |
| tool | team | Team A | admin@example.com |
| tool | private | Team A | admin@example.com |
| server | public | Team A | admin@example.com |
| server | team | Team A | admin@example.com |
| server | private | Team A | admin@example.com |
| resource | public | Team A | admin@example.com |
| ... (all 6 types x 3 visibilities) |
Scenario: Public resources visible to all
When any authenticated user lists <resource_type>
Then public resources appear in the results
Scenario: Team resources visible only to team members
Given user is a member of Team A
When user lists <resource_type>
Then team resources from Team A appear in the results
And team resources from Team B do NOT appear
Scenario: Private resources visible only to owner
Given user is NOT the resource owner
When user lists <resource_type>
Then private resources do NOT appear in the results
Scenario: Admin bypass sees all visibilities
Given admin has teams=null token (admin bypass)
When admin lists <resource_type>
Then public, team, AND private resources all appearTechnical Requirements:
- Cover all 6 resource types: tools, servers, resources, prompts, gateways, A2A agents
- Test list endpoints (filtering) and get-by-ID endpoints (direct access)
- Verify token scoping middleware correctly filters per visibility
- Test public-only tokens (
teams=[]) see only public resources - Test cross-team visibility (resources from teams user doesn't belong to)
US-5: Developer - Multi-Team Session Token Behavior
As a Developer working across multiple teams
I want tests verifying that session tokens correctly derive team context
So that my operations are authorized against the correct team's permissions
Acceptance Criteria:
Given a developer belongs to Team A and Team B
And has developer role on both teams
Scenario: Team derived from existing resource (Tier 1)
Given a tool exists in Team B with id "tool-123"
When the developer updates tool "tool-123" via session token
Then the team is derived from the tool's team_id (Team B)
And the developer's Team B permissions are checked
And the operation succeeds
Scenario: Team derived from request payload (Tier 3)
When the developer creates a tool with team_id=Team B in the payload
Then the team is derived from the payload
And the developer's Team B permissions are checked
And the operation succeeds
Scenario: No team derivable - check_any_team fallback
When the developer lists tools without a team filter
Then check_any_team=True is used
And permissions are checked across all user's team-scoped roles
And personal team roles are EXCLUDED from the check
And the operation succeeds
Scenario: Personal team exclusion prevents privilege escalation
Given a viewer belongs to Team A with viewer role
And a personal team exists with team_admin role (auto-created)
When the viewer lists tools (check_any_team=True)
Then the personal team's team_admin role is NOT included
And the viewer CANNOT create tools (viewer role has no tools.create)Technical Requirements:
- Test all 3 tiers of team derivation
- Test
check_any_team=Truewith personal team exclusion (fix: exclude personal team roles from check_any_team RBAC aggregation #2900) - Test session token team resolution from DB (not JWT-embedded)
- Test multi-team API tokens (JWT-embedded teams) for filtering and creation
- Verify correct team context propagation through the middleware chain
US-6: Operations Engineer - MCP Permissive Mode and Proxy Auth
As an Operations Engineer
I want tests verifying MCP permissive mode and proxy authentication behavior
So that deployments behind reverse proxies correctly handle authentication
Acceptance Criteria:
Scenario: MCP permissive mode (MCP_CLIENT_AUTH_ENABLED=false)
Given MCP_CLIENT_AUTH_ENABLED=false
And TRUST_PROXY_AUTH=true
When a request arrives with X-Authenticated-User header
Then the user is authenticated from the proxy header
And RBAC checks use the proxy-provided identity
And no JWT validation occurs
Scenario: Anonymous mode (AUTH_REQUIRED=false)
Given AUTH_REQUIRED=false
When a request arrives without credentials
Then the request is allowed
And the user is treated as anonymous
Scenario: Standard mode rejects unauthenticated requests
Given AUTH_REQUIRED=true (default)
When a request arrives without credentials
Then the request is denied (401)Technical Requirements:
- Test
MCP_CLIENT_AUTH_ENABLEDflag behavior - Test
TRUST_PROXY_AUTHwith header injection - Test
AUTH_REQUIRED=falseanonymous access - Verify proxy auth header cannot be injected when
TRUST_PROXY_AUTH=false
US-7: Security Engineer - Ownership Enforcement
As a Security Engineer
I want tests verifying that resource ownership is enforced for update and delete operations
So that users cannot modify resources they don't own
Acceptance Criteria:
Given admin creates a tool in Team A
And developer is a member of Team A with developer role
Scenario: Owner can update their resource
When admin updates the tool
Then the operation succeeds (200)
Scenario: Non-owner with same role cannot update
When developer attempts to update admin's tool
Then the operation is denied (403) with ownership error
Scenario: Team admin can manage team resources
Given team_admin is a member of Team A with team_admin role
When team_admin updates admin's tool (team-scoped)
Then the operation succeeds (team_admin has team management rights)
Scenario: Admin bypass overrides ownership
Given allow_admin_bypass=True (API endpoint)
When admin updates any tool regardless of ownership
Then the operation succeedsTechnical Requirements:
- Test owner vs non-owner for update and delete on all resource types
- Test team_admin override for team-scoped resources
- Test admin bypass for ownership checks
- Verify ownership checks work independently of RBAC permission checks
US-8: Security Engineer - User Management RBAC
As a Security Engineer
I want tests verifying that user management operations are restricted to authorized administrators
So that unauthorized users cannot create, modify, or delete user accounts
Acceptance Criteria:
Scenario: Only admin.user_management holders can create users
Given a platform_admin with admin.user_management permission
And a developer WITHOUT admin.user_management permission
When platform_admin creates a user via POST /auth/email/admin/users
Then the operation succeeds (201)
When developer creates a user via POST /auth/email/admin/users
Then the operation is denied (403)
Scenario: User CRUD operations require admin.user_management
Given users exist in the system
When a non-admin lists users via GET /auth/email/admin/users
Then the request is denied (403)
When a non-admin deletes a user via DELETE /auth/email/admin/users/{email}
Then the request is denied (403)
Scenario: Self-service endpoints work for all authenticated users
When any authenticated user calls GET /auth/email/me
Then they receive their own profile (200)
When any authenticated user calls POST /auth/email/change-password
Then they can change their own password
Scenario: Admin cannot delete themselves or the last admin
When admin attempts to delete their own account
Then the operation is denied
When admin attempts to delete the last active admin
Then the operation is deniedTechnical Requirements:
- Test all 6 admin user management endpoints with each role
- Test self-service endpoints (me, change-password, events) work for all users
- Test admin self-deletion and last-admin protection
- Test public registration when
PUBLIC_REGISTRATION_ENABLED=true/false
US-9: Platform Engineer - Team Lifecycle RBAC
As a Platform Engineer
I want tests verifying the complete team lifecycle respects RBAC and ownership rules
So that team creation, invitations, member management, and deletion are properly controlled
Acceptance Criteria:
Scenario: Any authenticated user can create a team (fallback permission)
Given any authenticated user (even a viewer)
When they create a team via POST /teams/
Then the operation succeeds (201)
And the creator becomes the team owner
Scenario: Only team owners can manage members
Given a team owned by user A
And user B is a member (not owner)
When user B invites someone via POST /teams/{id}/invitations
Then the operation is denied (403)
When user A invites someone via POST /teams/{id}/invitations
Then the operation succeeds (201)
Scenario: Only team owners can delete teams
Given a team owned by user A
When user B (team_admin role, not owner) deletes the team
Then the operation is denied (403)
When user A deletes the team
Then the operation succeeds (200)
Scenario: Team invitation flow
When an owner invites a user to a team
And the invited user accepts via POST /teams/invitations/{token}/accept
Then the user becomes a member
When the owner cancels a pending invitation
Then the invitation is removed
Scenario: Join request flow for public teams
Given a public team
When any user sends POST /teams/{id}/join
Then a join request is created
When the owner approves via POST /teams/{id}/join-requests/{id}/approve
Then the user becomes a member
Scenario: Users can leave teams they belong to
Given a user is a member of a team
When the user calls DELETE /teams/{id}/leave
Then the user is removed from the team
But users cannot leave personal teams
Scenario: Team visibility filtering
Given public and private teams exist
When a non-member lists teams
Then only public teams and their own teams appear
When an admin with bypass lists teams
Then all teams appearTechnical Requirements:
- Test all 18+ team endpoints with each role
- Test ownership enforcement (owner vs member vs non-member)
- Test invitation create/accept/cancel flow
- Test join request create/approve/reject flow
- Test team visibility (public vs private teams in list)
- Test personal team restrictions (cannot leave, cannot delete)
US-10: Security Engineer - Token Management RBAC and Security Boundaries
As a Security Engineer
I want tests verifying token management respects RBAC and enforces the API token security boundary
So that tokens cannot be used to escalate privileges
Acceptance Criteria:
Scenario: API tokens CANNOT create other API tokens
Given a user authenticates with an API token
When they attempt to create a new token via POST /tokens
Then the operation is denied (403)
And the error indicates session authentication is required
Scenario: Session tokens CAN create API tokens
Given a user authenticates with a session token (login JWT)
When they create a new API token via POST /tokens
Then the operation succeeds (201)
And the new token's permissions are limited to the user's permissions (scope containment)
Scenario: Users can only manage their own tokens
Given user A has an API token
When user B attempts to list user A's tokens
Then user B only sees their own tokens
When user B attempts to revoke user A's token
Then the operation is denied (403)
Scenario: Admin can manage all tokens
Given admin endpoints exist for token management
When an admin lists all tokens via GET /tokens/admin/all
Then all tokens across all users are returned
When an admin revokes any token via DELETE /tokens/admin/{id}
Then the token is revoked regardless of owner
Scenario: Scope containment prevents privilege escalation
Given a developer user with tools.read and tools.create permissions
When the developer creates an API token with tools.delete permission
Then the token creation fails or the permission is stripped
Because users cannot grant permissions they don't have
Scenario: Team token management
Given a team owned by user A
When user A creates a team token via POST /tokens/teams/{id}
Then the team token is created with team scope
When a non-owner attempts to create a team token
Then the operation is deniedTechnical Requirements:
- Test API token vs session token security boundary
- Test scope containment (cannot escalate permissions in token creation)
- Test user isolation (users only see/manage their own tokens)
- Test admin token management endpoints
- Test team token creation with ownership enforcement
- Test anonymous and unauthenticated access is blocked
US-11: Security Auditor - RBAC Administration Controls
As a Security Auditor
I want tests verifying that RBAC role management is restricted to platform administrators
So that unauthorized users cannot create roles or grant themselves elevated permissions
Acceptance Criteria:
Scenario: Only platform admins can create/modify roles
Given role CRUD uses @require_admin_permission() (allow_admin_bypass=False)
When a developer attempts to create a role via POST /rbac/roles
Then the operation is denied (403)
When a platform_admin creates a role
Then the operation succeeds
Scenario: System roles cannot be deleted
Given built-in roles (platform_admin, team_admin, developer, viewer, platform_viewer)
And they have is_system_role=True
When admin attempts to delete a system role
Then the operation is denied
Scenario: Role assignment requires admin.user_management
When a non-admin attempts to assign a role via POST /rbac/users/{email}/roles
Then the operation is denied (403)
When an admin assigns a role
Then the operation succeeds
Scenario: Permission checking requires admin.security_audit
When a non-admin checks another user's permissions via POST /rbac/permissions/check
Then the operation is denied (403)
Scenario: Self-service RBAC endpoints work for all users
When any authenticated user calls GET /rbac/my/roles
Then they see their own roles
When any authenticated user calls GET /rbac/my/permissions
Then they see their own effective permissionsTechnical Requirements:
- Test
@require_admin_permission()enforcement (stricter than@require_permission) - Test system role protection (cannot delete built-in roles)
- Test role assignment/revocation with correct permissions
- Test permission audit endpoints require
admin.security_audit - Test self-service endpoints work for all authenticated users
US-12: Platform Admin - SSO Provider and Admin-Only Operations
As a Platform Administrator
I want tests verifying that SSO provider management and admin-only operations require correct permissions
So that critical infrastructure configuration cannot be modified by unauthorized users
Acceptance Criteria:
Scenario: SSO provider management requires admin.sso_providers:* permissions
When a non-admin creates an SSO provider via POST /auth/sso/admin/providers
Then the operation is denied (403)
When a platform_admin creates an SSO provider
Then the operation succeeds
And CRUD operations on SSO providers are all protected
Scenario: Observability endpoints require admin.system_config
When a developer queries traces via GET /observability/traces
Then the operation is denied (403)
When a platform_admin queries traces
Then the operation succeeds
Scenario: LLM provider management requires admin.system_config
When a non-admin creates an LLM provider via POST /llm/providers
Then the operation is denied (403)
Scenario: SSO user approval requires admin.user_management
When a non-admin approves a pending SSO user
Then the operation is denied (403)
When an admin approves a pending SSO user
Then the user is activated
Scenario: Admin UI routes use allow_admin_bypass=False
Given admin UI endpoints set allow_admin_bypass=False
When a user with is_admin=True but no explicit roles accesses admin UI
Then access depends on explicit role permissions, not admin flagTechnical Requirements:
- Test SSO provider CRUD with
admin.sso_providers:create/read/update/delete - Test observability endpoints with
admin.system_config - Test LLM provider/model management with
admin.system_config - Test SSO approval workflow with
admin.user_management - Test admin UI
allow_admin_bypass=Falseenforcement - Test toolops and cancellation endpoints with
admin.system_config
🏗 Architecture
Test Layer Architecture
graph TD
subgraph "Test Layers"
A[Unit Tests<br/>Permission + Token Scoping Logic]
B[Service Tests<br/>list_*/create_*/update_*/delete_* filters]
C[Integration Tests<br/>HTTP endpoints + auth chain]
D[E2E Tests<br/>Playwright UI + REST API]
end
subgraph "Endpoint Categories Under Test"
E1[MCP Resources<br/>tools, servers, resources,<br/>prompts, gateways, A2A]
E2[Management<br/>users, teams, tokens,<br/>RBAC admin, SSO]
E3[Admin-Only<br/>observability, LLM,<br/>toolops, cancellation]
end
subgraph "Cross-Cutting Matrices"
M1[Role x Permission Matrix]
M2[Visibility x Token Type Matrix]
M3[Resource Type x Operation Matrix]
M4[Team Derivation Tier Matrix]
M5[Security Boundaries<br/>API token boundary,<br/>scope containment,<br/>ownership enforcement]
end
A --> B --> C --> D
E1 --> C
E2 --> C
E3 --> C
M1 --> A
M2 --> B
M3 --> C
M4 --> C
M5 --> C
RBAC Security Model Under Test
graph LR
subgraph "Layer 1: Token Scoping<br/>(What can I SEE?)"
TS1[Admin Bypass<br/>teams=null + is_admin]
TS2[Team Scoped<br/>teams=t1,t2]
TS3[Public Only<br/>teams=empty]
end
subgraph "Layer 2: RBAC<br/>(What can I DO?)"
R1[platform_admin<br/>wildcard *]
R2[team_admin<br/>34 perms]
R3[developer<br/>32 perms]
R4[viewer<br/>8 perms]
R5[platform_viewer<br/>8 perms global]
end
subgraph "Visibility"
V1[Public]
V2[Team]
V3[Private]
end
TS1 --> V1 & V2 & V3
TS2 --> V1 & V2
TS3 --> V1
R1 --> |allow_admin_bypass| TS1
R2 --> TS2
R3 --> TS2
R4 --> TS2
R5 --> TS2
Permission Matrix (Operations x Roles)
MCP Resource Types
platform_admin team_admin developer viewer platform_viewer
tools.create ✅ ✅ ✅ ❌ ❌
tools.read ✅ ✅ ✅ ✅ ✅
tools.update ✅ ✅ ✅ ❌ ❌
tools.delete ✅ ✅ ✅ ❌ ❌
tools.execute ✅ ✅ ✅ ❌ ❌
servers.create ✅ ✅ ✅ ❌ ❌
servers.read ✅ ✅ ✅ ✅ ✅
servers.update ✅ ✅ ✅ ❌ ❌
servers.delete ✅ ✅ ✅ ❌ ❌
gateways.create ✅ ✅ ✅ ❌ ❌
gateways.read ✅ ✅ ✅ ✅ ✅
gateways.update ✅ ✅ ✅ ❌ ❌
gateways.delete ✅ ✅ ✅ ❌ ❌
resources.create ✅ ✅ ✅ ❌ ❌
resources.read ✅ ✅ ✅ ✅ ✅
resources.update ✅ ✅ ✅ ❌ ❌
resources.delete ✅ ✅ ✅ ❌ ❌
prompts.create ✅ ✅ ✅ ❌ ❌
prompts.read ✅ ✅ ✅ ✅ ✅
prompts.update ✅ ✅ ✅ ❌ ❌
prompts.delete ✅ ✅ ✅ ❌ ❌
a2a.create ✅ ✅ ✅ ❌ ❌
a2a.read ✅ ✅ ✅ ✅ ✅
a2a.update ✅ ✅ ✅ ❌ ❌
a2a.delete ✅ ✅ ✅ ❌ ❌
Team Management
platform_admin team_admin developer viewer platform_viewer Fallback
teams.create ✅ ✅ ✅ ✅ ✅ ✅ (all users)
teams.read ✅ ✅ ✅ ✅ ✅ ✅ (members)
teams.update ✅ ✅ ❌ ❌ ❌ ✅ (owners)
teams.delete ✅ ✅ ❌ ❌ ❌ ✅ (owners)
teams.join ✅ ✅ ✅ ✅ ❌ —
teams.manage_members ✅ ✅ ❌ ❌ ❌ ✅ (owners)
Notes:
- Team creation uses fallback permission — all authenticated users can create teams
- Team update/delete/manage_members has RBAC check AND ownership check (must be team owner)
- Admin bypass can override ownership checks on API routes
- Personal teams cannot be left or deleted
User Management (admin.user_management)
platform_admin team_admin developer viewer platform_viewer
POST /auth/email/admin/users ✅ ❌ ❌ ❌ ❌
GET /auth/email/admin/users ✅ ❌ ❌ ❌ ❌
PUT /auth/email/admin/users/{e} ✅ ❌ ❌ ❌ ❌
DELETE /auth/email/admin/users/{e} ✅ ❌ ❌ ❌ ❌
GET /auth/email/me ✅ ✅ ✅ ✅ ✅ (self-service)
POST /auth/email/change-password ✅ ✅ ✅ ✅ ✅ (self-service)
Token Management (tokens.*)
platform_admin team_admin developer viewer platform_viewer Fallback
tokens.create ✅ ✅ ✅ ✅ ✅ ✅ (own tokens)
tokens.read ✅ ✅ ✅ ✅ ✅ ✅ (own tokens)
tokens.update ✅ ✅ ✅ ✅ ✅ ✅ (own tokens)
tokens.revoke ✅ ✅ ✅ ✅ ✅ ✅ (own tokens)
GET /tokens/admin/all ✅ ❌ ❌ ❌ ❌ — (custom admin check)
DELETE /tokens/admin/{id} ✅ ❌ ❌ ❌ ❌ — (custom admin check)
Notes:
- Token management uses fallback permission — all users can manage their own tokens
- Security boundary: API tokens CANNOT create new API tokens (session token required)
- Scope containment: Created tokens can only have permissions the creator has
- User isolation: Users only see their own tokens (admin sees all)
RBAC Administration
platform_admin team_admin developer viewer platform_viewer
POST /rbac/roles (create) ✅* ❌ ❌ ❌ ❌
PUT /rbac/roles/{id} (update) ✅* ❌ ❌ ❌ ❌
DELETE /rbac/roles/{id} (delete) ✅* ❌ ❌ ❌ ❌
GET /rbac/roles (list) ✅ ❌ ❌ ❌ ❌
POST /rbac/users/{e}/roles ✅ ❌ ❌ ❌ ❌
DELETE /rbac/users/{e}/roles/{id} ✅ ❌ ❌ ❌ ❌
POST /rbac/permissions/check ✅ ❌ ❌ ❌ ❌
GET /rbac/my/roles ✅ ✅ ✅ ✅ ✅ (self-service)
GET /rbac/my/permissions ✅ ✅ ✅ ✅ ✅ (self-service)
*Uses @require_admin_permission() with allow_admin_bypass=False — requires explicit platform_admin role, not just is_admin flag
SSO Provider Management (admin.sso_providers:*)
platform_admin team_admin developer viewer platform_viewer
POST /auth/sso/admin/providers ✅ ❌ ❌ ❌ ❌
GET /auth/sso/admin/providers ✅ ❌ ❌ ❌ ❌
PUT /auth/sso/admin/providers/{} ✅ ❌ ❌ ❌ ❌
DELETE /auth/sso/admin/providers/{} ✅ ❌ ❌ ❌ ❌
GET/POST /auth/sso/pending-approvals ✅ ❌ ❌ ❌ ❌
GET /auth/sso/providers (public) ✅ ✅ ✅ ✅ ✅
Admin-Only Operations (admin.system_config)
platform_admin team_admin developer viewer platform_viewer
/observability/* ✅ ❌ ❌ ❌ ❌
/llm/providers/* ✅ ❌ ❌ ❌ ❌
/llm/models/* ✅ ❌ ❌ ❌ ❌
/toolops/* ✅ ❌ ❌ ❌ ❌
/cancellation/* ✅ ❌ ❌ ❌ ❌
admin.dashboard (UI) ✅ ✅ ✅ ✅ ✅
admin.overview (UI) ✅ ❌ ❌ ❌ ❌
admin.events (UI) ✅ ❌ ❌ ❌ ❌
admin.plugins (UI) ✅ ❌ ❌ ❌ ❌
📊 Current Test Coverage Baseline
Existing RBAC Tests (~294 unit tests + 15 E2E)
| Test File | Tests | Coverage Area |
|---|---|---|
tests/unit/mcpgateway/middleware/test_rbac.py |
~105 | RBAC decorators, team derivation, multi-team session tokens, plugin hooks |
tests/unit/mcpgateway/middleware/test_token_scoping.py |
~93 | Token normalization, visibility, IP/time restrictions, resource ownership |
tests/unit/mcpgateway/services/test_permission_service*.py |
~96 | Permission checks, caching, roles, admin bypass, audit, fallback |
tests/unit/mcpgateway/routers/test_rbac_router.py |
~20 | RBAC API endpoints (roles CRUD, assignment, permission check) |
tests/integration/test_rbac_ownership_http.py |
~30 | HTTP-level ownership enforcement, team_id fallback |
tests/playwright/test_rbac_permissions.py |
~15 | E2E UI: developer create gateway/server, admin delete, REST API |
Identified Gaps
| Gap | Priority | Description |
|---|---|---|
| User management RBAC | CRITICAL | Zero automated tests for admin.user_management endpoints (create/delete/update users) |
| Team lifecycle RBAC | CRITICAL | No tests for team invitation flow, member management, join requests, ownership enforcement, personal team restrictions |
| Token management RBAC | CRITICAL | No tests for API token security boundary (API tokens can't create tokens), scope containment, user isolation |
| RBAC admin endpoints | HIGH | No tests for @require_admin_permission() enforcement on role CRUD, system role deletion protection |
| SSO/admin-only endpoints | HIGH | No tests for admin.sso_providers:*, admin.system_config on observability/LLM/toolops endpoints |
| Built-in role behavior | HIGH | No tests verify each role's exact permission boundary across all resource types |
| Visibility x token type matrix | HIGH | Token scoping + visibility tested per-type but not as a parameterized matrix |
| Cross-layer integration | HIGH | Token scoping (L1) + RBAC (L2) intersection not systematically tested |
| Personal team exclusion | HIGH | Only 3 unit tests (#2900) — no integration or E2E coverage |
| Multi-team token team derivation | MEDIUM | Tier 1/2/3 derivation has unit tests but no integration tests |
allow_admin_bypass=False |
MEDIUM | Only 1 Playwright test — no unit/integration coverage |
| Role lifecycle | MEDIUM | No tests for expired roles, inactive roles, role removal mid-session |
| Cache invalidation | MEDIUM | No tests for cache clear on role/membership changes |
| A2A agent RBAC | MEDIUM | Minimal test coverage for A2A-specific RBAC paths |
| Fallback permissions | MEDIUM | No tests for implicit grants (teams.create for all users, tokens.* for own tokens, team owner permissions) |
| Ownership enforcement | LOW | Integration tests exist but not parameterized across all resource types |
📋 Implementation Tasks
Phase 1: Test Infrastructure and Fixtures
-
Shared RBAC test fixtures (
tests/conftest_rbac.pyor fixtures in existing conftest)-
rbac_team_a/rbac_team_b— Two test teams (module-scoped) -
rbac_platform_admin— User withplatform_adminrole (global) -
rbac_team_admin— User withteam_adminrole on Team A -
rbac_developer— User withdeveloperrole on Team A -
rbac_viewer— User withviewerrole on Team A -
rbac_platform_viewer— User withplatform_viewerrole (global) -
rbac_outsider— User with no team membership (only personal team) -
rbac_multi_team_dev— User withdeveloperrole on both Team A and Team B - Token factories:
make_session_token(user),make_api_token(user, teams),make_admin_bypass_token() - Resource factories:
make_tool(team, visibility, owner),make_server(...), etc. for all 6 types
-
-
Parameterized test helpers
-
RESOURCE_TYPES— List of(type_name, create_endpoint, list_endpoint, create_body_factory)tuples -
ROLES— List of(role_name, fixture_name, expected_permissions)tuples -
VISIBILITIES—["public", "team", "private"] -
TOKEN_TYPES—["admin_bypass", "team_scoped", "public_only", "multi_team", "session"] -
OPERATIONS—["create", "read", "update", "delete", "list"]
-
Phase 2: Unit Tests — Permission Service Matrix
-
Role permission boundary tests (
test_permission_service_roles.py)- Parameterized: for each
(role, permission)pair, verifycheck_permission()returns the expected result - Test
platform_adminwildcard grants all permissions - Test
team_adminhas exactly 34 permissions (not more, not less) - Test
developerhas exactly 32 permissions (noteams.delete, noteams.manage_members) - Test
viewerhas exactly 8 permissions (read-only) - Test
platform_viewerhas exactly 8 global permissions - Test permission removal: removing a permission from a role denies access
- Parameterized: for each
-
Admin bypass tests
-
allow_admin_bypass=True+is_admin=True→ bypass permission check -
allow_admin_bypass=False+is_admin=True→ still require explicit permission -
allow_admin_bypass=True+is_admin=False→ no bypass -
_is_user_admin()correctly identifies admin vs non-admin
-
-
Personal team exclusion tests (expand beyond fix: exclude personal team roles from check_any_team RBAC aggregation #2900)
-
check_any_team=Trueexcludes personal team roles from aggregation -
check_any_team=Falsewith explicit personal team ID still works - Viewer with personal team
team_admincannot create resources viacheck_any_team - Developer with personal team
team_adminuses developer permissions (not team_admin)
-
-
Fallback permission tests
- All users can create teams (
teams.createfallback) - All users can read teams they belong to (
teams.readfallback) - Team owners get full
teams.*permissions - All users can manage their own tokens (
tokens.*fallback)
- All users can create teams (
Phase 3: Unit Tests — Token Scoping Matrix
-
Visibility x token type parameterized matrix (
test_token_scoping_matrix.py)- Parameterized:
(resource_type, visibility, token_type)→ expected access decision - Admin bypass (
teams=null+is_admin=true) → sees all visibilities - Team-scoped token → sees public + own team resources
- Public-only token (
teams=[]) → sees only public resources - Multi-team token → sees public + all scoped teams' resources
- Cover all 6 resource types: tools, servers, resources, prompts, gateways, A2A agents
- Parameterized:
-
Token teams normalization edge cases
- Missing
teamskey →[](public-only) -
teams: null+is_admin: false→[](no bypass for non-admins) -
teams: null+is_admin: true→None(admin bypass) -
teams: []→[](explicit public-only) -
teams: [dict_format]→ normalized to ID strings -
teams: ["id1", "id2"]→ as-is
- Missing
-
Session vs API token behavior
- Session tokens resolve teams from DB (not embedded in JWT)
- API tokens use JWT-embedded teams
- Single-team API token sets
request.state.team_idto that team - Multi-team API token sets
request.state.team_id = None - Session token always sets
request.state.team_id = None
Phase 4: Unit Tests — RBAC Middleware Team Derivation
-
Tier 1: Resource-based derivation (
_derive_team_from_resource)- Derive team from existing tool's
team_id(update/delete endpoints) - Derive team from existing server's
team_id - Handle missing resource (returns None)
- Handle resource without
team_id(returns None)
- Derive team from existing tool's
-
Tier 2: URL path parameter
- Extract
team_idfrom URL kwargs - Fallback to
user_context.get("team_id")
- Extract
-
Tier 3: Payload-based derivation (
_derive_team_from_payload)- Extract
team_idfrom Pydantic model (e.g.,ServerCreate.team_id) - Extract from nested body (e.g.,
{"tool": {...}, "team_id": "t1"}) - Extract from form data (admin UI POST)
- Handle missing
team_idin payload (returns None)
- Extract
-
Fallback:
check_any_team=True- Triggered when all tiers return None
- Aggregates permissions across all user's team-scoped roles
- Excludes personal team roles
- Works for list endpoints and create endpoints without explicit team
Phase 5: Integration Tests — HTTP Endpoint Matrix
-
CRUD permission matrix (
test_rbac_http_matrix.py)- Parameterized:
(role, resource_type, operation)→ expected HTTP status - Use
TestClientwith real SQLite DB - Bootstrap roles, create users with role assignments
- Test all 6 resource types x 5 operations x 5 roles = 150 test cases
- Verify correct HTTP status codes: 200/201 for allowed, 403 for denied
- Parameterized:
-
Cross-team access tests (
test_rbac_cross_team_http.py)- Developer in Team A creates resource in Team B (should fail)
- Developer in both Team A and B creates resource in Team B (should succeed)
- Admin bypasses team restrictions
- Public-only token cannot create team/private resources
- Multi-team API token can filter by non-primary team ([BUG][AUTH]: Multi-team users denied access to non-primary teams and cannot see public resources from other teams #2189 regression)
-
Ownership enforcement tests (expand
test_rbac_ownership_http.py)- Parameterized across all 6 resource types
- Owner can update/delete their resource
- Non-owner with same role cannot update/delete
- Team admin can manage team-scoped resources
- Admin bypass overrides ownership check
-
Admin UI strict RBAC tests
- Admin routes use
allow_admin_bypass=False - User with
admin.dashboardcan access dashboard - User without
admin.dashboardgets 403 - Platform admin with wildcard
*can access all admin routes
- Admin routes use
Phase 6: Integration Tests — User Management
-
User admin RBAC (
test_rbac_user_management_http.py)-
POST /auth/email/admin/users— onlyadmin.user_managementholders can create users -
GET /auth/email/admin/users— non-admins get 403 -
PUT /auth/email/admin/users/{email}— non-admins get 403 -
DELETE /auth/email/admin/users/{email}— non-admins get 403 - Admin cannot delete themselves
- Admin cannot delete the last active admin
- User deletion fails if user still has team memberships
-
-
Self-service endpoints (no special RBAC — just authenticated)
-
GET /auth/email/me— all authenticated users can access -
POST /auth/email/change-password— all authenticated users can change own password -
GET /auth/email/events— users see only their own events
-
-
Registration controls
-
POST /auth/email/register— works whenPUBLIC_REGISTRATION_ENABLED=true -
POST /auth/email/register— rejected whenPUBLIC_REGISTRATION_ENABLED=false
-
Phase 7: Integration Tests — Team Lifecycle
-
Team CRUD with RBAC (
test_rbac_team_lifecycle_http.py)- Any authenticated user can create a team (fallback permission)
- Team creator becomes owner automatically
- Owner can update team (
teams.update+ ownership) - Non-owner with
teams.updateRBAC permission cannot update team (ownership required) - Owner can delete team (
teams.delete+ ownership) - Non-owner cannot delete team even with
teams.deletepermission - Admin bypass can override ownership checks
-
Team invitation flow
- Owner can invite users (
teams.manage_members) - Non-owner member cannot invite (
teams.manage_memberscheck fails or ownership check fails) - Invited user can accept invitation (
teams.readorteams.join) - Owner can cancel pending invitations
- Cannot invite user already in team
- Owner can invite users (
-
Team join request flow (public teams)
- Any user can request to join a public team
- Owner can approve join requests (
teams.manage_members) - Owner can reject join requests
- Non-owner cannot approve/reject join requests
-
Team member management
- Owner can update member roles (
teams.manage_members) - Owner can remove members (
teams.manage_members) - Members can leave teams (no special permission required)
- Users cannot leave personal teams
- Owner can update member roles (
-
Team visibility
- Listing teams shows only public teams + user's own teams
- Admin bypass shows all teams
-
/teams/discovershows public teams available for joining
-
Personal team restrictions
- Cannot delete personal teams
- Cannot leave personal teams
- Personal team auto-created on user registration
Phase 8: Integration Tests — Token Management
-
Token security boundary (
test_rbac_token_management_http.py)- Session token (login JWT) can create API tokens (201)
- API token CANNOT create new API tokens (403)
- Anonymous auth cannot create tokens (403)
- Error message for API-token-creating-token clearly states session required
-
Scope containment
- Token created with permissions subset of creator's → succeeds
- Token created with permissions exceeding creator's → fails or permissions stripped
- Developer creating token with
admin.system_config→ denied
-
User isolation
- User A listing tokens sees only their own
- User A cannot revoke User B's token (403)
- User A cannot view User B's token details (403)
-
Admin token management
- Admin can list all tokens via
GET /tokens/admin/all - Non-admin gets 403 on admin token endpoints
- Admin can revoke any user's token via
DELETE /tokens/admin/{id}
- Admin can list all tokens via
-
Team token management
- Team owner can create team tokens via
POST /tokens/teams/{id} - Non-owner cannot create team tokens (403)
- Team owner can list team tokens via
GET /tokens/teams/{id}
- Team owner can create team tokens via
Phase 9: Integration Tests — RBAC Administration
-
Role management (
test_rbac_admin_http.py)-
@require_admin_permission()blocks non-admins from role CRUD - Platform admin can create custom roles
- Platform admin can update custom roles
- Platform admin can delete custom roles
- System roles (
is_system_role=True) cannot be deleted — even by admin -
is_admin=Truealone is not sufficient (needs explicitplatform_adminrole due toallow_admin_bypass=False)
-
-
Role assignment
-
admin.user_managementrequired to assign roles -
admin.user_managementrequired to revoke roles - Non-admin gets 403 on role assignment endpoints
- Assigned role takes effect for permission checks
-
-
Permission audit endpoints
-
admin.security_auditrequired forPOST /rbac/permissions/check -
admin.security_auditrequired forGET /rbac/permissions/user/{email} -
GET /rbac/permissions/availableworks for all authenticated users
-
-
Self-service RBAC
-
GET /rbac/my/roles— all authenticated users see their own roles -
GET /rbac/my/permissions— all authenticated users see their effective permissions
-
Phase 10: Integration Tests — SSO & Admin-Only Endpoints
-
SSO provider management (
test_rbac_sso_admin_http.py)-
admin.sso_providers:createrequired forPOST /auth/sso/admin/providers -
admin.sso_providers:readrequired forGET /auth/sso/admin/providers -
admin.sso_providers:updaterequired forPUT /auth/sso/admin/providers/{id} -
admin.sso_providers:deleterequired forDELETE /auth/sso/admin/providers/{id} - Non-admin gets 403 on all SSO admin endpoints
- Public SSO endpoint (
GET /auth/sso/providers) works for all users - SSO pending approval requires
admin.user_management
-
-
Admin-only operations (
test_rbac_admin_only_http.py)- Observability endpoints require
admin.system_config - LLM provider/model management requires
admin.system_config - Toolops endpoints require
admin.system_config - Cancellation endpoints require
admin.system_config - Non-admin gets 403 on all admin-only endpoints
- Observability endpoints require
-
Admin UI
allow_admin_bypass=False- Admin UI routes enforce explicit permissions, not just
is_adminflag - User with
admin.dashboardcan access dashboard - User without
admin.dashboardgets 403 on dashboard - Platform admin with
*wildcard can access all admin routes
- Admin UI routes enforce explicit permissions, not just
Phase 11: Integration Tests — Edge Cases
-
Role lifecycle tests
- Expired role (
expires_atin past) is not effective - Inactive role (
is_active=False) is not effective - Role assigned mid-session takes effect
- Role revoked mid-session takes effect (cache invalidation)
- Expired role (
-
Token edge cases
- API token with teams claim that includes deleted team
- Session token for user removed from team (membership revoked)
- Expired JWT rejected before RBAC check
- Revoked JWT rejected before RBAC check
-
Bootstrap and custom roles
- System roles (
is_system_role=True) cannot be deleted - Custom roles can be created and assigned
- Custom role permissions are enforced
- System roles (
-
Cache behavior
- Permission cache hit returns correct result
- Cache invalidation on role assignment
- Cache invalidation on role revocation
- Cache TTL expiration (5 minutes)
Phase 12: E2E Playwright Tests
-
Expand
test_rbac_permissions.py- Add viewer denial tests (viewer clicks "Create" → operation denied)
- Add cross-team access tests (developer in Team A cannot see Team B private resources)
- Add resource/prompt/A2A creation tests (not just gateway/server)
- Add team admin tests (can manage members, delete team)
- Add platform_viewer tests (can read across all teams, cannot create)
-
Admin UI RBAC tests (
test_rbac_admin_ui.py)- Login as viewer → can access dashboard, cannot see create buttons
- Login as developer → can create resources via UI
- Login as team_admin → can manage team members
- Login as platform_admin → can access all admin features
- Role change takes effect on next page load
-
Team management UI tests
- Owner can invite members via team UI
- Owner can remove members via team UI
- Non-owner cannot see invite/remove buttons
- Personal team shows restrictions (no leave/delete)
-
Token management UI tests
- User can create API token via tokens page
- User can revoke their own tokens
- User cannot see other users' tokens
- Admin can see all tokens via admin view
-
User management UI tests (admin only)
- Platform admin can create users via admin UI
- Platform admin can deactivate users
- Non-admin cannot access user management pages
Phase 13: Regression Tests for Known Issues
- Issue [BUG][RBAC]: Getting 403 when adding MCP server or virtual server from team #2883 regression: Developer create from "All Teams" view without
team_id - Issue [BUG][RBAC]: Platform admin blocked by RBAC on gateway delete (allow_admin_bypass=False) #2891 regression: Admin delete with
allow_admin_bypass=True - Issue fix: exclude personal team roles from check_any_team RBAC aggregation #2900 regression: Personal team
team_adminrole excluded fromcheck_any_team - Issue [BUG][AUTH]: Multi-team users denied access to non-primary teams and cannot see public resources from other teams #2189 regression: Multi-team token filtering by non-primary team
- Issue [BUG][AUTH]: Multi-team users denied access to non-primary teams and cannot see public resources from other teams #2189 regression: Public tools visible regardless of team membership
⚙️ Test Configuration
Running the Suite
# Run all RBAC unit tests
.venv/bin/python -m pytest tests/unit/mcpgateway/middleware/test_rbac*.py tests/unit/mcpgateway/services/test_permission_service*.py tests/unit/mcpgateway/middleware/test_token_scoping*.py -v
# Run RBAC integration tests
.venv/bin/python -m pytest tests/integration/test_rbac*.py -v
# Run RBAC Playwright tests (requires running server)
.venv/bin/python -m pytest tests/playwright/test_rbac*.py -v --base-url http://localhost:8080
# Run full RBAC regression suite
.venv/bin/python -m pytest -k "rbac or permission or token_scoping" -v
# Run with coverage report
.venv/bin/python -m pytest -k "rbac or permission or token_scoping" --cov=mcpgateway.middleware.rbac --cov=mcpgateway.services.permission_service --cov=mcpgateway.middleware.token_scoping --cov-report=term-missingTest Data Conventions
# Email domain for test users
TEST_DOMAIN = "@rbac-test.example.com"
# Test user naming
USERS = {
"platform_admin": f"rbac-padmin-{{uid}}{TEST_DOMAIN}",
"team_admin": f"rbac-tadmin-{{uid}}{TEST_DOMAIN}",
"developer": f"rbac-dev-{{uid}}{TEST_DOMAIN}",
"viewer": f"rbac-viewer-{{uid}}{TEST_DOMAIN}",
"platform_viewer": f"rbac-pviewer-{{uid}}{TEST_DOMAIN}",
"outsider": f"rbac-outsider-{{uid}}{TEST_DOMAIN}",
"multi_team_dev": f"rbac-multidev-{{uid}}{TEST_DOMAIN}",
}
# Test password
TEST_PASSWORD = "RBACTest1234!"✅ Success Criteria
- MCP resource matrix: All combinations of
role x resource_type x operationtested for 6 resource types (150+ test cases) - Management endpoint matrix: User management, team lifecycle, token management, RBAC admin, SSO admin all tested with each role (100+ test cases)
- Visibility enforcement: All
visibility x token_type x resource_typecombinations tested (90+ test cases) - Security boundaries: API token security boundary, scope containment, ownership enforcement, personal team exclusion all tested
- Zero false positives: All tests pass deterministically on fresh and existing databases
- Known regression tests: All 5 known RBAC issues ([BUG][RBAC]: Getting 403 when adding MCP server or virtual server from team #2883, [BUG][RBAC]: Platform admin blocked by RBAC on gateway delete (allow_admin_bypass=False) #2891, fix: exclude personal team roles from check_any_team RBAC aggregation #2900, [BUG][AUTH]: Multi-team users denied access to non-primary teams and cannot see public resources from other teams #2189, [BUG][AUTH]: Multi-team users denied access to non-primary teams and cannot see public resources from other teams #2189-visibility) have dedicated regression tests
- Cross-layer validation: Token scoping (L1) + RBAC (L2) intersection tested for scope restriction
- Performance: Full RBAC unit test suite runs in under 60 seconds
- CI integration: All tests run in the existing CI pipeline without new dependencies
- Code coverage: RBAC middleware, permission service, and token scoping at 90%+ line coverage
🏁 Definition of Done
- Parameterized role x permission matrix implemented and passing (MCP resource types)
- User management RBAC tests implemented and passing
- Team lifecycle RBAC tests implemented and passing (CRUD, invitations, members, join requests)
- Token management RBAC tests implemented and passing (security boundary, scope containment, isolation)
- RBAC administration tests implemented and passing (role CRUD, assignment, system role protection)
- SSO provider management tests implemented and passing
- Admin-only endpoint tests implemented and passing (observability, LLM, toolops)
- Visibility x token type matrix implemented and passing
- Cross-team access tests implemented and passing
- Personal team exclusion tested at unit, integration, and E2E levels
- Multi-team token team derivation tiers tested
- Ownership enforcement parameterized across all resource types
- Admin UI strict RBAC (
allow_admin_bypass=False) tested - Fallback permissions tested (teams.create, tokens.*, team owner implicit grants)
- Role lifecycle edge cases tested (expired, inactive, revoked)
- Token edge cases tested (deleted team, revoked membership)
- Regression tests for [BUG][RBAC]: Getting 403 when adding MCP server or virtual server from team #2883, [BUG][RBAC]: Platform admin blocked by RBAC on gateway delete (allow_admin_bypass=False) #2891, fix: exclude personal team roles from check_any_team RBAC aggregation #2900, [BUG][AUTH]: Multi-team users denied access to non-primary teams and cannot see public resources from other teams #2189 implemented
- All tests pass in CI on
make test - Code passes
make verifychecks - Test coverage at 90%+ for RBAC middleware, permission service, token scoping
📝 Additional Notes
Test Architecture Principles:
- Tests validate the security contract, not implementation details
- Parameterized matrices ensure design coverage without brittle duplication
- Module-scoped fixtures minimize setup overhead for large test matrices
- Integration tests use real DB +
TestClient(not mocks) for auth chain fidelity - E2E Playwright tests validate the UI correctly enforces RBAC (not just API)
Security Model Reference:
- Layer 1 (Token Scoping): Controls visibility — what resources appear in list responses and can be accessed by ID
- Layer 2 (RBAC): Controls actions — what operations (CRUD) are permitted on visible resources
- Scope intersection rule: A token can only restrict RBAC permissions, never expand them
- Admin bypass: Requires both
teams: nullANDis_admin: true— either alone is insufficient - Personal team isolation: Personal team
team_adminrole is excluded fromcheck_any_teamaggregation to prevent privilege escalation (fix: exclude personal team roles from check_any_team RBAC aggregation #2900)
Key Implementation Files:
mcpgateway/middleware/rbac.py— RBAC decorators, team derivation, permission enforcementmcpgateway/services/permission_service.py— Permission checks, role queries, admin bypass, cachingmcpgateway/middleware/token_scoping.py— Token scope enforcement, visibility filteringmcpgateway/auth.py— Token normalization (normalize_token_teams), team resolutionmcpgateway/bootstrap_db.py— Built-in role definitions and permission setsmcpgateway/db.py—Permissionsclass with all 73+ permission constants
🔗 Related Issues
- [TESTING][SECURITY]: RBAC manual test plan (visibility, teams, token scope) #2388 — RBAC manual test plan (superseded by this automated suite)
- [BUG][RBAC]: Getting 403 when adding MCP server or virtual server from team #2883 — Developer create from "All Teams" view denied (fixed, needs regression test)
- [BUG][RBAC]: Platform admin blocked by RBAC on gateway delete (allow_admin_bypass=False) #2891 — Admin delete with bypass denied (fixed, needs regression test)
- fix: exclude personal team roles from check_any_team RBAC aggregation #2900 — Personal team role aggregation bypass (fixed, needs regression test)
- [BUG][AUTH]: Multi-team users denied access to non-primary teams and cannot see public resources from other teams #2189 — Multi-team users denied access to non-primary teams (fixed, needs regression test)
- [TESTING][RBAC]: Add unit tests for RBAC Tier 1/2/3 team derivation and session token permission paths #2769 — Add unit tests for RBAC Tier 1/2/3 team derivation
📚 References
- RBAC Documentation — Built-in roles, permission model, team scoping
- Multi-Tenancy Architecture — Team model, visibility tiers
- Token Scoping Rules —
normalize_token_teams()function (L292-342) - RBAC Middleware —
require_permission()decorator - Permission Service —
check_permission()method - NIST RBAC Model (SP 800-207)