fix(security): enforce token_teams narrowing across all Layer 2 RBAC paths#3932
Conversation
d70ff28 to
5867861
Compare
…paths Signed-off-by: Bogdan-Marius-Catanus <bogdan-marius.catanus@ibm.com>
5867861 to
768e35e
Compare
- PermissionChecker.has_admin_permission: forward token_teams to check_admin_permission (middleware/rbac.py) - has_admin_permission on PermissionService: add token_teams param, suppress admin bypass for public-only tokens (permission_service.py) - AdminAuthMiddleware: forward token_teams to has_admin_permission (main.py) - get_my_permissions endpoint: forward token_teams to get_user_permissions (routers/rbac.py) - _get_user_roles: reject explicit team_id when token_teams=[] (public-only tokens must not access team-specific roles) - Fix stale test asserting token_teams=[] retains team perms (contradicts Option A strict isolation) - Fix stale test asserting team_id ignores token_teams (team_id is now validated against token_teams) - Add regression tests for all new paths Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Update test assertions in test_permission_service.py and test_main_extended.py to include the new token_teams parameter added to has_admin_permission(). Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Address all 4 findings from the Codex code review: Finding 1 (HIGH) — _get_caller_permissions admin bypass: - Only treat admin as unrestricted when token_teams is None. Narrowed/public-only admin sessions now derive permissions through the token-aware path (routers/tokens.py). - create_token auto-inheritance now applies to narrowed admins too. Finding 2 (HIGH) — scope_id IS NULL team roles on public-only: - _get_user_roles with team_id=None, include_all_teams=False now excludes scope='team', scope_id=NULL roles when token_teams=[] (permission_service.py). Finding 3 (HIGH) — list_all_tokens/admin_revoke_token admin guard: - Both endpoints now require un-narrowed admin (token_teams is None). Narrowed and public-only admin sessions are rejected with 403 (routers/tokens.py). Finding 4 (LOW) — cache key collision None vs []: - Cache keys now use __public__ sentinel for token_teams=[] and include token_teams in team_id cache keys (permission_service.py). Add 12 regression tests covering all fixed paths. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Review Changes (3 fixup commits)Rebased cleanly on Commit
|
🔗 Related Issue
Closes #3924
📝 Summary
This PR fixes pre-existing security gaps where
token_teams(Layer 1 scoping) was either not forwarded or ignored in several RBAC code paths, allowing narrowed or public-only tokens to satisfy permission checks using roles from teams outside their scope.Key Fixes:
token_teams=[]by changing from separateiftoelifincheck_permission()(lines 126-136)base_conditionwas unconditionally appended even fortoken_teams=[], now properly excludes ALL team roles for public-only tokens (lines 528-550)Already Implemented (Verified):
check_admin_permissionacceptstoken_teams(lines 389-410, rbac.py 784-793)token_teams(tokens.py line 96)token_teams(admin.py line 510)Design Decision: Implemented strict isolation where
token_teams=[]means public-only access at both Layer 1 (visibility) and Layer 2 (RBAC), providing defense-in-depth security.🏷️ Type of Change
🧪 Verification
make lintmake testmake coverage✅ Checklist
make black isort pre-commit)📓 Notes
Test Coverage Added
Added 9 comprehensive regression tests in
test_permission_service_token_narrowing.py:Explicit team_id validation (2 tests):
test_explicit_team_id_rejects_out_of_scope_team- Verifies rejection when team_id not in token_teamstest_explicit_team_id_allows_in_scope_team- Verifies access when team_id is in token_teamsAdmin bypass behavior (3 tests):
test_admin_bypass_suppressed_for_public_only_non_admin_perm- Verifies bypass suppression for token_teams=[]test_admin_bypass_works_for_narrowed_token- Verifies bypass works for narrowed tokenstest_admin_bypass_works_for_unnarrowed_token- Verifies bypass works for un-narrowed tokensAdmin permission checks (2 tests):
test_check_admin_permission_respects_token_teams- Verifies token_teams=[] filters out team rolestest_check_admin_permission_allows_with_proper_scope- Verifies proper scoping allows accessTeam role filtering (2 tests):
test_public_only_token_excludes_all_team_roles- Verifies token_teams=[] excludes ALL team rolestest_narrowed_token_includes_specified_teams- Verifies narrowed tokens filter to specified teamsSecurity Impact
These fixes prevent privilege escalation scenarios where:
Files Modified
mcpgateway/services/permission_service.py- Core RBAC logic fixesmcpgateway/admin.py- Admin UI menu token scope forwarding (verified)mcpgateway/middleware/rbac.py- RBAC middleware token scope forwarding (verified)mcpgateway/routers/tokens.py- Token minting token scope forwarding (verified)tests/unit/mcpgateway/services/test_permission_service_token_narrowing.py- Comprehensive test coverage.secrets.baseline- Updated baseline for secret scanning