From 402881b621729630c13afe7f680dc4e51006ca1d Mon Sep 17 00:00:00 2001 From: DrMelone <27028174+Classic298@users.noreply.github.com> Date: Mon, 13 Apr 2026 01:46:12 +0200 Subject: [PATCH] fix: fold admin bypass into runtime base-model checks in openai/ollama MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review feedback: the runtime check_base_model_access calls in routers/openai.py and routers/ollama.py only considered request.state.bypass_filter and BYPASS_MODEL_ACCESS_CONTROL, omitting the admin + BYPASS_ADMIN_ACCESS_CONTROL term that main.py, functions.py, and routers/models.py already threaded in. Because check_base_model_access enforces grants for every role when bypass_filter is False, admins in BYPASS_ADMIN_ACCESS_CONTROL deployments could be blocked from chained bases they did not own or hold a grant on — a regression relative to their behavior on non-chained models. Fold the admin bypass term into the effective filter at every runtime call site: the native ollama and openai chat completion paths, plus the four secondary ollama paths (openai completion, openai chat completion, anthropic messages, responses). --- backend/open_webui/routers/ollama.py | 33 +++++++++++++++++++++++----- backend/open_webui/routers/openai.py | 9 +++++++- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/backend/open_webui/routers/ollama.py b/backend/open_webui/routers/ollama.py index 406a1c4213..4bb16922d0 100644 --- a/backend/open_webui/routers/ollama.py +++ b/backend/open_webui/routers/ollama.py @@ -63,6 +63,7 @@ from open_webui.utils.payload import ( ) from open_webui.utils.auth import get_admin_user, get_verified_user from open_webui.config import ( + BYPASS_ADMIN_ACCESS_CONTROL, UPLOAD_DIR, ) from open_webui.env import ( @@ -1106,7 +1107,13 @@ async def generate_chat_completion( # Re-run the access check against the resolved base model. The check # on the user-facing wrapper does not authorize the chain target: # grants or ownership on the wrapper must not leak into the base. - await check_base_model_access(user, base_model_id, bypass_filter) + # Fold admin bypass in so admins behave consistently with the other + # new call sites (main.py, functions.py, models.py). + await check_base_model_access( + user, + base_model_id, + bypass_filter or (user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL), + ) params = model_info.params.model_dump() @@ -1198,7 +1205,11 @@ async def generate_openai_completion( model_id = form_data.model model_info = await Models.get_model_by_id(model_id) - base_bypass_filter = getattr(request.state, 'bypass_filter', False) or BYPASS_MODEL_ACCESS_CONTROL + base_bypass_filter = ( + getattr(request.state, 'bypass_filter', False) + or BYPASS_MODEL_ACCESS_CONTROL + or (user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL) + ) if model_info: if model_info.base_model_id: payload['model'] = model_info.base_model_id @@ -1262,7 +1273,11 @@ async def generate_openai_chat_completion( model_id = completion_form.model model_info = await Models.get_model_by_id(model_id) - base_bypass_filter = getattr(request.state, 'bypass_filter', False) or BYPASS_MODEL_ACCESS_CONTROL + base_bypass_filter = ( + getattr(request.state, 'bypass_filter', False) + or BYPASS_MODEL_ACCESS_CONTROL + or (user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL) + ) if model_info: if model_info.base_model_id: payload['model'] = model_info.base_model_id @@ -1324,7 +1339,11 @@ async def generate_anthropic_messages( model_id = payload.get('model', '') model_info = await Models.get_model_by_id(model_id) - base_bypass_filter = getattr(request.state, 'bypass_filter', False) or BYPASS_MODEL_ACCESS_CONTROL + base_bypass_filter = ( + getattr(request.state, 'bypass_filter', False) + or BYPASS_MODEL_ACCESS_CONTROL + or (user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL) + ) if model_info: if model_info.base_model_id: payload['model'] = model_info.base_model_id @@ -1384,7 +1403,11 @@ async def generate_responses( model_id = form_data.model model_info = await Models.get_model_by_id(model_id) - base_bypass_filter = getattr(request.state, 'bypass_filter', False) or BYPASS_MODEL_ACCESS_CONTROL + base_bypass_filter = ( + getattr(request.state, 'bypass_filter', False) + or BYPASS_MODEL_ACCESS_CONTROL + or (user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL) + ) if model_info: if model_info.base_model_id: payload['model'] = model_info.base_model_id diff --git a/backend/open_webui/routers/openai.py b/backend/open_webui/routers/openai.py index 65539ffca3..d34ec44948 100644 --- a/backend/open_webui/routers/openai.py +++ b/backend/open_webui/routers/openai.py @@ -34,6 +34,7 @@ from open_webui.utils.access_control import ( check_base_model_access, ) from open_webui.config import ( + BYPASS_ADMIN_ACCESS_CONTROL, CACHE_DIR, ) from open_webui.env import ( @@ -1065,7 +1066,13 @@ async def generate_chat_completion( # Re-run the access check against the resolved base model. The check # on the user-facing wrapper does not authorize the chain target: # grants or ownership on the wrapper must not leak into the base. - await check_base_model_access(user, base_model_id, bypass_filter) + # Fold admin bypass into the filter so admins mirror the behaviour + # of the other new call sites (main.py, functions.py, models.py). + await check_base_model_access( + user, + base_model_id, + bypass_filter or (user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL), + ) params = model_info.params.model_dump()