Skip to content

fix: allow team-scoped RBAC roles for mutate operations without explicit team_id#2894

Merged
crivetimihai merged 3 commits intomainfrom
2883-2891-rbac-permissions
Feb 13, 2026
Merged

fix: allow team-scoped RBAC roles for mutate operations without explicit team_id#2894
crivetimihai merged 3 commits intomainfrom
2883-2891-rbac-permissions

Conversation

@crivetimihai
Copy link
Copy Markdown
Member

Summary

  • RBAC require_permission decorator excluded team-scoped roles (e.g. developer, team_admin, platform_admin) when team_id could not be derived from the request, causing 403 on create/delete operations
  • Changed the fail-closed mutate logic to use check_any_team=True for both read and mutate operations, separating authorization from resource scoping
  • Applied the same fix to require_any_permission for consistency
  • Added team_id injection to handleDeleteSubmit() in admin.js as defense-in-depth

Root cause

rbac.py:552-558 had mutate-specific fail-closed logic that proceeded with team_id=None when team context couldn't be derived. This caused _get_user_roles() to exclude all team-scoped roles with a specific scope_id, resulting in 403 for any user whose permissions came from team-scoped role assignments.

What was affected

Why this is safe

The RBAC check only answers "does this user have this permission in any team?" — resource team assignment is enforced downstream by verify_team_for_user(), token membership checks, and inline team validation in endpoint logic. Read operations already used check_any_team=True without security issues.

Verified

Test Before After
Team dev POST /gateways (no team_id) 403 RBAC passes
Team dev POST /servers (no team_id) 403 201
Admin DELETE /gateways/{id} 403 200
Admin UI "All Teams" → Add Gateway 403 RBAC passes
Admin UI → Delete Gateway 403 Success
Viewer POST /gateways 403 403 (no regression)
Unauthenticated POST /gateways 401 401 (no regression)

Closes #2883
Closes #2891

…cit team_id

The RBAC `require_permission` decorator's fail-closed mutate logic excluded
team-scoped roles when team_id could not be derived from the request. This
caused 403 errors for team developers creating gateways/servers and platform
admins deleting resources.

