fix: enforce automation limits in the builtin automation tools (#27523)

The `create_automation` and `update_automation` builtin tools wrote straight to `Automations.insert` / `Automations.update_by_id`, skipping the limit checks that `/api/v1/automations/create` and `/api/v1/automations/{id}/update` run through `check_automation_limits`. A non-admin user could therefore ask the model to create automations indefinitely, ignoring `AUTOMATION_MAX_COUNT`, and could schedule them below `AUTOMATION_MIN_INTERVAL`, on both create and update.

Both tools now call the same `check_automation_limits` helper the routers use, so the limits and the admin bypass cannot drift between the chat path and the HTTP path. A rejection is returned to the model as a plain error message instead of raising. `update_automation` also gained the missing user lookup guard, since the helper needs the user's role.

The `automations.enable` toggle and the `features.automations` user permission were already enforced when the tool set is assembled, so they are unaffected.

Fixes #27121
This commit is contained in:
Classic298
2026-07-26 23:55:56 +02:00
committed by GitHub
parent 3ce734c6c6
commit 076a84e3f0
+17 -3
View File
@@ -14,7 +14,7 @@ import logging
import time
from typing import Literal, Optional
from fastapi import Request
from fastapi import HTTPException, Request
from open_webui.models.channels import Channel, ChannelMember, Channels
from open_webui.models.chats import Chats
@@ -3292,6 +3292,7 @@ async def create_automation(
try:
from open_webui.models.automations import AutomationData, AutomationForm, Automations
from open_webui.models.users import Users
from open_webui.routers.automations import check_automation_limits
from open_webui.utils.automations import next_n_runs_ns, next_run_ns, validate_rrule
user_id = __user__.get('id')
@@ -3313,6 +3314,11 @@ async def create_automation(
except ValueError as e:
return json.dumps({'error': f'Invalid schedule: {e}'})
try:
await check_automation_limits(__request__, user, rrule, None, is_create=True)
except HTTPException as e:
return json.dumps({'error': e.detail})
tz = user.timezone
form = AutomationForm(
name=name,
@@ -3370,10 +3376,13 @@ async def update_automation(
try:
from open_webui.models.automations import AutomationData, AutomationForm, Automations
from open_webui.models.users import Users
from open_webui.routers.automations import check_automation_limits
from open_webui.utils.automations import next_n_runs_ns, next_run_ns, validate_rrule
user_id = __user__.get('id')
user = await Users.get_user_by_id(user_id)
if not user:
return json.dumps({'error': 'User not found'})
automation = await Automations.get_by_id(automation_id)
if not automation:
@@ -3390,11 +3399,16 @@ async def update_automation(
# Validate RRULE if changed
if rrule is not None:
try:
validate_rrule(new_rrule, tz=user.timezone if user else None)
validate_rrule(new_rrule, tz=user.timezone)
except ValueError as e:
return json.dumps({'error': f'Invalid schedule: {e}'})
tz = user.timezone if user else None
try:
await check_automation_limits(__request__, user, new_rrule, None)
except HTTPException as e:
return json.dumps({'error': e.detail})
tz = user.timezone
form = AutomationForm(
name=new_name,
data=AutomationData(