-
Notifications
You must be signed in to change notification settings - Fork 52
MGMT-21338: allow disabling query model and provider #475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -29,6 +29,7 @@ | |||||||||||||||||||||||
| from models.config import Action, ModelContextProtocolServer | ||||||||||||||||||||||||
| from models.database.conversations import UserConversation | ||||||||||||||||||||||||
| from utils.types import ToolCallSummary, TurnSummary | ||||||||||||||||||||||||
| from authorization.resolvers import NoopRolesResolver | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| MOCK_AUTH = ("mock_user_id", "mock_username", "mock_token") | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
@@ -1507,3 +1508,58 @@ def test_evaluate_model_hints( | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| assert provider_id == expected_provider | ||||||||||||||||||||||||
| assert model_id == expected_model | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @pytest.mark.asyncio | ||||||||||||||||||||||||
| async def test_query_endpoint_rejects_model_provider_override_without_permission( | ||||||||||||||||||||||||
| mocker, dummy_request | ||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||
| """Assert 403 and message when request includes model/provider without MODEL_OVERRIDE.""" | ||||||||||||||||||||||||
| # Patch endpoint configuration (no need to set customization) | ||||||||||||||||||||||||
| cfg = AppConfig() | ||||||||||||||||||||||||
| cfg.init_from_dict( | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| "name": "test", | ||||||||||||||||||||||||
| "service": { | ||||||||||||||||||||||||
| "host": "localhost", | ||||||||||||||||||||||||
| "port": 8080, | ||||||||||||||||||||||||
| "auth_enabled": False, | ||||||||||||||||||||||||
| "workers": 1, | ||||||||||||||||||||||||
| "color_log": True, | ||||||||||||||||||||||||
| "access_log": True, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| "llama_stack": { | ||||||||||||||||||||||||
| "api_key": "test-key", | ||||||||||||||||||||||||
| "url": "http://test.com:1234", | ||||||||||||||||||||||||
| "use_as_library_client": False, | ||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||
| "user_data_collection": {"transcripts_enabled": False}, | ||||||||||||||||||||||||
| "mcp_servers": [], | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| mocker.patch("app.endpoints.query.configuration", cfg) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Patch authorization to exclude MODEL_OVERRIDE from authorized actions | ||||||||||||||||||||||||
| access_resolver = mocker.Mock() | ||||||||||||||||||||||||
| access_resolver.check_access.return_value = True | ||||||||||||||||||||||||
| access_resolver.get_actions.return_value = set(Action) - {Action.MODEL_OVERRIDE} | ||||||||||||||||||||||||
| mocker.patch( | ||||||||||||||||||||||||
| "authorization.middleware.get_authorization_resolvers", | ||||||||||||||||||||||||
| return_value=(NoopRolesResolver(), access_resolver), | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
Comment on lines
+1546
to
+1550
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure the test uses restricted authorized_actions. Apply this diff: mocker.patch(
"authorization.middleware.get_authorization_resolvers",
return_value=(NoopRolesResolver(), access_resolver),
)
+ # Simulate middleware: restrict request actions for this test
+ dummy_request.state.authorized_actions = set(Action) - {Action.MODEL_OVERRIDE}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| # Build a request that tries to override model/provider | ||||||||||||||||||||||||
| query_request = QueryRequest(query="What?", model="m", provider="p") | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| with pytest.raises(HTTPException) as exc_info: | ||||||||||||||||||||||||
| await query_endpoint_handler( | ||||||||||||||||||||||||
| request=dummy_request, query_request=query_request, auth=MOCK_AUTH | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| expected_msg = ( | ||||||||||||||||||||||||
| "This instance does not permit overriding model/provider in the query request " | ||||||||||||||||||||||||
| "(missing permission: MODEL_OVERRIDE). Please remove the model and provider " | ||||||||||||||||||||||||
| "fields from your request." | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| assert exc_info.value.status_code == status.HTTP_403_FORBIDDEN | ||||||||||||||||||||||||
| assert exc_info.value.detail["response"] == expected_msg | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -43,7 +43,8 @@ | |||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| from models.requests import QueryRequest, Attachment | ||||||||||||||||||||||||||||||||||||
| from models.config import ModelContextProtocolServer | ||||||||||||||||||||||||||||||||||||
| from models.config import ModelContextProtocolServer, Action | ||||||||||||||||||||||||||||||||||||
| from authorization.resolvers import NoopRolesResolver | ||||||||||||||||||||||||||||||||||||
| from utils.types import ToolCallSummary, TurnSummary | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| MOCK_AUTH = ("mock_user_id", "mock_username", "mock_token") | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1515,3 +1516,61 @@ async def test_retrieve_response_no_tools_false_preserves_functionality( | |||||||||||||||||||||||||||||||||||
| stream=True, | ||||||||||||||||||||||||||||||||||||
| toolgroups=expected_toolgroups, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| @pytest.mark.asyncio | ||||||||||||||||||||||||||||||||||||
| async def test_streaming_query_endpoint_rejects_model_provider_override_without_permission( | ||||||||||||||||||||||||||||||||||||
| mocker, | ||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||
| """Assert 403 when request includes model/provider without MODEL_OVERRIDE.""" | ||||||||||||||||||||||||||||||||||||
| cfg = AppConfig() | ||||||||||||||||||||||||||||||||||||
| cfg.init_from_dict( | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| "name": "test", | ||||||||||||||||||||||||||||||||||||
| "service": { | ||||||||||||||||||||||||||||||||||||
| "host": "localhost", | ||||||||||||||||||||||||||||||||||||
| "port": 8080, | ||||||||||||||||||||||||||||||||||||
| "auth_enabled": False, | ||||||||||||||||||||||||||||||||||||
| "workers": 1, | ||||||||||||||||||||||||||||||||||||
| "color_log": True, | ||||||||||||||||||||||||||||||||||||
| "access_log": True, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| "llama_stack": { | ||||||||||||||||||||||||||||||||||||
| "api_key": "test-key", | ||||||||||||||||||||||||||||||||||||
| "url": "http://test.com:1234", | ||||||||||||||||||||||||||||||||||||
| "use_as_library_client": False, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| "user_data_collection": {"transcripts_enabled": False}, | ||||||||||||||||||||||||||||||||||||
| "mcp_servers": [], | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| mocker.patch("app.endpoints.streaming_query.configuration", cfg) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # Patch authorization to exclude MODEL_OVERRIDE from authorized actions | ||||||||||||||||||||||||||||||||||||
| access_resolver = mocker.Mock() | ||||||||||||||||||||||||||||||||||||
| access_resolver.check_access.return_value = True | ||||||||||||||||||||||||||||||||||||
| access_resolver.get_actions.return_value = set(Action) - {Action.MODEL_OVERRIDE} | ||||||||||||||||||||||||||||||||||||
| mocker.patch( | ||||||||||||||||||||||||||||||||||||
| "authorization.middleware.get_authorization_resolvers", | ||||||||||||||||||||||||||||||||||||
| return_value=(NoopRolesResolver(), access_resolver), | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # Build a query request that tries to override model/provider | ||||||||||||||||||||||||||||||||||||
| query_request = QueryRequest(query="What?", model="m", provider="p") | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| request = Request( | ||||||||||||||||||||||||||||||||||||
| scope={ | ||||||||||||||||||||||||||||||||||||
| "type": "http", | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| with pytest.raises(HTTPException) as exc_info: | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1561
to
+1567
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Set request.state.authorized_actions in the test to avoid AttributeError and ensure 403 path. Apply this diff after creating the Request: request = Request(
scope={
"type": "http",
}
)
+ # Simulate middleware-provided actions for this test
+ request.state.authorized_actions = set(Action) - {Action.MODEL_OVERRIDE}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| await streaming_query_endpoint_handler(request, query_request, auth=MOCK_AUTH) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| expected_msg = ( | ||||||||||||||||||||||||||||||||||||
| "This instance does not permit overriding model/provider in the query request " | ||||||||||||||||||||||||||||||||||||
| "(missing permission: MODEL_OVERRIDE). Please remove the model and provider " | ||||||||||||||||||||||||||||||||||||
| "fields from your request." | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| assert exc_info.value.status_code == status.HTTP_403_FORBIDDEN | ||||||||||||||||||||||||||||||||||||
| assert exc_info.value.detail["response"] == expected_msg | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing this!