mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-24 14:23:59 -05:00
fix: enforce action availability and model access on the chat action route (#27243)
The chat action route loaded a Function by its raw action_id and executed its action callable after only checking that the id and the requested model existed. The model list that the client renders actions from resolves each model's actions to the active action-type Functions that are global or assigned to that model, and the action route did not mirror that resolution, so a disabled, unassigned, or wrong-type Function, or an action on a model the caller cannot access, could be reached by calling the route directly. Gate the route on the same rules the model resolution applies: the Function must be an active action, and for server-resolved models the caller must have model access and the action must be one the model actually surfaces (matched by function id, the prefix of each model actions entry, so single and sub-actions both resolve). Direct connections carry a client-supplied model the caller already owns, so the model-bound checks are scoped to non-direct calls; the active-action check always applies. Executing admin-authored Function code remains intended behaviour — this only keeps the route consistent with which actions each model exposes. Co-authored-by: komyunghan <komyunghan@users.noreply.github.com>
This commit is contained in:
@@ -9,7 +9,7 @@ from open_webui.models.functions import Functions
|
||||
from open_webui.models.users import UserModel
|
||||
from open_webui.socket.main import get_event_call, get_event_emitter
|
||||
from open_webui.utils.middleware import process_tool_result
|
||||
from open_webui.utils.models import get_all_models
|
||||
from open_webui.utils.models import check_model_access, get_all_models
|
||||
from open_webui.utils.plugin import get_function_module_from_cache
|
||||
|
||||
logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL)
|
||||
@@ -46,6 +46,23 @@ async def chat_action(request: Request, action_id: str, form_data: dict, user: A
|
||||
raise Exception('Model not found')
|
||||
model = models[model_id]
|
||||
|
||||
# Availability gate — keep this route consistent with the actions a model
|
||||
# actually surfaces to the client. Executing admin-authored Function code is
|
||||
# intended; this only stops a disabled, unassigned, or access-restricted
|
||||
# action from being reached by calling the route with a raw action_id.
|
||||
if action.type != 'action' or not action.is_active:
|
||||
raise Exception(f'Action not available: {action_id}')
|
||||
|
||||
# Direct connections carry a client-supplied model the caller already owns,
|
||||
# so scope the model-bound checks to server-resolved models.
|
||||
if not getattr(request.state, 'direct', False) and user.role != 'admin':
|
||||
await check_model_access(user, model)
|
||||
# model['actions'] entries are '<function_id>' or '<function_id>.<sub_id>';
|
||||
# the function id is always the prefix.
|
||||
surfaced_action_ids = {item.get('id', '').split('.', 1)[0] for item in model.get('actions', [])}
|
||||
if action_id not in surfaced_action_ids:
|
||||
raise Exception(f'Action not available: {action_id}')
|
||||
|
||||
__event_emitter__ = await get_event_emitter(
|
||||
{
|
||||
'chat_id': data['chat_id'],
|
||||
|
||||
Reference in New Issue
Block a user