feat(rbac): enforce session-token team narrowing in Layer 2 permission checks#3919
Conversation
… checks Signed-off-by: Bogdan-Marius-Catanus <bogdan-marius.catanus@ibm.com>
Signed-off-by: Bogdan-Marius-Catanus <bogdan-marius.catanus@ibm.com>
Signed-off-by: Bogdan-Marius-Catanus <bogdan-marius.catanus@ibm.com>
- Fix test_check_permission_public_only_token that passed due to wildcard string mismatch rather than actual role filtering; renamed to test_check_permission_public_only_token_retains_non_admin_team_perms with realistic permissions and accurate assertion (Layer 1 handles public-only visibility, not Layer 2 role filtering) - Remove unused imports (datetime, timezone) and unused variable (role_c) - Remove trailing "Made with Bob" comment - Apply black/isort formatting to match project standards - Clean up duplicate inline comments on or_() clause Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
…sion test Address two findings from code review: 1. Cache key deduplication: token_teams with duplicates (e.g. ["team-a", "team-a"]) now produces the same cache key as the deduplicated form (["team-a"]), preventing avoidable cache fragmentation. Added test_cache_key_deduplicates_token_teams. 2. Strengthen test_check_permission_narrowed_session_restricts_to_token_teams: replaced unrealistic "teams.*" wildcard permissions with explicit permissions matching bootstrap_db.py built-in roles (teams.create, teams.read, etc.). The previous test passed even when team-B roles leaked because "teams.create" != "teams.*" in exact string matching. The new test would correctly fail if role filtering broke. Signed-off-by: Mihai Criveti <crivetimihai@gmail.com>
809a8b8 to
a27c51a
Compare
Review changesRebased onto Commit:
|
crivetimihai
left a comment
There was a problem hiding this comment.
Reviewed and approved. Core logic is correct — token_teams narrowing is properly enforced in the check_any_team path, cache keys are isolated, and global team roles (scope_id=NULL) are correctly preserved. Added review-fix commits for cache key deduplication, strengthened regression test with realistic permissions, and cleaned up lint/formatting issues. Pre-existing scoping gaps in other code paths tracked in #3924.
🔗 Related Issue
Closes #3799
📝 Summary
Fixes a security gap in the two-layer RBAC model where session-token team narrowing (Layer 1 / visibility) was not enforced in permission checks (Layer 2 / RBAC).
Problem: When
check_any_team=True,PermissionService.check_permission()aggregated permissions from ALL of a user's team-scoped roles, ignoring thetoken_teamsnarrowing applied at Layer 1. This allowed a session narrowed to team A to satisfy RBAC checks using roles the user holds in team B.Example scenario:
developerrole in team A (tools.read,tools.execute)team_adminrole in team B (teams.*,tools.create, etc.)teams: ["A"]teams.*from team BSolution: Filter team-scoped roles in
_get_user_roles()to only include roles from teams specified intoken_teamswheninclude_all_teams=Trueandtoken_teamsis non-empty. Updated cache key generation to prevent cross-contamination between different narrowing scopes.🏷️ Type of Change
🧪 Verification
make lintmake testmake coverage✅ Checklist
make black isort pre-commit)📓 Notes
Changes Made
mcpgateway/services/permission_service.py:get_user_permissions()(line 212):token_teams: Optional[List[str]] = Noneparametertoken_teamsfor proper isolationtoken_teamsto_get_user_roles()(line 253)_get_user_roles()(line 461):token_teams: Optional[List[str]] = Noneparameterinclude_all_teams=Trueandtoken_teamsis non-emptycheck_permission()(line 134):token_teams=token_teamstoget_user_permissions()tests/unit/mcpgateway/services/test_permission_service_token_narrowing.py(NEW):Created comprehensive unit test suite with 16 tests covering:
1. Cache Key Generation (4 tests):
token_teamsto prevent cross-contaminationtoken_teamsproduce different cache keystoken_teams=None) use base cache key2. Role Filtering (4 tests):
_get_user_roles()filters bytoken_teamswhen narrowedtoken_teamsis None (un-narrowed)token_teamsis empty list (public-only)scope_id=None) preserved when narrowed3. End-to-End Permission Checks (8 tests):
token_teamsonlyadmin.*even for admin userstoken_teamsaggregates from all specifiedteam_idoverridestoken_teamsTest Results: All 16 new tests pass ✅