From e038bab66dec8d17212eec35b5cb6d6b785a4200 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Wed, 17 Jun 2026 03:05:41 +0200 Subject: [PATCH] Authorize POST /api/v1/images/edit (enforce ENABLE_IMAGE_EDIT + image-gen permission) (#26009) The direct image-edit route was the only image-edit surface with no authorization: it ran on get_verified_user alone, while POST /generations enforces ENABLE_IMAGE_GENERATION + features.image_generation and the built-in edit_image tool enforces ENABLE_IMAGE_EDIT + features.image_generation. A verified non-admin user could therefore reach the configured image-edit provider (spending IMAGES_EDIT_OPENAI_API_KEY) even when the administrator had globally disabled image editing (ENABLE_IMAGE_EDIT=False) or denied the user image generation. Split the route from the shared impl (mirroring generate_images/image_generations): the new /edit wrapper enforces ENABLE_IMAGE_EDIT and the per-user image-generation permission, then delegates to image_edits(). The internal callers (the edit_image tool and the chat middleware) already gate themselves and call image_edits() directly, so they are unaffected. Co-authored-by: jagstack <52110932+jagstack@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) --- backend/open_webui/routers/images.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/backend/open_webui/routers/images.py b/backend/open_webui/routers/images.py index 465893580b..951fd905e7 100644 --- a/backend/open_webui/routers/images.py +++ b/backend/open_webui/routers/images.py @@ -768,6 +768,28 @@ class EditImageForm(BaseModel): @router.post('/edit') +async def edit_images(request: Request, form_data: EditImageForm, user=Depends(get_verified_user)): + # Authorize the direct route like /generations and the edit_image tool: enforce the + # global image-edit switch and the per-user image-generation permission. The internal + # callers (edit_image tool, chat middleware) gate themselves and call image_edits() + # directly, so they are unaffected by this wrapper. + if not request.app.state.config.ENABLE_IMAGE_EDIT: + raise HTTPException( + status_code=403, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED, + ) + + if user.role != 'admin' and not await has_permission( + user.id, 'features.image_generation', request.app.state.config.USER_PERMISSIONS + ): + raise HTTPException( + status_code=403, + detail=ERROR_MESSAGES.ACCESS_PROHIBITED, + ) + + return await image_edits(request, form_data, user=user) + + async def image_edits( request: Request, form_data: EditImageForm,