Skip to content

Commit 2b9b2b5

Browse files
authored
Merge pull request #4 from dheerajoruganty/pr-3
UI Specific scopes
2 parents fdc6420 + 44fff24 commit 2b9b2b5

File tree

6 files changed

+356
-90
lines changed

6 files changed

+356
-90
lines changed

auth_server/scopes.yml

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,62 @@
11
# This file defines the scopes for the MCP servers, detailing which servers and tools are accessible under different scopes.
2-
mcp-registry-admin:
3-
- list_service
4-
- register_service
5-
- health_check_service
6-
- toggle_service
7-
- modify_service
82

9-
mcp-registry-user:
10-
- list_service:
11-
- mcpgw
3+
UI-Scopes:
4+
mcp-registry-admin:
5+
list_service:
6+
- all
7+
register_service:
8+
- all
9+
health_check_service:
10+
- all
11+
toggle_service:
12+
- all
13+
modify_service:
14+
- all
15+
16+
mcp-registry-user:
17+
list_service:
18+
- mcpgw
19+
- realserverfaketools
20+
health_check_service:
21+
- mcpgw
22+
23+
mcp-registry-developer:
24+
list_service:
25+
- all
26+
register_service:
27+
- all
28+
health_check_service:
29+
- all
30+
31+
mcp-registry-operator:
32+
list_service:
33+
- all
34+
health_check_service:
35+
- all
36+
toggle_service:
37+
- all
38+
39+
# Group mappings - maps Cognito groups to both UI scopes AND server scopes
40+
group_mappings:
41+
mcp-registry-admin:
42+
# Admin gets both UI permissions and unrestricted server access
43+
- mcp-registry-admin
44+
- mcp-servers-unrestricted/read
45+
- mcp-servers-unrestricted/execute
46+
mcp-registry-user:
47+
# Users get limited UI permissions and restricted server access
48+
- mcp-registry-user
49+
- mcp-servers-restricted/read
50+
mcp-registry-developer:
51+
# Developers can register services and have restricted access
52+
- mcp-registry-developer
53+
- mcp-servers-restricted/read
54+
- mcp-servers-restricted/execute
55+
mcp-registry-operator:
56+
# Operators can control services but not register new ones
57+
- mcp-registry-operator
58+
- mcp-servers-restricted/read
59+
- mcp-servers-restricted/execute
1260

1361

1462
mcp-servers-unrestricted/read:

auth_server/server.py

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,24 @@ def load_scopes_config():
5050

5151
def map_cognito_groups_to_scopes(groups: List[str]) -> List[str]:
5252
"""
53-
Map Cognito groups to MCP scopes using the same format as M2M resource server scopes.
53+
Map Cognito groups to MCP scopes using the group_mappings from scopes.yml configuration.
5454
5555
Args:
5656
groups: List of Cognito group names
5757
5858
Returns:
59-
List of MCP scopes in resource server format
59+
List of MCP scopes
6060
"""
6161
scopes = []
62+
group_mappings = SCOPES_CONFIG.get('group_mappings', {})
6263

6364
for group in groups:
64-
if group == 'mcp-admin':
65-
# Admin gets unrestricted read and execute access
66-
scopes.append('mcp-servers-unrestricted/read')
67-
scopes.append('mcp-servers-unrestricted/execute')
68-
elif group == 'mcp-user':
69-
# Regular users get restricted read access by default
70-
scopes.append('mcp-servers-restricted/read')
71-
elif group.startswith('mcp-server-'):
72-
# Server-specific groups grant access based on server permissions
73-
# For now, grant restricted execute access for specific servers
74-
# This allows access to the servers defined in the restricted scope
75-
scopes.append('mcp-servers-restricted/execute')
76-
77-
# Note: The actual server access control is handled by the
78-
# validate_server_tool_access function which checks the scopes.yml
79-
# configuration. The group names are preserved in the 'groups' field
80-
# for potential future fine-grained access control.
65+
if group in group_mappings:
66+
group_scopes = group_mappings[group]
67+
scopes.extend(group_scopes)
68+
logger.debug(f"Mapped group '{group}' to scopes: {group_scopes}")
69+
else:
70+
logger.debug(f"No scope mapping found for group: {group}")
8171

8272
# Remove duplicates while preserving order
8373
seen = set()
@@ -87,6 +77,7 @@ def map_cognito_groups_to_scopes(groups: List[str]) -> List[str]:
8777
seen.add(scope)
8878
unique_scopes.append(scope)
8979

80+
logger.info(f"Final mapped scopes: {unique_scopes}")
9081
return unique_scopes
9182

9283
def validate_session_cookie(cookie_value: str) -> Dict[str, any]:

registry/api/server_routes.py