The fix uses `check_any_team=True` for both read and mutate operations when
no team_id is available, separating authorization ("does this user have the
permission?") from resource scoping ("which team owns this?"). Team assignment
is still enforced downstream by endpoint logic (verify_team_for_user, token
membership checks).

Also adds team_id injection to delete forms in admin.js as defense-in-depth.

Closes #2883
Closes #2891

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
@crivetimihai crivetimihai self-assigned this Feb 12, 2026
@crivetimihai crivetimihai added this to the Release 1.0.0-RC1 milestone Feb 12, 2026
@crivetimihai crivetimihai added the ica ICA related issues label Feb 12, 2026
If the platform_admin role creation fails but the role already exists in
the DB (e.g. from a previous boot), the assignment was silently skipped
because the role wasn't in the created_roles list. Now falls back to a
DB lookup. Also improves error messages to mention the downstream impact
(allow_admin_bypass=False routes returning 403).

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Fix accept_invitation returning bool instead of EmailTeamMember, which
caused the router to always return 404 despite memberships being created
in the DB. Add comprehensive E2E Playwright tests for RBAC permission
enforcement on admin UI operations (regression tests for #2883 and #2891).

Closes #2883
Closes #2891

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
@crivetimihai
Copy link
Copy Markdown
Member Author

Regression Testing Update

Added comprehensive E2E Playwright tests for RBAC permission enforcement in tests/playwright/test_rbac_permissions.py (11 tests).

Bug Fix Included

Invitation acceptance 404TeamInvitationService.accept_invitation() returned bool (True) instead of the EmailTeamMember object. The router at teams.py:738 checks hasattr(member, "id"), which always fails for booleans, resulting in a 404 despite membership being created in DB. Fixed by returning the membership object directly.

Tests Cover

Test Scenario Verifies
test_developer_create_gateway_all_teams_view Developer creates gateway without team_id #2883 regression
test_developer_create_gateway_team_view Developer creates gateway with team scope Baseline
test_viewer_cannot_create_gateway Viewer denied gateway creation Security
test_admin_create_gateway_all_teams_view Admin creates gateway Admin bypass
test_developer_create_server_all_teams_view Developer creates server without team_id #2883 regression
test_developer_create_server_team_view Developer creates server with team scope Baseline
test_admin_delete_gateway Admin deletes gateway #2891 regression (skipped — can't create gateway with fake URL via API)
test_developer_api_create_gateway_no_team_id REST API: developer, no team_id #2883 regression
test_developer_api_create_server_no_team_id REST API: developer, no team_id #2883 regression
test_viewer_api_create_gateway_denied REST API: viewer denied Security
test_admin_api_create_gateway REST API: admin creates gateway Admin bypass

Results

10 passed, 1 skipped (admin delete gateway — requires real MCP server for gateway creation via API).

tests/playwright/test_rbac_permissions.py::TestRBACGatewayCreate::test_developer_create_gateway_all_teams_view PASSED
tests/playwright/test_rbac_permissions.py::TestRBACGatewayCreate::test_developer_create_gateway_team_view PASSED
tests/playwright/test_rbac_permissions.py::TestRBACGatewayCreate::test_viewer_cannot_create_gateway PASSED
tests/playwright/test_rbac_permissions.py::TestRBACGatewayCreate::test_admin_create_gateway_all_teams_view PASSED
tests/playwright/test_rbac_permissions.py::TestRBACServerCreate::test_developer_create_server_all_teams_view PASSED
tests/playwright/test_rbac_permissions.py::TestRBACServerCreate::test_developer_create_server_team_view PASSED
tests/playwright/test_rbac_permissions.py::TestRBACGatewayDelete::test_admin_delete_gateway SKIPPED
tests/playwright/test_rbac_permissions.py::TestRBACRestAPI::test_developer_api_create_gateway_no_team_id PASSED
tests/playwright/test_rbac_permissions.py::TestRBACRestAPI::test_developer_api_create_server_no_team_id PASSED
tests/playwright/test_rbac_permissions.py::TestRBACRestAPI::test_viewer_api_create_gateway_denied PASSED
tests/playwright/test_rbac_permissions.py::TestRBACRestAPI::test_admin_api_create_gateway PASSED

@crivetimihai crivetimihai merged commit 19a8c92 into main Feb 13, 2026
52 checks passed
@crivetimihai crivetimihai deleted the 2883-2891-rbac-permissions branch February 13, 2026 00:54
ja8zyjits pushed a commit that referenced this pull request Feb 13, 2026
…cit team_id (#2894)

* fix: allow team-scoped RBAC roles for mutate operations without explicit team_id

The RBAC `require_permission` decorator's fail-closed mutate logic excluded
team-scoped roles when team_id could not be derived from the request. This
caused 403 errors for team developers creating gateways/servers and platform
admins deleting resources.

The fix uses `check_any_team=True` for both read and mutate operations when
no team_id is available, separating authorization ("does this user have the
permission?") from resource scoping ("which team owns this?"). Team assignment
is still enforced downstream by endpoint logic (verify_team_for_user, token
membership checks).

Also adds team_id injection to delete forms in admin.js as defense-in-depth.

Closes #2883
Closes #2891

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: harden bootstrap platform_admin role assignment with DB fallback

If the platform_admin role creation fails but the role already exists in
the DB (e.g. from a previous boot), the assignment was silently skipped
because the role wasn't in the created_roles list. Now falls back to a
DB lookup. Also improves error messages to mention the downstream impact
(allow_admin_bypass=False routes returning 403).

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: repair invitation acceptance and add E2E RBAC Playwright tests

Fix accept_invitation returning bool instead of EmailTeamMember, which
caused the router to always return 404 despite memberships being created
in the DB. Add comprehensive E2E Playwright tests for RBAC permission
enforcement on admin UI operations (regression tests for #2883 and #2891).

Closes #2883
Closes #2891

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
vishu-bh pushed a commit that referenced this pull request Feb 18, 2026
…cit team_id (#2894)

* fix: allow team-scoped RBAC roles for mutate operations without explicit team_id

The RBAC `require_permission` decorator's fail-closed mutate logic excluded
team-scoped roles when team_id could not be derived from the request. This
caused 403 errors for team developers creating gateways/servers and platform
admins deleting resources.

The fix uses `check_any_team=True` for both read and mutate operations when
no team_id is available, separating authorization ("does this user have the
permission?") from resource scoping ("which team owns this?"). Team assignment
is still enforced downstream by endpoint logic (verify_team_for_user, token
membership checks).

Also adds team_id injection to delete forms in admin.js as defense-in-depth.

Closes #2883
Closes #2891

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: harden bootstrap platform_admin role assignment with DB fallback

If the platform_admin role creation fails but the role already exists in
the DB (e.g. from a previous boot), the assignment was silently skipped
because the role wasn't in the created_roles list. Now falls back to a
DB lookup. Also improves error messages to mention the downstream impact
(allow_admin_bypass=False routes returning 403).

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: repair invitation acceptance and add E2E RBAC Playwright tests

Fix accept_invitation returning bool instead of EmailTeamMember, which
caused the router to always return 404 despite memberships being created
in the DB. Add comprehensive E2E Playwright tests for RBAC permission
enforcement on admin UI operations (regression tests for #2883 and #2891).

Closes #2883
Closes #2891

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Vishu Bhatnagar <vishu.bhatnagar@ibm.com>
kcostell06 pushed a commit to kcostell06/mcp-context-forge that referenced this pull request Feb 24, 2026
…cit team_id (IBM#2894)

* fix: allow team-scoped RBAC roles for mutate operations without explicit team_id

The RBAC `require_permission` decorator's fail-closed mutate logic excluded
team-scoped roles when team_id could not be derived from the request. This
caused 403 errors for team developers creating gateways/servers and platform
admins deleting resources.

The fix uses `check_any_team=True` for both read and mutate operations when
no team_id is available, separating authorization ("does this user have the
permission?") from resource scoping ("which team owns this?"). Team assignment
is still enforced downstream by endpoint logic (verify_team_for_user, token
membership checks).

Also adds team_id injection to delete forms in admin.js as defense-in-depth.

Closes IBM#2883
Closes IBM#2891

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: harden bootstrap platform_admin role assignment with DB fallback

If the platform_admin role creation fails but the role already exists in
the DB (e.g. from a previous boot), the assignment was silently skipped
because the role wasn't in the created_roles list. Now falls back to a
DB lookup. Also improves error messages to mention the downstream impact
(allow_admin_bypass=False routes returning 403).

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

* fix: repair invitation acceptance and add E2E RBAC Playwright tests

Fix accept_invitation returning bool instead of EmailTeamMember, which
caused the router to always return 404 despite memberships being created
in the DB. Add comprehensive E2E Playwright tests for RBAC permission
enforcement on admin UI operations (regression tests for IBM#2883 and IBM#2891).

Closes IBM#2883
Closes IBM#2891

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>

---------

Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ica ICA related issues

Projects

None yet

1 participant