Skip to content

[EPIC][TESTING][SECURITY]: RBAC automated regression suite (visibility, teams, token scope) #2387

@crivetimihai

Description

@crivetimihai

🧪 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:

  1. Cross-layer integration tests: Token scoping (Layer 1) and RBAC (Layer 2) are tested in isolation, but their intersection is not systematically validated
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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 minutes

Technical 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 results

Technical 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 type

Technical 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) and allow_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 appear

Technical 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:

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_ENABLED flag behavior
  • Test TRUST_PROXY_AUTH with header injection
  • Test AUTH_REQUIRED=false anonymous 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 succeeds

Technical 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 denied

Technical 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 appear

Technical 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 denied

Technical 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 permissions

Technical 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 flag

Technical 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=False enforcement
  • 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
Loading

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
Loading

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.py or fixtures in existing conftest)

    • rbac_team_a / rbac_team_b — Two test teams (module-scoped)
    • rbac_platform_admin — User with platform_admin role (global)
    • rbac_team_admin — User with team_admin role on Team A
    • rbac_developer — User with developer role on Team A
    • rbac_viewer — User with viewer role on Team A
    • rbac_platform_viewer — User with platform_viewer role (global)
    • rbac_outsider — User with no team membership (only personal team)
    • rbac_multi_team_dev — User with developer role 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, verify check_permission() returns the expected result
    • Test platform_admin wildcard grants all permissions
    • Test team_admin has exactly 34 permissions (not more, not less)
    • Test developer has exactly 32 permissions (no teams.delete, no teams.manage_members)
    • Test viewer has exactly 8 permissions (read-only)
    • Test platform_viewer has exactly 8 global permissions
    • Test permission removal: removing a permission from a role denies access
  • 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=True excludes personal team roles from aggregation
    • check_any_team=False with explicit personal team ID still works
    • Viewer with personal team team_admin cannot create resources via check_any_team
    • Developer with personal team team_admin uses developer permissions (not team_admin)
  • Fallback permission tests

    • All users can create teams (teams.create fallback)
    • All users can read teams they belong to (teams.read fallback)
    • Team owners get full teams.* permissions
    • All users can manage their own tokens (tokens.* fallback)

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
  • Token teams normalization edge cases

    • Missing teams key → [] (public-only)
    • teams: null + is_admin: false[] (no bypass for non-admins)
    • teams: null + is_admin: trueNone (admin bypass)
    • teams: [][] (explicit public-only)
    • teams: [dict_format] → normalized to ID strings
    • teams: ["id1", "id2"] → as-is
  • 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_id to 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)
  • Tier 2: URL path parameter

    • Extract team_id from URL kwargs
    • Fallback to user_context.get("team_id")
  • Tier 3: Payload-based derivation (_derive_team_from_payload)

    • Extract team_id from 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_id in payload (returns None)
  • 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 TestClient with 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
  • Cross-team access tests (test_rbac_cross_team_http.py)

  • 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.dashboard can access dashboard
    • User without admin.dashboard gets 403
    • Platform admin with wildcard * can access all admin routes

Phase 6: Integration Tests — User Management

  • User admin RBAC (test_rbac_user_management_http.py)

    • POST /auth/email/admin/users — only admin.user_management holders 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 when PUBLIC_REGISTRATION_ENABLED=true
    • POST /auth/email/register — rejected when PUBLIC_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.update RBAC permission cannot update team (ownership required)
    • Owner can delete team (teams.delete + ownership)
    • Non-owner cannot delete team even with teams.delete permission
    • Admin bypass can override ownership checks
  • Team invitation flow

    • Owner can invite users (teams.manage_members)
    • Non-owner member cannot invite (teams.manage_members check fails or ownership check fails)
    • Invited user can accept invitation (teams.read or teams.join)
    • Owner can cancel pending invitations
    • Cannot invite user already in team
  • 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
  • Team visibility

    • Listing teams shows only public teams + user's own teams
    • Admin bypass shows all teams
    • /teams/discover shows 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}
  • 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}

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=True alone is not sufficient (needs explicit platform_admin role due to allow_admin_bypass=False)
  • Role assignment

    • admin.user_management required to assign roles
    • admin.user_management required to revoke roles
    • Non-admin gets 403 on role assignment endpoints
    • Assigned role takes effect for permission checks
  • Permission audit endpoints

    • admin.security_audit required for POST /rbac/permissions/check
    • admin.security_audit required for GET /rbac/permissions/user/{email}
    • GET /rbac/permissions/available works 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:create required for POST /auth/sso/admin/providers
    • admin.sso_providers:read required for GET /auth/sso/admin/providers
    • admin.sso_providers:update required for PUT /auth/sso/admin/providers/{id}
    • admin.sso_providers:delete required for DELETE /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
  • Admin UI allow_admin_bypass=False

    • Admin UI routes enforce explicit permissions, not just is_admin flag
    • User with admin.dashboard can access dashboard
    • User without admin.dashboard gets 403 on dashboard
    • Platform admin with * wildcard can access all admin routes

Phase 11: Integration Tests — Edge Cases

  • Role lifecycle tests

    • Expired role (expires_at in 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)
  • 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
  • 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


⚙️ 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-missing

Test 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


🏁 Definition of Done


📝 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: null AND is_admin: true — either alone is insufficient
  • Personal team isolation: Personal team team_admin role is excluded from check_any_team aggregation 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 enforcement
  • mcpgateway/services/permission_service.py — Permission checks, role queries, admin bypass, caching
  • mcpgateway/middleware/token_scoping.py — Token scope enforcement, visibility filtering
  • mcpgateway/auth.py — Token normalization (normalize_token_teams), team resolution
  • mcpgateway/bootstrap_db.py — Built-in role definitions and permission sets
  • mcpgateway/db.pyPermissions class with all 73+ permission constants

🔗 Related Issues


📚 References

Metadata

Metadata

Assignees

Labels

MUSTP1: Non-negotiable, critical requirements without which the product is non-functional or unsafeenhancementNew feature or requestepicLarge feature spanning multiple issuespythonPython / backend development (FastAPI)rbacRole-based Access ControlsecurityImproves securitytest-automationAutomated testingtestingTesting (unit, e2e, manual, automated, etc)

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions