Gate scheduled automations and fail closed on per-model access for non-user roles (#26047)

Two lifecycle/authorization gaps let a deactivated (pending) account keep acting:

1. The background automation scheduler (execute_automation) rehydrated the owner by ID and
   dispatched the chat pipeline without re-checking the owner. A user later set to pending,
   or one whose features.automations permission was revoked, kept running scheduled
   automations on the operator's provider credentials, even though the HTTP create/update/run
   routes already gate on get_verified_user + features.automations. Re-gate the rehydrated
   owner before dispatch: require role user/admin and, for non-admins, the features.automations
   permission; otherwise record an error and skip the run.

2. check_model_access enforced per-model ACLs only for exactly role == 'user', so any other
   non-admin role (a pending principal) fell through and was granted access. Enforce for every
   non-admin role (admins still bypass), so the check fails closed (CWE-862, CWE-863).

Co-authored-by: rexpository <30176934+rexpository@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Classic298
2026-06-16 22:45:27 +02:00
committed by GitHub
co-authored by rexpository Claude Opus 4.8
parent 05098d25a5
commit 920b655f46
2 changed files with 12 additions and 1 deletions
@@ -321,7 +321,8 @@ async def check_model_access(
return
if model_info:
if user.role == 'user':
# Enforce for every non-admin role (including pending); never fail open.
if user.role != 'admin':
from open_webui.models.access_grants import AccessGrants
user_group_ids = {group.id for group in await Groups.get_groups_by_member_id(user.id)}
+10
View File
@@ -359,6 +359,16 @@ async def execute_automation(app, automation: AutomationModel) -> None:
await _record_run(automation.id, 'error', error='User not found')
return
# Re-gate the rehydrated owner: a demoted/deactivated or de-permissioned owner must not run.
from open_webui.utils.access_control import has_permission
if user.role not in ('user', 'admin') or (
user.role != 'admin'
and not await has_permission(user.id, 'features.automations', app.state.config.USER_PERMISSIONS)
):
await _record_run(automation.id, 'error', error='Owner no longer permitted to run automations')
return
prompt = await prompt_template(automation.data['prompt'], user)
model_id = automation.data['model_id']
terminal_config = automation.data.get('terminal')