-
Notifications
You must be signed in to change notification settings - Fork 384
Description
Implement Ownership Checks for Public Resources
Problem
Currently, when a user creates a resource with visibility: public, any authenticated user can delete or edit that resource. The expected behavior is that only the owner (or team admin for team resources) should be able to modify or delete resources, regardless of visibility level.
Visibility should control read access, not write access:
private: Only owner can see/use/edit/deleteteam: Team members can see/use, only owner/admin can edit/deletepublic: Anyone can see/use, only owner/admin can edit/delete ← needs implementation
Current State
The system has all necessary infrastructure:
- ✅ Ownership tracking fields (
owner_email,team_id,visibility) - ✅ Permission service with RBAC capabilities
- ✅ User-scoped list methods (mostly implemented)
Missing implementation:
- ❌ Ownership validation in delete/update operations
- ❌ Admin UI gateway list uses unfiltered method
Scope
Affected Entities (6):
- Gateways
- Virtual Servers
- Tools
- Resources
- Prompts
- A2A Agents
Operations (2 per entity):
- Delete operations
- Update operations
Interfaces (2):
- REST API endpoints
- Admin UI routes
Implementation Tasks
1. Service Layer - Add Ownership Validation
Add ownership check to delete/update methods in:
mcpgateway/services/gateway_service.py(lines 1023, 1534)mcpgateway/services/server_service.py(lines 657, 931)mcpgateway/services/tool_service.py(lines 704, 1122)mcpgateway/services/resource_service.py(lines 933, 1053)mcpgateway/services/prompt_service.py(lines 729, 944)mcpgateway/services/a2a_service.py(lines 369, 467)
Pattern to implement:
async def delete_X(self, db: Session, x_id: str, user_email: str) -> None:
resource = db.get(DbX, x_id)
if not resource:
raise NotFoundError(...)
# Check ownership
if resource.owner_email != user_email:
# Allow team admins for team resources
if resource.visibility == "team" and resource.team_id:
team_service = TeamManagementService(db)
user_role = await team_service.get_user_role(user_email, resource.team_id)
if user_role != "owner":
raise PermissionError("Only the owner or team admin can delete this resource")
else:
raise PermissionError("Only the owner can delete this resource")
db.delete(resource)
db.commit()2. API Layer - Pass User Email to Services
Update REST API endpoints in mcpgateway/main.py:
- Delete endpoints: 1543, 2286, 2644, 2987, 3233
- Update endpoints: (corresponding lines for PUT operations)
Pattern:
async def delete_gateway(
gateway_id: str,
db: Session = Depends(get_db),
user=Depends(get_current_user_with_permissions)
):
await gateway_service.delete_gateway(
db,
gateway_id,
user_email=user['email'] # ← Add this
)3. Admin UI - Fix Gateway List Method
File: mcpgateway/admin.py:1759
Change:
gateways = await gateway_service.list_gateways(db, include_inactive=include_inactive)To:
user_email = get_user_email(user)
gateways = await gateway_service.list_gateways_for_user(db, user_email, include_inactive=include_inactive)Note: The list_gateways_for_user() method already exists (line 953 in gateway_service.py).
4. Admin UI - Update Delete Routes
Update admin delete routes to pass user email:
admin_delete_server(line 1353)admin_delete_tool(line 5354)admin_delete_gateway(line 6202)admin_delete_resource(line 6627)admin_delete_prompt(line 7134)admin_delete_a2a_agent(line 9093)
5. Admin UI - Conditionally Render Delete Buttons
Update HTML template generation to only show delete/edit buttons for owned resources:
# Check ownership before rendering button
user_email = get_user_email(user)
if resource.owner_email == user_email:
delete_button_html = f'<button hx-delete="...">Delete</button>'
else:
delete_button_html = '' # No button for non-owned resources6. Permission Service - Add Resource Ownership Helper
Implement the TODO at line 255 in permission_service.py:
async def check_resource_ownership(
self,
user_email: str,
resource: Any,
team_id: Optional[str] = None
) -> bool:
"""Check if user owns resource or is team admin."""
# Check direct ownership
if resource.owner_email == user_email:
return True
# Check team admin for team resources
if resource.visibility == "team" and resource.team_id:
return await self._is_team_admin(user_email, resource.team_id)
return FalseAcceptance Criteria
Backend
- User A cannot delete User B's public gateway
- User A cannot update User B's public server
- User A can delete their own public resources
- Team owner can delete team member's team resources
- Team member cannot delete team owner's team resources
- All 6 entity types enforce ownership on delete and update
Admin UI
- Gateway list only shows gateways user has access to
- Delete buttons only appear for resources user owns
- Update buttons only appear for resources user owns
REST API
-
DELETE /gateways/{id}returns 403 for non-owned resources -
PUT /servers/{id}returns 403 for non-owned resources - Appropriate error messages for permission denials
Notes
- The Permission Service infrastructure already exists and works correctly
- Most list methods already filter by user (tools, servers, resources, prompts)
- Create operations already set ownership correctly
- This change only affects delete and update operations
- Private resources already work correctly (not visible to other users)
Files Summary
Service Layer: 6 files
API Layer: main.py (12+ endpoints)
Admin UI: admin.py (1 list fix + 6 delete routes + HTML templates)
Permission Service: permission_service.py (add helper method)
References
- Investigation Report:
todo/rbac-issues.md(detailed analysis with line numbers) - Permission Service:
mcpgateway/services/permission_service.py - Database Models:
mcpgateway/db.py - Schemas:
mcpgateway/schemas.py
RBAC Ownership Implementation Tracker
Service Layer - Add Ownership Checks
Gateway Service (gateway_service.py)
-
delete_gateway(line 1534) - add user_email param + ownership check -
update_gateway(line 1023) - add ownership check
Server Service (server_service.py)
-
delete_server(line 931) - add user_email param + ownership check -
update_server(line 657) - add ownership check (has user_email already)
Tool Service (tool_service.py)
-
delete_tool(line 704) - add user_email param + ownership check -
update_tool(line 1122) - add ownership check
Resource Service (resource_service.py)
-
delete_resource(line 1053) - add user_email param + ownership check -
update_resource(line 933) - add ownership check
Prompt Service (prompt_service.py)
-
delete_prompt(line 944) - add user_email param + ownership check -
update_prompt(line 729) - add ownership check
A2A Service (a2a_service.py)
-
delete_agent(line 467) - add user_email param + ownership check -
update_agent(line 369) - add ownership check
API Layer - Pass User Email (main.py)
Delete Endpoints
-
delete_server(line 1543) -
delete_tool(line 2286) -
delete_resource(line 2644) -
delete_prompt(line 2987) -
delete_gateway(line 3233) -
delete_a2a_agent(find line)
Update Endpoints
-
update_server(find line) -
update_tool(find line) -
update_resource(find line) -
update_prompt(find line) -
update_gateway(find line) -
update_a2a_agent(find line)
Admin UI Layer (admin.py)
List Methods
- Fix
admin_list_gateways(line 1759) - uselist_gateways_for_user
Delete Routes - Pass User Email
-
admin_delete_server(line 1353) -
admin_delete_tool(line 5354) -
admin_delete_gateway(line 6202) -
admin_delete_resource(line 6627) -
admin_delete_prompt(line 7134) -
admin_delete_a2a_agent(line 9093)
HTML Templates - Conditional Buttons
- Server list - check ownership before rendering delete button
- Tool list - check ownership before rendering delete button
- Gateway list - check ownership before rendering delete button
- Resource list - check ownership before rendering delete button
- Prompt list - check ownership before rendering delete button
- A2A list - check ownership before rendering delete button
Permission Service
- Add
check_resource_ownershiphelper method (line 255 TODO)
Testing
Unit Tests
- User A cannot delete User B's public resource
- User A can delete their own public resource
- Team admin can delete team member's team resource
- Team member cannot delete team owner's team resource
Integration Tests
- REST API returns 403 for non-owned delete
- REST API returns 403 for non-owned update
- Admin UI blocks non-owned deletes
UI Tests
- Delete buttons only show for owned resources
- Gateway list filtered by user access