Lines changed: 79 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ async def read_root(
3737
except HTTPException as e:
3838
logger.info(f"Authentication failed at root route: {e.detail}, redirecting to login")
3939
return RedirectResponse(url="/login", status_code=302)
40+
41+
from ..auth.dependencies import user_has_ui_permission_for_service
42+
43+
# Helper function for templates
44+
def can_perform_action(permission: str, service_name: str) -> bool:
45+
"""Check if user has UI permission for a specific service"""
46+
return user_has_ui_permission_for_service(permission, service_name, user_context.get('ui_permissions', {}))
47+
4048
service_data = []
4149
search_query = query.lower() if query else ""
4250

@@ -55,10 +63,18 @@ async def read_root(
5563
key=lambda p: all_servers[p]["server_name"]
5664
)
5765

66+
# Filter services based on UI permissions
67+
accessible_services = user_context.get('accessible_services', [])
68+
5869
for path in sorted_server_paths:
5970
server_info = all_servers[path]
6071
server_name = server_info["server_name"]
6172

73+
# Check if user can list this service
74+
if 'all' not in accessible_services and server_name not in accessible_services:
75+
logger.debug(f"Filtering out service '{server_name}' - user doesn't have list_service permission")
76+
continue
77+
6278
# Include description and tags in search
6379
searchable_text = f"{server_name.lower()} {server_info.get('description', '').lower()} {' '.join(server_info.get('tags', []))}"
6480
if not search_query or search_query in searchable_text:
@@ -88,7 +104,8 @@ async def read_root(
88104
"request": request,
89105
"services": service_data,
90106
"username": user_context['username'],
91-
"user_context": user_context # Pass full user context to template
107+
"user_context": user_context, # Pass full user context to template
108+
"can_perform_action": can_perform_action # Helper function for permission checks
92109
},
93110
)
94111

@@ -100,25 +117,28 @@ async def toggle_service_route(
100117
enabled: Annotated[str | None, Form()] = None,
101118
user_context: Annotated[dict, Depends(enhanced_auth)] = None,
102119
):
103-
"""Toggle a service on/off (requires modification permissions)."""
120+
"""Toggle a service on/off (requires toggle_service UI permission)."""
104121
from ..search.service import faiss_service
105122
from ..health.service import health_service
106123
from ..core.nginx_service import nginx_service
107-
108-
# Check if user can modify servers
109-
if not user_context['can_modify_servers']:
110-
logger.warning(f"User {user_context['username']} attempted to toggle service {service_path} without modify permissions")
111-
raise HTTPException(
112-
status_code=status.HTTP_403_FORBIDDEN,
113-
detail="You do not have permission to modify servers"
114-
)
124+
from ..auth.dependencies import user_has_ui_permission_for_service
115125

116126
if not service_path.startswith("/"):
117127
service_path = "/" + service_path
118128

119129
server_info = server_service.get_server_info(service_path)
120130
if not server_info:
121131
raise HTTPException(status_code=404, detail="Service path not registered")
132+
133+
service_name = server_info["server_name"]
134+
135+
# Check if user has toggle_service permission for this specific service
136+
if not user_has_ui_permission_for_service('toggle_service', service_name, user_context.get('ui_permissions', {})):
137+
logger.warning(f"User {user_context['username']} attempted to toggle service {service_name} without toggle_service permission")
138+
raise HTTPException(
139+
status_code=status.HTTP_403_FORBIDDEN,
140+
detail=f"You do not have permission to toggle {service_name}"
141+
)
122142

123143
# For non-admin users, check if they have access to this specific server
124144
if not user_context['is_admin']:
@@ -194,17 +214,21 @@ async def register_service(
194214
license_str: Annotated[str, Form(alias="license")] = "N/A",
195215
user_context: Annotated[dict, Depends(enhanced_auth)] = None,
196216
):
197-
"""Register a new service (requires modification permissions)."""
217+
"""Register a new service (requires register_service UI permission)."""
198218
from ..search.service import faiss_service
199219
from ..health.service import health_service
200220
from ..core.nginx_service import nginx_service
221+
from ..auth.dependencies import user_has_ui_permission_for_service
222+
223+
# Check if user has register_service permission for any service
224+
ui_permissions = user_context.get('ui_permissions', {})
225+
register_permissions = ui_permissions.get('register_service', [])
201226

202-
# Check if user can modify servers
203-
if not user_context['can_modify_servers']:
204-
logger.warning(f"User {user_context['username']} attempted to register service without modify permissions")
227+
if not register_permissions:
228+
logger.warning(f"User {user_context['username']} attempted to register service without register_service permission")
205229
raise HTTPException(
206230
status_code=status.HTTP_403_FORBIDDEN,
207-
detail="You do not have permission to register new servers"
231+
detail="You do not have permission to register new services"
208232
)
209233

210234
logger.info(f"Service registration request from user '{user_context['username']}'")
@@ -270,14 +294,8 @@ async def edit_server_form(
270294
service_path: str,
271295
user_context: Annotated[dict, Depends(enhanced_auth)]
272296
):
273-
"""Show edit form for a service (requires modification permissions)."""
274-
# Check if user can modify servers
275-
if not user_context['can_modify_servers']:
276-
logger.warning(f"User {user_context['username']} attempted to access edit form for {service_path} without modify permissions")
277-
raise HTTPException(
278-
status_code=status.HTTP_403_FORBIDDEN,
279-
detail="You do not have permission to edit servers"
280-
)
297+
"""Show edit form for a service (requires modify_service UI permission)."""
298+
from ..auth.dependencies import user_has_ui_permission_for_service
281299

282300
if not service_path.startswith('/'):
283301
service_path = '/' + service_path
@@ -286,6 +304,16 @@ async def edit_server_form(
286304
if not server_info:
287305
raise HTTPException(status_code=404, detail="Service path not found")
288306

307+
service_name = server_info["server_name"]
308+
309+
# Check if user has modify_service permission for this specific service
310+
if not user_has_ui_permission_for_service('modify_service', service_name, user_context.get('ui_permissions', {})):
311+
logger.warning(f"User {user_context['username']} attempted to access edit form for {service_name} without modify_service permission")
312+
raise HTTPException(
313+
status_code=status.HTTP_403_FORBIDDEN,
314+
detail=f"You do not have permission to modify {service_name}"
315+
)
316+
289317
# For non-admin users, check if they have access to this specific server
290318
if not user_context['is_admin']:
291319
if not server_service.user_can_access_server_path(service_path, user_context['accessible_servers']):
@@ -319,24 +347,29 @@ async def edit_server_submit(
319347
is_python: Annotated[bool | None, Form()] = False,
320348
license_str: Annotated[str, Form(alias="license")] = "N/A",
321349
):
322-
"""Handle server edit form submission (requires modification permissions)."""
350+
"""Handle server edit form submission (requires modify_service UI permission)."""
323351
from ..search.service import faiss_service
324352
from ..core.nginx_service import nginx_service
325-
326-
# Check if user can modify servers
327-
if not user_context['can_modify_servers']:
328-
logger.warning(f"User {user_context['username']} attempted to edit service {service_path} without modify permissions")
329-
raise HTTPException(
330-
status_code=status.HTTP_403_FORBIDDEN,
331-
detail="You do not have permission to edit servers"
332-
)
353+
from ..auth.dependencies import user_has_ui_permission_for_service
333354

334355
if not service_path.startswith('/'):
335356
service_path = '/' + service_path
336357

337-
# Check if the server exists
338-
if not server_service.get_server_info(service_path):
358+
# Check if the server exists and get service name
359+
server_info = server_service.get_server_info(service_path)
360+
if not server_info:
339361
raise HTTPException(status_code=404, detail="Service path not found")
362+
363+
service_name = server_info["server_name"]
364+
365+
# Check if user has modify_service permission for this specific service
366+
if not user_has_ui_permission_for_service('modify_service', service_name, user_context.get('ui_permissions', {})):
367+
logger.warning(f"User {user_context['username']} attempted to edit service {service_name} without modify_service permission")
368+
raise HTTPException(
369+
status_code=status.HTTP_403_FORBIDDEN,
370+
detail=f"You do not have permission to modify {service_name}"
371+
)
372+
340373

341374
# For non-admin users, check if they have access to this specific server
342375
if not user_context['is_admin']:
@@ -537,18 +570,29 @@ async def refresh_service(
537570
service_path: str,
538571
user_context: Annotated[dict, Depends(enhanced_auth)]
539572
):
540-
"""Refresh service health and tool information (requires read access)."""
573+
"""Refresh service health and tool information (requires health_check_service permission)."""
541574
from ..search.service import faiss_service
542575
from ..health.service import health_service
543576
from ..core.mcp_client import mcp_client_service
544577
from ..core.nginx_service import nginx_service
578+
from ..auth.dependencies import user_has_ui_permission_for_service
545579

546580
if not service_path.startswith('/'):
547581
service_path = '/' + service_path
548582

549583
server_info = server_service.get_server_info(service_path)
550584
if not server_info:
551585
raise HTTPException(status_code=404, detail="Service path not registered")
586+
587+
service_name = server_info["server_name"]
588+
589+
# Check if user has health_check_service permission for this specific service
590+
if not user_has_ui_permission_for_service('health_check_service', service_name, user_context.get('ui_permissions', {})):
591+
logger.warning(f"User {user_context['username']} attempted to refresh service {service_name} without health_check_service permission")
592+
raise HTTPException(
593+
status_code=status.HTTP_403_FORBIDDEN,
594+
detail=f"You do not have permission to refresh {service_name}"
595+
)
552596

553597
# For non-admin users, check if they have access to this specific server
554598
if not user_context['is_admin']:

0 commit comments

Comments
 (0)